summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-05-16 18:45:44 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2024-05-16 18:45:44 +0300
commit6fd600d742744dc7ef7fc65ca26daa2b1163158a (patch)
tree24f1262aa7d564eac47de264ce5f9260d889dbb9
parent972a2543e3dd87f7310d65944b857631b4290e12 (diff)
parent8771b7f31b7fff91a998e6afdb60650d4bac59a5 (diff)
downloadlinux-6fd600d742744dc7ef7fc65ca26daa2b1163158a.tar.xz
Merge tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - New V4L2 ioctl VIDIOC_REMOVE_BUFS - experimental support for using generic metaformats on V4L2 core - New drivers: Intel IPU6 controller driver, Broadcom BCM283x/BCM271x - More cleanups at atomisp driver - Usual bunch of driver cleanups, improvements and fixes * tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (328 commits) media: bcm2835-unicam: Depend on COMMON_CLK Revert "media: v4l2-ctrls: show all owned controls in log_status" media: ov2740: Ensure proper reset sequence on probe() media: intel/ipu6: Don't print user-triggerable errors to kernel log media: bcm2835-unicam: Fix driver path in MAINTAINERS media: bcm2835-unicam: Fix a NULL vs IS_ERR() check media: bcm2835-unicam: Do not print error when irq not found media: bcm2835-unicam: Do not replace IRQ retcode during probe media: bcm2835-unicam: Convert to platform remove callback returning void media: media: intel/ipu6: Fix spelling mistake "remappinp" -> "remapping" media: intel/ipu6: explicitly include vmalloc.h media: cec.h: Fix kerneldoc media: uvcvideo: Refactor iterators media: v4l: async: refactor v4l2_async_create_ancillary_links media: intel/ipu6: Don't re-allocate memory for firmware media: dvb-frontends: tda10048: Fix integer overflow media: tc358746: Use the correct div_ function media: i2c: st-mipid02: Use the correct div function media: tegra-vde: Refactor timeout handling media: stk1160: Use min macro ...
-rw-r--r--Documentation/admin-guide/media/ipu6-isys.rst161
-rw-r--r--Documentation/admin-guide/media/ipu6_isys_graph.svg548
-rw-r--r--Documentation/admin-guide/media/mgb4.rst35
-rw-r--r--Documentation/admin-guide/media/v4l-drivers.rst1
-rw-r--r--Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml127
-rw-r--r--Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml35
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml (renamed from Documentation/devicetree/bindings/media/i2c/ov8856.yaml)2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml5
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml11
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml512
-rw-r--r--Documentation/driver-api/media/drivers/index.rst1
-rw-r--r--Documentation/driver-api/media/drivers/ipu6.rst205
-rw-r--r--Documentation/userspace-api/media/cec/cec-func-open.rst4
-rw-r--r--Documentation/userspace-api/media/dvb/frontend_f_open.rst2
-rw-r--r--Documentation/userspace-api/media/glossary.rst12
-rw-r--r--Documentation/userspace-api/media/v4l/dev-meta.rst21
-rw-r--r--Documentation/userspace-api/media/v4l/dev-subdev.rst31
-rw-r--r--Documentation/userspace-api/media/v4l/func-open.rst4
-rw-r--r--Documentation/userspace-api/media/v4l/meta-formats.rst3
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-generic.rst340
-rw-r--r--Documentation/userspace-api/media/v4l/mmap.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst48
-rw-r--r--Documentation/userspace-api/media/v4l/subdev-formats.rst269
-rw-r--r--Documentation/userspace-api/media/v4l/user-func.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst7
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst86
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst6
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst51
-rw-r--r--Documentation/userspace-api/media/videodev2.h.rst.exceptions1
-rw-r--r--MAINTAINERS19
-rw-r--r--drivers/media/cec/core/cec-adap.c24
-rw-r--r--drivers/media/cec/core/cec-api.c5
-rw-r--r--drivers/media/cec/core/cec-core.c4
-rw-r--r--drivers/media/cec/platform/cros-ec/cros-ec-cec.c9
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c8
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c231
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c34
-rw-r--r--drivers/media/dvb-core/dvbdev.c2
-rw-r--r--drivers/media/dvb-frontends/as102_fe_types.h2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Kconfig2
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h2
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c58
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c5
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c7
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.c22
-rw-r--r--drivers/media/dvb-frontends/si2165.c6
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c2
-rw-r--r--drivers/media/dvb-frontends/tda10048.c9
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c4
-rw-r--r--drivers/media/i2c/Kconfig2
-rw-r--r--drivers/media/i2c/adv7180.c2
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c16
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c16
-rw-r--r--drivers/media/i2c/adv7604.c20
-rw-r--r--drivers/media/i2c/adv7842.c25
-rw-r--r--drivers/media/i2c/dw9714.c6
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c4
-rw-r--r--drivers/media/i2c/hi556.c105
-rw-r--r--drivers/media/i2c/imx214.c1
-rw-r--r--drivers/media/i2c/imx219.c77
-rw-r--r--drivers/media/i2c/imx335.c637
-rw-r--r--drivers/media/i2c/max9271.h5
-rw-r--r--drivers/media/i2c/ov2680.c103
-rw-r--r--drivers/media/i2c/ov2740.c20
-rw-r--r--drivers/media/i2c/ov4689.c673
-rw-r--r--drivers/media/i2c/rdacm20.c4
-rw-r--r--drivers/media/i2c/st-mipid02.c2
-rw-r--r--drivers/media/i2c/tc358743.c25
-rw-r--r--drivers/media/i2c/tc358746.c3
-rw-r--r--drivers/media/i2c/tda1997x.c14
-rw-r--r--drivers/media/i2c/ths7303.c10
-rw-r--r--drivers/media/i2c/ths8200.c14
-rw-r--r--drivers/media/i2c/tvp7002.c32
-rw-r--r--drivers/media/mc/mc-devnode.c5
-rw-r--r--drivers/media/mc/mc-entity.c6
-rw-r--r--drivers/media/mmc/siano/smssdio.c25
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c12
-rw-r--r--drivers/media/pci/intel/Kconfig4
-rw-r--r--drivers/media/pci/intel/Makefile1
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c66
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c29
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h4
-rw-r--r--drivers/media/pci/intel/ipu6/Kconfig18
-rw-r--r--drivers/media/pci/intel/ipu6/Makefile23
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.c165
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.h58
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.c917
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.h92
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.c362
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.h105
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.c502
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.h19
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.c413
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.h47
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.c487
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.h596
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c663
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h82
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c536
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c242
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c720
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.c810
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.h78
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c403
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h59
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c1420
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.h141
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.c1367
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h206
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.c846
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.h73
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h226
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h172
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-regs.h179
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c856
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.h342
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c20
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c4
-rw-r--r--drivers/media/pci/mgb4/mgb4_regs.c2
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c2
-rw-r--r--drivers/media/pci/ngene/ngene-core.c4
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c9
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-core.c16
-rw-r--r--drivers/media/pci/ttpci/budget-av.c573
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c495
-rw-r--r--drivers/media/pci/ttpci/budget-core.c38
-rw-r--r--drivers/media/pci/ttpci/budget.c173
-rw-r--r--drivers/media/pci/ttpci/budget.h21
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/broadcom/Kconfig23
-rw-r--r--drivers/media/platform/broadcom/Makefile3
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam-regs.h246
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c2739
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c26
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c17
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c13
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c13
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c125
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h4
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c10
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c6
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h2
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c23
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c42
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h15
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c14
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h2
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c6
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c34
-rw-r--r--drivers/media/platform/qcom/camss/Makefile2
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c108
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c1
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-17x.c (renamed from drivers/media/platform/qcom/camss/camss-vfe-170.c)0
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c25
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c1
-rw-r--r--drivers/media/platform/qcom/camss/camss.c307
-rw-r--r--drivers/media/platform/qcom/camss/camss.h1
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c9
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h2
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Kconfig1
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Makefile7
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c3
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h5
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.c3
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c7
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig1
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c4
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c2
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c5
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_regs.h4
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c1
-rw-r--r--drivers/media/radio/radio-shark2.c2
-rw-r--r--drivers/media/rc/gpio-ir-recv.c1
-rw-r--r--drivers/media/rc/imon.c12
-rw-r--r--drivers/media/rc/ir-spi.c41
-rw-r--r--drivers/media/rc/lirc_dev.c18
-rw-r--r--drivers/media/spi/cxd2880-spi.c2
-rw-r--r--drivers/media/spi/gs1662.c27
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c1
-rw-r--r--drivers/media/test-drivers/vimc/vimc-capture.c3
-rw-r--r--drivers/media/test-drivers/visl/visl-video.c1
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c9
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-out.c4
-rw-r--r--drivers/media/test-drivers/vivid/vivid-touch-cap.c4
-rw-r--r--drivers/media/tuners/xc5000.c39
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c2
-rw-r--r--drivers/media/usb/au0828/au0828-video.c5
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.c17
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c10
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c4
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c18
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c462
-rw-r--r--drivers/media/usb/go7007/go7007-fw.c4
-rw-r--r--drivers/media/usb/gspca/cpia1.c6
-rw-r--r--drivers/media/usb/s2255/s2255drv.c20
-rw-r--r--drivers/media/usb/siano/smsusb.c20
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c30
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c26
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c54
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h2
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c30
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c33
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c24
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c12
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c73
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c15
-rw-r--r--drivers/media/v4l2-core/v4l2-spi.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c113
-rw-r--r--drivers/staging/media/atomisp/Makefile1
-rw-r--r--drivers/staging/media/atomisp/i2c/Kconfig15
-rw-r--r--drivers/staging/media/atomisp/i2c/Makefile5
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c2
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-lm3554.c955
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c4
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c14
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2722.h1
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp.h81
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h6
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_platform.h41
-rw-r--r--drivers/staging/media/atomisp/include/media/lm3554.h132
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c281
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.h13
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.c31
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.c26
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.c49
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c123
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_internal.h19
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c493
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c102
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.h17
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tpg.c164
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tpg.h39
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c183
-rw-r--r--drivers/staging/media/atomisp/pci/bits.h4
-rw-r--r--drivers/staging/media/atomisp/pci/defs.h37
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h1
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c38
-rw-r--r--drivers/staging/media/atomisp/pci/hive_types.h19
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css.h1
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_frame_public.h8
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_stream_public.h17
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_tpg.h79
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_global.h1
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_public.h15
-rw-r--r--drivers/staging/media/atomisp/pci/isp2401_input_system_global.h1
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c20
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c28
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css.c138
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_internal.h1
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mipi.c2
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.c127
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.h7
-rw-r--r--drivers/staging/media/atomisp/pci/system_global.h12
-rw-r--r--drivers/staging/media/imx/Kconfig1
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.c4
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.h2
-rw-r--r--drivers/staging/media/ipu3/ipu3-css.c1
-rw-r--r--drivers/staging/media/ipu3/ipu3.c10
-rw-r--r--drivers/staging/media/max96712/max96712.c2
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.c12
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.c10
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c3
-rw-r--r--drivers/staging/media/tegra-video/tegra20.c10
-rw-r--r--drivers/staging/media/tegra-video/vi.c12
-rw-r--r--include/media/cec.h4
-rw-r--r--include/media/ipu6-pci-table.h28
-rw-r--r--include/media/media-device.h6
-rw-r--r--include/media/v4l2-async.h4
-rw-r--r--include/media/v4l2-device.h7
-rw-r--r--include/media/v4l2-ioctl.h4
-rw-r--r--include/media/v4l2-mem2mem.h2
-rw-r--r--include/media/v4l2-subdev.h70
-rw-r--r--include/media/videobuf2-core.h52
-rw-r--r--include/media/videobuf2-v4l2.h2
-rw-r--r--include/uapi/linux/dvb/frontend.h2
-rw-r--r--include/uapi/linux/media-bus-format.h9
-rw-r--r--include/uapi/linux/v4l2-mediabus.h18
-rw-r--r--include/uapi/linux/v4l2-subdev.h14
-rw-r--r--include/uapi/linux/videodev2.h45
307 files changed, 22908 insertions, 5428 deletions
diff --git a/Documentation/admin-guide/media/ipu6-isys.rst b/Documentation/admin-guide/media/ipu6-isys.rst
new file mode 100644
index 000000000000..0721e920b5e6
--- /dev/null
+++ b/Documentation/admin-guide/media/ipu6-isys.rst
@@ -0,0 +1,161 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+========================================================
+Intel Image Processing Unit 6 (IPU6) Input System driver
+========================================================
+
+Copyright |copy| 2023--2024 Intel Corporation
+
+Introduction
+============
+
+This file documents the Intel IPU6 (6th generation Image Processing Unit)
+Input System (MIPI CSI2 receiver) drivers located under
+drivers/media/pci/intel/ipu6.
+
+The Intel IPU6 can be found in certain Intel SoCs but not in all SKUs:
+
+* Tiger Lake
+* Jasper Lake
+* Alder Lake
+* Raptor Lake
+* Meteor Lake
+
+Intel IPU6 is made up of two components - Input System (ISYS) and Processing
+System (PSYS).
+
+The Input System mainly works as MIPI CSI-2 receiver which receives and
+processes the image data from the sensors and outputs the frames to memory.
+
+There are 2 driver modules - intel-ipu6 and intel-ipu6-isys. intel-ipu6 is an
+IPU6 common driver which does PCI configuration, firmware loading and parsing,
+firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit)
+configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2
+sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected
+to the IPU6 ISYS through V4L2 sub-device sensor drivers.
+
+.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more
+ information about the IPU6 hardware.
+
+Input system driver
+===================
+
+The Input System driver mainly configures CSI-2 D-PHY, constructs the firmware
+stream configuration, sends commands to firmware, gets response from hardware
+and firmware and then returns buffers to user. The ISYS is represented as
+several V4L2 sub-devices as well as video nodes.
+
+.. kernel-figure:: ipu6_isys_graph.svg
+ :alt: ipu6 isys media graph with multiple streams support
+
+ IPU6 ISYS media graph with multiple streams support
+
+The graph has been produced using the following command:
+
+.. code-block:: none
+
+ fdp -Gsplines=true -Tsvg < dot > dot.svg
+
+Capturing frames with IPU6 ISYS
+-------------------------------
+
+IPU6 ISYS is used to capture frames from the camera sensors connected to the
+CSI2 ports. The supported input formats of ISYS are listed in table below:
+
+.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}|
+
+.. flat-table::
+ :header-rows: 1
+
+ * - IPU6 ISYS supported input formats
+
+ * - RGB565, RGB888
+
+ * - UYVY8, YUYV8
+
+ * - RAW8, RAW10, RAW12
+
+.. _ipu6_isys_capture_examples:
+
+Examples
+~~~~~~~~
+
+Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this
+machine, ov01a10 sensor is connected to IPU ISYS CSI-2 port 2, which can
+generate images at sBGGR10 with resolution 1280x800.
+
+Using the media controller APIs, we can configure ov01a10 sensor by
+media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS.
+
+.. code-block:: none
+
+ # Example 1 capture frame from ov01a10 camera sensor
+ # This example assumes /dev/media0 as the IPU ISYS media device
+ export MDEV=/dev/media0
+
+ # Establish the link for the media devices using media-ctl
+ media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]"
+
+ # Set the format for the media devices
+ media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]"
+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]"
+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]"
+
+Once the media pipeline is configured, desired sensor specific settings
+(such as exposure and gain settings) can be set, using the yavta tool.
+
+e.g
+
+.. code-block:: none
+
+ # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36
+ export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036")
+
+ yavta -w 0x009e0903 400 $SDEV
+ yavta -w 0x009e0913 1000 $SDEV
+ yavta -w 0x009e0911 2000 $SDEV
+
+Once the desired sensor settings are set, frame captures can be done as below.
+
+e.g
+
+.. code-block:: none
+
+ yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \
+ -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0")
+
+With the above command, 10 frames are captured at 1280x800 resolution with
+sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files.
+
+Here is another example of IPU6 ISYS RAW and metadata capture from camera
+sensor ov2740 on Lenovo X1 Yoga laptop.
+
+.. code-block:: none
+
+ media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]"
+ media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]"
+ media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]"
+
+ # set routing
+ media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]"
+
+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]"
+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]"
+ media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]"
+ media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]"
+
+ CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0")
+ ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \
+ -f SGRBG10 ${CAPTURE_DEV}
+
+ CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1")
+ ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \
+ --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META}
+
+References
+==========
+
+.. [#f1] https://git.ideasonboard.org/media-ctl.git
+.. [#f2] https://git.ideasonboard.org/yavta.git
diff --git a/Documentation/admin-guide/media/ipu6_isys_graph.svg b/Documentation/admin-guide/media/ipu6_isys_graph.svg
new file mode 100644
index 000000000000..c8539ef320d2
--- /dev/null
+++ b/Documentation/admin-guide/media/ipu6_isys_graph.svg
@@ -0,0 +1,548 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.43.0 (0)
+ -->
+<!-- Title: board Pages: 1 -->
+<svg width="1703pt" height="1473pt"
+ viewBox="0.00 0.00 1703.00 1473.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1469)">
+<title>board</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-1469 1699,-1469 1699,4 -4,4"/>
+<!-- n00000001 -->
+<g id="node1" class="node">
+<title>n00000001</title>
+<polygon fill="yellow" stroke="black" points="832.99,-750.08 629.99,-750.08 629.99,-712.08 832.99,-712.08 832.99,-750.08"/>
+<text text-anchor="middle" x="731.49" y="-734.88" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text>
+<text text-anchor="middle" x="731.49" y="-719.88" font-family="Times,serif" font-size="14.00">/dev/video0</text>
+</g>
+<!-- n00000005 -->
+<g id="node2" class="node">
+<title>n00000005</title>
+<polygon fill="yellow" stroke="black" points="1396.59,-771.88 1193.59,-771.88 1193.59,-733.88 1396.59,-733.88 1396.59,-771.88"/>
+<text text-anchor="middle" x="1295.09" y="-756.68" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text>
+<text text-anchor="middle" x="1295.09" y="-741.68" font-family="Times,serif" font-size="14.00">/dev/video1</text>
+</g>
+<!-- n00000009 -->
+<g id="node3" class="node">
+<title>n00000009</title>
+<polygon fill="yellow" stroke="black" points="1118.52,-690.47 915.52,-690.47 915.52,-652.47 1118.52,-652.47 1118.52,-690.47"/>
+<text text-anchor="middle" x="1017.02" y="-675.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text>
+<text text-anchor="middle" x="1017.02" y="-660.27" font-family="Times,serif" font-size="14.00">/dev/video2</text>
+</g>
+<!-- n0000000d -->
+<g id="node4" class="node">
+<title>n0000000d</title>
+<polygon fill="yellow" stroke="black" points="1105.89,-838.84 902.89,-838.84 902.89,-800.84 1105.89,-800.84 1105.89,-838.84"/>
+<text text-anchor="middle" x="1004.39" y="-823.64" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text>
+<text text-anchor="middle" x="1004.39" y="-808.64" font-family="Times,serif" font-size="14.00">/dev/video3</text>
+</g>
+<!-- n00000011 -->
+<g id="node5" class="node">
+<title>n00000011</title>
+<polygon fill="yellow" stroke="black" points="1279.22,-992.95 1076.22,-992.95 1076.22,-954.95 1279.22,-954.95 1279.22,-992.95"/>
+<text text-anchor="middle" x="1177.72" y="-977.75" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 4</text>
+<text text-anchor="middle" x="1177.72" y="-962.75" font-family="Times,serif" font-size="14.00">/dev/video4</text>
+</g>
+<!-- n00000015 -->
+<g id="node6" class="node">
+<title>n00000015</title>
+<polygon fill="yellow" stroke="black" points="939.18,-984.91 736.18,-984.91 736.18,-946.91 939.18,-946.91 939.18,-984.91"/>
+<text text-anchor="middle" x="837.68" y="-969.71" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 5</text>
+<text text-anchor="middle" x="837.68" y="-954.71" font-family="Times,serif" font-size="14.00">/dev/video5</text>
+</g>
+<!-- n00000019 -->
+<g id="node7" class="node">
+<title>n00000019</title>
+<polygon fill="yellow" stroke="black" points="957.87,-527.99 754.87,-527.99 754.87,-489.99 957.87,-489.99 957.87,-527.99"/>
+<text text-anchor="middle" x="856.37" y="-512.79" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 6</text>
+<text text-anchor="middle" x="856.37" y="-497.79" font-family="Times,serif" font-size="14.00">/dev/video6</text>
+</g>
+<!-- n0000001d -->
+<g id="node8" class="node">
+<title>n0000001d</title>
+<polygon fill="yellow" stroke="black" points="1291.02,-542.15 1088.02,-542.15 1088.02,-504.15 1291.02,-504.15 1291.02,-542.15"/>
+<text text-anchor="middle" x="1189.52" y="-526.95" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 7</text>
+<text text-anchor="middle" x="1189.52" y="-511.95" font-family="Times,serif" font-size="14.00">/dev/video7</text>
+</g>
+<!-- n00000021 -->
+<g id="node9" class="node">
+<title>n00000021</title>
+<polygon fill="yellow" stroke="black" points="202.74,-611.46 -0.26,-611.46 -0.26,-573.46 202.74,-573.46 202.74,-611.46"/>
+<text text-anchor="middle" x="101.24" y="-596.26" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 8</text>
+<text text-anchor="middle" x="101.24" y="-581.26" font-family="Times,serif" font-size="14.00">/dev/video8</text>
+</g>
+<!-- n00000025 -->
+<g id="node10" class="node">
+<title>n00000025</title>
+<polygon fill="yellow" stroke="black" points="764.86,-637.89 561.86,-637.89 561.86,-599.89 764.86,-599.89 764.86,-637.89"/>
+<text text-anchor="middle" x="663.36" y="-622.69" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 9</text>
+<text text-anchor="middle" x="663.36" y="-607.69" font-family="Times,serif" font-size="14.00">/dev/video9</text>
+</g>
+<!-- n00000029 -->
+<g id="node11" class="node">
+<title>n00000029</title>
+<polygon fill="yellow" stroke="black" points="358.62,-519.5 146.62,-519.5 146.62,-481.5 358.62,-481.5 358.62,-519.5"/>
+<text text-anchor="middle" x="252.62" y="-504.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 10</text>
+<text text-anchor="middle" x="252.62" y="-489.3" font-family="Times,serif" font-size="14.00">/dev/video10</text>
+</g>
+<!-- n0000002d -->
+<g id="node12" class="node">
+<title>n0000002d</title>
+<polygon fill="yellow" stroke="black" points="481.4,-662.59 269.4,-662.59 269.4,-624.59 481.4,-624.59 481.4,-662.59"/>
+<text text-anchor="middle" x="375.4" y="-647.39" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 11</text>
+<text text-anchor="middle" x="375.4" y="-632.39" font-family="Times,serif" font-size="14.00">/dev/video11</text>
+</g>
+<!-- n00000031 -->
+<g id="node13" class="node">
+<title>n00000031</title>
+<polygon fill="yellow" stroke="black" points="637.17,-837.47 425.17,-837.47 425.17,-799.47 637.17,-799.47 637.17,-837.47"/>
+<text text-anchor="middle" x="531.17" y="-822.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 12</text>
+<text text-anchor="middle" x="531.17" y="-807.27" font-family="Times,serif" font-size="14.00">/dev/video12</text>
+</g>
+<!-- n00000035 -->
+<g id="node14" class="node">
+<title>n00000035</title>
+<polygon fill="yellow" stroke="black" points="337.75,-833.67 125.75,-833.67 125.75,-795.67 337.75,-795.67 337.75,-833.67"/>
+<text text-anchor="middle" x="231.75" y="-818.47" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 13</text>
+<text text-anchor="middle" x="231.75" y="-803.47" font-family="Times,serif" font-size="14.00">/dev/video13</text>
+</g>
+<!-- n00000039 -->
+<g id="node15" class="node">
+<title>n00000039</title>
+<polygon fill="yellow" stroke="black" points="393.07,-317.96 181.07,-317.96 181.07,-279.96 393.07,-279.96 393.07,-317.96"/>
+<text text-anchor="middle" x="287.07" y="-302.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 14</text>
+<text text-anchor="middle" x="287.07" y="-287.76" font-family="Times,serif" font-size="14.00">/dev/video14</text>
+</g>
+<!-- n0000003d -->
+<g id="node16" class="node">
+<title>n0000003d</title>
+<polygon fill="yellow" stroke="black" points="701.46,-391.04 489.46,-391.04 489.46,-353.04 701.46,-353.04 701.46,-391.04"/>
+<text text-anchor="middle" x="595.46" y="-375.84" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 15</text>
+<text text-anchor="middle" x="595.46" y="-360.84" font-family="Times,serif" font-size="14.00">/dev/video15</text>
+</g>
+<!-- n00000041 -->
+<g id="node17" class="node">
+<title>n00000041</title>
+<polygon fill="yellow" stroke="black" points="212.45,-1228.8 0.45,-1228.8 0.45,-1190.8 212.45,-1190.8 212.45,-1228.8"/>
+<text text-anchor="middle" x="106.45" y="-1213.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 16</text>
+<text text-anchor="middle" x="106.45" y="-1198.6" font-family="Times,serif" font-size="14.00">/dev/video16</text>
+</g>
+<!-- n00000045 -->
+<g id="node18" class="node">
+<title>n00000045</title>
+<polygon fill="yellow" stroke="black" points="784.86,-1252.38 572.86,-1252.38 572.86,-1214.38 784.86,-1214.38 784.86,-1252.38"/>
+<text text-anchor="middle" x="678.86" y="-1237.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 17</text>
+<text text-anchor="middle" x="678.86" y="-1222.18" font-family="Times,serif" font-size="14.00">/dev/video17</text>
+</g>
+<!-- n00000049 -->
+<g id="node19" class="node">
+<title>n00000049</title>
+<polygon fill="yellow" stroke="black" points="503.14,-1169.96 291.14,-1169.96 291.14,-1131.96 503.14,-1131.96 503.14,-1169.96"/>
+<text text-anchor="middle" x="397.14" y="-1154.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 18</text>
+<text text-anchor="middle" x="397.14" y="-1139.76" font-family="Times,serif" font-size="14.00">/dev/video18</text>
+</g>
+<!-- n0000004d -->
+<g id="node20" class="node">
+<title>n0000004d</title>
+<polygon fill="yellow" stroke="black" points="492.62,-1319.4 280.62,-1319.4 280.62,-1281.4 492.62,-1281.4 492.62,-1319.4"/>
+<text text-anchor="middle" x="386.62" y="-1304.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 19</text>
+<text text-anchor="middle" x="386.62" y="-1289.2" font-family="Times,serif" font-size="14.00">/dev/video19</text>
+</g>
+<!-- n00000051 -->
+<g id="node21" class="node">
+<title>n00000051</title>
+<polygon fill="yellow" stroke="black" points="680.74,-1464.66 468.74,-1464.66 468.74,-1426.66 680.74,-1426.66 680.74,-1464.66"/>
+<text text-anchor="middle" x="574.74" y="-1449.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 20</text>
+<text text-anchor="middle" x="574.74" y="-1434.46" font-family="Times,serif" font-size="14.00">/dev/video20</text>
+</g>
+<!-- n00000055 -->
+<g id="node22" class="node">
+<title>n00000055</title>
+<polygon fill="yellow" stroke="black" points="302.42,-1452.56 90.42,-1452.56 90.42,-1414.56 302.42,-1414.56 302.42,-1452.56"/>
+<text text-anchor="middle" x="196.42" y="-1437.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 21</text>
+<text text-anchor="middle" x="196.42" y="-1422.36" font-family="Times,serif" font-size="14.00">/dev/video21</text>
+</g>
+<!-- n00000059 -->
+<g id="node23" class="node">
+<title>n00000059</title>
+<polygon fill="yellow" stroke="black" points="319.89,-1018.32 107.89,-1018.32 107.89,-980.32 319.89,-980.32 319.89,-1018.32"/>
+<text text-anchor="middle" x="213.89" y="-1003.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 22</text>
+<text text-anchor="middle" x="213.89" y="-988.12" font-family="Times,serif" font-size="14.00">/dev/video22</text>
+</g>
+<!-- n0000005d -->
+<g id="node24" class="node">
+<title>n0000005d</title>
+<polygon fill="yellow" stroke="black" points="692.62,-1031.39 480.62,-1031.39 480.62,-993.39 692.62,-993.39 692.62,-1031.39"/>
+<text text-anchor="middle" x="586.62" y="-1016.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 23</text>
+<text text-anchor="middle" x="586.62" y="-1001.19" font-family="Times,serif" font-size="14.00">/dev/video23</text>
+</g>
+<!-- n00000061 -->
+<g id="node25" class="node">
+<title>n00000061</title>
+<polygon fill="yellow" stroke="black" points="1122.45,-248.8 910.45,-248.8 910.45,-210.8 1122.45,-210.8 1122.45,-248.8"/>
+<text text-anchor="middle" x="1016.45" y="-233.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 24</text>
+<text text-anchor="middle" x="1016.45" y="-218.6" font-family="Times,serif" font-size="14.00">/dev/video24</text>
+</g>
+<!-- n00000065 -->
+<g id="node26" class="node">
+<title>n00000065</title>
+<polygon fill="yellow" stroke="black" points="1694.86,-272.38 1482.86,-272.38 1482.86,-234.38 1694.86,-234.38 1694.86,-272.38"/>
+<text text-anchor="middle" x="1588.86" y="-257.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 25</text>
+<text text-anchor="middle" x="1588.86" y="-242.18" font-family="Times,serif" font-size="14.00">/dev/video25</text>
+</g>
+<!-- n00000069 -->
+<g id="node27" class="node">
+<title>n00000069</title>
+<polygon fill="yellow" stroke="black" points="1413.14,-189.96 1201.14,-189.96 1201.14,-151.96 1413.14,-151.96 1413.14,-189.96"/>
+<text text-anchor="middle" x="1307.14" y="-174.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 26</text>
+<text text-anchor="middle" x="1307.14" y="-159.76" font-family="Times,serif" font-size="14.00">/dev/video26</text>
+</g>
+<!-- n0000006d -->
+<g id="node28" class="node">
+<title>n0000006d</title>
+<polygon fill="yellow" stroke="black" points="1402.62,-339.4 1190.62,-339.4 1190.62,-301.4 1402.62,-301.4 1402.62,-339.4"/>
+<text text-anchor="middle" x="1296.62" y="-324.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 27</text>
+<text text-anchor="middle" x="1296.62" y="-309.2" font-family="Times,serif" font-size="14.00">/dev/video27</text>
+</g>
+<!-- n00000071 -->
+<g id="node29" class="node">
+<title>n00000071</title>
+<polygon fill="yellow" stroke="black" points="1590.74,-484.66 1378.74,-484.66 1378.74,-446.66 1590.74,-446.66 1590.74,-484.66"/>
+<text text-anchor="middle" x="1484.74" y="-469.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 28</text>
+<text text-anchor="middle" x="1484.74" y="-454.46" font-family="Times,serif" font-size="14.00">/dev/video28</text>
+</g>
+<!-- n00000075 -->
+<g id="node30" class="node">
+<title>n00000075</title>
+<polygon fill="yellow" stroke="black" points="1212.42,-472.56 1000.42,-472.56 1000.42,-434.56 1212.42,-434.56 1212.42,-472.56"/>
+<text text-anchor="middle" x="1106.42" y="-457.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 29</text>
+<text text-anchor="middle" x="1106.42" y="-442.36" font-family="Times,serif" font-size="14.00">/dev/video29</text>
+</g>
+<!-- n00000079 -->
+<g id="node31" class="node">
+<title>n00000079</title>
+<polygon fill="yellow" stroke="black" points="1229.89,-38.32 1017.89,-38.32 1017.89,-0.32 1229.89,-0.32 1229.89,-38.32"/>
+<text text-anchor="middle" x="1123.89" y="-23.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 30</text>
+<text text-anchor="middle" x="1123.89" y="-8.12" font-family="Times,serif" font-size="14.00">/dev/video30</text>
+</g>
+<!-- n0000007d -->
+<g id="node32" class="node">
+<title>n0000007d</title>
+<polygon fill="yellow" stroke="black" points="1602.62,-51.39 1390.62,-51.39 1390.62,-13.39 1602.62,-13.39 1602.62,-51.39"/>
+<text text-anchor="middle" x="1496.62" y="-36.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 31</text>
+<text text-anchor="middle" x="1496.62" y="-21.19" font-family="Times,serif" font-size="14.00">/dev/video31</text>
+</g>
+<!-- n00000081 -->
+<g id="node33" class="node">
+<title>n00000081</title>
+<path fill="green" stroke="black" d="M924.28,-700.28C924.28,-700.28 1108.28,-700.28 1108.28,-700.28 1114.28,-700.28 1120.28,-706.28 1120.28,-712.28 1120.28,-712.28 1120.28,-772.28 1120.28,-772.28 1120.28,-778.28 1114.28,-784.28 1108.28,-784.28 1108.28,-784.28 924.28,-784.28 924.28,-784.28 918.28,-784.28 912.28,-778.28 912.28,-772.28 912.28,-772.28 912.28,-712.28 912.28,-712.28 912.28,-706.28 918.28,-700.28 924.28,-700.28"/>
+<text text-anchor="middle" x="1016.28" y="-769.08" font-family="Times,serif" font-size="14.00">0</text>
+<polyline fill="none" stroke="black" points="912.28,-761.28 1120.28,-761.28 "/>
+<text text-anchor="middle" x="1016.28" y="-746.08" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text>
+<text text-anchor="middle" x="1016.28" y="-731.08" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev0</text>
+<polyline fill="none" stroke="black" points="912.28,-723.28 1120.28,-723.28 "/>
+<text text-anchor="middle" x="925.28" y="-708.08" font-family="Times,serif" font-size="14.00">1</text>
+<polyline fill="none" stroke="black" points="938.28,-700.28 938.28,-723.28 "/>
+<text text-anchor="middle" x="951.28" y="-708.08" font-family="Times,serif" font-size="14.00">2</text>
+<polyline fill="none" stroke="black" points="964.28,-700.28 964.28,-723.28 "/>
+<text text-anchor="middle" x="977.28" y="-708.08" font-family="Times,serif" font-size="14.00">3</text>
+<polyline fill="none" stroke="black" points="990.28,-700.28 990.28,-723.28 "/>
+<text text-anchor="middle" x="1003.28" y="-708.08" font-family="Times,serif" font-size="14.00">4</text>
+<polyline fill="none" stroke="black" points="1016.28,-700.28 1016.28,-723.28 "/>
+<text text-anchor="middle" x="1029.28" y="-708.08" font-family="Times,serif" font-size="14.00">5</text>
+<polyline fill="none" stroke="black" points="1042.28,-700.28 1042.28,-723.28 "/>
+<text text-anchor="middle" x="1055.28" y="-708.08" font-family="Times,serif" font-size="14.00">6</text>
+<polyline fill="none" stroke="black" points="1068.28,-700.28 1068.28,-723.28 "/>
+<text text-anchor="middle" x="1081.28" y="-708.08" font-family="Times,serif" font-size="14.00">7</text>
+<polyline fill="none" stroke="black" points="1094.28,-700.28 1094.28,-723.28 "/>
+<text text-anchor="middle" x="1107.28" y="-708.08" font-family="Times,serif" font-size="14.00">8</text>
+</g>
+<!-- n00000081&#45;&gt;n00000001 -->
+<g id="edge1" class="edge">
+<title>n00000081:port1&#45;&gt;n00000001</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M912.28,-711.28C912.28,-711.28 880.33,-714.78 843.28,-718.84"/>
+<polygon fill="black" stroke="black" points="842.81,-715.37 833.25,-719.94 843.57,-722.33 842.81,-715.37"/>
+</g>
+<!-- n00000081&#45;&gt;n00000005 -->
+<g id="edge2" class="edge">
+<title>n00000081:port2&#45;&gt;n00000005</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M951.38,-700.28C951.38,-700.28 1086.18,-688.61 1123.48,-697.08 1155.93,-704.45 1158.99,-719.67 1190.39,-730.68 1190.49,-730.71 1190.59,-730.75 1190.69,-730.78"/>
+<polygon fill="black" stroke="black" points="1189.45,-734.06 1200.05,-733.86 1191.64,-727.41 1189.45,-734.06"/>
+</g>
+<!-- n00000081&#45;&gt;n00000009 -->
+<g id="edge3" class="edge">
+<title>n00000081:port3&#45;&gt;n00000009</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M977.28,-700.28C977.28,-700.28 979.31,-698.81 982.45,-696.54"/>
+<polygon fill="black" stroke="black" points="984.7,-699.23 990.74,-690.53 980.59,-693.56 984.7,-699.23"/>
+</g>
+<!-- n00000081&#45;&gt;n0000000d -->
+<g id="edge4" class="edge">
+<title>n00000081:port4&#45;&gt;n0000000d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1003.38,-700.26C1003.38,-700.26 916.62,-689.8 909.08,-697.08 880.2,-725.01 885.68,-754.82 909.08,-787.48 910.88,-789.99 918.96,-793.59 929.7,-797.47"/>
+<polygon fill="black" stroke="black" points="928.69,-800.82 939.28,-800.79 930.98,-794.21 928.69,-800.82"/>
+</g>
+<!-- n00000081&#45;&gt;n00000011 -->
+<g id="edge5" class="edge">
+<title>n00000081:port5&#45;&gt;n00000011</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1029.19,-700.26C1029.19,-700.26 1115.28,-690.56 1123.48,-697.08 1198.37,-756.64 1190.55,-886.51 1182.64,-944.71"/>
+<polygon fill="black" stroke="black" points="1179.16,-944.31 1181.18,-954.71 1186.09,-945.32 1179.16,-944.31"/>
+</g>
+<!-- n00000081&#45;&gt;n00000015 -->
+<g id="edge6" class="edge">
+<title>n00000081:port6&#45;&gt;n00000015</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1055.18,-700.28C1055.18,-700.28 915.57,-692.2 909.08,-697.08 834.02,-753.51 831.79,-879.34 835.06,-936.56"/>
+<polygon fill="black" stroke="black" points="831.58,-936.99 835.74,-946.73 838.56,-936.52 831.58,-936.99"/>
+</g>
+<!-- n00000081&#45;&gt;n00000019 -->
+<g id="edge7" class="edge">
+<title>n00000081:port7&#45;&gt;n00000019</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1081.28,-700.28C1081.28,-700.28 916.04,-696.54 912.32,-693.67 864.52,-656.73 856.3,-580.22 855.62,-538.2"/>
+<polygon fill="black" stroke="black" points="859.11,-538.05 855.59,-528.06 852.11,-538.07 859.11,-538.05"/>
+</g>
+<!-- n00000081&#45;&gt;n0000001d -->
+<g id="edge8" class="edge">
+<title>n00000081:port8&#45;&gt;n0000001d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1107.28,-700.28C1107.28,-700.28 1119.29,-696.23 1121.72,-693.67 1159.76,-653.62 1177.38,-589.6 1184.78,-552.46"/>
+<polygon fill="black" stroke="black" points="1188.29,-552.76 1186.69,-542.29 1181.41,-551.47 1188.29,-552.76"/>
+</g>
+<!-- n0000008b -->
+<g id="node34" class="node">
+<title>n0000008b</title>
+<path fill="green" stroke="black" d="M293.1,-532.08C293.1,-532.08 477.1,-532.08 477.1,-532.08 483.1,-532.08 489.1,-538.08 489.1,-544.08 489.1,-544.08 489.1,-604.08 489.1,-604.08 489.1,-610.08 483.1,-616.08 477.1,-616.08 477.1,-616.08 293.1,-616.08 293.1,-616.08 287.1,-616.08 281.1,-610.08 281.1,-604.08 281.1,-604.08 281.1,-544.08 281.1,-544.08 281.1,-538.08 287.1,-532.08 293.1,-532.08"/>
+<text text-anchor="middle" x="385.1" y="-600.88" font-family="Times,serif" font-size="14.00">0</text>
+<polyline fill="none" stroke="black" points="281.1,-593.08 489.1,-593.08 "/>
+<text text-anchor="middle" x="385.1" y="-577.88" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text>
+<text text-anchor="middle" x="385.1" y="-562.88" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev1</text>
+<polyline fill="none" stroke="black" points="281.1,-555.08 489.1,-555.08 "/>
+<text text-anchor="middle" x="294.1" y="-539.88" font-family="Times,serif" font-size="14.00">1</text>
+<polyline fill="none" stroke="black" points="307.1,-532.08 307.1,-555.08 "/>
+<text text-anchor="middle" x="320.1" y="-539.88" font-family="Times,serif" font-size="14.00">2</text>
+<polyline fill="none" stroke="black" points="333.1,-532.08 333.1,-555.08 "/>
+<text text-anchor="middle" x="346.1" y="-539.88" font-family="Times,serif" font-size="14.00">3</text>
+<polyline fill="none" stroke="black" points="359.1,-532.08 359.1,-555.08 "/>
+<text text-anchor="middle" x="372.1" y="-539.88" font-family="Times,serif" font-size="14.00">4</text>
+<polyline fill="none" stroke="black" points="385.1,-532.08 385.1,-555.08 "/>
+<text text-anchor="middle" x="398.1" y="-539.88" font-family="Times,serif" font-size="14.00">5</text>
+<polyline fill="none" stroke="black" points="411.1,-532.08 411.1,-555.08 "/>
+<text text-anchor="middle" x="424.1" y="-539.88" font-family="Times,serif" font-size="14.00">6</text>
+<polyline fill="none" stroke="black" points="437.1,-532.08 437.1,-555.08 "/>
+<text text-anchor="middle" x="450.1" y="-539.88" font-family="Times,serif" font-size="14.00">7</text>
+<polyline fill="none" stroke="black" points="463.1,-532.08 463.1,-555.08 "/>
+<text text-anchor="middle" x="476.1" y="-539.88" font-family="Times,serif" font-size="14.00">8</text>
+</g>
+<!-- n0000008b&#45;&gt;n00000021 -->
+<g id="edge9" class="edge">
+<title>n0000008b:port1&#45;&gt;n00000021</title>
+<path fill="none" stroke="black" d="M281.1,-543.08C281.1,-543.08 240.1,-560.51 205.94,-570.26 205.35,-570.43 204.77,-570.59 204.18,-570.76"/>
+<polygon fill="black" stroke="black" points="203.2,-567.39 194.47,-573.39 205.03,-574.15 203.2,-567.39"/>
+</g>
+<!-- n0000008b&#45;&gt;n00000025 -->
+<g id="edge10" class="edge">
+<title>n0000008b:port2&#45;&gt;n00000025</title>
+<path fill="none" stroke="black" d="M320.2,-532.07C320.2,-532.07 456.9,-514.37 492.3,-528.88 528.42,-543.68 522.86,-571.78 556.11,-594.53"/>
+<polygon fill="black" stroke="black" points="554.54,-597.67 564.9,-599.88 558.18,-591.69 554.54,-597.67"/>
+</g>
+<!-- n0000008b&#45;&gt;n00000029 -->
+<g id="edge11" class="edge">
+<title>n0000008b:port3&#45;&gt;n00000029</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M346.1,-532.08C346.1,-532.08 333.93,-527.96 318.37,-522.71"/>
+<polygon fill="black" stroke="black" points="319.48,-519.39 308.88,-519.5 317.24,-526.02 319.48,-519.39"/>
+</g>
+<!-- n0000008b&#45;&gt;n0000002d -->
+<g id="edge12" class="edge">
+<title>n0000008b:port4&#45;&gt;n0000002d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M372.19,-532.05C372.19,-532.05 292.97,-514.3 277.9,-528.88 249.01,-556.8 253.16,-587.62 277.9,-619.28 278.34,-619.85 280.33,-620.69 283.45,-621.71"/>
+<polygon fill="black" stroke="black" points="282.71,-625.14 293.29,-624.58 284.67,-618.42 282.71,-625.14"/>
+</g>
+<!-- n0000008b&#45;&gt;n00000031 -->
+<g id="edge13" class="edge">
+<title>n0000008b:port5&#45;&gt;n00000031</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M398,-532.05C398,-532.05 476.28,-515.34 492.3,-528.88 568.49,-593.29 550.55,-729.67 538.14,-789.41"/>
+<polygon fill="black" stroke="black" points="534.69,-788.79 535.99,-799.31 541.53,-790.28 534.69,-788.79"/>
+</g>
+<!-- n0000008b&#45;&gt;n00000035 -->
+<g id="edge14" class="edge">
+<title>n0000008b:port6&#45;&gt;n00000035</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M424,-532.07C424,-532.07 290.37,-518.48 277.9,-528.88 202.27,-591.86 215.34,-725.69 225.66,-785.15"/>
+<polygon fill="black" stroke="black" points="222.29,-786.14 227.54,-795.35 229.17,-784.88 222.29,-786.14"/>
+</g>
+<!-- n0000008b&#45;&gt;n00000039 -->
+<g id="edge15" class="edge">
+<title>n0000008b:port7&#45;&gt;n00000039</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M450.1,-532.08C450.1,-532.08 395.22,-528.13 383.45,-518.65 375.46,-512.21 322.64,-385.46 298.76,-327.47"/>
+<polygon fill="black" stroke="black" points="301.96,-326.05 294.92,-318.14 295.49,-328.72 301.96,-326.05"/>
+</g>
+<!-- n0000008b&#45;&gt;n0000003d -->
+<g id="edge16" class="edge">
+<title>n0000008b:port8&#45;&gt;n0000003d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M476.1,-532.08C476.1,-532.08 522.37,-522.39 526.85,-518.65 563.15,-488.33 581.38,-434.52 589.6,-401.2"/>
+<polygon fill="black" stroke="black" points="593.08,-401.69 591.93,-391.16 586.26,-400.11 593.08,-401.69"/>
+</g>
+<!-- n00000095 -->
+<g id="node35" class="node">
+<title>n00000095</title>
+<path fill="green" stroke="black" d="M301.38,-1180.11C301.38,-1180.11 485.38,-1180.11 485.38,-1180.11 491.38,-1180.11 497.38,-1186.11 497.38,-1192.11 497.38,-1192.11 497.38,-1252.11 497.38,-1252.11 497.38,-1258.11 491.38,-1264.11 485.38,-1264.11 485.38,-1264.11 301.38,-1264.11 301.38,-1264.11 295.38,-1264.11 289.38,-1258.11 289.38,-1252.11 289.38,-1252.11 289.38,-1192.11 289.38,-1192.11 289.38,-1186.11 295.38,-1180.11 301.38,-1180.11"/>
+<text text-anchor="middle" x="393.38" y="-1248.91" font-family="Times,serif" font-size="14.00">0</text>
+<polyline fill="none" stroke="black" points="289.38,-1241.11 497.38,-1241.11 "/>
+<text text-anchor="middle" x="393.38" y="-1225.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text>
+<text text-anchor="middle" x="393.38" y="-1210.91" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev2</text>
+<polyline fill="none" stroke="black" points="289.38,-1203.11 497.38,-1203.11 "/>
+<text text-anchor="middle" x="302.38" y="-1187.91" font-family="Times,serif" font-size="14.00">1</text>
+<polyline fill="none" stroke="black" points="315.38,-1180.11 315.38,-1203.11 "/>
+<text text-anchor="middle" x="328.38" y="-1187.91" font-family="Times,serif" font-size="14.00">2</text>
+<polyline fill="none" stroke="black" points="341.38,-1180.11 341.38,-1203.11 "/>
+<text text-anchor="middle" x="354.38" y="-1187.91" font-family="Times,serif" font-size="14.00">3</text>
+<polyline fill="none" stroke="black" points="367.38,-1180.11 367.38,-1203.11 "/>
+<text text-anchor="middle" x="380.38" y="-1187.91" font-family="Times,serif" font-size="14.00">4</text>
+<polyline fill="none" stroke="black" points="393.38,-1180.11 393.38,-1203.11 "/>
+<text text-anchor="middle" x="406.38" y="-1187.91" font-family="Times,serif" font-size="14.00">5</text>
+<polyline fill="none" stroke="black" points="419.38,-1180.11 419.38,-1203.11 "/>
+<text text-anchor="middle" x="432.38" y="-1187.91" font-family="Times,serif" font-size="14.00">6</text>
+<polyline fill="none" stroke="black" points="445.38,-1180.11 445.38,-1203.11 "/>
+<text text-anchor="middle" x="458.38" y="-1187.91" font-family="Times,serif" font-size="14.00">7</text>
+<polyline fill="none" stroke="black" points="471.38,-1180.11 471.38,-1203.11 "/>
+<text text-anchor="middle" x="484.38" y="-1187.91" font-family="Times,serif" font-size="14.00">8</text>
+</g>
+<!-- n00000095&#45;&gt;n00000041 -->
+<g id="edge17" class="edge">
+<title>n00000095:port1&#45;&gt;n00000041</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M289.38,-1191.11C289.38,-1191.11 258.94,-1194.22 222.89,-1197.91"/>
+<polygon fill="black" stroke="black" points="222.19,-1194.46 212.6,-1198.96 222.9,-1201.42 222.19,-1194.46"/>
+</g>
+<!-- n00000095&#45;&gt;n00000045 -->
+<g id="edge18" class="edge">
+<title>n00000095:port2&#45;&gt;n00000045</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M328.48,-1180.11C328.48,-1180.11 463.26,-1168.53 500.58,-1176.91 534.02,-1184.43 537.24,-1200.06 569.66,-1211.18 569.76,-1211.22 569.86,-1211.25 569.96,-1211.29"/>
+<polygon fill="black" stroke="black" points="568.86,-1214.61 579.45,-1214.34 571,-1207.95 568.86,-1214.61"/>
+</g>
+<!-- n00000095&#45;&gt;n00000049 -->
+<g id="edge19" class="edge">
+<title>n00000095:port3&#45;&gt;n00000049</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M354.38,-1180.11C354.38,-1180.11 356.8,-1178.46 360.49,-1175.94"/>
+<polygon fill="black" stroke="black" points="362.56,-1178.77 368.86,-1170.24 358.62,-1172.98 362.56,-1178.77"/>
+</g>
+<!-- n00000095&#45;&gt;n0000004d -->
+<g id="edge20" class="edge">
+<title>n00000095:port4&#45;&gt;n0000004d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M380.47,-1180.09C380.47,-1180.09 293.71,-1169.63 286.18,-1176.91 257.29,-1204.84 262.63,-1234.76 286.18,-1267.31 288.16,-1270.05 297.33,-1273.96 309.38,-1278.13"/>
+<polygon fill="black" stroke="black" points="308.49,-1281.53 319.09,-1281.36 310.7,-1274.88 308.49,-1281.53"/>
+</g>
+<!-- n00000095&#45;&gt;n00000051 -->
+<g id="edge21" class="edge">
+<title>n00000095:port5&#45;&gt;n00000051</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M406.28,-1180.09C406.28,-1180.09 492.13,-1170.7 500.58,-1176.91 576.41,-1232.66 579.83,-1358.79 577.09,-1416.2"/>
+<polygon fill="black" stroke="black" points="573.59,-1416.23 576.51,-1426.41 580.58,-1416.63 573.59,-1416.23"/>
+</g>
+<!-- n00000095&#45;&gt;n00000055 -->
+<g id="edge22" class="edge">
+<title>n00000095:port6&#45;&gt;n00000055</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M432.28,-1180.11C432.28,-1180.11 292.85,-1172.29 286.18,-1176.91 211.26,-1228.86 198.3,-1348.49 196.45,-1404.12"/>
+<polygon fill="black" stroke="black" points="192.94,-1404.28 196.21,-1414.36 199.94,-1404.44 192.94,-1404.28"/>
+</g>
+<!-- n00000095&#45;&gt;n00000059 -->
+<g id="edge23" class="edge">
+<title>n00000095:port7&#45;&gt;n00000059</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M458.38,-1180.11C458.38,-1180.11 291.84,-1175.85 287.94,-1173.16 239.87,-1139.96 222.85,-1068.83 216.94,-1028.6"/>
+<polygon fill="black" stroke="black" points="220.39,-1028.06 215.6,-1018.61 213.46,-1028.98 220.39,-1028.06"/>
+</g>
+<!-- n00000095&#45;&gt;n0000005d -->
+<g id="edge24" class="edge">
+<title>n00000095:port8&#45;&gt;n0000005d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M484.38,-1180.11C484.38,-1180.11 502.45,-1176.49 506.34,-1173.16 547.25,-1138.2 569.47,-1077.38 579.62,-1041.41"/>
+<polygon fill="black" stroke="black" points="583.06,-1042.09 582.28,-1031.53 576.3,-1040.27 583.06,-1042.09"/>
+</g>
+<!-- n0000009f -->
+<g id="node36" class="node">
+<title>n0000009f</title>
+<path fill="green" stroke="black" d="M1211.38,-200.11C1211.38,-200.11 1395.38,-200.11 1395.38,-200.11 1401.38,-200.11 1407.38,-206.11 1407.38,-212.11 1407.38,-212.11 1407.38,-272.11 1407.38,-272.11 1407.38,-278.11 1401.38,-284.11 1395.38,-284.11 1395.38,-284.11 1211.38,-284.11 1211.38,-284.11 1205.38,-284.11 1199.38,-278.11 1199.38,-272.11 1199.38,-272.11 1199.38,-212.11 1199.38,-212.11 1199.38,-206.11 1205.38,-200.11 1211.38,-200.11"/>
+<text text-anchor="middle" x="1303.38" y="-268.91" font-family="Times,serif" font-size="14.00">0</text>
+<polyline fill="none" stroke="black" points="1199.38,-261.11 1407.38,-261.11 "/>
+<text text-anchor="middle" x="1303.38" y="-245.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text>
+<text text-anchor="middle" x="1303.38" y="-230.91" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev3</text>
+<polyline fill="none" stroke="black" points="1199.38,-223.11 1407.38,-223.11 "/>
+<text text-anchor="middle" x="1212.38" y="-207.91" font-family="Times,serif" font-size="14.00">1</text>
+<polyline fill="none" stroke="black" points="1225.38,-200.11 1225.38,-223.11 "/>
+<text text-anchor="middle" x="1238.38" y="-207.91" font-family="Times,serif" font-size="14.00">2</text>
+<polyline fill="none" stroke="black" points="1251.38,-200.11 1251.38,-223.11 "/>
+<text text-anchor="middle" x="1264.38" y="-207.91" font-family="Times,serif" font-size="14.00">3</text>
+<polyline fill="none" stroke="black" points="1277.38,-200.11 1277.38,-223.11 "/>
+<text text-anchor="middle" x="1290.38" y="-207.91" font-family="Times,serif" font-size="14.00">4</text>
+<polyline fill="none" stroke="black" points="1303.38,-200.11 1303.38,-223.11 "/>
+<text text-anchor="middle" x="1316.38" y="-207.91" font-family="Times,serif" font-size="14.00">5</text>
+<polyline fill="none" stroke="black" points="1329.38,-200.11 1329.38,-223.11 "/>
+<text text-anchor="middle" x="1342.38" y="-207.91" font-family="Times,serif" font-size="14.00">6</text>
+<polyline fill="none" stroke="black" points="1355.38,-200.11 1355.38,-223.11 "/>
+<text text-anchor="middle" x="1368.38" y="-207.91" font-family="Times,serif" font-size="14.00">7</text>
+<polyline fill="none" stroke="black" points="1381.38,-200.11 1381.38,-223.11 "/>
+<text text-anchor="middle" x="1394.38" y="-207.91" font-family="Times,serif" font-size="14.00">8</text>
+</g>
+<!-- n0000009f&#45;&gt;n00000061 -->
+<g id="edge25" class="edge">
+<title>n0000009f:port1&#45;&gt;n00000061</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1199.38,-211.11C1199.38,-211.11 1168.94,-214.22 1132.89,-217.91"/>
+<polygon fill="black" stroke="black" points="1132.19,-214.46 1122.6,-218.96 1132.9,-221.42 1132.19,-214.46"/>
+</g>
+<!-- n0000009f&#45;&gt;n00000065 -->
+<g id="edge26" class="edge">
+<title>n0000009f:port2&#45;&gt;n00000065</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1238.48,-200.11C1238.48,-200.11 1373.26,-188.53 1410.58,-196.91 1444.02,-204.43 1447.24,-220.06 1479.66,-231.18 1479.76,-231.22 1479.86,-231.25 1479.96,-231.29"/>
+<polygon fill="black" stroke="black" points="1478.86,-234.61 1489.45,-234.34 1481,-227.95 1478.86,-234.61"/>
+</g>
+<!-- n0000009f&#45;&gt;n00000069 -->
+<g id="edge27" class="edge">
+<title>n0000009f:port3&#45;&gt;n00000069</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1264.38,-200.11C1264.38,-200.11 1266.8,-198.46 1270.49,-195.94"/>
+<polygon fill="black" stroke="black" points="1272.56,-198.77 1278.86,-190.24 1268.62,-192.98 1272.56,-198.77"/>
+</g>
+<!-- n0000009f&#45;&gt;n0000006d -->
+<g id="edge28" class="edge">
+<title>n0000009f:port4&#45;&gt;n0000006d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1290.47,-200.09C1290.47,-200.09 1203.71,-189.63 1196.18,-196.91 1167.29,-224.84 1172.63,-254.76 1196.18,-287.31 1198.16,-290.05 1207.33,-293.96 1219.38,-298.13"/>
+<polygon fill="black" stroke="black" points="1218.49,-301.53 1229.09,-301.36 1220.7,-294.88 1218.49,-301.53"/>
+</g>
+<!-- n0000009f&#45;&gt;n00000071 -->
+<g id="edge29" class="edge">
+<title>n0000009f:port5&#45;&gt;n00000071</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1316.28,-200.09C1316.28,-200.09 1402.13,-190.7 1410.58,-196.91 1486.41,-252.66 1489.83,-378.79 1487.09,-436.2"/>
+<polygon fill="black" stroke="black" points="1483.59,-436.23 1486.51,-446.41 1490.58,-436.63 1483.59,-436.23"/>
+</g>
+<!-- n0000009f&#45;&gt;n00000075 -->
+<g id="edge30" class="edge">
+<title>n0000009f:port6&#45;&gt;n00000075</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1342.28,-200.11C1342.28,-200.11 1202.85,-192.29 1196.18,-196.91 1121.26,-248.86 1108.3,-368.49 1106.45,-424.12"/>
+<polygon fill="black" stroke="black" points="1102.94,-424.28 1106.21,-434.36 1109.94,-424.44 1102.94,-424.28"/>
+</g>
+<!-- n0000009f&#45;&gt;n00000079 -->
+<g id="edge31" class="edge">
+<title>n0000009f:port7&#45;&gt;n00000079</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1368.38,-200.11C1368.38,-200.11 1201.84,-195.85 1197.94,-193.16 1149.87,-159.96 1132.85,-88.83 1126.94,-48.6"/>
+<polygon fill="black" stroke="black" points="1130.39,-48.06 1125.6,-38.61 1123.46,-48.98 1130.39,-48.06"/>
+</g>
+<!-- n0000009f&#45;&gt;n0000007d -->
+<g id="edge32" class="edge">
+<title>n0000009f:port8&#45;&gt;n0000007d</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1394.38,-200.11C1394.38,-200.11 1412.45,-196.49 1416.34,-193.16 1457.25,-158.2 1479.47,-97.38 1489.62,-61.41"/>
+<polygon fill="black" stroke="black" points="1493.06,-62.09 1492.28,-51.53 1486.3,-60.27 1493.06,-62.09"/>
+</g>
+<!-- n000000e9 -->
+<g id="node37" class="node">
+<title>n000000e9</title>
+<path fill="green" stroke="black" d="M398.65,-431.45C398.65,-431.45 511.65,-431.45 511.65,-431.45 517.65,-431.45 523.65,-437.45 523.65,-443.45 523.65,-443.45 523.65,-503.45 523.65,-503.45 523.65,-509.45 517.65,-515.45 511.65,-515.45 511.65,-515.45 398.65,-515.45 398.65,-515.45 392.65,-515.45 386.65,-509.45 386.65,-503.45 386.65,-503.45 386.65,-443.45 386.65,-443.45 386.65,-437.45 392.65,-431.45 398.65,-431.45"/>
+<text text-anchor="middle" x="420.65" y="-500.25" font-family="Times,serif" font-size="14.00">1</text>
+<polyline fill="none" stroke="black" points="454.65,-492.45 454.65,-515.45 "/>
+<text text-anchor="middle" x="489.15" y="-500.25" font-family="Times,serif" font-size="14.00">2</text>
+<polyline fill="none" stroke="black" points="386.65,-492.45 523.65,-492.45 "/>
+<text text-anchor="middle" x="455.15" y="-477.25" font-family="Times,serif" font-size="14.00">ov2740 4&#45;0036</text>
+<text text-anchor="middle" x="455.15" y="-462.25" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev4</text>
+<polyline fill="none" stroke="black" points="386.65,-454.45 523.65,-454.45 "/>
+<text text-anchor="middle" x="455.15" y="-439.25" font-family="Times,serif" font-size="14.00">0</text>
+</g>
+<!-- n000000e9&#45;&gt;n0000008b -->
+<g id="edge33" class="edge">
+<title>n000000e9:port0&#45;&gt;n0000008b:port0</title>
+<path fill="none" stroke="black" stroke-width="2" d="M386.14,-442.55C386.14,-442.55 361.11,-493.23 383.45,-518.65 391.47,-527.78 484.31,-519.72 492.3,-528.88 508.64,-547.6 499.26,-579.87 493.12,-595.68"/>
+<polygon fill="black" stroke="black" stroke-width="2" points="489.86,-594.41 489.11,-604.98 496.29,-597.19 489.86,-594.41"/>
+</g>
+</g>
+</svg>
diff --git a/Documentation/admin-guide/media/mgb4.rst b/Documentation/admin-guide/media/mgb4.rst
index 2977f74d7e26..e434d4a9eeb3 100644
--- a/Documentation/admin-guide/media/mgb4.rst
+++ b/Documentation/admin-guide/media/mgb4.rst
@@ -1,8 +1,10 @@
.. SPDX-License-Identifier: GPL-2.0
-====================
-mgb4 sysfs interface
-====================
+The mgb4 driver
+===============
+
+sysfs interface
+---------------
The mgb4 driver provides a sysfs interface, that is used to configure video
stream related parameters (some of them must be set properly before the v4l2
@@ -12,9 +14,8 @@ There are two types of parameters - global / PCI card related, found under
``/sys/class/video4linux/videoX/device`` and module specific found under
``/sys/class/video4linux/videoX``.
-
Global (PCI card) parameters
-============================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**module_type** (R):
Module type.
@@ -42,9 +43,8 @@ Global (PCI card) parameters
where each component is a 8b number.
-
Common FPDL3/GMSL input parameters
-==================================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**input_id** (R):
Input number ID, zero based.
@@ -190,9 +190,8 @@ Common FPDL3/GMSL input parameters
*Note: This parameter can not be changed while the input v4l2 device is
open.*
-
Common FPDL3/GMSL output parameters
-===================================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**output_id** (R):
Output number ID, zero based.
@@ -282,9 +281,8 @@ Common FPDL3/GMSL output parameters
Number of video lines between the end of the last valid pixel line (marked
by DE=1) and assertion of the VSYNC signal. The default value is 2.
-
FPDL3 specific input parameters
-===============================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**fpdl3_input_width** (RW):
Number of deserializer input lines.
@@ -294,7 +292,7 @@ FPDL3 specific input parameters
| 2 - dual
FPDL3 specific output parameters
-================================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**fpdl3_output_width** (RW):
Number of serializer output lines.
@@ -304,7 +302,7 @@ FPDL3 specific output parameters
| 2 - dual
GMSL specific input parameters
-==============================
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**gmsl_mode** (RW):
GMSL speed mode.
@@ -328,10 +326,8 @@ GMSL specific input parameters
| 0 - disabled
| 1 - enabled (default)
-
-====================
-mgb4 mtd partitions
-====================
+MTD partitions
+--------------
The mgb4 driver creates a MTD device with two partitions:
- mgb4-fw.X - FPGA firmware.
@@ -344,9 +340,8 @@ also have a third partition named *mgb4-flash* available in the system. This
partition represents the whole, unpartitioned, card's FLASH memory and one should
not fiddle with it...
-====================
-mgb4 iio (triggers)
-====================
+IIO (triggers)
+--------------
The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and
signal level status capability. The following scan elements are available:
diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
index f4bb2605f07e..4120eded9a13 100644
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation
imx
imx7
ipu3
+ ipu6-isys
ivtv
mgb4
omap3isp
diff --git a/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml b/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
new file mode 100644
index 000000000000..5fb5d60f069a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/brcm,bcm2835-unicam.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom BCM283x Camera Interface (Unicam)
+
+maintainers:
+ - Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+
+description: |-
+ The Unicam block on BCM283x SoCs is the receiver for either
+ CSI-2 or CCP2 data from image sensors or similar devices.
+
+ The main platform using this SoC is the Raspberry Pi family of boards. On
+ the Pi the VideoCore firmware can also control this hardware block, and
+ driving it from two different processors will cause issues. To avoid this,
+ the firmware checks the device tree configuration during boot. If it finds
+ device tree nodes whose name starts with 'csi' then it will stop the firmware
+ accessing the block, and it can then safely be used via the device tree
+ binding.
+
+properties:
+ compatible:
+ const: brcm,bcm2835-unicam
+
+ reg:
+ items:
+ - description: Unicam block.
+ - description: Clock Manager Image (CMI) block.
+
+ reg-names:
+ items:
+ - const: unicam
+ - const: cmi
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Clock to drive the LP state machine of Unicam.
+ - description: Clock for the VPU (core clock).
+
+ clock-names:
+ items:
+ - const: lp
+ - const: vpu
+
+ power-domains:
+ items:
+ - description: Unicam power domain
+
+ brcm,num-data-lanes:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [ 2, 4 ]
+ description: |
+ Number of CSI-2 data lanes supported by this Unicam instance. The number
+ of data lanes actively used is specified with the data-lanes endpoint
+ property.
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ additionalProperties: false
+
+ properties:
+ bus-type:
+ enum: [ 3, 4 ]
+
+ clock-noncontinuous: true
+ data-lanes: true
+ remote-endpoint: true
+
+ required:
+ - bus-type
+ - data-lanes
+ - remote-endpoint
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+ - brcm,num-data-lanes
+ - port
+
+additionalProperties: False
+
+examples:
+ - |
+ #include <dt-bindings/clock/bcm2835.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/power/raspberrypi-power.h>
+
+ csi1: csi@7e801000 {
+ compatible = "brcm,bcm2835-unicam";
+ reg = <0x7e801000 0x800>,
+ <0x7e802004 0x4>;
+ reg-names = "unicam", "cmi";
+ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clocks BCM2835_CLOCK_CAM1>,
+ <&firmware_clocks 4>;
+ clock-names = "lp", "vpu";
+ power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>;
+ brcm,num-data-lanes = <2>;
+ port {
+ csi1_ep: endpoint {
+ remote-endpoint = <&imx219_0>;
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml
index f81e7daed67b..2bf1a81feaf4 100644
--- a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml
@@ -15,7 +15,7 @@ description: |
They include an ISP capable of auto exposure and auto white balance.
allOf:
- - $ref: ../video-interface-devices.yaml#
+ - $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml
index 1726ecca4c77..9eac588de0bc 100644
--- a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml
@@ -19,7 +19,7 @@ description:
either through a parallel interface or through MIPI CSI-2.
allOf:
- - $ref: ../video-interface-devices.yaml#
+ - $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml
index cf456f8d9ddc..634d3b821b8c 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml
@@ -37,31 +37,45 @@ properties:
active low.
maxItems: 1
- dovdd-supply:
+ DOVDD-supply:
description:
Definition of the regulator used as interface power supply.
- avdd-supply:
+ AVDD-supply:
description:
Definition of the regulator used as analog power supply.
- dvdd-supply:
+ DVDD-supply:
description:
Definition of the regulator used as digital power supply.
port:
- $ref: /schemas/graph.yaml#/properties/port
description:
A node containing an output port node.
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ additionalProperties: false
+
+ properties:
+ link-frequencies: true
+
+ remote-endpoint: true
+
+ required:
+ - link-frequencies
required:
- compatible
- reg
- clocks
- clock-names
- - dovdd-supply
- - avdd-supply
- - dvdd-supply
+ - DOVDD-supply
+ - AVDD-supply
+ - DVDD-supply
- reset-gpios
- port
@@ -82,13 +96,14 @@ examples:
clock-names = "xvclk";
reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
- dovdd-supply = <&sw2_reg>;
- dvdd-supply = <&sw2_reg>;
- avdd-supply = <&reg_peri_3p15v>;
+ DOVDD-supply = <&sw2_reg>;
+ DVDD-supply = <&sw2_reg>;
+ AVDD-supply = <&reg_peri_3p15v>;
port {
ov2680_to_mipi: endpoint {
remote-endpoint = <&mipi_from_sensor>;
+ link-frequencies = /bits/ 64 <330000000>;
};
};
};
diff --git a/Documentation/devicetree/bindings/media/i2c/ov8856.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
index 816dac9c6f60..3f6f72c35485 100644
--- a/Documentation/devicetree/bindings/media/i2c/ov8856.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
@@ -2,7 +2,7 @@
# Copyright (c) 2019 MediaTek Inc.
%YAML 1.2
---
-$id: http://devicetree.org/schemas/media/i2c/ov8856.yaml#
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov8856.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Omnivision OV8856 CMOS Sensor
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml
index 60903da84e1f..0162eec8ca99 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml
@@ -16,7 +16,7 @@ description: |
maximum throughput of 1.2Gbps/lane.
allOf:
- - $ref: ../video-interface-devices.yaml#
+ - $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml
index a531badc16c9..bf05ca48601a 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml
@@ -23,6 +23,9 @@ description: |-
is treated the same as this as it was the original compatible string.
imx290llr is the mono version of the sensor.
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
properties:
compatible:
oneOf:
@@ -101,7 +104,7 @@ required:
- vdddo-supply
- port
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
index 9a00dab2e8a3..34962c5c7006 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
@@ -18,7 +18,7 @@ description: |-
available via CSI-2 serial data output (two or four lanes).
allOf:
- - $ref: ../video-interface-devices.yaml#
+ - $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
index e4665469a86c..4d5348d456a1 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
@@ -84,6 +84,7 @@ allOf:
properties:
port@0:
description: MIPI CSI-2 RX
+ port@1: false
required:
- port@0
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
index 3d9d1db37040..2be30c5fdc83 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
@@ -31,6 +31,11 @@ properties:
reg:
maxItems: 1
+ clocks:
+ items:
+ - description: AXI DMA engine clock for fetching JPEG bitstream from memory (per)
+ - description: IP bus clock for register access (ipg)
+
interrupts:
description: |
There are 4 slots available in the IP, which the driver may use
@@ -49,6 +54,7 @@ properties:
required:
- compatible
- reg
+ - clocks
- interrupts
- power-domains
@@ -56,12 +62,15 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/clock/imx8-lpcg.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/firmware/imx/rsrc.h>
jpegdec: jpegdec@58400000 {
compatible = "nxp,imx8qxp-jpgdec";
reg = <0x58400000 0x00050000 >;
+ clocks = <&img_jpeg_dec_lpcg IMX_LPCG_CLK_0>,
+ <&img_jpeg_dec_lpcg IMX_LPCG_CLK_4>;
interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>,
@@ -76,6 +85,8 @@ examples:
jpegenc: jpegenc@58450000 {
compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc";
reg = <0x58450000 0x00050000 >;
+ clocks = <&img_jpeg_enc_lpcg IMX_LPCG_CLK_0>,
+ <&img_jpeg__lpcg IMX_LPCG_CLK_4>;
interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
new file mode 100644
index 000000000000..c0bc31709873
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
@@ -0,0 +1,512 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,sc8280xp-camss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SC8280XP Camera Subsystem (CAMSS)
+
+maintainers:
+ - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+description: |
+ The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms.
+
+properties:
+ compatible:
+ const: qcom,sc8280xp-camss
+
+ clocks:
+ maxItems: 40
+
+ clock-names:
+ items:
+ - const: camnoc_axi
+ - const: cpas_ahb
+ - const: csiphy0
+ - const: csiphy0_timer
+ - const: csiphy1
+ - const: csiphy1_timer
+ - const: csiphy2
+ - const: csiphy2_timer
+ - const: csiphy3
+ - const: csiphy3_timer
+ - const: vfe0_axi
+ - const: vfe0
+ - const: vfe0_cphy_rx
+ - const: vfe0_csid
+ - const: vfe1_axi
+ - const: vfe1
+ - const: vfe1_cphy_rx
+ - const: vfe1_csid
+ - const: vfe2_axi
+ - const: vfe2
+ - const: vfe2_cphy_rx
+ - const: vfe2_csid
+ - const: vfe3_axi
+ - const: vfe3
+ - const: vfe3_cphy_rx
+ - const: vfe3_csid
+ - const: vfe_lite0
+ - const: vfe_lite0_cphy_rx
+ - const: vfe_lite0_csid
+ - const: vfe_lite1
+ - const: vfe_lite1_cphy_rx
+ - const: vfe_lite1_csid
+ - const: vfe_lite2
+ - const: vfe_lite2_cphy_rx
+ - const: vfe_lite2_csid
+ - const: vfe_lite3
+ - const: vfe_lite3_cphy_rx
+ - const: vfe_lite3_csid
+ - const: gcc_axi_hf
+ - const: gcc_axi_sf
+
+ interrupts:
+ maxItems: 20
+
+ interrupt-names:
+ items:
+ - const: csid1_lite
+ - const: vfe_lite1
+ - const: csiphy3
+ - const: csid0
+ - const: vfe0
+ - const: csid1
+ - const: vfe1
+ - const: csid0_lite
+ - const: vfe_lite0
+ - const: csiphy0
+ - const: csiphy1
+ - const: csiphy2
+ - const: csid2
+ - const: vfe2
+ - const: csid3_lite
+ - const: csid2_lite
+ - const: vfe_lite3
+ - const: vfe_lite2
+ - const: csid3
+ - const: vfe3
+
+ iommus:
+ maxItems: 16
+
+ interconnects:
+ maxItems: 4
+
+ interconnect-names:
+ items:
+ - const: cam_ahb
+ - const: cam_hf_mnoc
+ - const: cam_sf_mnoc
+ - const: cam_sf_icp_mnoc
+
+ power-domains:
+ items:
+ - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: IFE3 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller.
+
+ power-domain-names:
+ items:
+ - const: ife0
+ - const: ife1
+ - const: ife2
+ - const: ife3
+ - const: top
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ description:
+ CSI input ports.
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port for receiving CSI data from CSIPHY0.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ clock-lanes:
+ maxItems: 1
+
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - clock-lanes
+ - data-lanes
+
+ port@1:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port for receiving CSI data from CSIPHY1.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ clock-lanes:
+ maxItems: 1
+
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - clock-lanes
+ - data-lanes
+
+ port@2:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port for receiving CSI data from CSIPHY2.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ clock-lanes:
+ maxItems: 1
+
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - clock-lanes
+ - data-lanes
+
+ port@3:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ Input port for receiving CSI data from CSIPHY3.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ clock-lanes:
+ maxItems: 1
+
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - clock-lanes
+ - data-lanes
+
+ reg:
+ maxItems: 20
+
+ reg-names:
+ items:
+ - const: csiphy2
+ - const: csiphy3
+ - const: csiphy0
+ - const: csiphy1
+ - const: vfe0
+ - const: csid0
+ - const: vfe1
+ - const: csid1
+ - const: vfe2
+ - const: csid2
+ - const: vfe_lite0
+ - const: csid0_lite
+ - const: vfe_lite1
+ - const: csid1_lite
+ - const: vfe_lite2
+ - const: csid2_lite
+ - const: vfe_lite3
+ - const: csid3_lite
+ - const: vfe3
+ - const: csid3
+
+ vdda-phy-supply:
+ description:
+ Phandle to a regulator supply to PHY core block.
+
+ vdda-pll-supply:
+ description:
+ Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+required:
+ - clock-names
+ - clocks
+ - compatible
+ - interconnects
+ - interconnect-names
+ - interrupts
+ - interrupt-names
+ - iommus
+ - power-domains
+ - power-domain-names
+ - reg
+ - reg-names
+ - vdda-phy-supply
+ - vdda-pll-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
+ #include <dt-bindings/clock/qcom,sc8280xp-camcc.h>
+ #include <dt-bindings/interconnect/qcom,sc8280xp.h>
+ #include <dt-bindings/power/qcom-rpmpd.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ camss: camss@ac5a000 {
+ compatible = "qcom,sc8280xp-camss";
+
+ reg = <0 0x0ac5a000 0 0x2000>,
+ <0 0x0ac5c000 0 0x2000>,
+ <0 0x0ac65000 0 0x2000>,
+ <0 0x0ac67000 0 0x2000>,
+ <0 0x0acaf000 0 0x4000>,
+ <0 0x0acb3000 0 0x1000>,
+ <0 0x0acb6000 0 0x4000>,
+ <0 0x0acba000 0 0x1000>,
+ <0 0x0acbd000 0 0x4000>,
+ <0 0x0acc1000 0 0x1000>,
+ <0 0x0acc4000 0 0x4000>,
+ <0 0x0acc8000 0 0x1000>,
+ <0 0x0accb000 0 0x4000>,
+ <0 0x0accf000 0 0x1000>,
+ <0 0x0acd2000 0 0x4000>,
+ <0 0x0acd6000 0 0x1000>,
+ <0 0x0acd9000 0 0x4000>,
+ <0 0x0acdd000 0 0x1000>,
+ <0 0x0ace0000 0 0x4000>,
+ <0 0x0ace4000 0 0x1000>;
+
+ reg-names = "csiphy2",
+ "csiphy3",
+ "csiphy0",
+ "csiphy1",
+ "vfe0",
+ "csid0",
+ "vfe1",
+ "csid1",
+ "vfe2",
+ "csid2",
+ "vfe_lite0",
+ "csid0_lite",
+ "vfe_lite1",
+ "csid1_lite",
+ "vfe_lite2",
+ "csid2_lite",
+ "vfe_lite3",
+ "csid3_lite",
+ "vfe3",
+ "csid3";
+
+ vdda-phy-supply = <&vreg_l6d>;
+ vdda-pll-supply = <&vreg_l4d>;
+
+ interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 464 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 477 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 478 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 479 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 640 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 641 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 758 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 759 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 760 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 761 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 762 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 764 IRQ_TYPE_LEVEL_HIGH>;
+
+ interrupt-names = "csid1_lite",
+ "vfe_lite1",
+ "csiphy3",
+ "csid0",
+ "vfe0",
+ "csid1",
+ "vfe1",
+ "csid0_lite",
+ "vfe_lite0",
+ "csiphy0",
+ "csiphy1",
+ "csiphy2",
+ "csid2",
+ "vfe2",
+ "csid3_lite",
+ "csid2_lite",
+ "vfe_lite3",
+ "vfe_lite2",
+ "csid3",
+ "vfe3";
+
+ power-domains = <&camcc IFE_0_GDSC>,
+ <&camcc IFE_1_GDSC>,
+ <&camcc IFE_2_GDSC>,
+ <&camcc IFE_3_GDSC>,
+ <&camcc TITAN_TOP_GDSC>;
+
+ power-domain-names = "ife0",
+ "ife1",
+ "ife2",
+ "ife3",
+ "top";
+
+ clocks = <&camcc CAMCC_CAMNOC_AXI_CLK>,
+ <&camcc CAMCC_CPAS_AHB_CLK>,
+ <&camcc CAMCC_CSIPHY0_CLK>,
+ <&camcc CAMCC_CSI0PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY1_CLK>,
+ <&camcc CAMCC_CSI1PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY2_CLK>,
+ <&camcc CAMCC_CSI2PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY3_CLK>,
+ <&camcc CAMCC_CSI3PHYTIMER_CLK>,
+ <&camcc CAMCC_IFE_0_AXI_CLK>,
+ <&camcc CAMCC_IFE_0_CLK>,
+ <&camcc CAMCC_IFE_0_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_0_CSID_CLK>,
+ <&camcc CAMCC_IFE_1_AXI_CLK>,
+ <&camcc CAMCC_IFE_1_CLK>,
+ <&camcc CAMCC_IFE_1_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_1_CSID_CLK>,
+ <&camcc CAMCC_IFE_2_AXI_CLK>,
+ <&camcc CAMCC_IFE_2_CLK>,
+ <&camcc CAMCC_IFE_2_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_2_CSID_CLK>,
+ <&camcc CAMCC_IFE_3_AXI_CLK>,
+ <&camcc CAMCC_IFE_3_CLK>,
+ <&camcc CAMCC_IFE_3_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_3_CSID_CLK>,
+ <&camcc CAMCC_IFE_LITE_0_CLK>,
+ <&camcc CAMCC_IFE_LITE_0_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_LITE_0_CSID_CLK>,
+ <&camcc CAMCC_IFE_LITE_1_CLK>,
+ <&camcc CAMCC_IFE_LITE_1_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_LITE_1_CSID_CLK>,
+ <&camcc CAMCC_IFE_LITE_2_CLK>,
+ <&camcc CAMCC_IFE_LITE_2_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_LITE_2_CSID_CLK>,
+ <&camcc CAMCC_IFE_LITE_3_CLK>,
+ <&camcc CAMCC_IFE_LITE_3_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_LITE_3_CSID_CLK>,
+ <&gcc GCC_CAMERA_HF_AXI_CLK>,
+ <&gcc GCC_CAMERA_SF_AXI_CLK>;
+
+ clock-names = "camnoc_axi",
+ "cpas_ahb",
+ "csiphy0",
+ "csiphy0_timer",
+ "csiphy1",
+ "csiphy1_timer",
+ "csiphy2",
+ "csiphy2_timer",
+ "csiphy3",
+ "csiphy3_timer",
+ "vfe0_axi",
+ "vfe0",
+ "vfe0_cphy_rx",
+ "vfe0_csid",
+ "vfe1_axi",
+ "vfe1",
+ "vfe1_cphy_rx",
+ "vfe1_csid",
+ "vfe2_axi",
+ "vfe2",
+ "vfe2_cphy_rx",
+ "vfe2_csid",
+ "vfe3_axi",
+ "vfe3",
+ "vfe3_cphy_rx",
+ "vfe3_csid",
+ "vfe_lite0",
+ "vfe_lite0_cphy_rx",
+ "vfe_lite0_csid",
+ "vfe_lite1",
+ "vfe_lite1_cphy_rx",
+ "vfe_lite1_csid",
+ "vfe_lite2",
+ "vfe_lite2_cphy_rx",
+ "vfe_lite2_csid",
+ "vfe_lite3",
+ "vfe_lite3_cphy_rx",
+ "vfe_lite3_csid",
+ "gcc_axi_hf",
+ "gcc_axi_sf";
+
+
+ iommus = <&apps_smmu 0x2000 0x4e0>,
+ <&apps_smmu 0x2020 0x4e0>,
+ <&apps_smmu 0x2040 0x4e0>,
+ <&apps_smmu 0x2060 0x4e0>,
+ <&apps_smmu 0x2080 0x4e0>,
+ <&apps_smmu 0x20e0 0x4e0>,
+ <&apps_smmu 0x20c0 0x4e0>,
+ <&apps_smmu 0x20a0 0x4e0>,
+ <&apps_smmu 0x2400 0x4e0>,
+ <&apps_smmu 0x2420 0x4e0>,
+ <&apps_smmu 0x2440 0x4e0>,
+ <&apps_smmu 0x2460 0x4e0>,
+ <&apps_smmu 0x2480 0x4e0>,
+ <&apps_smmu 0x24e0 0x4e0>,
+ <&apps_smmu 0x24c0 0x4e0>,
+ <&apps_smmu 0x24a0 0x4e0>;
+
+ interconnects = <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_CAMERA_CFG 0>,
+ <&mmss_noc MASTER_CAMNOC_HF 0 &mc_virt SLAVE_EBI1 0>,
+ <&mmss_noc MASTER_CAMNOC_SF 0 &mc_virt SLAVE_EBI1 0>,
+ <&mmss_noc MASTER_CAMNOC_ICP 0 &mc_virt SLAVE_EBI1 0>;
+ interconnect-names = "cam_ahb",
+ "cam_hf_mnoc",
+ "cam_sf_mnoc",
+ "cam_sf_icp_mnoc";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ csiphy_ep0: endpoint@0 {
+ reg = <0>;
+ clock-lanes = <7>;
+ data-lanes = <0 1>;
+ remote-endpoint = <&sensor_ep>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst
index c4123a16b5f9..7f6f3dcd5c90 100644
--- a/Documentation/driver-api/media/drivers/index.rst
+++ b/Documentation/driver-api/media/drivers/index.rst
@@ -26,6 +26,7 @@ Video4Linux (V4L) drivers
vimc-devel
zoran
ccs/ccs
+ ipu6
Digital TV drivers
diff --git a/Documentation/driver-api/media/drivers/ipu6.rst b/Documentation/driver-api/media/drivers/ipu6.rst
new file mode 100644
index 000000000000..6e1dd19b36fb
--- /dev/null
+++ b/Documentation/driver-api/media/drivers/ipu6.rst
@@ -0,0 +1,205 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================
+Intel IPU6 Driver
+==================
+
+Author: Bingbu Cao <bingbu.cao@intel.com>
+
+Overview
+=========
+
+Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some
+Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and
+Meteor Lake. IPU6 consists of two major systems: Input System (ISYS) and
+Processing System (PSYS). IPU6 are visible on the PCI bus as a single device, it
+can be found by ``lspci``:
+
+``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)``
+
+IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is
+visible for driver.
+
+Buttress
+=========
+
+The IPU6 is connecting to the system fabric with Buttress which is enabling host
+driver to control the IPU6, it also allows IPU6 access the system memory to
+store and load frame pixel streams and any other metadata.
+
+Buttress mainly manages several system functionalities: power management,
+interrupt handling, firmware authentication and global timer sync.
+
+ISYS and PSYS Power flow
+------------------------
+
+IPU6 driver initialize the ISYS and PSYS power up or down request by setting the
+Buttress frequency control register for ISYS and PSYS
+(``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL``) in
+function:
+
+.. c:function:: int ipu6_buttress_power(...)
+
+Buttress forwards the request to Punit, after Punit execute the power up flow,
+Buttress indicates driver that ISYS or PSYS is powered up by updating the power
+status registers.
+
+.. Note:: ISYS power up needs take place prior to PSYS power up, ISYS power down
+ needs take place after PSYS power down due to hardware limitation.
+
+Interrupt
+---------
+
+IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered when
+ISYS, PSYS, Buttress event or error happen, driver can get the interrupt cause
+by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``, driver
+clears the irq status and then calls specific ISYS or PSYS irq handler.
+
+.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...)
+
+Security and firmware authentication
+-------------------------------------
+
+To address the IPU6 firmware security concerns, the IPU6 firmware needs to
+undergo an authentication process before it is allowed to executed on the IPU6
+internal processors. The IPU6 driver will work with Converged Security Engine
+(CSE) to complete authentication process. The CSE is responsible of
+authenticating the IPU6 firmware. The authenticated firmware binary is copied
+into an isolated memory region. Firmware authentication process is implemented
+by CSE following an IPC handshake with the IPU6 driver. There are some Buttress
+registers used by the CSE and the IPU6 driver to communicate with each other via
+IPC.
+
+.. c:function:: int ipu6_buttress_authenticate(...)
+
+Global timer sync
+-----------------
+
+The IPU6 driver initiates a Hammock Harbor synchronization flow each time it
+starts camera operation. The IPU6 will synchronizes an internal counter in the
+Buttress with a copy of the SoC time, this counter maintains the up-to-date time
+until camera operation is stopped. The IPU6 driver can use this time counter to
+calibrate the timestamp based on the timestamp in response event from firmware.
+
+.. c:function:: int ipu6_buttress_start_tsc_sync(...)
+
+DMA and MMU
+============
+
+The IPU6 has its own scalar processor where the firmware run at and an internal
+32-bit virtual address space. The IPU6 has MMU address translation hardware to
+allow that scalar processors to access the internal memory and external system
+memory through IPU6 virtual address. The address translation is based on two
+levels of page lookup tables stored in system memory which are maintained by the
+IPU6 driver. The IPU6 driver sets the level-1 page table base address to MMU
+register and allows MMU to perform page table lookups.
+
+The IPU6 driver exports its own DMA operations. The IPU6 driver will update the
+page table entries for each DMA operation and invalidate the MMU TLB after each
+unmap and free.
+
+.. code-block:: none
+
+ const struct dma_map_ops ipu6_dma_ops = {
+ .alloc = ipu6_dma_alloc,
+ .free = ipu6_dma_free,
+ .mmap = ipu6_dma_mmap,
+ .map_sg = ipu6_dma_map_sg,
+ .unmap_sg = ipu6_dma_unmap_sg,
+ ...
+ };
+
+.. Note:: IPU6 MMU works behind IOMMU so for each IPU6 DMA ops, driver will call
+ generic PCI DMA ops to ask IOMMU to do the additional mapping if VT-d
+ enabled.
+
+Firmware file format
+====================
+
+The IPU6 firmware is in Code Partition Directory (CPD) file format. The CPD
+firmware contains a CPD header, several CPD entries and components. The CPD
+component includes 3 entries - manifest, metadata and module data. Manifest and
+metadata are defined by CSE and used by CSE for authentication. Module data is
+specific to IPU6 which holds the binary data of firmware called package
+directory. The IPU6 driver (``ipu6-cpd.c`` in particular) parses and validates
+the CPD firmware file and gets the package directory binary data of the IPU6
+firmware, copies it to specific DMA buffer and sets its base address to Buttress
+``FW_SOURCE_BASE`` register. Finally the CSE will do authentication for this
+firmware binary.
+
+
+Syscom interface
+================
+
+The IPU6 driver communicates with firmware via the Syscom ABI. Syscom is an
+inter-processor communication mechanism between the IPU scalar processors and
+the CPU. There are a number of resources shared between firmware and software.
+A system memory region where the message queues reside, firmware can access the
+memory region via the IPU MMU. The Syscom queues are FIFO fixed depth queues
+with a configurable number of tokens (messages). There are also common IPU6 MMIO
+registers where the queue read and write indices reside. Software and firmware
+function as producer and consumer of tokens in the queues and update the write
+and read indices separately when sending or receiving each message.
+
+The IPU6 driver must prepare and configure the number of input and output
+queues, configure the count of tokens per queue and the size of per token before
+initiating and starting the communication with firmware. Firmware and software
+must use same configurations. The IPU6 Buttress has a number of firmware boot
+parameter registers which can be used to store the address of configuration and
+initialise the Syscom state, then driver can request firmware to start and run via
+setting the scalar processor control status register.
+
+Input System
+============
+
+IPU6 input system consists of MIPI D-PHY and several CSI-2 receivers. It can
+capture image pixel data from camera sensors or other MIPI CSI-2 output devices.
+
+D-PHYs and CSI-2 ports lane mapping
+-----------------------------------
+
+The IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and
+Alder Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL
+D-PHY and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an
+adaptional layer between D-PHY and CSI-2 receiver controller which includes port
+configuration, PHY wrapper or private test interfaces for D-PHY. There are 3
+D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and
+``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6.
+
+Different IPU6 versions have different D-PHY lanes mappings, On Tiger Lake,
+there are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports,
+see the PPI mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper
+Lake and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, the IPU6 supports
+maximum 4 CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock
+lanes so IPU6 support maximum 6 CSI-2 ports.
+
+.. Note:: Each pair of CSI-2 two ports is a single unit that can share the data
+ lanes. For example, for CSI-2 port 0 and 1, CSI-2 port 0 support
+ maximum 4 data lanes, CSI-2 port 1 support maximum 2 data lanes, CSI-2
+ port 0 with 2 data lanes can work together with CSI-2 port 1 with 2
+ data lanes. If trying to use CSI-2 port 0 with 4 lanes, CSI-2 port 1
+ will not be available as the 4 data lanes are shared by CSI-2 port 0
+ and 1. The same applies to CSI ports 2/3, 4/5 and 7/8.
+
+ISYS firmware ABIs
+------------------
+
+The IPU6 firmware implements a series of ABIs for software access. In general,
+software firstly prepares the stream configuration ``struct
+ipu6_fw_isys_stream_cfg_data_abi`` and sends the configuration to firmware via
+sending ``STREAM_OPEN`` command. Stream configuration includes input pins and
+output pins, input pin ``struct ipu6_fw_isys_input_pin_info_abi`` defines the
+resolution and data type of input source, output pin ``struct
+ipu6_fw_isys_output_pin_info_abi`` defines the output resolution, stride and
+frame format, etc.
+
+Once the driver gets the interrupt from firmware that indicates stream open
+successfully, the driver will send the ``STREAM_START`` and ``STREAM_CAPTURE``
+command to request firmware to start capturing image frames. ``STREAM_CAPTURE``
+command queues the buffers to firmware with ``struct
+ipu6_fw_isys_frame_buff_set``, software then waits for the interrupt and
+response from firmware, ``PIN_DATA_READY`` means a buffer is ready on a specific
+output pin and then software can return the buffer to user.
+
+.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do
+ capture by IPU6 ISYS driver.
diff --git a/Documentation/userspace-api/media/cec/cec-func-open.rst b/Documentation/userspace-api/media/cec/cec-func-open.rst
index d86563a34b9e..125c8ac6680b 100644
--- a/Documentation/userspace-api/media/cec/cec-func-open.rst
+++ b/Documentation/userspace-api/media/cec/cec-func-open.rst
@@ -70,5 +70,5 @@ include:
``ENOMEM``
Insufficient kernel memory was available.
-``ENXIO``
- No device corresponding to this device special file exists.
+``ENODEV``
+ Device not found or was removed.
diff --git a/Documentation/userspace-api/media/dvb/frontend_f_open.rst b/Documentation/userspace-api/media/dvb/frontend_f_open.rst
index bb37eded0870..70e169b8f601 100644
--- a/Documentation/userspace-api/media/dvb/frontend_f_open.rst
+++ b/Documentation/userspace-api/media/dvb/frontend_f_open.rst
@@ -91,7 +91,7 @@ appropriately.
- The caller has no permission to access the device.
- - ``EBUSY``
- - The the device driver is already in use.
+ - The device driver is already in use.
- - ``EMFILE``
- The process already has the maximum number of files open.
diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst
index 96a360edbf3b..55a9c37130ba 100644
--- a/Documentation/userspace-api/media/glossary.rst
+++ b/Documentation/userspace-api/media/glossary.rst
@@ -25,6 +25,13 @@ Glossary
See :ref:`cec`.
+ Data Unit
+
+ Unit of data transported by a bus. On parallel buses, the data unit
+ consists of one or more related samples while on serial buses the data
+ unit is logical. If the data unit is image data, it may also be called a
+ pixel.
+
Device Driver
Part of the Linux Kernel that implements support for a hardware
component.
@@ -173,6 +180,11 @@ Glossary
An integrated circuit that integrates all components of a computer
or other electronic systems.
+ Stream
+ A distinct flow of data (image data or metadata) from an initial source
+ to a final sink. The initial source may be e.g. an image sensor and the
+ final sink e.g. a memory buffer.
+
V4L2 API
**V4L2 userspace API**
diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst
index 0e7e1ee1471a..5eee9ab60395 100644
--- a/Documentation/userspace-api/media/v4l/dev-meta.rst
+++ b/Documentation/userspace-api/media/v4l/dev-meta.rst
@@ -47,6 +47,12 @@ member of the ``fmt`` union as needed per the desired operation. Both drivers
and applications must set the remainder of the :c:type:`v4l2_format` structure
to 0.
+Devices that capture metadata by line have the struct v4l2_fmtdesc
+``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such
+devices can typically also :ref:`capture image data <capture>`. This primarily
+involves devices that receive the data from a different devices such as a camera
+sensor.
+
.. c:type:: v4l2_meta_format
.. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}|
@@ -65,3 +71,18 @@ to 0.
- ``buffersize``
- Maximum buffer size in bytes required for data. The value is set by the
driver.
+ * - __u32
+ - ``width``
+ - Width of a line of metadata in Data Units. Valid when
+ :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set,
+ otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
+ * - __u32
+ - ``height``
+ - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag
+ ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
+ :c:func:`VIDIOC_ENUM_FMT`.
+ * - __u32
+ - ``bytesperline``
+ - Offset in bytes between the beginning of two consecutive lines. Valid
+ when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is
+ set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst
index 43988516acdd..0f9eda3187f3 100644
--- a/Documentation/userspace-api/media/v4l/dev-subdev.rst
+++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst
@@ -506,6 +506,8 @@ source pads.
subdev-formats
+.. _subdev-routing:
+
Streams, multiplexed media pads and internal routing
----------------------------------------------------
@@ -527,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source
pads.
Subdevice drivers that support multiplexed streams are compatible with
-non-multiplexed subdev drivers, but, of course, require a routing configuration
-where the link between those two types of drivers contains only a single
-stream.
+non-multiplexed subdev drivers. However, if the driver at the sink end of a link
+does not support streams, then only stream 0 of source end may be captured.
+There may be additional limitations specific to the sink device.
Understanding streams
^^^^^^^^^^^^^^^^^^^^^
@@ -570,6 +572,29 @@ Any configurations of a stream within a pad, such as format or selections,
are independent of similar configurations on other streams. This is
subject to change in the future.
+Device types and routing setup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Different kinds of sub-devices have differing behaviour for route activation,
+depending on the hardware. In all cases, however, only routes that have the
+``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active.
+
+Devices generating the streams may allow enabling and disabling some of the
+routes or have a fixed routing configuration. If the routes can be disabled, not
+declaring the routes (or declaring them without
+``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will
+disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes
+back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE``
+flag unset.
+
+Devices transporting the streams almost always have more configurability with
+respect to routing. Typically any route between the sub-device's sink and source
+pads is possible, and multiple routes (usually up to certain limited number) may
+be active simultaneously. For such devices, no routes are created by the driver
+and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is
+called on the sub-device. Such newly created routes have the device's default
+configuration for format and selection rectangles.
+
Configuring streams
^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/userspace-api/media/v4l/func-open.rst b/Documentation/userspace-api/media/v4l/func-open.rst
index ba23ff1e45dd..be3808cbf876 100644
--- a/Documentation/userspace-api/media/v4l/func-open.rst
+++ b/Documentation/userspace-api/media/v4l/func-open.rst
@@ -65,8 +65,8 @@ EBUSY
The driver does not support multiple opens and the device is already
in use.
-ENXIO
- No device corresponding to this device special file exists.
+ENODEV
+ Device not found or was removed.
ENOMEM
Not enough kernel memory was available to complete the request.
diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
index 0bb61fc5bc00..c23aac823d2c 100644
--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
+++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
@@ -13,9 +13,10 @@ These formats are used for the :ref:`metadata` interface only.
:maxdepth: 1
metafmt-d4xx
+ metafmt-generic
metafmt-intel-ipu3
metafmt-rkisp1
metafmt-uvc
+ metafmt-vivid
metafmt-vsp1-hgo
metafmt-vsp1-hgt
- metafmt-vivid
diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
new file mode 100644
index 000000000000..78ab56b21682
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
@@ -0,0 +1,340 @@
+.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
+
+********************************************************************************************************************************************************************************************************************************************************************************
+V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O')
+********************************************************************************************************************************************************************************************************************************************************************************
+
+
+Generic line-based metadata formats
+
+
+Description
+===========
+
+These generic line-based metadata formats define the memory layout of the data
+without defining the format or meaning of the metadata itself.
+
+.. _v4l2-meta-fmt-generic-8:
+
+V4L2_META_FMT_GENERIC_8
+-----------------------
+
+The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format
+is used on CSI-2 for 8 bits per :term:`Data Unit`.
+
+Additionally it is used for 16 bits per Data Unit when two bytes of metadata are
+packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is
+:ref:`V4L2_META_FMT_GENERIC_CSI2_16 <v4l2-meta-fmt-generic-csi2-16>`.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_8.**
+Each cell is one byte. "M" denotes a byte of metadata.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - M\ :sub:`10`
+ - M\ :sub:`20`
+ - M\ :sub:`30`
+ * - start + 4:
+ - M\ :sub:`01`
+ - M\ :sub:`11`
+ - M\ :sub:`21`
+ - M\ :sub:`31`
+
+.. _v4l2-meta-fmt-generic-csi2-10:
+
+V4L2_META_FMT_GENERIC_CSI2_10
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit
+Data Units, with one padding byte after every four bytes of metadata. This
+format is typically used by CSI-2 receivers with a source that transmits
+MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory
+as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+This format is also used in conjunction with 20 bits per :term:`Data Unit`
+formats that pack two bytes of metadata into one Data Unit. Otherwise the
+20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20
+<v4l2-meta-fmt-generic-csi2-20>`.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - M\ :sub:`10`
+ - M\ :sub:`20`
+ - M\ :sub:`30`
+ - x
+ * - start + 5:
+ - M\ :sub:`01`
+ - M\ :sub:`11`
+ - M\ :sub:`21`
+ - M\ :sub:`31`
+ - x
+
+.. _v4l2-meta-fmt-generic-csi2-12:
+
+V4L2_META_FMT_GENERIC_CSI2_12
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit
+Data Units, with one padding byte after every two bytes of metadata. This format
+is typically used by CSI-2 receivers with a source that transmits
+MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory
+as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+This format is also used in conjunction with 24 bits per :term:`Data Unit`
+formats that pack two bytes of metadata into one Data Unit. Otherwise the
+24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24
+<v4l2-meta-fmt-generic-csi2-24>`.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - M\ :sub:`10`
+ - x
+ - M\ :sub:`20`
+ - M\ :sub:`30`
+ - x
+ * - start + 6:
+ - M\ :sub:`01`
+ - M\ :sub:`11`
+ - x
+ - M\ :sub:`21`
+ - M\ :sub:`31`
+ - x
+
+.. _v4l2-meta-fmt-generic-csi2-14:
+
+V4L2_META_FMT_GENERIC_CSI2_14
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit
+Data Units, with three padding bytes after every four bytes of metadata. This
+format is typically used by CSI-2 receivers with a source that transmits
+MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory
+as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - M\ :sub:`10`
+ - M\ :sub:`20`
+ - M\ :sub:`30`
+ - x
+ - x
+ - x
+ * - start + 7:
+ - M\ :sub:`01`
+ - M\ :sub:`11`
+ - M\ :sub:`21`
+ - M\ :sub:`31`
+ - x
+ - x
+ - x
+
+.. _v4l2-meta-fmt-generic-csi2-16:
+
+V4L2_META_FMT_GENERIC_CSI2_16
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit
+Data Units, with one padding byte after every byte of metadata. This format is
+typically used by CSI-2 receivers with a source that transmits
+MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory
+as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+Some devices support more efficient packing of metadata in conjunction with
+16-bit image data. In that case the dataformat is
+:ref:`V4L2_META_FMT_GENERIC_8 <v4l2-meta-fmt-generic-8>`.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - x
+ - M\ :sub:`10`
+ - x
+ - M\ :sub:`20`
+ - x
+ - M\ :sub:`30`
+ - x
+ * - start + 8:
+ - M\ :sub:`01`
+ - x
+ - M\ :sub:`11`
+ - x
+ - M\ :sub:`21`
+ - x
+ - M\ :sub:`31`
+ - x
+
+.. _v4l2-meta-fmt-generic-csi2-20:
+
+V4L2_META_FMT_GENERIC_CSI2_20
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit
+Data Units, with alternating one or two padding bytes after every byte of
+metadata. This format is typically used by CSI-2 receivers with a source that
+transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data
+to memory as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+Some devices support more efficient packing of metadata in conjunction with
+16-bit image data. In that case the dataformat is
+:ref:`V4L2_META_FMT_GENERIC_CSI2_10 <v4l2-meta-fmt-generic-csi2-10>`.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - x
+ - M\ :sub:`10`
+ - x
+ - x
+ - M\ :sub:`20`
+ - x
+ - M\ :sub:`30`
+ - x
+ - x
+ * - start + 10:
+ - M\ :sub:`01`
+ - x
+ - M\ :sub:`11`
+ - x
+ - x
+ - M\ :sub:`21`
+ - x
+ - M\ :sub:`31`
+ - x
+ - x
+
+.. _v4l2-meta-fmt-generic-csi2-24:
+
+V4L2_META_FMT_GENERIC_CSI2_24
+-----------------------------
+
+V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit
+Data Units, with two padding bytes after every byte of metadata. This format is
+typically used by CSI-2 receivers with a source that transmits
+MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory
+as-is.
+
+The packing of the data follows the MIPI CSI-2 specification and the padding of
+the data is defined in the MIPI CCS specification.
+
+Some devices support more efficient packing of metadata in conjunction with
+16-bit image data. In that case the dataformat is
+:ref:`V4L2_META_FMT_GENERIC_CSI2_12 <v4l2-meta-fmt-generic-csi2-12>`.
+
+This format is little endian.
+
+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.**
+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
+
+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
+
+.. flat-table:: Sample 4x2 Metadata Frame
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8
+
+ * - start + 0:
+ - M\ :sub:`00`
+ - x
+ - x
+ - M\ :sub:`10`
+ - x
+ - x
+ - M\ :sub:`20`
+ - x
+ - x
+ - M\ :sub:`30`
+ - x
+ - x
+ * - start + 12:
+ - M\ :sub:`01`
+ - x
+ - x
+ - M\ :sub:`11`
+ - x
+ - x
+ - M\ :sub:`21`
+ - x
+ - x
+ - M\ :sub:`31`
+ - x
+ - x
diff --git a/Documentation/userspace-api/media/v4l/mmap.rst b/Documentation/userspace-api/media/v4l/mmap.rst
index a5672573dd6f..1a48c3cbda17 100644
--- a/Documentation/userspace-api/media/v4l/mmap.rst
+++ b/Documentation/userspace-api/media/v4l/mmap.rst
@@ -188,7 +188,7 @@ Example: Mapping buffers in the multi-planar API
buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
PROT_READ | PROT_WRITE, /* recommended */
MAP_SHARED, /* recommended */
- fd, buffer.m.planes[j].m.offset);
+ fd, buffer.m.planes[j].m.mem_offset);
if (MAP_FAILED == buffers[i].start[j]) {
/* If you do not exit here you should unmap() and free()
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
index cf8e4dfbfbd4..b3c5779521d8 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
@@ -36,6 +36,8 @@ are often referred to as greyscale formats.
- Byte 2
- Byte 3
- Byte 4
+ - Byte 5
+ - Byte 6
* .. _V4L2-PIX-FMT-GREY:
@@ -47,6 +49,8 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-IPU3-Y10:
@@ -58,6 +62,8 @@ are often referred to as greyscale formats.
- Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6]
- Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4]
- Y'\ :sub:`3`\ [9:2]
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y10:
@@ -69,6 +75,8 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y10BPACK:
@@ -80,6 +88,8 @@ are often referred to as greyscale formats.
- Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6]
- Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8]
- Y'\ :sub:`3`\ [7:0]
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y10P:
@@ -91,6 +101,8 @@ are often referred to as greyscale formats.
- Y'\ :sub:`2`\ [9:2]
- Y'\ :sub:`3`\ [9:2]
- Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0]
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y12:
@@ -102,6 +114,8 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y012:
@@ -113,6 +127,21 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
+
+ * .. _V4L2-PIX-FMT-Y12P:
+
+ - ``V4L2_PIX_FMT_Y12P``
+ - 'Y12P'
+
+ - Y'\ :sub:`0`\ [11:4]
+ - Y'\ :sub:`1`\ [11:4]
+ - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`0`\ [3:0]
+ - ...
+ - ...
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y14:
@@ -124,6 +153,21 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
+
+ * .. _V4L2-PIX-FMT-Y14P:
+
+ - ``V4L2_PIX_FMT_Y14P``
+ - 'Y14P'
+
+ - Y'\ :sub:`0`\ [13:6]
+ - Y'\ :sub:`1`\ [13:6]
+ - Y'\ :sub:`2`\ [13:6]
+ - Y'\ :sub:`3`\ [13:6]
+ - Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [5:0]
+ - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [5:2]
+ - Y'\ :sub:`3`\ [5:0] Y'\ :sub:`2`\ [5:4]
* .. _V4L2-PIX-FMT-Y16:
@@ -135,6 +179,8 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
* .. _V4L2-PIX-FMT-Y16-BE:
@@ -146,6 +192,8 @@ are often referred to as greyscale formats.
- ...
- ...
- ...
+ - ...
+ - ...
.. raw:: latex
diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
index eb3cd20b0cf2..d2a6cd2e1eb2 100644
--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
@@ -33,7 +33,7 @@ Media Bus Formats
* - __u32
- ``field``
- Field order, from enum :c:type:`v4l2_field`. See
- :ref:`field-order` for details.
+ :ref:`field-order` for details. Zero for metadata mbus codes.
* - __u32
- ``colorspace``
- Image colorspace, from enum :c:type:`v4l2_colorspace`.
@@ -45,7 +45,7 @@ Media Bus Formats
conversion is supported by setting the flag
V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
- See :ref:`v4l2-subdev-mbus-code-flags`.
+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
* - union {
- (anonymous)
* - __u16
@@ -61,7 +61,7 @@ Media Bus Formats
that ycbcr_enc conversion is supported by setting the flag
V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
- See :ref:`v4l2-subdev-mbus-code-flags`.
+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
* - __u16
- ``hsv_enc``
- HSV encoding, from enum :c:type:`v4l2_hsv_encoding`.
@@ -75,7 +75,7 @@ Media Bus Formats
that hsv_enc conversion is supported by setting the flag
V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct
:c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
- See :ref:`v4l2-subdev-mbus-code-flags`
+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
* - }
-
* - __u16
@@ -90,8 +90,8 @@ Media Bus Formats
The driver indicates that quantization conversion is supported by
setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the
corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
- during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
-
+ during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
+ metadata mbus codes.
* - __u16
- ``xfer_func``
- Transfer function, from enum :c:type:`v4l2_xfer_func`.
@@ -104,7 +104,8 @@ Media Bus Formats
The driver indicates that the transfer function conversion is supported by
setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the
corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
- during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
+ during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
+ metadata mbus codes.
* - __u16
- ``flags``
- flags See: :ref:v4l2-mbus-framefmt-flags
@@ -8306,3 +8307,257 @@ The following table lists the existing metadata formats.
both sides of the link and the bus format is a fixed
metadata format that is not configurable from userspace.
Width and height will be set to 0 for this format.
+
+Generic Serial Metadata Formats
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Generic serial metadata formats are used on serial buses where the actual data
+content is more or less device specific but the data is transmitted and received
+by multiple devices that do not process the data in any way, simply writing
+it to system memory for processing in software at the end of the pipeline.
+
+"b" in an array cell signifies a byte of data, followed by the number of the bit
+and finally the bit number in subscript. "x" indicates a padding bit.
+
+.. _media-bus-format-generic-meta:
+
+.. cssclass: longtable
+
+.. flat-table:: Generic Serial Metadata Formats
+ :header-rows: 2
+ :stub-columns: 0
+
+ * - Identifier
+ - Code
+ -
+ - :cspan:`23` Data organization within bus :term:`Data Unit`
+ * -
+ -
+ - Bit
+ - 23
+ - 22
+ - 21
+ - 20
+ - 19
+ - 18
+ - 17
+ - 16
+ - 15
+ - 14
+ - 13
+ - 12
+ - 11
+ - 10
+ - 9
+ - 8
+ - 7
+ - 6
+ - 5
+ - 4
+ - 3
+ - 2
+ - 1
+ - 0
+ * .. _MEDIA-BUS-FMT-META-8:
+
+ - MEDIA_BUS_FMT_META_8
+ - 0x8001
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ * .. _MEDIA-BUS-FMT-META-10:
+
+ - MEDIA_BUS_FMT_META_10
+ - 0x8002
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ * .. _MEDIA-BUS-FMT-META-12:
+
+ - MEDIA_BUS_FMT_META_12
+ - 0x8003
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ - x
+ - x
+ * .. _MEDIA-BUS-FMT-META-14:
+
+ - MEDIA_BUS_FMT_META_14
+ - 0x8004
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ * .. _MEDIA-BUS-FMT-META-16:
+
+ - MEDIA_BUS_FMT_META_16
+ - 0x8005
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ * .. _MEDIA-BUS-FMT-META-20:
+
+ - MEDIA_BUS_FMT_META_20
+ - 0x8006
+ -
+ -
+ -
+ -
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ * .. _MEDIA-BUS-FMT-META-24:
+
+ - MEDIA_BUS_FMT_META_24
+ - 0x8007
+ -
+ - b0\ :sub:`7`
+ - b0\ :sub:`6`
+ - b0\ :sub:`5`
+ - b0\ :sub:`4`
+ - b0\ :sub:`3`
+ - b0\ :sub:`2`
+ - b0\ :sub:`1`
+ - b0\ :sub:`0`
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
+ - x
diff --git a/Documentation/userspace-api/media/v4l/user-func.rst b/Documentation/userspace-api/media/v4l/user-func.rst
index 15ff0bf7bbe6..6f661138801c 100644
--- a/Documentation/userspace-api/media/v4l/user-func.rst
+++ b/Documentation/userspace-api/media/v4l/user-func.rst
@@ -62,6 +62,7 @@ Function Reference
vidioc-query-dv-timings
vidioc-querystd
vidioc-reqbufs
+ vidioc-remove-bufs
vidioc-s-hw-freq-seek
vidioc-streamon
vidioc-subdev-enum-frame-interval
diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
index 000c154b0f98..3adb3d205531 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
@@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently:
The application can ask to configure the quantization of the capture
device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with
:ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set.
+ * - ``V4L2_FMT_FLAG_META_LINE_BASED``
+ - 0x0200
+ - The metadata format is line-based. In this case the ``width``,
+ ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are
+ valid. The buffer consists of ``height`` lines, each having ``width``
+ Data Units of data and the offset (in bytes) between the beginning of
+ each two consecutive lines is ``bytesperline``.
Return Value
============
diff --git a/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst b/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
new file mode 100644
index 000000000000..1995b39af9ba
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
@@ -0,0 +1,86 @@
+.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
+
+.. _VIDIOC_REMOVE_BUFS:
+
+************************
+ioctl VIDIOC_REMOVE_BUFS
+************************
+
+Name
+====
+
+VIDIOC_REMOVE_BUFS - Removes buffers from a queue
+
+Synopsis
+========
+
+.. c:macro:: VIDIOC_REMOVE_BUFS
+
+``int ioctl(int fd, VIDIOC_REMOVE_BUFS, struct v4l2_remove_buffers *argp)``
+
+Arguments
+=========
+
+``fd``
+ File descriptor returned by :c:func:`open()`.
+
+``argp``
+ Pointer to struct :c:type:`v4l2_remove_buffers`.
+
+Description
+===========
+
+Applications can optionally call the :ref:`VIDIOC_REMOVE_BUFS` ioctl to
+remove buffers from a queue.
+:ref:`VIDIOC_CREATE_BUFS` ioctl support is mandatory to enable :ref:`VIDIOC_REMOVE_BUFS`.
+This ioctl is available if the ``V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS`` capability
+is set on the queue when :c:func:`VIDIOC_REQBUFS` or :c:func:`VIDIOC_CREATE_BUFS`
+are invoked.
+
+.. c:type:: v4l2_remove_buffers
+
+.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.5cm}|
+
+.. flat-table:: struct v4l2_remove_buffers
+ :header-rows: 0
+ :stub-columns: 0
+ :widths: 1 1 2
+
+ * - __u32
+ - ``index``
+ - The starting buffer index to remove. This field is ignored if count == 0.
+ * - __u32
+ - ``count``
+ - The number of buffers to be removed with indices 'index' until 'index + count - 1'.
+ All buffers in this range must be valid and in DEQUEUED state.
+ :ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type`, if it is
+ invalid it returns ``EINVAL`` error code.
+ If count is set to 0 :ref:`VIDIOC_REMOVE_BUFS` will do nothing and return 0.
+ * - __u32
+ - ``type``
+ - Type of the stream or buffers, this is the same as the struct
+ :c:type:`v4l2_format` ``type`` field. See
+ :c:type:`v4l2_buf_type` for valid values.
+ * - __u32
+ - ``reserved``\ [13]
+ - A place holder for future extensions. Drivers and applications
+ must set the array to zero.
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter. If an error occurs, no
+buffers will be freed and one of the error codes below will be returned:
+
+EBUSY
+ File I/O is in progress.
+ One or more of the buffers in the range ``index`` to ``index + count - 1`` are not
+ in DEQUEUED state.
+
+EINVAL
+ One or more of the buffers in the range ``index`` to ``index + count - 1`` do not
+ exist in the queue.
+ The buffer type (``type`` field) is not valid.
diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst
index 0b3a41a45d05..bbc22dd76032 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst
@@ -121,6 +121,7 @@ aborting or finishing any DMA in progress, an implicit
.. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF:
.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS:
.. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS:
+.. _V4L2-BUF-CAP-SUPPORTS-REMOVE-BUFS:
.. raw:: latex
diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst
index 92d933631fda..88a748103a71 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst
@@ -37,9 +37,9 @@ Description
.. note::
- This is an :ref:`obsolete` interface and may be removed
- in the future. It is superseded by
- :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`.
+ This is an :ref:`obsolete` interface and may be removed in the future. It is
+ superseded by :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. No new
+ extensions to the :c:type:`v4l2_subdev_crop` structure will be accepted.
To retrieve the current crop rectangle applications set the ``pad``
field of a struct :c:type:`v4l2_subdev_crop` to the
diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst
index 26b5004bfe6d..1cf795480602 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst
@@ -43,23 +43,39 @@ The routing configuration determines the flows of data inside an entity.
Drivers report their current routing tables using the
``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes
with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and
-setting or clearing flags of the ``flags`` field of a
-struct :c:type:`v4l2_subdev_route`.
+setting or clearing flags of the ``flags`` field of a struct
+:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also
+``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user.
-All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This
-means that the userspace must reconfigure all streams after calling the ioctl
-with e.g. ``VIDIOC_SUBDEV_S_FMT``.
+All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called.
+This means that the userspace must reconfigure all stream formats and selections
+after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``.
Only subdevices which have both sink and source pads can support routing.
-When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application
-provided ``num_routes`` is not big enough to contain all the available routes
-the subdevice exposes, drivers return the ENOSPC error code and adjust the
-value of the ``num_routes`` field. Application should then reserve enough memory
-for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again.
-
-On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
-``num_routes`` field to reflect the actual number of routes returned.
+The ``len_routes`` field indicates the number of routes that can fit in the
+``routes`` array allocated by userspace. It is set by applications for both
+ioctls to indicate how many routes the kernel can return, and is never modified
+by the kernel.
+
+The ``num_routes`` field indicates the number of routes in the routing
+table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of
+routes that the application stored in the ``routes`` array. For both ioctls, it
+is returned by the kernel and indicates how many routes are stored in the
+subdevice routing table. This may be smaller or larger than the value of
+``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as
+drivers may adjust the requested routing table.
+
+The kernel can return a ``num_routes`` value larger than ``len_routes`` from
+both ioctls. This indicates thare are more routes in the routing table than fits
+the ``routes`` array. In this case, the ``routes`` array is filled by the kernel
+with the first ``len_routes`` entries of the subdevice routing table. This is
+not considered to be an error, and the ioctl call succeeds. If the applications
+wants to retrieve the missing routes, it can issue a new
+``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array.
+
+``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in
+``num_routes`` field due to e.g. hardware properties.
.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
@@ -74,6 +90,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
- ``which``
- Routing table to be accessed, from enum
:ref:`v4l2_subdev_format_whence <v4l2-subdev-format-whence>`.
+ * - __u32
+ - ``len_routes``
+ - The length of the array (as in memory reserved for the array)
* - struct :c:type:`v4l2_subdev_route`
- ``routes[]``
- Array of struct :c:type:`v4l2_subdev_route` entries
@@ -81,7 +100,7 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
- ``num_routes``
- Number of entries of the routes array
* - __u32
- - ``reserved``\ [5]
+ - ``reserved``\ [11]
- Reserved for future extensions. Applications and drivers must set
the array to zero.
@@ -135,10 +154,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set
appropriately. The generic error codes are described at the
:ref:`Generic Error Codes <gen-errors>` chapter.
-ENOSPC
- The application provided ``num_routes`` is not big enough to contain
- all the available routes the subdevice exposes.
-
EINVAL
The sink or source pad identifiers reference a non-existing pad or reference
pads of different types (ie. the sink_pad identifiers refers to a source
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 3e58aac4ef0b..bdc628e8c1d6 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags
replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags
replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags
replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags
+replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags
# V4L2 timecode types
replace define V4L2_TC_TYPE_24FPS timecode-type
diff --git a/MAINTAINERS b/MAINTAINERS
index c4e39b7cd472..e2479fa32469 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4110,6 +4110,13 @@ N: bcm113*
N: bcm216*
N: kona
+BROADCOM BCM2835 CAMERA DRIVERS
+M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
+F: drivers/media/platform/broadcom/bcm2835-unicam*
+
BROADCOM BCM47XX MIPS ARCHITECTURE
M: Hauke Mehrtens <hauke@hauke-m.de>
M: Rafał Miłecki <zajec5@gmail.com>
@@ -11091,6 +11098,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg
F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
F: drivers/staging/media/ipu3/
+INTEL IPU6 INPUT SYSTEM DRIVER
+M: Sakari Ailus <sakari.ailus@linux.intel.com>
+M: Bingbu Cao <bingbu.cao@intel.com>
+R: Tianshu Qiu <tian.shu.qiu@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/admin-guide/media/ipu6-isys.rst
+F: drivers/media/pci/intel/ipu6/
+
INTEL ISHTP ECLITE DRIVER
M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
L: platform-driver-x86@vger.kernel.org
@@ -16539,7 +16556,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/ov8856.yaml
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
F: drivers/media/i2c/ov8856.c
OMNIVISION OV8858 SENSOR DRIVER
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 559a172ebc6c..da09834990b8 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -490,6 +490,15 @@ int cec_thread_func(void *_adap)
goto unlock;
}
+ if (adap->transmit_in_progress &&
+ adap->transmit_in_progress_aborted) {
+ if (adap->transmitting)
+ cec_data_cancel(adap->transmitting,
+ CEC_TX_STATUS_ABORTED, 0);
+ adap->transmit_in_progress = false;
+ adap->transmit_in_progress_aborted = false;
+ goto unlock;
+ }
if (adap->transmit_in_progress && timeout) {
/*
* If we timeout, then log that. Normally this does
@@ -771,6 +780,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
{
struct cec_data *data;
bool is_raw = msg_is_raw(msg);
+ int err;
if (adap->devnode.unregistered)
return -ENODEV;
@@ -935,11 +945,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
* Release the lock and wait, retake the lock afterwards.
*/
mutex_unlock(&adap->lock);
- wait_for_completion_killable(&data->c);
- if (!data->completed)
- cancel_delayed_work_sync(&data->work);
+ err = wait_for_completion_killable(&data->c);
+ cancel_delayed_work_sync(&data->work);
mutex_lock(&adap->lock);
+ if (err)
+ adap->transmit_in_progress_aborted = true;
+
/* Cancel the transmit if it was interrupted */
if (!data->completed) {
if (data->msg.tx_status & CEC_TX_STATUS_OK)
@@ -1575,9 +1587,12 @@ unconfigure:
*/
static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
{
- if (WARN_ON(adap->is_configuring || adap->is_configured))
+ if (WARN_ON(adap->is_claiming_log_addrs ||
+ adap->is_configuring || adap->is_configured))
return;
+ adap->is_claiming_log_addrs = true;
+
init_completion(&adap->config_completion);
/* Ready to kick off the thread */
@@ -1592,6 +1607,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
wait_for_completion(&adap->config_completion);
mutex_lock(&adap->lock);
}
+ adap->is_claiming_log_addrs = false;
}
/*
diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c
index 67dc79ef1705..3ef915344304 100644
--- a/drivers/media/cec/core/cec-api.c
+++ b/drivers/media/cec/core/cec-api.c
@@ -178,7 +178,7 @@ static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
CEC_LOG_ADDRS_FL_CDC_ONLY;
mutex_lock(&adap->lock);
- if (!adap->is_configuring &&
+ if (!adap->is_claiming_log_addrs && !adap->is_configuring &&
(!log_addrs.num_log_addrs || !adap->is_configured) &&
!cec_is_busy(adap, fh)) {
err = __cec_s_log_addrs(adap, &log_addrs, block);
@@ -664,6 +664,8 @@ static int cec_release(struct inode *inode, struct file *filp)
list_del_init(&data->xfer_list);
}
mutex_unlock(&adap->lock);
+
+ mutex_lock(&fh->lock);
while (!list_empty(&fh->msgs)) {
struct cec_msg_entry *entry =
list_first_entry(&fh->msgs, struct cec_msg_entry, list);
@@ -681,6 +683,7 @@ static int cec_release(struct inode *inode, struct file *filp)
kfree(entry);
}
}
+ mutex_unlock(&fh->lock);
kfree(fh);
cec_put_device(devnode);
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 5a54db839e5d..6f940df0230c 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -62,12 +62,12 @@ int cec_get_device(struct cec_devnode *devnode)
*/
mutex_lock(&devnode->lock);
/*
- * return ENXIO if the cec device has been removed
+ * return ENODEV if the cec device has been removed
* already or if it is not registered anymore.
*/
if (!devnode->registered) {
mutex_unlock(&devnode->lock);
- return -ENXIO;
+ return -ENODEV;
}
/* and increase the device refcount */
get_device(&devnode->dev);
diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
index 48ed2993d2f0..8fbbb4091455 100644
--- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
+++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/pci.h>
@@ -573,6 +574,12 @@ static void cros_ec_cec_remove(struct platform_device *pdev)
}
}
+static const struct platform_device_id cros_ec_cec_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
+
static struct platform_driver cros_ec_cec_driver = {
.probe = cros_ec_cec_probe,
.remove_new = cros_ec_cec_remove,
@@ -580,6 +587,7 @@ static struct platform_driver cros_ec_cec_driver = {
.name = DRV_NAME,
.pm = &cros_ec_cec_pm_ops,
},
+ .id_table = cros_ec_cec_id,
};
module_platform_driver(cros_ec_cec_driver);
@@ -587,4 +595,3 @@ module_platform_driver(cros_ec_cec_driver);
MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
index 7569d8cdd4d8..fe3348af543e 100644
--- a/drivers/media/common/saa7146/saa7146_hlp.c
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
@@ -122,7 +122,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev,
xacm = 0;
/* set horizontal filter parameters (CXY = CXUV) */
- cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
+ cxy = hps_h_coeff_tab[min(xpsc - 1, 63)].hps_coeff;
cxuv = cxy;
/* calculate and set horizontal fine scale (xsci) */
@@ -151,7 +151,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev,
xacm = 0;
/* get best match in the table of attenuations
for horizontal scaling */
- h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
+ h_atten = hps_h_coeff_tab[min(xpsc - 1, 63)].weight_sum;
for (i = 0; h_attenuation[i] != 0; i++) {
if (h_attenuation[i] >= h_atten)
@@ -283,10 +283,10 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field
}
/* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
- cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
+ cya_cyb = hps_v_coeff_tab[min(yacl, 63)].hps_coeff;
/* get best match in the table of attenuations for vertical scaling */
- v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
+ v_atten = hps_v_coeff_tab[min(yacl, 63)].weight_sum;
for (i = 0; v_attenuation[i] != 0; i++) {
if (v_attenuation[i] >= v_atten)
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index b6bf8f232f48..358f1fe42975 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -421,11 +421,12 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb)
*/
static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index)
{
- WARN_ON(index >= q->max_num_buffers || q->bufs[index] || vb->vb2_queue);
+ WARN_ON(index >= q->max_num_buffers || test_bit(index, q->bufs_bitmap) || vb->vb2_queue);
q->bufs[index] = vb;
vb->index = index;
vb->vb2_queue = q;
+ set_bit(index, q->bufs_bitmap);
}
/**
@@ -434,6 +435,7 @@ static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, uns
*/
static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
{
+ clear_bit(vb->index, vb->vb2_queue->bufs_bitmap);
vb->vb2_queue->bufs[vb->index] = NULL;
vb->vb2_queue = NULL;
}
@@ -442,16 +444,19 @@ static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
* __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type)
* video buffer memory for all buffers/planes on the queue and initializes the
* queue
+ * @first_index: index of the first created buffer, all newly allocated buffers
+ * have indices in the range [first_index..first_index+count-1]
*
* Returns the number of buffers successfully allocated.
*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
unsigned int num_buffers, unsigned int num_planes,
- const unsigned plane_sizes[VB2_MAX_PLANES])
+ const unsigned int plane_sizes[VB2_MAX_PLANES],
+ unsigned int *first_index)
{
- unsigned int q_num_buffers = vb2_get_num_buffers(q);
unsigned int buffer, plane;
struct vb2_buffer *vb;
+ unsigned long index = q->max_num_buffers;
int ret;
/*
@@ -459,7 +464,25 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
* in the queue is below q->max_num_buffers
*/
num_buffers = min_t(unsigned int, num_buffers,
- q->max_num_buffers - q_num_buffers);
+ q->max_num_buffers - vb2_get_num_buffers(q));
+
+ while (num_buffers) {
+ index = bitmap_find_next_zero_area(q->bufs_bitmap, q->max_num_buffers,
+ 0, num_buffers, 0);
+
+ if (index < q->max_num_buffers)
+ break;
+ /* Try to find free space for less buffers */
+ num_buffers--;
+ }
+
+ /* If there is no space left to allocate buffers return 0 to indicate the error */
+ if (!num_buffers) {
+ *first_index = 0;
+ return 0;
+ }
+
+ *first_index = index;
for (buffer = 0; buffer < num_buffers; ++buffer) {
/* Allocate vb2 buffer structures */
@@ -479,7 +502,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
vb->planes[plane].min_length = plane_sizes[plane];
}
- vb2_queue_add_buffer(q, vb, q_num_buffers + buffer);
+ vb2_queue_add_buffer(q, vb, index++);
call_void_bufop(q, init_buffer, vb);
/* Allocate video buffer memory for the MMAP type */
@@ -517,17 +540,16 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
}
/*
- * __vb2_free_mem() - release all video buffer memory for a given queue
+ * __vb2_free_mem() - release video buffer memory for a given range of
+ * buffers in a given queue
*/
-static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int start, unsigned int count)
{
- unsigned int buffer;
+ unsigned int i;
struct vb2_buffer *vb;
- unsigned int q_num_buffers = vb2_get_num_buffers(q);
- for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
- ++buffer) {
- vb = vb2_get_buffer(q, buffer);
+ for (i = start; i < start + count; i++) {
+ vb = vb2_get_buffer(q, i);
if (!vb)
continue;
@@ -542,35 +564,33 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
}
/*
- * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * __vb2_queue_free() - free @count buffers from @start index of the queue - video memory and
* related information, if no buffers are left return the queue to an
* uninitialized state. Might be called even if the queue has already been freed.
*/
-static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned int count)
{
- unsigned int buffer;
- unsigned int q_num_buffers = vb2_get_num_buffers(q);
+ unsigned int i;
lockdep_assert_held(&q->mmap_lock);
/* Call driver-provided cleanup function for each buffer, if provided */
- for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
- ++buffer) {
- struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
if (vb && vb->planes[0].mem_priv)
call_void_vb_qop(vb, buf_cleanup, vb);
}
/* Release video buffer memory */
- __vb2_free_mem(q, buffers);
+ __vb2_free_mem(q, start, count);
#ifdef CONFIG_VIDEO_ADV_DEBUG
/*
* Check that all the calls were balanced during the life-time of this
* queue. If not then dump the counters to the kernel log.
*/
- if (q_num_buffers) {
+ if (vb2_get_num_buffers(q)) {
bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
q->cnt_prepare_streaming != q->cnt_unprepare_streaming ||
q->cnt_wait_prepare != q->cnt_wait_finish;
@@ -596,8 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
q->cnt_stop_streaming = 0;
q->cnt_unprepare_streaming = 0;
}
- for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) {
- struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
bool unbalanced;
if (!vb)
@@ -614,7 +634,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
if (unbalanced) {
pr_info("unbalanced counters for queue %p, buffer %d:\n",
- q, buffer);
+ q, i);
if (vb->cnt_buf_init != vb->cnt_buf_cleanup)
pr_info(" buf_init: %u buf_cleanup: %u\n",
vb->cnt_buf_init, vb->cnt_buf_cleanup);
@@ -648,9 +668,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
#endif
/* Free vb2 buffers */
- for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
- ++buffer) {
- struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
if (!vb)
continue;
@@ -659,7 +678,6 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
kfree(vb);
}
- q->num_buffers -= buffers;
if (!vb2_get_num_buffers(q)) {
q->memory = VB2_MEMORY_UNKNOWN;
INIT_LIST_HEAD(&q->queued_list);
@@ -691,7 +709,7 @@ EXPORT_SYMBOL(vb2_buffer_in_use);
static bool __buffers_in_use(struct vb2_queue *q)
{
unsigned int buffer;
- for (buffer = 0; buffer < vb2_get_num_buffers(q); ++buffer) {
+ for (buffer = 0; buffer < q->max_num_buffers; ++buffer) {
struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
if (!vb)
@@ -813,6 +831,32 @@ static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem)
return true;
}
+static int vb2_core_allocated_buffers_storage(struct vb2_queue *q)
+{
+ if (!q->bufs)
+ q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
+ if (!q->bufs)
+ return -ENOMEM;
+
+ if (!q->bufs_bitmap)
+ q->bufs_bitmap = bitmap_zalloc(q->max_num_buffers, GFP_KERNEL);
+ if (!q->bufs_bitmap) {
+ kfree(q->bufs);
+ q->bufs = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void vb2_core_free_buffers_storage(struct vb2_queue *q)
+{
+ kfree(q->bufs);
+ q->bufs = NULL;
+ bitmap_free(q->bufs_bitmap);
+ q->bufs_bitmap = NULL;
+}
+
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int flags, unsigned int *count)
{
@@ -820,7 +864,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int q_num_bufs = vb2_get_num_buffers(q);
unsigned plane_sizes[VB2_MAX_PLANES] = { };
bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
- unsigned int i;
+ unsigned int i, first_index;
int ret = 0;
if (q->streaming) {
@@ -851,9 +895,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* queued without ever calling STREAMON.
*/
__vb2_queue_cancel(q);
- __vb2_queue_free(q, q_num_bufs);
+ __vb2_queue_free(q, 0, q->max_num_buffers);
mutex_unlock(&q->mmap_lock);
+ q->is_busy = 0;
/*
* In case of REQBUFS(0) return immediately without calling
* driver's queue_setup() callback and allocating resources.
@@ -865,7 +910,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
/*
* Make sure the requested values and current defaults are sane.
*/
- num_buffers = max_t(unsigned int, *count, q->min_queued_buffers);
+ num_buffers = max_t(unsigned int, *count, q->min_reqbufs_allocation);
num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers);
memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
/*
@@ -873,10 +918,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* in the queue_setup op.
*/
mutex_lock(&q->mmap_lock);
- if (!q->bufs)
- q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
- if (!q->bufs)
- ret = -ENOMEM;
+ ret = vb2_core_allocated_buffers_storage(q);
q->memory = memory;
mutex_unlock(&q->mmap_lock);
if (ret)
@@ -906,8 +948,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
/* Finally, allocate buffers and video memory */
allocated_buffers =
- __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
+ __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, &first_index);
if (allocated_buffers == 0) {
+ /* There shouldn't be any buffers allocated, so first_index == 0 */
+ WARN_ON(first_index);
dprintk(q, 1, "memory allocation failed\n");
ret = -ENOMEM;
goto error;
@@ -917,7 +961,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* There is no point in continuing if we can't allocate the minimum
* number of buffers needed by this vb2_queue.
*/
- if (allocated_buffers < q->min_queued_buffers)
+ if (allocated_buffers < q->min_reqbufs_allocation)
ret = -ENOMEM;
/*
@@ -946,7 +990,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
}
mutex_lock(&q->mmap_lock);
- q->num_buffers = allocated_buffers;
if (ret < 0) {
/*
@@ -954,7 +997,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* from already queued buffers and it will reset q->memory to
* VB2_MEMORY_UNKNOWN.
*/
- __vb2_queue_free(q, allocated_buffers);
+ __vb2_queue_free(q, first_index, allocated_buffers);
mutex_unlock(&q->mmap_lock);
return ret;
}
@@ -966,6 +1009,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
*/
*count = allocated_buffers;
q->waiting_for_buffers = !q->is_output;
+ q->is_busy = 1;
return 0;
@@ -973,6 +1017,7 @@ error:
mutex_lock(&q->mmap_lock);
q->memory = VB2_MEMORY_UNKNOWN;
mutex_unlock(&q->mmap_lock);
+ vb2_core_free_buffers_storage(q);
return ret;
}
EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
@@ -980,7 +1025,8 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int flags, unsigned int *count,
unsigned int requested_planes,
- const unsigned int requested_sizes[])
+ const unsigned int requested_sizes[],
+ unsigned int *first_index)
{
unsigned int num_planes = 0, num_buffers, allocated_buffers;
unsigned plane_sizes[VB2_MAX_PLANES] = { };
@@ -1005,11 +1051,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
* value in the queue_setup op.
*/
mutex_lock(&q->mmap_lock);
+ ret = vb2_core_allocated_buffers_storage(q);
q->memory = memory;
- if (!q->bufs)
- q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
- if (!q->bufs)
- ret = -ENOMEM;
mutex_unlock(&q->mmap_lock);
if (ret)
return ret;
@@ -1042,7 +1085,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
/* Finally, allocate buffers and video memory */
allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
- num_planes, plane_sizes);
+ num_planes, plane_sizes, first_index);
if (allocated_buffers == 0) {
dprintk(q, 1, "memory allocation failed\n");
ret = -ENOMEM;
@@ -1072,7 +1115,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
}
mutex_lock(&q->mmap_lock);
- q->num_buffers += allocated_buffers;
if (ret < 0) {
/*
@@ -1080,7 +1122,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
* from already queued buffers and it will reset q->memory to
* VB2_MEMORY_UNKNOWN.
*/
- __vb2_queue_free(q, allocated_buffers);
+ __vb2_queue_free(q, *first_index, allocated_buffers);
mutex_unlock(&q->mmap_lock);
return -ENOMEM;
}
@@ -1091,6 +1133,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
* to the userspace.
*/
*count = allocated_buffers;
+ q->is_busy = 1;
return 0;
@@ -1648,6 +1691,44 @@ int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb)
}
EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
+int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count)
+{
+ unsigned int i, ret = 0;
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+
+ if (count == 0)
+ return 0;
+
+ if (count > q_num_bufs)
+ return -EINVAL;
+
+ if (start > q->max_num_buffers - count)
+ return -EINVAL;
+
+ mutex_lock(&q->mmap_lock);
+
+ /* Check that all buffers in the range exist */
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+
+ if (!vb) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ }
+ __vb2_queue_free(q, start, count);
+ dprintk(q, 2, "%u buffers removed\n", count);
+
+unlock:
+ mutex_unlock(&q->mmap_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_core_remove_bufs);
+
/*
* vb2_start_streaming() - Attempt to start streaming.
* @q: videobuf2 queue
@@ -1694,7 +1775,7 @@ static int vb2_start_streaming(struct vb2_queue *q)
* Forcefully reclaim buffers if the driver did not
* correctly return them to vb2.
*/
- for (i = 0; i < vb2_get_num_buffers(q); ++i) {
+ for (i = 0; i < q->max_num_buffers; ++i) {
vb = vb2_get_buffer(q, i);
if (!vb)
@@ -2100,7 +2181,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
* to vb2 in stop_streaming().
*/
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
- for (i = 0; i < vb2_get_num_buffers(q); i++) {
+ for (i = 0; i < q->max_num_buffers; i++) {
struct vb2_buffer *vb = vb2_get_buffer(q, i);
if (!vb)
@@ -2144,7 +2225,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
* call to __fill_user_buffer() after buf_finish(). That order can't
* be changed, so we can't move the buf_finish() to __vb2_dqbuf().
*/
- for (i = 0; i < vb2_get_num_buffers(q); i++) {
+ for (i = 0; i < q->max_num_buffers; i++) {
struct vb2_buffer *vb;
struct media_request *req;
@@ -2503,7 +2584,7 @@ int vb2_core_queue_init(struct vb2_queue *q)
WARN_ON(!q->ops->buf_queue))
return -EINVAL;
- if (WARN_ON(q->max_num_buffers > MAX_BUFFER_INDEX) ||
+ if (WARN_ON(q->max_num_buffers < VB2_MAX_FRAME) ||
WARN_ON(q->min_queued_buffers > q->max_num_buffers))
return -EINVAL;
@@ -2521,6 +2602,25 @@ int vb2_core_queue_init(struct vb2_queue *q)
if (WARN_ON(q->supports_requests && q->min_queued_buffers))
return -EINVAL;
+ /*
+ * The minimum requirement is 2: one buffer is used
+ * by the hardware while the other is being processed by userspace.
+ */
+ if (q->min_reqbufs_allocation < 2)
+ q->min_reqbufs_allocation = 2;
+
+ /*
+ * If the driver needs 'min_queued_buffers' in the queue before
+ * calling start_streaming() then the minimum requirement is
+ * 'min_queued_buffers + 1' to keep at least one buffer available
+ * for userspace.
+ */
+ if (q->min_reqbufs_allocation < q->min_queued_buffers + 1)
+ q->min_reqbufs_allocation = q->min_queued_buffers + 1;
+
+ if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers))
+ return -EINVAL;
+
INIT_LIST_HEAD(&q->queued_list);
INIT_LIST_HEAD(&q->done_list);
spin_lock_init(&q->done_lock);
@@ -2552,9 +2652,9 @@ void vb2_core_queue_release(struct vb2_queue *q)
__vb2_cleanup_fileio(q);
__vb2_queue_cancel(q);
mutex_lock(&q->mmap_lock);
- __vb2_queue_free(q, vb2_get_num_buffers(q));
- kfree(q->bufs);
- q->bufs = NULL;
+ __vb2_queue_free(q, 0, q->max_num_buffers);
+ vb2_core_free_buffers_storage(q);
+ q->is_busy = 0;
mutex_unlock(&q->mmap_lock);
}
EXPORT_SYMBOL_GPL(vb2_core_queue_release);
@@ -2713,7 +2813,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
struct vb2_fileio_data *fileio;
struct vb2_buffer *vb;
int i, ret;
- unsigned int count = 0;
/*
* Sanity check
@@ -2734,18 +2833,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
if (q->streaming || vb2_get_num_buffers(q) > 0)
return -EBUSY;
- /*
- * Start with q->min_queued_buffers + 1, driver can increase it in
- * queue_setup()
- *
- * 'min_queued_buffers' buffers need to be queued up before you
- * can start streaming, plus 1 for userspace (or in this case,
- * kernelspace) processing.
- */
- count = max(2, q->min_queued_buffers + 1);
-
dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
- (read) ? "read" : "write", count, q->fileio_read_once,
+ (read) ? "read" : "write", q->min_reqbufs_allocation, q->fileio_read_once,
q->fileio_write_immediately);
fileio = kzalloc(sizeof(*fileio), GFP_KERNEL);
@@ -2759,13 +2848,19 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
* Request buffers and use MMAP type to force driver
* to allocate buffers by itself.
*/
- fileio->count = count;
+ fileio->count = q->min_reqbufs_allocation;
fileio->memory = VB2_MEMORY_MMAP;
fileio->type = q->type;
q->fileio = fileio;
ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
if (ret)
goto err_kfree;
+ /* vb2_fileio_data supports max VB2_MAX_FRAME buffers */
+ if (fileio->count > VB2_MAX_FRAME) {
+ dprintk(q, 1, "fileio: more than VB2_MAX_FRAME buffers requested\n");
+ ret = -ENOSPC;
+ goto err_reqbufs;
+ }
/*
* Userspace can never add or delete buffers later, so there
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index c575198e8354..293f3d5f1c4e 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -685,7 +685,7 @@ static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory,
*flags &= V4L2_MEMORY_FLAG_NON_COHERENT;
}
- *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
+ *caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
if (q->io_modes & VB2_MMAP)
*caps |= V4L2_BUF_CAP_SUPPORTS_MMAP;
if (q->io_modes & VB2_USERPTR)
@@ -795,11 +795,15 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
for (i = 0; i < requested_planes; i++)
if (requested_sizes[i] == 0)
return -EINVAL;
- return ret ? ret : vb2_core_create_bufs(q, create->memory,
- create->flags,
- &create->count,
- requested_planes,
- requested_sizes);
+ if (ret)
+ return ret;
+
+ return vb2_core_create_bufs(q, create->memory,
+ create->flags,
+ &create->count,
+ requested_planes,
+ requested_sizes,
+ &create->index);
}
EXPORT_SYMBOL_GPL(vb2_create_bufs);
@@ -997,6 +1001,24 @@ EXPORT_SYMBOL_GPL(vb2_poll);
/* vb2 ioctl helpers */
+int vb2_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *d)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->queue->type != d->type)
+ return -EINVAL;
+
+ if (d->count == 0)
+ return 0;
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+
+ return vb2_core_remove_bufs(vdev->queue, d->index, d->count);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs);
+
int vb2_ioctl_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 733d0bc4b4cc..b43695bc51e7 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -956,7 +956,7 @@ int dvb_usercopy(struct file *file,
int (*func)(struct file *file,
unsigned int cmd, void *arg))
{
- char sbuf[128];
+ char sbuf[128] = {};
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
diff --git a/drivers/media/dvb-frontends/as102_fe_types.h b/drivers/media/dvb-frontends/as102_fe_types.h
index 297f9520ebf9..8a4e392c8896 100644
--- a/drivers/media/dvb-frontends/as102_fe_types.h
+++ b/drivers/media/dvb-frontends/as102_fe_types.h
@@ -174,6 +174,6 @@ struct as10x_register_addr {
uint32_t addr;
/* register mode access */
uint8_t mode;
-};
+} __packed;
#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
index 9d989676e800..94a8e0b936b9 100644
--- a/drivers/media/dvb-frontends/cxd2880/Kconfig
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -5,4 +5,4 @@ config DVB_CXD2880
depends on DVB_CORE && SPI
default m if !MEDIA_SUBDRV_AUTOSELECT
help
- Say Y when you want to support this frontend. \ No newline at end of file
+ Say Y when you want to support this frontend.
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
index 15f7e58c5a30..2c2fd4bf79cc 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
@@ -33,7 +33,6 @@
#include <linux/kernel.h>
#include <linux/errno.h>
-#include <linux/firmware.h>
#include <linux/i2c.h>
/*
@@ -1910,7 +1909,6 @@ struct drx_demod_instance {
/* generic demodulator data */
struct i2c_adapter *i2c;
- const struct firmware *firmware;
};
/*-------------------------------------------------------------------------
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 19d8de400a68..6fcaf07e1b82 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -56,6 +56,7 @@ INCLUDE FILES
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
#include <linux/module.h>
+#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
@@ -1444,8 +1445,7 @@ static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
/* Read block from I2C **************************************************** */
do {
- u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ?
- datasize : DRXDAP_MAX_RCHUNKSIZE);
+ u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE);
bufx = 0;
@@ -1659,7 +1659,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
Address must be rewritten because HI is reset after data transport and
expects an address.
*/
- todo = (block_size < datasize ? block_size : datasize);
+ todo = min(block_size, datasize);
if (todo == 0) {
u16 overhead_size_i2c_addr = 0;
u16 data_block_size = 0;
@@ -1681,9 +1681,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
first_err = st;
}
bufx = 0;
- todo =
- (data_block_size <
- datasize ? data_block_size : datasize);
+ todo = min(data_block_size, datasize);
}
memcpy(&buf[bufx], data, todo);
/* write (address if can do and) data */
@@ -11750,6 +11748,7 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
u8 *mc_data = NULL;
unsigned size;
char *mc_file;
+ const struct firmware *fw;
/* Check arguments */
if (!mc_info || !mc_info->mc_file)
@@ -11757,28 +11756,22 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
mc_file = mc_info->mc_file;
- if (!demod->firmware) {
- const struct firmware *fw = NULL;
-
- rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
- if (rc < 0) {
- pr_err("Couldn't read firmware %s\n", mc_file);
- return rc;
- }
- demod->firmware = fw;
-
- if (demod->firmware->size < 2 * sizeof(u16)) {
- rc = -EINVAL;
- pr_err("Firmware is too short!\n");
- goto release;
- }
+ rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
+ if (rc < 0) {
+ pr_err("Couldn't read firmware %s\n", mc_file);
+ return rc;
+ }
- pr_info("Firmware %s, size %zu\n",
- mc_file, demod->firmware->size);
+ if (fw->size < 2 * sizeof(u16)) {
+ rc = -EINVAL;
+ pr_err("Firmware is too short!\n");
+ goto release;
}
- mc_data_init = demod->firmware->data;
- size = demod->firmware->size;
+ pr_info("Firmware %s, size %zu\n", mc_file, fw->size);
+
+ mc_data_init = fw->data;
+ size = fw->size;
mc_data = (void *)mc_data_init;
/* Check data */
@@ -11874,7 +11867,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
0x0000)) {
pr_err("error reading firmware at pos %zd\n",
mc_data - mc_data_init);
- return -EIO;
+ rc = -EIO;
+ goto release;
}
result = memcmp(curr_ptr, mc_data_buffer,
@@ -11883,7 +11877,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
if (result) {
pr_err("error verifying firmware at pos %zd\n",
mc_data - mc_data_init);
- return -EIO;
+ rc = -EIO;
+ goto release;
}
curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
@@ -11893,17 +11888,17 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
break;
}
default:
- return -EINVAL;
+ rc = -EINVAL;
+ goto release;
}
mc_data += mc_block_nr_bytes;
}
- return 0;
+ rc = 0;
release:
- release_firmware(demod->firmware);
- demod->firmware = NULL;
+ release_firmware(fw);
return rc;
}
@@ -12271,7 +12266,6 @@ static void drx39xxj_release(struct dvb_frontend *fe)
kfree(demod->my_ext_attr);
kfree(demod->my_common_attr);
kfree(demod->my_i2c_dev_addr);
- release_firmware(demod->firmware);
kfree(demod);
kfree(state);
}
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index 263887592415..231b45632ad5 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -2176,6 +2176,11 @@ static int lgdt3306a_probe(struct i2c_client *client)
struct dvb_frontend *fe;
int ret;
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "platform data is mandatory\n");
+ return -EINVAL;
+ }
+
config = kmemdup(client->dev.platform_data,
sizeof(struct lgdt3306a_config), GFP_KERNEL);
if (config == NULL) {
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index e0272054fca5..389548fa2e0c 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1000,6 +1000,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ /* to light up the LOCK led */
+ ret = m88ds3103_update_bits(dev, 0x11, 0x80, 0x00);
+ if (ret)
+ goto err;
+ }
+
dev->delivery_system = c->delivery_system;
return 0;
diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c
index 4ebbcf05cc09..91e9c378397c 100644
--- a/drivers/media/dvb-frontends/mxl5xx.c
+++ b/drivers/media/dvb-frontends/mxl5xx.c
@@ -1381,57 +1381,57 @@ static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id,
u32 nco_count_min = 0;
u32 clk_type = 0;
- struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
{0x90700010, 8, 1}, {0x90700010, 9, 1},
{0x90700010, 10, 1}, {0x90700010, 11, 1},
{0x90700010, 12, 1}, {0x90700010, 13, 1},
{0x90700010, 14, 1}, {0x90700010, 15, 1} };
- struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
{0x90700010, 16, 1}, {0x90700010, 17, 1},
{0x90700010, 18, 1}, {0x90700010, 19, 1},
{0x90700010, 20, 1}, {0x90700010, 21, 1},
{0x90700010, 22, 1}, {0x90700010, 23, 1} };
- struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
{0x90700014, 0, 1}, {0x90700014, 1, 1},
{0x90700014, 2, 1}, {0x90700014, 3, 1},
{0x90700014, 4, 1}, {0x90700014, 5, 1},
{0x90700014, 6, 1}, {0x90700014, 7, 1} };
- struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
{0x90700018, 0, 3}, {0x90700018, 4, 3},
{0x90700018, 8, 3}, {0x90700018, 12, 3},
{0x90700018, 16, 3}, {0x90700018, 20, 3},
{0x90700018, 24, 3}, {0x90700018, 28, 3} };
- struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
{0x9070000C, 16, 1}, {0x9070000C, 17, 1},
{0x9070000C, 18, 1}, {0x9070000C, 19, 1},
{0x9070000C, 20, 1}, {0x9070000C, 21, 1},
{0x9070000C, 22, 1}, {0x9070000C, 23, 1} };
- struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
{0x90700010, 0, 1}, {0x90700010, 1, 1},
{0x90700010, 2, 1}, {0x90700010, 3, 1},
{0x90700010, 4, 1}, {0x90700010, 5, 1},
{0x90700010, 6, 1}, {0x90700010, 7, 1} };
- struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
{0x9070000C, 0, 1}, {0x9070000C, 1, 1},
{0x9070000C, 2, 1}, {0x9070000C, 3, 1},
{0x9070000C, 4, 1}, {0x9070000C, 5, 1},
{0x9070000C, 6, 1}, {0x9070000C, 7, 1} };
- struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
{0x9070000C, 24, 1}, {0x9070000C, 25, 1},
{0x9070000C, 26, 1}, {0x9070000C, 27, 1},
{0x9070000C, 28, 1}, {0x9070000C, 29, 1},
{0x9070000C, 30, 1}, {0x9070000C, 31, 1} };
- struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
{0x90700014, 8, 1}, {0x90700014, 9, 1},
{0x90700014, 10, 1}, {0x90700014, 11, 1},
{0x90700014, 12, 1}, {0x90700014, 13, 1},
{0x90700014, 14, 1}, {0x90700014, 15, 1} };
- struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
{0x907001D4, 0, 1}, {0x907001D4, 1, 1},
{0x907001D4, 2, 1}, {0x907001D4, 3, 1},
{0x907001D4, 4, 1}, {0x907001D4, 5, 1},
{0x907001D4, 6, 1}, {0x907001D4, 7, 1} };
- struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
+ static const struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
{0x90700044, 16, 80}, {0x90700044, 16, 81},
{0x90700044, 16, 82}, {0x90700044, 16, 83},
{0x90700044, 16, 84}, {0x90700044, 16, 85},
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 434d003bf397..013d423d3263 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -513,10 +513,8 @@ static int si2165_upload_firmware(struct si2165_state *state)
ret = 0;
state->firmware_loaded = true;
error:
- if (fw) {
- release_firmware(fw);
- fw = NULL;
- }
+ release_firmware(fw);
+ fw = NULL;
return ret;
}
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index 2f4d8fb400cd..35634f9a8ab5 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -1277,7 +1277,7 @@ static int stb0899_get_dev_id(struct stb0899_state *state)
dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
- if (! (chip_id > 0)) {
+ if (!chip_id) {
dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
return -ENODEV;
diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c
index 5d5e4e9e4422..3e725cdcc66b 100644
--- a/drivers/media/dvb-frontends/tda10048.c
+++ b/drivers/media/dvb-frontends/tda10048.c
@@ -410,6 +410,7 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw)
struct tda10048_config *config = &state->config;
int i;
u32 if_freq_khz;
+ u64 sample_freq;
dprintk(1, "%s(bw = %d)\n", __func__, bw);
@@ -451,9 +452,11 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw)
dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor);
/* Calculate the sample frequency */
- state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45);
- state->sample_freq /= (state->pll_nfactor + 1);
- state->sample_freq /= (state->pll_pfactor + 4);
+ sample_freq = state->xtal_hz;
+ sample_freq *= state->pll_mfactor + 45;
+ do_div(sample_freq, state->pll_nfactor + 1);
+ do_div(sample_freq, state->pll_pfactor + 4);
+ state->sample_freq = sample_freq;
dprintk(1, "- sample_freq = %d\n", state->sample_freq);
/* Update the I/F */
diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c
index a34834487943..fd928787207e 100644
--- a/drivers/media/dvb-frontends/tda18271c2dd.c
+++ b/drivers/media/dvb-frontends/tda18271c2dd.c
@@ -328,7 +328,7 @@ static int CalcMainPLL(struct tda_state *state, u32 freq)
OscFreq = (u64) freq * (u64) Div;
OscFreq *= (u64) 16384;
- do_div(OscFreq, (u64)16000000);
+ do_div(OscFreq, 16000000);
MainDiv = OscFreq;
state->m_Regs[MPD] = PostDiv & 0x77;
@@ -352,7 +352,7 @@ static int CalcCalPLL(struct tda_state *state, u32 freq)
OscFreq = (u64)freq * (u64)Div;
/* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
OscFreq *= (u64)16384;
- do_div(OscFreq, (u64)16000000);
+ do_div(OscFreq, 16000000);
CalDiv = OscFreq;
state->m_Regs[CPD] = PostDiv;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 56f276b920ab..c6d3ee472d81 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -195,6 +195,7 @@ config VIDEO_IMX334
config VIDEO_IMX335
tristate "Sony IMX335 sensor support"
depends on OF_GPIO
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
IMX335 camera.
@@ -405,6 +406,7 @@ config VIDEO_OV2740
config VIDEO_OV4689
tristate "OmniVision OV4689 sensor support"
depends on GPIOLIB
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor-level driver for the OmniVision
OV4689 camera.
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 4829cbe32419..819ff9f7c90f 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1486,7 +1486,7 @@ static int adv7180_probe(struct i2c_client *client)
if (ret)
goto err_media_entity_cleanup;
- if (state->irq) {
+ if (state->irq > 0) {
ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
KBUILD_MODNAME, state);
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index ec151dc69c23..a4db9bae5f79 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -214,7 +214,7 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state,
* v4l2_subdev_video_ops
*/
-static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd,
+static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -254,7 +254,7 @@ error:
return ret;
}
-static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
+static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -269,7 +269,7 @@ static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd,
+static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -392,9 +392,6 @@ static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
}
static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
- .s_dv_timings = adv748x_hdmi_s_dv_timings,
- .g_dv_timings = adv748x_hdmi_g_dv_timings,
- .query_dv_timings = adv748x_hdmi_query_dv_timings,
.g_input_status = adv748x_hdmi_g_input_status,
.s_stream = adv748x_hdmi_s_stream,
.g_pixelaspect = adv748x_hdmi_g_pixelaspect,
@@ -413,7 +410,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi)
if (!tx)
return -ENOLINK;
- adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings);
+ adv748x_hdmi_query_dv_timings(&hdmi->sd, 0, &timings);
return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock);
}
@@ -610,6 +607,9 @@ static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = {
.get_fmt = adv748x_hdmi_get_format,
.get_edid = adv748x_hdmi_get_edid,
.set_edid = adv748x_hdmi_set_edid,
+ .s_dv_timings = adv748x_hdmi_s_dv_timings,
+ .g_dv_timings = adv748x_hdmi_g_dv_timings,
+ .query_dv_timings = adv748x_hdmi_query_dv_timings,
.dv_timings_cap = adv748x_hdmi_dv_timings_cap,
.enum_dv_timings = adv748x_hdmi_enum_dv_timings,
};
@@ -734,7 +734,7 @@ int adv748x_hdmi_init(struct adv748x_hdmi *hdmi)
struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30;
int ret;
- adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720);
+ adv748x_hdmi_s_dv_timings(&hdmi->sd, 0, &cea1280x720);
/* Initialise a default 16:9 aspect ratio */
hdmi->aspect_ratio.numerator = 16;
diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c
index 0f780eb6ef63..79946e9c7401 100644
--- a/drivers/media/i2c/adv7511-v4l2.c
+++ b/drivers/media/i2c/adv7511-v4l2.c
@@ -995,8 +995,8 @@ static int adv7511_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv7511_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv7511_state *state = get_adv7511_state(sd);
struct v4l2_bt_timings *bt = &timings->bt;
@@ -1004,6 +1004,9 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
/* quick sanity check */
if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL))
return -EINVAL;
@@ -1042,13 +1045,16 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int adv7511_g_dv_timings(struct v4l2_subdev *sd,
+static int adv7511_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv7511_state *state = get_adv7511_state(sd);
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
if (!timings)
return -EINVAL;
@@ -1078,8 +1084,6 @@ static int adv7511_dv_timings_cap(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops adv7511_video_ops = {
.s_stream = adv7511_s_stream,
- .s_dv_timings = adv7511_s_dv_timings,
- .g_dv_timings = adv7511_g_dv_timings,
};
/* ------------------------------ AUDIO OPS ------------------------------ */
@@ -1403,6 +1407,8 @@ static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
.enum_mbus_code = adv7511_enum_mbus_code,
.get_fmt = adv7511_get_fmt,
.set_fmt = adv7511_set_fmt,
+ .s_dv_timings = adv7511_s_dv_timings,
+ .g_dv_timings = adv7511_g_dv_timings,
.enum_dv_timings = adv7511_enum_dv_timings,
.dv_timings_cap = adv7511_dv_timings_cap,
};
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 319db3e847c4..48230d5109f0 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -1557,8 +1557,8 @@ static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd)
return freq;
}
-static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
const struct adv76xx_chip_info *info = state->info;
@@ -1687,8 +1687,8 @@ found:
return 0;
}
-static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
struct v4l2_bt_timings *bt;
@@ -1730,8 +1730,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct adv76xx_state *state = to_state(sd);
@@ -2607,7 +2607,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
stdi.lcf, stdi.bl, stdi.lcvs,
stdi.interlaced ? "interlaced" : "progressive",
stdi.hs_pol, stdi.vs_pol);
- if (adv76xx_query_dv_timings(sd, &timings))
+ if (adv76xx_query_dv_timings(sd, 0, &timings))
v4l2_info(sd, "No video detected\n");
else
v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -2726,9 +2726,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
.s_routing = adv76xx_s_routing,
.g_input_status = adv76xx_g_input_status,
- .s_dv_timings = adv76xx_s_dv_timings,
- .g_dv_timings = adv76xx_g_dv_timings,
- .query_dv_timings = adv76xx_query_dv_timings,
};
static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
@@ -2738,6 +2735,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
.set_fmt = adv76xx_set_format,
.get_edid = adv76xx_get_edid,
.set_edid = adv76xx_set_edid,
+ .s_dv_timings = adv76xx_s_dv_timings,
+ .g_dv_timings = adv76xx_g_dv_timings,
+ .query_dv_timings = adv76xx_query_dv_timings,
.dv_timings_cap = adv76xx_dv_timings_cap,
.enum_dv_timings = adv76xx_enum_dv_timings,
};
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 2ad0f9f5503d..f2d4217310e7 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -1518,7 +1518,7 @@ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS;
}
-static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
+static int adv7842_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv7842_state *state = to_state(sd);
@@ -1527,6 +1527,9 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
memset(timings, 0, sizeof(struct v4l2_dv_timings));
/* SDP block */
@@ -1643,7 +1646,7 @@ found:
return 0;
}
-static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
+static int adv7842_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv7842_state *state = to_state(sd);
@@ -1652,6 +1655,9 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
if (state->mode == ADV7842_MODE_SDP)
return -ENODATA;
@@ -1689,11 +1695,14 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int adv7842_g_dv_timings(struct v4l2_subdev *sd,
+static int adv7842_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct adv7842_state *state = to_state(sd);
+ if (pad != 0)
+ return -EINVAL;
+
if (state->mode == ADV7842_MODE_SDP)
return -ENODATA;
*timings = state->timings;
@@ -2780,7 +2789,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
"interlaced" : "progressive",
hs_pol, vs_pol);
}
- if (adv7842_query_dv_timings(sd, &timings))
+ if (adv7842_query_dv_timings(sd, 0, &timings))
v4l2_info(sd, "No video detected\n");
else
v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -3226,7 +3235,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)
memset(&state->timings, 0, sizeof(struct v4l2_dv_timings));
- adv7842_s_dv_timings(sd, &timings);
+ adv7842_s_dv_timings(sd, 0, &timings);
return ret;
}
@@ -3298,9 +3307,6 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
.s_routing = adv7842_s_routing,
.querystd = adv7842_querystd,
.g_input_status = adv7842_g_input_status,
- .s_dv_timings = adv7842_s_dv_timings,
- .g_dv_timings = adv7842_g_dv_timings,
- .query_dv_timings = adv7842_query_dv_timings,
};
static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
@@ -3309,6 +3315,9 @@ static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
.set_fmt = adv7842_set_format,
.get_edid = adv7842_get_edid,
.set_edid = adv7842_set_edid,
+ .s_dv_timings = adv7842_s_dv_timings,
+ .g_dv_timings = adv7842_g_dv_timings,
+ .query_dv_timings = adv7842_query_dv_timings,
.enum_dv_timings = adv7842_enum_dv_timings,
.dv_timings_cap = adv7842_dv_timings_cap,
};
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 84d29bcf0ccd..0e88ce0ef8d7 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -310,8 +310,8 @@ module_i2c_driver(dw9714_i2c_driver);
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_AUTHOR("Jian Xu Zheng");
-MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
-MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>");
-MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>");
+MODULE_AUTHOR("Yuning Pu");
+MODULE_AUTHOR("Jouni Ukkonen");
+MODULE_AUTHOR("Tommi Franttila");
MODULE_DESCRIPTION("DW9714 VCM driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index f548b1bb75fb..e932d25ca7b3 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -1475,7 +1475,7 @@ err_mutex:
return ret;
}
-static void __exit et8ek8_remove(struct i2c_client *client)
+static void et8ek8_remove(struct i2c_client *client)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
@@ -1517,7 +1517,7 @@ static struct i2c_driver et8ek8_i2c_driver = {
.of_match_table = et8ek8_of_table,
},
.probe = et8ek8_probe,
- .remove = __exit_p(et8ek8_remove),
+ .remove = et8ek8_remove,
.id_table = et8ek8_id_table,
};
diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
index 38c77d515786..b440f386f062 100644
--- a/drivers/media/i2c/hi556.c
+++ b/drivers/media/i2c/hi556.c
@@ -3,10 +3,13 @@
#include <asm/unaligned.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -633,6 +636,11 @@ struct hi556 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ /* GPIOs, clocks, etc. */
+ struct gpio_desc *reset_gpio;
+ struct clk *clk;
+ struct regulator *avdd;
+
/* Current mode */
const struct hi556_mode *cur_mode;
@@ -1206,8 +1214,18 @@ static int hi556_check_hwcfg(struct device *dev)
int ret = 0;
unsigned int i, j;
- if (!fwnode)
- return -ENXIO;
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver,
+ * wait for this.
+ */
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -EPROBE_DEFER;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
if (ret) {
@@ -1220,15 +1238,6 @@ static int hi556_check_hwcfg(struct device *dev)
return -EINVAL;
}
- ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
- if (!ep)
- return -ENXIO;
-
- ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
- fwnode_handle_put(ep);
- if (ret)
- return ret;
-
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
dev_err(dev, "number of CSI2 data lanes %d is not supported",
bus_cfg.bus.mipi_csi2.num_data_lanes);
@@ -1275,6 +1284,47 @@ static void hi556_remove(struct i2c_client *client)
mutex_destroy(&hi556->mutex);
}
+static int hi556_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hi556 *hi556 = to_hi556(sd);
+ int ret;
+
+ gpiod_set_value_cansleep(hi556->reset_gpio, 1);
+
+ ret = regulator_disable(hi556->avdd);
+ if (ret) {
+ dev_err(dev, "failed to disable avdd: %d\n", ret);
+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ return ret;
+ }
+
+ clk_disable_unprepare(hi556->clk);
+ return 0;
+}
+
+static int hi556_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hi556 *hi556 = to_hi556(sd);
+ int ret;
+
+ ret = clk_prepare_enable(hi556->clk);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(hi556->avdd);
+ if (ret) {
+ dev_err(dev, "failed to enable avdd: %d\n", ret);
+ clk_disable_unprepare(hi556->clk);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ usleep_range(5000, 5500);
+ return 0;
+}
+
static int hi556_probe(struct i2c_client *client)
{
struct hi556 *hi556;
@@ -1294,12 +1344,35 @@ static int hi556_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops);
+ hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(hi556->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ hi556->clk = devm_clk_get_optional(&client->dev, "clk");
+ if (IS_ERR(hi556->clk))
+ return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
+ "failed to get clock\n");
+
+ /* The regulator core will provide a "dummy" regulator if necessary */
+ hi556->avdd = devm_regulator_get(&client->dev, "avdd");
+ if (IS_ERR(hi556->avdd))
+ return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
+ "failed to get avdd regulator\n");
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
+ /* Ensure non ACPI managed resources are enabled */
+ ret = hi556_resume(&client->dev);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to power on sensor\n");
+
ret = hi556_identify_module(hi556);
if (ret) {
dev_err(&client->dev, "failed to find sensor: %d", ret);
- return ret;
+ goto probe_error_power_off;
}
}
@@ -1344,9 +1417,16 @@ probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(hi556->sd.ctrl_handler);
mutex_destroy(&hi556->mutex);
+probe_error_power_off:
+ if (full_power)
+ hi556_suspend(&client->dev);
+
return ret;
}
+static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume,
+ NULL);
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id hi556_acpi_ids[] = {
{"INT3537"},
@@ -1360,6 +1440,7 @@ static struct i2c_driver hi556_i2c_driver = {
.driver = {
.name = "hi556",
.acpi_match_table = ACPI_PTR(hi556_acpi_ids),
+ .pm = pm_sleep_ptr(&hi556_pm_ops),
},
.probe = hi556_probe,
.remove = hi556_remove,
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index 10b6ad66d126..4962cfe7c83d 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -1114,6 +1114,7 @@ free_ctrl:
v4l2_ctrl_handler_free(&imx214->ctrls);
error_power_off:
pm_runtime_disable(imx214->dev);
+ regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies);
return ret;
}
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index e17ef2e9d9d0..51ebf5453fce 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -551,8 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219)
if (ctrl_hdlr->error) {
ret = ctrl_hdlr->error;
- dev_err(&client->dev, "%s control init failed (%d)\n",
- __func__, ret);
+ dev_err_probe(&client->dev, ret, "Control init failed\n");
goto error;
}
@@ -1024,17 +1023,15 @@ static int imx219_identify_module(struct imx219 *imx219)
u64 val;
ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL);
- if (ret) {
- dev_err(&client->dev, "failed to read chip id %x\n",
- IMX219_CHIP_ID);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to read chip id %x\n",
+ IMX219_CHIP_ID);
- if (val != IMX219_CHIP_ID) {
- dev_err(&client->dev, "chip id mismatch: %x!=%llx\n",
- IMX219_CHIP_ID, val);
- return -EIO;
- }
+ if (val != IMX219_CHIP_ID)
+ return dev_err_probe(&client->dev, -EIO,
+ "chip id mismatch: %x!=%llx\n",
+ IMX219_CHIP_ID, val);
return 0;
}
@@ -1048,35 +1045,36 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
int ret = -EINVAL;
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
- if (!endpoint) {
- dev_err(dev, "endpoint node not found\n");
- return -EINVAL;
- }
+ if (!endpoint)
+ return dev_err_probe(dev, -EINVAL, "endpoint node not found\n");
if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
- dev_err(dev, "could not parse endpoint\n");
+ dev_err_probe(dev, -EINVAL, "could not parse endpoint\n");
goto error_out;
}
/* Check the number of MIPI CSI2 data lanes */
if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
- dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
+ dev_err_probe(dev, -EINVAL,
+ "only 2 or 4 data lanes are currently supported\n");
goto error_out;
}
imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
/* Check the link frequency set in device tree */
if (!ep_cfg.nr_of_link_frequencies) {
- dev_err(dev, "link-frequency property not found in DT\n");
+ dev_err_probe(dev, -EINVAL,
+ "link-frequency property not found in DT\n");
goto error_out;
}
if (ep_cfg.nr_of_link_frequencies != 1 ||
(ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
- dev_err(dev, "Link frequency not supported: %lld\n",
- ep_cfg.link_frequencies[0]);
+ dev_err_probe(dev, -EINVAL,
+ "Link frequency not supported: %lld\n",
+ ep_cfg.link_frequencies[0]);
goto error_out;
}
@@ -1107,31 +1105,25 @@ static int imx219_probe(struct i2c_client *client)
return -EINVAL;
imx219->regmap = devm_cci_regmap_init_i2c(client, 16);
- if (IS_ERR(imx219->regmap)) {
- ret = PTR_ERR(imx219->regmap);
- dev_err(dev, "failed to initialize CCI: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(imx219->regmap))
+ return dev_err_probe(dev, PTR_ERR(imx219->regmap),
+ "failed to initialize CCI\n");
/* Get system clock (xclk) */
imx219->xclk = devm_clk_get(dev, NULL);
- if (IS_ERR(imx219->xclk)) {
- dev_err(dev, "failed to get xclk\n");
- return PTR_ERR(imx219->xclk);
- }
+ if (IS_ERR(imx219->xclk))
+ return dev_err_probe(dev, PTR_ERR(imx219->xclk),
+ "failed to get xclk\n");
imx219->xclk_freq = clk_get_rate(imx219->xclk);
- if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
- dev_err(dev, "xclk frequency not supported: %d Hz\n",
- imx219->xclk_freq);
- return -EINVAL;
- }
+ if (imx219->xclk_freq != IMX219_XCLK_FREQ)
+ return dev_err_probe(dev, -EINVAL,
+ "xclk frequency not supported: %d Hz\n",
+ imx219->xclk_freq);
ret = imx219_get_regulators(imx219);
- if (ret) {
- dev_err(dev, "failed to get regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
/* Request optional enable pin */
imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -1183,20 +1175,21 @@ static int imx219_probe(struct i2c_client *client)
ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
if (ret) {
- dev_err(dev, "failed to init entity pads: %d\n", ret);
+ dev_err_probe(dev, ret, "failed to init entity pads\n");
goto error_handler_free;
}
imx219->sd.state_lock = imx219->ctrl_handler.lock;
ret = v4l2_subdev_init_finalize(&imx219->sd);
if (ret < 0) {
- dev_err(dev, "subdev init error: %d\n", ret);
+ dev_err_probe(dev, ret, "subdev init error\n");
goto error_media_entity;
}
ret = v4l2_async_register_subdev_sensor(&imx219->sd);
if (ret < 0) {
- dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
+ dev_err_probe(dev, ret,
+ "failed to register sensor sub-device\n");
goto error_subdev_cleanup;
}
diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c
index dab6d080bc4c..990d74214cc2 100644
--- a/drivers/media/i2c/imx335.c
+++ b/drivers/media/i2c/imx335.c
@@ -11,57 +11,108 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
/* Streaming Mode */
-#define IMX335_REG_MODE_SELECT 0x3000
-#define IMX335_MODE_STANDBY 0x01
-#define IMX335_MODE_STREAMING 0x00
+#define IMX335_REG_MODE_SELECT CCI_REG8(0x3000)
+#define IMX335_MODE_STANDBY 0x01
+#define IMX335_MODE_STREAMING 0x00
+
+/* Group hold register */
+#define IMX335_REG_HOLD CCI_REG8(0x3001)
+
+#define IMX335_REG_MASTER_MODE CCI_REG8(0x3002)
+#define IMX335_REG_BCWAIT_TIME CCI_REG8(0x300c)
+#define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d)
+#define IMX335_REG_WINMODE CCI_REG8(0x3018)
+#define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c)
+#define IMX335_REG_HNUM CCI_REG8(0x302e)
/* Lines per frame */
-#define IMX335_REG_LPFR 0x3030
+#define IMX335_REG_VMAX CCI_REG24_LE(0x3030)
-/* Chip ID */
-#define IMX335_REG_ID 0x3912
-#define IMX335_ID 0x00
-
-/* Exposure control */
-#define IMX335_REG_SHUTTER 0x3058
-#define IMX335_EXPOSURE_MIN 1
-#define IMX335_EXPOSURE_OFFSET 9
-#define IMX335_EXPOSURE_STEP 1
-#define IMX335_EXPOSURE_DEFAULT 0x0648
-
-/* Analog gain control */
-#define IMX335_REG_AGAIN 0x30e8
-#define IMX335_AGAIN_MIN 0
-#define IMX335_AGAIN_MAX 240
-#define IMX335_AGAIN_STEP 1
-#define IMX335_AGAIN_DEFAULT 0
+#define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c)
+#define IMX335_REG_ADBIT CCI_REG8(0x3050)
+#define IMX335_REG_Y_OUT_SIZE CCI_REG16_LE(0x3056)
-/* Group hold register */
-#define IMX335_REG_HOLD 0x3001
+#define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058)
+#define IMX335_EXPOSURE_MIN 1
+#define IMX335_EXPOSURE_OFFSET 9
+#define IMX335_EXPOSURE_STEP 1
+#define IMX335_EXPOSURE_DEFAULT 0x0648
+
+#define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074)
+#define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076)
+
+/* Analog and Digital gain control */
+#define IMX335_REG_GAIN CCI_REG8(0x30e8)
+#define IMX335_AGAIN_MIN 0
+#define IMX335_AGAIN_MAX 100
+#define IMX335_AGAIN_STEP 1
+#define IMX335_AGAIN_DEFAULT 0
+
+#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148)
+
+#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c)
+#define IMX335_REG_INCLKSEL2 CCI_REG8(0x315a)
+#define IMX335_REG_INCLKSEL3 CCI_REG8(0x3168)
+#define IMX335_REG_INCLKSEL4 CCI_REG8(0x316a)
+
+#define IMX335_REG_MDBIT CCI_REG8(0x319d)
+#define IMX335_REG_SYSMODE CCI_REG8(0x319e)
+
+#define IMX335_REG_XVS_XHS_DRV CCI_REG8(0x31a1)
/* Test pattern generator */
-#define IMX335_REG_TPG 0x329e
-#define IMX335_TPG_ALL_000 0
-#define IMX335_TPG_ALL_FFF 1
-#define IMX335_TPG_ALL_555 2
-#define IMX335_TPG_ALL_AAA 3
-#define IMX335_TPG_TOG_555_AAA 4
-#define IMX335_TPG_TOG_AAA_555 5
-#define IMX335_TPG_TOG_000_555 6
-#define IMX335_TPG_TOG_555_000 7
-#define IMX335_TPG_TOG_000_FFF 8
-#define IMX335_TPG_TOG_FFF_000 9
-#define IMX335_TPG_H_COLOR_BARS 10
-#define IMX335_TPG_V_COLOR_BARS 11
+#define IMX335_REG_TPG_DIG_CLP_MODE CCI_REG8(0x3280)
+#define IMX335_REG_TPG_EN_DUOUT CCI_REG8(0x329c)
+#define IMX335_REG_TPG CCI_REG8(0x329e)
+#define IMX335_TPG_ALL_000 0
+#define IMX335_TPG_ALL_FFF 1
+#define IMX335_TPG_ALL_555 2
+#define IMX335_TPG_ALL_AAA 3
+#define IMX335_TPG_TOG_555_AAA 4
+#define IMX335_TPG_TOG_AAA_555 5
+#define IMX335_TPG_TOG_000_555 6
+#define IMX335_TPG_TOG_555_000 7
+#define IMX335_TPG_TOG_000_FFF 8
+#define IMX335_TPG_TOG_FFF_000 9
+#define IMX335_TPG_H_COLOR_BARS 10
+#define IMX335_TPG_V_COLOR_BARS 11
+#define IMX335_REG_TPG_COLORWIDTH CCI_REG8(0x32a0)
+
+#define IMX335_REG_BLKLEVEL CCI_REG16_LE(0x3302)
+
+#define IMX335_REG_WRJ_OPEN CCI_REG8(0x336c)
+
+#define IMX335_REG_ADBIT1 CCI_REG16_LE(0x341c)
+
+/* Chip ID */
+#define IMX335_REG_ID CCI_REG8(0x3912)
+#define IMX335_ID 0x00
+
+/* Data Lanes */
+#define IMX335_REG_LANEMODE CCI_REG8(0x3a01)
+#define IMX335_2LANE 1
+#define IMX335_4LANE 3
+
+#define IMX335_REG_TCLKPOST CCI_REG16_LE(0x3a18)
+#define IMX335_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a)
+#define IMX335_REG_TCLK_TRAIL CCI_REG16_LE(0x3a1c)
+#define IMX335_REG_TCLK_ZERO CCI_REG16_LE(0x3a1e)
+#define IMX335_REG_THS_PREPARE CCI_REG16_LE(0x3a20)
+#define IMX335_REG_THS_ZERO CCI_REG16_LE(0x3a22)
+#define IMX335_REG_THS_TRAIL CCI_REG16_LE(0x3a24)
+#define IMX335_REG_THS_EXIT CCI_REG16_LE(0x3a26)
+#define IMX335_REG_TPLX CCI_REG16_LE(0x3a28)
/* Input clock rate */
-#define IMX335_INCLK_RATE 24000000
+#define IMX335_INCLK_RATE 24000000
/* CSI2 HW configuration */
#define IMX335_LINK_FREQ_594MHz 594000000LL
@@ -69,9 +120,6 @@
#define IMX335_NUM_DATA_LANES 4
-#define IMX335_REG_MIN 0x00
-#define IMX335_REG_MAX 0xfffff
-
/* IMX335 native and active pixel array size. */
#define IMX335_NATIVE_WIDTH 2616U
#define IMX335_NATIVE_HEIGHT 1964U
@@ -81,23 +129,13 @@
#define IMX335_PIXEL_ARRAY_HEIGHT 1944U
/**
- * struct imx335_reg - imx335 sensor register
- * @address: Register address
- * @val: Register value
- */
-struct imx335_reg {
- u16 address;
- u8 val;
-};
-
-/**
* struct imx335_reg_list - imx335 sensor register list
* @num_of_regs: Number of registers in the list
* @regs: Pointer to register list
*/
struct imx335_reg_list {
u32 num_of_regs;
- const struct imx335_reg *regs;
+ const struct cci_reg_sequence *regs;
};
static const char * const imx335_supply_name[] = {
@@ -138,6 +176,7 @@ struct imx335_mode {
* @pad: Media pad. Only one pad supported
* @reset_gpio: Sensor reset gpio
* @supplies: Regulator supplies to handle power control
+ * @cci: CCI register map
* @inclk: Sensor input clock
* @ctrl_handler: V4L2 control handler
* @link_freq_ctrl: Pointer to link frequency control
@@ -147,6 +186,7 @@ struct imx335_mode {
* @exp_ctrl: Pointer to exposure control
* @again_ctrl: Pointer to analog gain control
* @vblank: Vertical blanking in lines
+ * @lane_mode: Mode for number of connected data lanes
* @cur_mode: Pointer to current selected sensor mode
* @mutex: Mutex for serializing sensor controls
* @link_freq_bitmap: Menu bitmap for link_freq_ctrl
@@ -159,6 +199,7 @@ struct imx335 {
struct media_pad pad;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)];
+ struct regmap *cci;
struct clk *inclk;
struct v4l2_ctrl_handler ctrl_handler;
@@ -171,6 +212,7 @@ struct imx335 {
struct v4l2_ctrl *again_ctrl;
};
u32 vblank;
+ u32 lane_mode;
const struct imx335_mode *cur_mode;
struct mutex mutex;
unsigned long link_freq_bitmap;
@@ -210,140 +252,135 @@ static const int imx335_tpg_val[] = {
};
/* Sensor mode registers */
-static const struct imx335_reg mode_2592x1940_regs[] = {
- {0x3000, 0x01},
- {0x3002, 0x00},
- {0x3018, 0x04},
- {0x302c, 0x3c},
- {0x302e, 0x20},
- {0x3056, 0x94},
- {0x3074, 0xc8},
- {0x3076, 0x28},
- {0x304c, 0x00},
- {0x31a1, 0x00},
- {0x3288, 0x21},
- {0x328a, 0x02},
- {0x3414, 0x05},
- {0x3416, 0x18},
- {0x3648, 0x01},
- {0x364a, 0x04},
- {0x364c, 0x04},
- {0x3678, 0x01},
- {0x367c, 0x31},
- {0x367e, 0x31},
- {0x3706, 0x10},
- {0x3708, 0x03},
- {0x3714, 0x02},
- {0x3715, 0x02},
- {0x3716, 0x01},
- {0x3717, 0x03},
- {0x371c, 0x3d},
- {0x371d, 0x3f},
- {0x372c, 0x00},
- {0x372d, 0x00},
- {0x372e, 0x46},
- {0x372f, 0x00},
- {0x3730, 0x89},
- {0x3731, 0x00},
- {0x3732, 0x08},
- {0x3733, 0x01},
- {0x3734, 0xfe},
- {0x3735, 0x05},
- {0x3740, 0x02},
- {0x375d, 0x00},
- {0x375e, 0x00},
- {0x375f, 0x11},
- {0x3760, 0x01},
- {0x3768, 0x1b},
- {0x3769, 0x1b},
- {0x376a, 0x1b},
- {0x376b, 0x1b},
- {0x376c, 0x1a},
- {0x376d, 0x17},
- {0x376e, 0x0f},
- {0x3776, 0x00},
- {0x3777, 0x00},
- {0x3778, 0x46},
- {0x3779, 0x00},
- {0x377a, 0x89},
- {0x377b, 0x00},
- {0x377c, 0x08},
- {0x377d, 0x01},
- {0x377e, 0x23},
- {0x377f, 0x02},
- {0x3780, 0xd9},
- {0x3781, 0x03},
- {0x3782, 0xf5},
- {0x3783, 0x06},
- {0x3784, 0xa5},
- {0x3788, 0x0f},
- {0x378a, 0xd9},
- {0x378b, 0x03},
- {0x378c, 0xeb},
- {0x378d, 0x05},
- {0x378e, 0x87},
- {0x378f, 0x06},
- {0x3790, 0xf5},
- {0x3792, 0x43},
- {0x3794, 0x7a},
- {0x3796, 0xa1},
- {0x37b0, 0x36},
- {0x3a00, 0x00},
+static const struct cci_reg_sequence mode_2592x1940_regs[] = {
+ { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
+ { IMX335_REG_MASTER_MODE, 0x00 },
+ { IMX335_REG_WINMODE, 0x04 },
+ { IMX335_REG_HTRIMMING_START, 48 },
+ { IMX335_REG_HNUM, 2592 },
+ { IMX335_REG_Y_OUT_SIZE, 1944 },
+ { IMX335_REG_AREA3_ST_ADR_1, 176 },
+ { IMX335_REG_AREA3_WIDTH_1, 3928 },
+ { IMX335_REG_OPB_SIZE_V, 0 },
+ { IMX335_REG_XVS_XHS_DRV, 0x00 },
+ { CCI_REG8(0x3288), 0x21 },
+ { CCI_REG8(0x328a), 0x02 },
+ { CCI_REG8(0x3414), 0x05 },
+ { CCI_REG8(0x3416), 0x18 },
+ { CCI_REG8(0x3648), 0x01 },
+ { CCI_REG8(0x364a), 0x04 },
+ { CCI_REG8(0x364c), 0x04 },
+ { CCI_REG8(0x3678), 0x01 },
+ { CCI_REG8(0x367c), 0x31 },
+ { CCI_REG8(0x367e), 0x31 },
+ { CCI_REG8(0x3706), 0x10 },
+ { CCI_REG8(0x3708), 0x03 },
+ { CCI_REG8(0x3714), 0x02 },
+ { CCI_REG8(0x3715), 0x02 },
+ { CCI_REG8(0x3716), 0x01 },
+ { CCI_REG8(0x3717), 0x03 },
+ { CCI_REG8(0x371c), 0x3d },
+ { CCI_REG8(0x371d), 0x3f },
+ { CCI_REG8(0x372c), 0x00 },
+ { CCI_REG8(0x372d), 0x00 },
+ { CCI_REG8(0x372e), 0x46 },
+ { CCI_REG8(0x372f), 0x00 },
+ { CCI_REG8(0x3730), 0x89 },
+ { CCI_REG8(0x3731), 0x00 },
+ { CCI_REG8(0x3732), 0x08 },
+ { CCI_REG8(0x3733), 0x01 },
+ { CCI_REG8(0x3734), 0xfe },
+ { CCI_REG8(0x3735), 0x05 },
+ { CCI_REG8(0x3740), 0x02 },
+ { CCI_REG8(0x375d), 0x00 },
+ { CCI_REG8(0x375e), 0x00 },
+ { CCI_REG8(0x375f), 0x11 },
+ { CCI_REG8(0x3760), 0x01 },
+ { CCI_REG8(0x3768), 0x1b },
+ { CCI_REG8(0x3769), 0x1b },
+ { CCI_REG8(0x376a), 0x1b },
+ { CCI_REG8(0x376b), 0x1b },
+ { CCI_REG8(0x376c), 0x1a },
+ { CCI_REG8(0x376d), 0x17 },
+ { CCI_REG8(0x376e), 0x0f },
+ { CCI_REG8(0x3776), 0x00 },
+ { CCI_REG8(0x3777), 0x00 },
+ { CCI_REG8(0x3778), 0x46 },
+ { CCI_REG8(0x3779), 0x00 },
+ { CCI_REG8(0x377a), 0x89 },
+ { CCI_REG8(0x377b), 0x00 },
+ { CCI_REG8(0x377c), 0x08 },
+ { CCI_REG8(0x377d), 0x01 },
+ { CCI_REG8(0x377e), 0x23 },
+ { CCI_REG8(0x377f), 0x02 },
+ { CCI_REG8(0x3780), 0xd9 },
+ { CCI_REG8(0x3781), 0x03 },
+ { CCI_REG8(0x3782), 0xf5 },
+ { CCI_REG8(0x3783), 0x06 },
+ { CCI_REG8(0x3784), 0xa5 },
+ { CCI_REG8(0x3788), 0x0f },
+ { CCI_REG8(0x378a), 0xd9 },
+ { CCI_REG8(0x378b), 0x03 },
+ { CCI_REG8(0x378c), 0xeb },
+ { CCI_REG8(0x378d), 0x05 },
+ { CCI_REG8(0x378e), 0x87 },
+ { CCI_REG8(0x378f), 0x06 },
+ { CCI_REG8(0x3790), 0xf5 },
+ { CCI_REG8(0x3792), 0x43 },
+ { CCI_REG8(0x3794), 0x7a },
+ { CCI_REG8(0x3796), 0xa1 },
+ { CCI_REG8(0x37b0), 0x36 },
+ { CCI_REG8(0x3a00), 0x00 },
};
-static const struct imx335_reg raw10_framefmt_regs[] = {
- {0x3050, 0x00},
- {0x319d, 0x00},
- {0x341c, 0xff},
- {0x341d, 0x01},
+static const struct cci_reg_sequence raw10_framefmt_regs[] = {
+ { IMX335_REG_ADBIT, 0x00 },
+ { IMX335_REG_MDBIT, 0x00 },
+ { IMX335_REG_ADBIT1, 0x1ff },
};
-static const struct imx335_reg raw12_framefmt_regs[] = {
- {0x3050, 0x01},
- {0x319d, 0x01},
- {0x341c, 0x47},
- {0x341d, 0x00},
+static const struct cci_reg_sequence raw12_framefmt_regs[] = {
+ { IMX335_REG_ADBIT, 0x01 },
+ { IMX335_REG_MDBIT, 0x01 },
+ { IMX335_REG_ADBIT1, 0x47 },
};
-static const struct imx335_reg mipi_data_rate_1188Mbps[] = {
- {0x300c, 0x3b},
- {0x300d, 0x2a},
- {0x314c, 0xc6},
- {0x314d, 0x00},
- {0x315a, 0x02},
- {0x3168, 0xa0},
- {0x316a, 0x7e},
- {0x319e, 0x01},
- {0x3a18, 0x8f},
- {0x3a1a, 0x4f},
- {0x3a1c, 0x47},
- {0x3a1e, 0x37},
- {0x3a1f, 0x01},
- {0x3a20, 0x4f},
- {0x3a22, 0x87},
- {0x3a24, 0x4f},
- {0x3a26, 0x7f},
- {0x3a28, 0x3f},
+static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = {
+ { IMX335_REG_BCWAIT_TIME, 0x3b },
+ { IMX335_REG_CPWAIT_TIME, 0x2a },
+ { IMX335_REG_INCLKSEL1, 0x00c6 },
+ { IMX335_REG_INCLKSEL2, 0x02 },
+ { IMX335_REG_INCLKSEL3, 0xa0 },
+ { IMX335_REG_INCLKSEL4, 0x7e },
+ { IMX335_REG_SYSMODE, 0x01 },
+ { IMX335_REG_TCLKPOST, 0x8f },
+ { IMX335_REG_TCLKPREPARE, 0x4f },
+ { IMX335_REG_TCLK_TRAIL, 0x47 },
+ { IMX335_REG_TCLK_ZERO, 0x0137 },
+ { IMX335_REG_THS_PREPARE, 0x4f },
+ { IMX335_REG_THS_ZERO, 0x87 },
+ { IMX335_REG_THS_TRAIL, 0x4f },
+ { IMX335_REG_THS_EXIT, 0x7f },
+ { IMX335_REG_TPLX, 0x3f },
};
-static const struct imx335_reg mipi_data_rate_891Mbps[] = {
- {0x300c, 0x3b},
- {0x300d, 0x2a},
- {0x314c, 0x29},
- {0x314d, 0x01},
- {0x315a, 0x06},
- {0x3168, 0xa0},
- {0x316a, 0x7e},
- {0x319e, 0x02},
- {0x3a18, 0x7f},
- {0x3a1a, 0x37},
- {0x3a1c, 0x37},
- {0x3a1e, 0xf7},
- {0x3a20, 0x3f},
- {0x3a22, 0x6f},
- {0x3a24, 0x3f},
- {0x3a26, 0x5f},
- {0x3a28, 0x2f},
+static const struct cci_reg_sequence mipi_data_rate_891Mbps[] = {
+ { IMX335_REG_BCWAIT_TIME, 0x3b },
+ { IMX335_REG_CPWAIT_TIME, 0x2a },
+ { IMX335_REG_INCLKSEL1, 0x0129 },
+ { IMX335_REG_INCLKSEL2, 0x06 },
+ { IMX335_REG_INCLKSEL3, 0xa0 },
+ { IMX335_REG_INCLKSEL4, 0x7e },
+ { IMX335_REG_SYSMODE, 0x02 },
+ { IMX335_REG_TCLKPOST, 0x7f },
+ { IMX335_REG_TCLKPREPARE, 0x37 },
+ { IMX335_REG_TCLK_TRAIL, 0x37 },
+ { IMX335_REG_TCLK_ZERO, 0xf7 },
+ { IMX335_REG_THS_PREPARE, 0x3f },
+ { IMX335_REG_THS_ZERO, 0x6f },
+ { IMX335_REG_THS_TRAIL, 0x3f },
+ { IMX335_REG_THS_EXIT, 0x5f },
+ { IMX335_REG_TPLX, 0x2f },
};
static const s64 link_freq[] = {
@@ -372,10 +409,10 @@ static const u32 imx335_mbus_codes[] = {
/* Supported sensor mode configurations */
static const struct imx335_mode supported_mode = {
.width = 2592,
- .height = 1940,
+ .height = 1944,
.hblank = 342,
- .vblank = 2560,
- .vblank_min = 2560,
+ .vblank = 2556,
+ .vblank_min = 2556,
.vblank_max = 133060,
.pclk = 396000000,
.reg_list = {
@@ -396,101 +433,6 @@ static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev)
}
/**
- * imx335_read_reg() - Read registers.
- * @imx335: pointer to imx335 device
- * @reg: register address
- * @len: length of bytes to read. Max supported bytes is 4
- * @val: pointer to register value to be filled.
- *
- * Big endian register addresses with little endian values.
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
- struct i2c_msg msgs[2] = {0};
- u8 addr_buf[2] = {0};
- u8 data_buf[4] = {0};
- int ret;
-
- if (WARN_ON(len > 4))
- return -EINVAL;
-
- put_unaligned_be16(reg, addr_buf);
-
- /* Write register address */
- msgs[0].addr = client->addr;
- msgs[0].flags = 0;
- msgs[0].len = ARRAY_SIZE(addr_buf);
- msgs[0].buf = addr_buf;
-
- /* Read data from register */
- msgs[1].addr = client->addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = data_buf;
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs))
- return -EIO;
-
- *val = get_unaligned_le32(data_buf);
-
- return 0;
-}
-
-/**
- * imx335_write_reg() - Write register
- * @imx335: pointer to imx335 device
- * @reg: register address
- * @len: length of bytes. Max supported bytes is 4
- * @val: register value
- *
- * Big endian register addresses with little endian values.
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
- u8 buf[6] = {0};
-
- if (WARN_ON(len > 4))
- return -EINVAL;
-
- put_unaligned_be16(reg, buf);
- put_unaligned_le32(val, buf + 2);
- if (i2c_master_send(client, buf, len + 2) != len + 2)
- return -EIO;
-
- return 0;
-}
-
-/**
- * imx335_write_regs() - Write a list of registers
- * @imx335: pointer to imx335 device
- * @regs: list of registers to be written
- * @len: length of registers array
- *
- * Return: 0 if successful. error code otherwise.
- */
-static int imx335_write_regs(struct imx335 *imx335,
- const struct imx335_reg *regs, u32 len)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < len; i++) {
- ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
* imx335_update_controls() - Update control ranges based on streaming mode
* @imx335: pointer to imx335 device
* @mode: pointer to imx335_mode sensor mode
@@ -526,7 +468,8 @@ static int imx335_update_controls(struct imx335 *imx335,
static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
{
u32 lpfr, shutter;
- int ret;
+ int ret_hold;
+ int ret = 0;
lpfr = imx335->vblank + imx335->cur_mode->height;
shutter = lpfr - exposure;
@@ -534,64 +477,55 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n",
exposure, gain, shutter, lpfr);
- ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1);
- if (ret)
- return ret;
-
- ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr);
- if (ret)
- goto error_release_group_hold;
-
- ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter);
- if (ret)
- goto error_release_group_hold;
-
- ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain);
-
-error_release_group_hold:
- imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0);
+ cci_write(imx335->cci, IMX335_REG_HOLD, 1, &ret);
+ cci_write(imx335->cci, IMX335_REG_VMAX, lpfr, &ret);
+ cci_write(imx335->cci, IMX335_REG_SHUTTER, shutter, &ret);
+ cci_write(imx335->cci, IMX335_REG_GAIN, gain, &ret);
+ /*
+ * Unconditionally attempt to release the hold, but track the
+ * error if the unhold itself fails.
+ */
+ ret_hold = cci_write(imx335->cci, IMX335_REG_HOLD, 0, NULL);
+ if (ret_hold)
+ ret = ret_hold;
return ret;
}
static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
{
- int ret;
+ int ret = 0;
if (pattern_index >= ARRAY_SIZE(imx335_tpg_val))
return -EINVAL;
if (pattern_index) {
- const struct imx335_reg tpg_enable_regs[] = {
- { 0x3148, 0x10 },
- { 0x3280, 0x00 },
- { 0x329c, 0x01 },
- { 0x32a0, 0x11 },
- { 0x3302, 0x00 },
- { 0x3303, 0x00 },
- { 0x336c, 0x00 },
+ const struct cci_reg_sequence tpg_enable_regs[] = {
+ { IMX335_REG_TPG_TESTCLKEN, 0x10 },
+ { IMX335_REG_TPG_DIG_CLP_MODE, 0x00 },
+ { IMX335_REG_TPG_EN_DUOUT, 0x01 },
+ { IMX335_REG_TPG_COLORWIDTH, 0x11 },
+ { IMX335_REG_BLKLEVEL, 0x00 },
+ { IMX335_REG_WRJ_OPEN, 0x00 },
};
- ret = imx335_write_reg(imx335, IMX335_REG_TPG, 1,
- imx335_tpg_val[pattern_index]);
- if (ret)
- return ret;
+ cci_write(imx335->cci, IMX335_REG_TPG,
+ imx335_tpg_val[pattern_index], &ret);
- ret = imx335_write_regs(imx335, tpg_enable_regs,
- ARRAY_SIZE(tpg_enable_regs));
+ cci_multi_reg_write(imx335->cci, tpg_enable_regs,
+ ARRAY_SIZE(tpg_enable_regs), &ret);
} else {
- const struct imx335_reg tpg_disable_regs[] = {
- { 0x3148, 0x00 },
- { 0x3280, 0x01 },
- { 0x329c, 0x00 },
- { 0x32a0, 0x10 },
- { 0x3302, 0x32 },
- { 0x3303, 0x00 },
- { 0x336c, 0x01 },
+ const struct cci_reg_sequence tpg_disable_regs[] = {
+ { IMX335_REG_TPG_TESTCLKEN, 0x00 },
+ { IMX335_REG_TPG_DIG_CLP_MODE, 0x01 },
+ { IMX335_REG_TPG_EN_DUOUT, 0x00 },
+ { IMX335_REG_TPG_COLORWIDTH, 0x10 },
+ { IMX335_REG_BLKLEVEL, 0x32 },
+ { IMX335_REG_WRJ_OPEN, 0x01 },
};
- ret = imx335_write_regs(imx335, tpg_disable_regs,
- ARRAY_SIZE(tpg_disable_regs));
+ cci_multi_reg_write(imx335->cci, tpg_disable_regs,
+ ARRAY_SIZE(tpg_disable_regs), &ret);
}
return ret;
@@ -890,12 +824,14 @@ static int imx335_set_framefmt(struct imx335 *imx335)
{
switch (imx335->cur_mbus_code) {
case MEDIA_BUS_FMT_SRGGB10_1X10:
- return imx335_write_regs(imx335, raw10_framefmt_regs,
- ARRAY_SIZE(raw10_framefmt_regs));
+ return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs,
+ ARRAY_SIZE(raw10_framefmt_regs),
+ NULL);
case MEDIA_BUS_FMT_SRGGB12_1X12:
- return imx335_write_regs(imx335, raw12_framefmt_regs,
- ARRAY_SIZE(raw12_framefmt_regs));
+ return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs,
+ ARRAY_SIZE(raw12_framefmt_regs),
+ NULL);
}
return -EINVAL;
@@ -914,7 +850,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
/* Setup PLL */
reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)];
- ret = imx335_write_regs(imx335, reg_list->regs, reg_list->num_of_regs);
+ ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
+ reg_list->num_of_regs, NULL);
if (ret) {
dev_err(imx335->dev, "%s failed to set plls\n", __func__);
return ret;
@@ -922,8 +859,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
/* Write sensor mode registers */
reg_list = &imx335->cur_mode->reg_list;
- ret = imx335_write_regs(imx335, reg_list->regs,
- reg_list->num_of_regs);
+ ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
+ reg_list->num_of_regs, NULL);
if (ret) {
dev_err(imx335->dev, "fail to write initial registers\n");
return ret;
@@ -936,6 +873,12 @@ static int imx335_start_streaming(struct imx335 *imx335)
return ret;
}
+ /* Configure lanes */
+ ret = cci_write(imx335->cci, IMX335_REG_LANEMODE,
+ imx335->lane_mode, NULL);
+ if (ret)
+ return ret;
+
/* Setup handler will write actual exposure and gain */
ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler);
if (ret) {
@@ -944,8 +887,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
}
/* Start streaming */
- ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
- 1, IMX335_MODE_STREAMING);
+ ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
+ IMX335_MODE_STREAMING, NULL);
if (ret) {
dev_err(imx335->dev, "fail to start streaming\n");
return ret;
@@ -965,8 +908,8 @@ static int imx335_start_streaming(struct imx335 *imx335)
*/
static int imx335_stop_streaming(struct imx335 *imx335)
{
- return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
- 1, IMX335_MODE_STANDBY);
+ return cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
+ IMX335_MODE_STANDBY, NULL);
}
/**
@@ -1017,14 +960,14 @@ error_unlock:
static int imx335_detect(struct imx335 *imx335)
{
int ret;
- u32 val;
+ u64 val;
- ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val);
+ ret = cci_read(imx335->cci, IMX335_REG_ID, &val, NULL);
if (ret)
return ret;
if (val != IMX335_ID) {
- dev_err(imx335->dev, "chip id mismatch: %x!=%x\n",
+ dev_err(imx335->dev, "chip id mismatch: %x!=%llx\n",
IMX335_ID, val);
return -ENXIO;
}
@@ -1096,7 +1039,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335)
if (ret)
return ret;
- if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) {
+ switch (bus_cfg.bus.mipi_csi2.num_data_lanes) {
+ case 2:
+ imx335->lane_mode = IMX335_2LANE;
+ break;
+ case 4:
+ imx335->lane_mode = IMX335_4LANE;
+ break;
+ default:
dev_err(imx335->dev,
"number of CSI2 data lanes %d is not supported\n",
bus_cfg.bus.mipi_csi2.num_data_lanes);
@@ -1208,10 +1158,16 @@ static int imx335_init_controls(struct imx335 *imx335)
{
struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler;
const struct imx335_mode *mode = imx335->cur_mode;
+ struct v4l2_fwnode_device_properties props;
u32 lpfr;
int ret;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 7);
+ ret = v4l2_fwnode_device_parse(imx335->dev, &props);
+ if (ret)
+ return ret;
+
+ /* v4l2_fwnode_device_properties can add two more controls */
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
if (ret)
return ret;
@@ -1228,6 +1184,14 @@ static int imx335_init_controls(struct imx335 *imx335)
IMX335_EXPOSURE_STEP,
IMX335_EXPOSURE_DEFAULT);
+ /*
+ * The sensor has an analog gain and a digital gain, both controlled
+ * through a single gain value, expressed in 0.3dB increments. Values
+ * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
+ * up to 72.0dB (240) add further digital gain. Limit the range to
+ * analog gain only, support for digital gain can be added separately
+ * if needed.
+ */
imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
&imx335_ctrl_ops,
V4L2_CID_ANALOGUE_GAIN,
@@ -1276,6 +1240,8 @@ static int imx335_init_controls(struct imx335 *imx335)
if (imx335->hblank_ctrl)
imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx335_ctrl_ops, &props);
+
if (ctrl_hdlr->error) {
dev_err(imx335->dev, "control init failed: %d\n",
ctrl_hdlr->error);
@@ -1304,6 +1270,11 @@ static int imx335_probe(struct i2c_client *client)
return -ENOMEM;
imx335->dev = &client->dev;
+ imx335->cci = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(imx335->cci)) {
+ dev_err(imx335->dev, "Unable to initialize I2C\n");
+ return -ENODEV;
+ }
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops);
diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h
index dc5e4e70ba6f..0bf1d40811eb 100644
--- a/drivers/media/i2c/max9271.h
+++ b/drivers/media/i2c/max9271.h
@@ -8,6 +8,9 @@
* Copyright (C) 2015 Cogent Embedded, Inc.
*/
+#ifndef __MEDIA_I2C_MAX9271_H__
+#define __MEDIA_I2C_MAX9271_H__
+
#include <linux/i2c.h>
#define MAX9271_DEFAULT_ADDR 0x40
@@ -231,3 +234,5 @@ int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr);
* Return 0 on success or a negative error code on failure
*/
int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
+
+#endif /* __MEDIA_I2C_MAX9271_H__ */
diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c
index 39d321e2b7f9..3ae0ea58668d 100644
--- a/drivers/media/i2c/ov2680.c
+++ b/drivers/media/i2c/ov2680.c
@@ -75,6 +75,8 @@
#define OV2680_ACTIVE_START_TOP 8
#define OV2680_MIN_CROP_WIDTH 2
#define OV2680_MIN_CROP_HEIGHT 2
+#define OV2680_MIN_VBLANK 4
+#define OV2680_MAX_VBLANK 0xffff
/* Fixed pre-div of 1/2 */
#define OV2680_PLL_PREDIV0 2
@@ -84,10 +86,7 @@
/* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */
#define OV2680_PIXELS_PER_LINE 1704
-#define OV2680_LINES_PER_FRAME 1294
-
-/* If possible send 16 extra rows / lines to the ISP as padding */
-#define OV2680_END_MARGIN 16
+#define OV2680_LINES_PER_FRAME_30FPS 1294
/* Max exposure time is VTS - 8 */
#define OV2680_INTEGRATION_TIME_MARGIN 8
@@ -130,6 +129,8 @@ struct ov2680_ctrls {
struct v4l2_ctrl *test_pattern;
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
};
struct ov2680_mode {
@@ -143,8 +144,6 @@ struct ov2680_mode {
u16 v_end;
u16 h_output_size;
u16 v_output_size;
- u16 hts;
- u16 vts;
};
struct ov2680_dev {
@@ -359,15 +358,11 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor)
sensor->mode.v_start = (sensor->mode.crop.top +
(sensor->mode.crop.height - height) / 2) & ~1;
sensor->mode.h_end =
- min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1,
- OV2680_NATIVE_WIDTH - 1);
+ min(sensor->mode.h_start + width - 1, OV2680_NATIVE_WIDTH - 1);
sensor->mode.v_end =
- min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1,
- OV2680_NATIVE_HEIGHT - 1);
+ min(sensor->mode.v_start + height - 1, OV2680_NATIVE_HEIGHT - 1);
sensor->mode.h_output_size = orig_width;
sensor->mode.v_output_size = orig_height;
- sensor->mode.hts = OV2680_PIXELS_PER_LINE;
- sensor->mode.vts = OV2680_LINES_PER_FRAME;
}
static int ov2680_set_mode(struct ov2680_dev *sensor)
@@ -402,9 +397,8 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
sensor->mode.v_output_size, &ret);
cci_write(sensor->regmap, OV2680_REG_TIMING_HTS,
- sensor->mode.hts, &ret);
- cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
- sensor->mode.vts, &ret);
+ OV2680_PIXELS_PER_LINE, &ret);
+ /* VTS gets set by the vblank ctrl */
cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
@@ -478,6 +472,15 @@ static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
NULL);
}
+static int ov2680_exposure_update_range(struct ov2680_dev *sensor)
+{
+ int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val -
+ OV2680_INTEGRATION_TIME_MARGIN;
+
+ return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
+ 1, exp_max);
+}
+
static int ov2680_stream_enable(struct ov2680_dev *sensor)
{
int ret;
@@ -644,7 +647,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *try_fmt;
const struct v4l2_rect *crop;
unsigned int width, height;
- int ret = 0;
+ int def, max, ret = 0;
crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad,
format->which);
@@ -673,6 +676,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
sensor->mode.fmt = format->format;
ov2680_calc_mode(sensor);
+ /* vblank range is height dependent adjust and reset to default */
+ max = OV2680_MAX_VBLANK - height;
+ def = OV2680_LINES_PER_FRAME_30FPS - height;
+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK,
+ max, 1, def);
+ if (ret)
+ goto unlock;
+
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
+ if (ret)
+ goto unlock;
+
+ /* exposure range depends on vts which may have changed */
+ ret = ov2680_exposure_update_range(sensor);
+ if (ret)
+ goto unlock;
+
+ /* adjust hblank value for new width */
+ def = OV2680_PIXELS_PER_LINE - width;
+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def);
+
unlock:
mutex_unlock(&sensor->lock);
@@ -842,6 +866,13 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
struct ov2680_dev *sensor = to_ov2680_dev(sd);
int ret;
+ /* Update exposure range on vblank changes */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ ret = ov2680_exposure_update_range(sensor);
+ if (ret)
+ return ret;
+ }
+
/* Only apply changes to the controls if the device is powered up */
if (!pm_runtime_get_if_in_use(sensor->sd.dev)) {
ov2680_set_bayer_order(sensor, &sensor->mode.fmt);
@@ -864,6 +895,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
ret = ov2680_test_pattern_set(sensor, ctrl->val);
break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
+ sensor->mode.fmt.height + ctrl->val, NULL);
+ break;
default:
ret = -EINVAL;
break;
@@ -922,8 +957,8 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
struct ov2680_ctrls *ctrls = &sensor->ctrls;
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
- int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN;
- int ret = 0;
+ struct v4l2_fwnode_device_properties props;
+ int def, max, ret = 0;
v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops);
sensor->sd.internal_ops = &ov2680_internal_ops;
@@ -948,8 +983,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
ARRAY_SIZE(test_pattern_menu) - 1,
0, 0, test_pattern_menu);
+ max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN;
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
- 0, exp_max, 1, exp_max);
+ 0, max, 1, max);
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
0, 1023, 1, 250);
@@ -960,6 +996,21 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
0, sensor->pixel_rate,
1, sensor->pixel_rate);
+ max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT;
+ def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT;
+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ OV2680_MIN_VBLANK, max, 1, def);
+
+ def = OV2680_PIXELS_PER_LINE - OV2680_DEFAULT_WIDTH;
+ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ def, def, 1, def);
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret)
+ goto cleanup_entity;
+
+ v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+
if (hdl->error) {
ret = hdl->error;
goto cleanup_entity;
@@ -968,6 +1019,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->sd.ctrl_handler = hdl;
@@ -1116,25 +1168,24 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor)
sensor->pixel_rate = sensor->link_freq[0] * 2;
do_div(sensor->pixel_rate, 10);
- /* Verify bus cfg */
- if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) {
- ret = dev_err_probe(dev, -EINVAL,
- "only a 1-lane CSI2 config is supported");
- goto out_free_bus_cfg;
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_warn(dev, "Consider passing 'link-frequencies' in DT\n");
+ goto skip_link_freq_validation;
}
for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
if (bus_cfg.link_frequencies[i] == sensor->link_freq[0])
break;
- if (bus_cfg.nr_of_link_frequencies == 0 ||
- bus_cfg.nr_of_link_frequencies == i) {
+ if (bus_cfg.nr_of_link_frequencies == i) {
ret = dev_err_probe(dev, -EINVAL,
"supported link freq %lld not found\n",
sensor->link_freq[0]);
goto out_free_bus_cfg;
}
+skip_link_freq_validation:
+ ret = 0;
out_free_bus_cfg:
v4l2_fwnode_endpoint_free(&bus_cfg);
return ret;
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 552935ccb4a9..c48dbcde9877 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -768,14 +768,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
cur_mode = ov2740->cur_mode;
size = ARRAY_SIZE(link_freq_menu_items);
- ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
- V4L2_CID_LINK_FREQ,
- size - 1, 0,
- link_freq_menu_items);
+ ov2740->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
+ V4L2_CID_LINK_FREQ, size - 1,
+ ov2740->supported_modes->link_freq_index,
+ link_freq_menu_items);
if (ov2740->link_freq)
ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- pixel_rate = to_pixel_rate(OV2740_LINK_FREQ_360MHZ_INDEX);
+ pixel_rate = to_pixel_rate(ov2740->supported_modes->link_freq_index);
ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_PIXEL_RATE, 0,
pixel_rate, 1, pixel_rate);
@@ -1332,9 +1333,16 @@ static int ov2740_probe(struct i2c_client *client)
return dev_err_probe(dev, ret, "failed to check HW configuration\n");
ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(ov2740->reset_gpio))
+ if (IS_ERR(ov2740->reset_gpio)) {
return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio),
"failed to get reset GPIO\n");
+ } else if (ov2740->reset_gpio) {
+ /*
+ * Ensure reset is asserted for at least 20 ms before
+ * ov2740_resume() deasserts it.
+ */
+ msleep(20);
+ }
ov2740->clk = devm_clk_get_optional(dev, "clk");
if (IS_ERR(ov2740->clk))
diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
index 403091651885..1c3a449f9354 100644
--- a/drivers/media/i2c/ov4689.c
+++ b/drivers/media/i2c/ov4689.c
@@ -3,7 +3,7 @@
* ov4689 driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
- * Copyright (C) 2022 Mikhail Rudenko
+ * Copyright (C) 2022, 2024 Mikhail Rudenko
*/
#include <linux/clk.h>
@@ -15,45 +15,86 @@
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-fwnode.h>
-#define CHIP_ID 0x004688
-#define OV4689_REG_CHIP_ID 0x300a
-
-#define OV4689_XVCLK_FREQ 24000000
-
-#define OV4689_REG_CTRL_MODE 0x0100
+#define OV4689_REG_CTRL_MODE CCI_REG8(0x0100)
#define OV4689_MODE_SW_STANDBY 0x0
#define OV4689_MODE_STREAMING BIT(0)
-#define OV4689_REG_EXPOSURE 0x3500
+#define OV4689_REG_CHIP_ID CCI_REG16(0x300a)
+#define CHIP_ID 0x004688
+
+#define OV4689_REG_EXPOSURE CCI_REG24(0x3500)
#define OV4689_EXPOSURE_MIN 4
#define OV4689_EXPOSURE_STEP 1
-#define OV4689_VTS_MAX 0x7fff
-#define OV4689_REG_GAIN_H 0x3508
-#define OV4689_REG_GAIN_L 0x3509
-#define OV4689_GAIN_H_MASK 0x07
-#define OV4689_GAIN_H_SHIFT 8
-#define OV4689_GAIN_L_MASK 0xff
+#define OV4689_REG_GAIN CCI_REG16(0x3508)
#define OV4689_GAIN_STEP 1
#define OV4689_GAIN_DEFAULT 0x80
-#define OV4689_REG_TEST_PATTERN 0x5040
-#define OV4689_TEST_PATTERN_ENABLE 0x80
-#define OV4689_TEST_PATTERN_DISABLE 0x0
+#define OV4689_REG_DIG_GAIN CCI_REG16(0x352a)
+#define OV4689_DIG_GAIN_MIN 1
+#define OV4689_DIG_GAIN_MAX 0x7fff
+#define OV4689_DIG_GAIN_STEP 1
+#define OV4689_DIG_GAIN_DEFAULT 0x800
-#define OV4689_REG_VTS 0x380e
+#define OV4689_REG_H_CROP_START CCI_REG16(0x3800)
+#define OV4689_REG_V_CROP_START CCI_REG16(0x3802)
+#define OV4689_REG_H_CROP_END CCI_REG16(0x3804)
+#define OV4689_REG_V_CROP_END CCI_REG16(0x3806)
+#define OV4689_REG_H_OUTPUT_SIZE CCI_REG16(0x3808)
+#define OV4689_REG_V_OUTPUT_SIZE CCI_REG16(0x380a)
-#define REG_NULL 0xFFFF
+#define OV4689_REG_HTS CCI_REG16(0x380c)
+#define OV4689_HTS_DIVIDER 4
+#define OV4689_HTS_MAX 0x7fff
-#define OV4689_REG_VALUE_08BIT 1
-#define OV4689_REG_VALUE_16BIT 2
-#define OV4689_REG_VALUE_24BIT 3
+#define OV4689_REG_VTS CCI_REG16(0x380e)
+#define OV4689_VTS_MAX 0x7fff
+
+#define OV4689_REG_H_WIN_OFF CCI_REG16(0x3810)
+#define OV4689_REG_V_WIN_OFF CCI_REG16(0x3812)
+
+#define OV4689_REG_TIMING_FORMAT1 CCI_REG8(0x3820) /* Vertical */
+#define OV4689_REG_TIMING_FORMAT2 CCI_REG8(0x3821) /* Horizontal */
+#define OV4689_TIMING_FLIP_MASK GENMASK(2, 1)
+#define OV4689_TIMING_FLIP_ARRAY BIT(1)
+#define OV4689_TIMING_FLIP_DIGITAL BIT(2)
+#define OV4689_TIMING_FLIP_BOTH (OV4689_TIMING_FLIP_ARRAY |\
+ OV4689_TIMING_FLIP_DIGITAL)
+
+#define OV4689_REG_ANCHOR_LEFT_START CCI_REG16(0x4020)
+#define OV4689_ANCHOR_LEFT_START_DEF 576
+#define OV4689_REG_ANCHOR_LEFT_END CCI_REG16(0x4022)
+#define OV4689_ANCHOR_LEFT_END_DEF 831
+#define OV4689_REG_ANCHOR_RIGHT_START CCI_REG16(0x4024)
+#define OV4689_ANCHOR_RIGHT_START_DEF 1984
+#define OV4689_REG_ANCHOR_RIGHT_END CCI_REG16(0x4026)
+#define OV4689_ANCHOR_RIGHT_END_DEF 2239
+
+#define OV4689_REG_VFIFO_CTRL_01 CCI_REG8(0x4601)
+
+#define OV4689_REG_WB_GAIN_RED CCI_REG16(0x500c)
+#define OV4689_REG_WB_GAIN_BLUE CCI_REG16(0x5010)
+#define OV4689_WB_GAIN_MIN 1
+#define OV4689_WB_GAIN_MAX 0xfff
+#define OV4689_WB_GAIN_STEP 1
+#define OV4689_WB_GAIN_DEFAULT 0x400
+
+#define OV4689_REG_TEST_PATTERN CCI_REG8(0x5040)
+#define OV4689_TEST_PATTERN_ENABLE 0x80
+#define OV4689_TEST_PATTERN_DISABLE 0x0
#define OV4689_LANES 4
+#define OV4689_XVCLK_FREQ 24000000
+
+#define OV4689_PIXEL_ARRAY_WIDTH 2720
+#define OV4689_PIXEL_ARRAY_HEIGHT 1536
+#define OV4689_DUMMY_ROWS 8 /* 8 dummy rows on each side */
+#define OV4689_DUMMY_COLUMNS 16 /* 16 dummy columns on each side */
static const char *const ov4689_supply_names[] = {
"avdd", /* Analog power */
@@ -61,11 +102,6 @@ static const char *const ov4689_supply_names[] = {
"dvdd", /* Digital core power */
};
-struct regval {
- u16 addr;
- u8 val;
-};
-
enum ov4689_mode_id {
OV4689_MODE_2688_1520 = 0,
OV4689_NUM_MODES,
@@ -75,20 +111,18 @@ struct ov4689_mode {
enum ov4689_mode_id id;
u32 width;
u32 height;
- u32 max_fps;
u32 hts_def;
+ u32 hts_min;
u32 vts_def;
u32 exp_def;
u32 pixel_rate;
- u32 sensor_width;
- u32 sensor_height;
- u32 crop_top;
- u32 crop_left;
- const struct regval *reg_list;
+ const struct cci_reg_sequence *reg_list;
+ unsigned int num_regs;
};
struct ov4689 {
- struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
struct clk *xvclk;
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
@@ -99,7 +133,6 @@ struct ov4689 {
u32 clock_rate;
- struct mutex mutex; /* lock to protect ctrls and cur_mode */
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *exposure;
@@ -119,95 +152,108 @@ struct ov4689_gain_range {
/*
* Xclk 24Mhz
- * max_framerate 30fps
+ * max_framerate 90fps
* mipi_datarate per lane 1008Mbps
*/
-static const struct regval ov4689_2688x1520_regs[] = {
- {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
- {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
- {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
- {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
- {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
- {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
- {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
- {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
- {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
- {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
- {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
- {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
- {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
- {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
- {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
- {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
- {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
- {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
- {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
- {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
- {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
- {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
- {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
- {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
- {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
- {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
- {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
- {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
- {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
- {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
- {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
- {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
- {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
- {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
- {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
- {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
- {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
- {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
- {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
- {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
- {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
- {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
- {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
- {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
- {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
- {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
- {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
- {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
- {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
- {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
- {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
- {0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12},
- {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
- {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
- {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
- {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
- {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
- {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
- {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
- {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
- {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
- {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
- {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
- {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
- {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
- {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
- {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
- {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
- {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
- {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
- {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
- {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
- {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
- {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
- {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
- {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
- {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
- {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
- {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
- {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
- {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
- {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
- {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
- {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
- {REG_NULL, 0x00},
+static const struct cci_reg_sequence ov4689_2688x1520_regs[] = {
+ /* System control*/
+ { CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */
+ { CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */
+ { CCI_REG8(0x3021), 0x03 }, /*
+ * SC_CMMN_MISC_CTRL fst_stby_ctr = 0,
+ * sleep_no_latch_enable = 0
+ */
+
+ /* AEC PK */
+ { CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */
+
+ /* ADC and analog control*/
+ { CCI_REG8(0x3603), 0x40 },
+ { CCI_REG8(0x3604), 0x02 },
+ { CCI_REG8(0x3609), 0x12 },
+ { CCI_REG8(0x360c), 0x08 },
+ { CCI_REG8(0x360f), 0xe5 },
+ { CCI_REG8(0x3608), 0x8f },
+ { CCI_REG8(0x3611), 0x00 },
+ { CCI_REG8(0x3613), 0xf7 },
+ { CCI_REG8(0x3616), 0x58 },
+ { CCI_REG8(0x3619), 0x99 },
+ { CCI_REG8(0x361b), 0x60 },
+ { CCI_REG8(0x361e), 0x79 },
+ { CCI_REG8(0x3634), 0x10 },
+ { CCI_REG8(0x3635), 0x10 },
+ { CCI_REG8(0x3636), 0x15 },
+ { CCI_REG8(0x3646), 0x86 },
+ { CCI_REG8(0x364a), 0x0b },
+
+ /* Sensor control */
+ { CCI_REG8(0x3700), 0x17 },
+ { CCI_REG8(0x3701), 0x22 },
+ { CCI_REG8(0x3703), 0x10 },
+ { CCI_REG8(0x370a), 0x37 },
+ { CCI_REG8(0x3706), 0x63 },
+ { CCI_REG8(0x3709), 0x3c },
+ { CCI_REG8(0x370c), 0x30 },
+ { CCI_REG8(0x3710), 0x24 },
+ { CCI_REG8(0x3720), 0x28 },
+ { CCI_REG8(0x3729), 0x7b },
+ { CCI_REG8(0x372b), 0xbd },
+ { CCI_REG8(0x372c), 0xbc },
+ { CCI_REG8(0x372e), 0x52 },
+ { CCI_REG8(0x373c), 0x0e },
+ { CCI_REG8(0x373e), 0x33 },
+ { CCI_REG8(0x3743), 0x10 },
+ { CCI_REG8(0x3744), 0x88 },
+ { CCI_REG8(0x3745), 0xc0 },
+ { CCI_REG8(0x374c), 0x00 },
+ { CCI_REG8(0x374e), 0x23 },
+ { CCI_REG8(0x3751), 0x7b },
+ { CCI_REG8(0x3753), 0xbd },
+ { CCI_REG8(0x3754), 0xbc },
+ { CCI_REG8(0x3756), 0x52 },
+ { CCI_REG8(0x376b), 0x20 },
+ { CCI_REG8(0x3774), 0x51 },
+ { CCI_REG8(0x3776), 0xbd },
+ { CCI_REG8(0x3777), 0xbd },
+ { CCI_REG8(0x3781), 0x18 },
+ { CCI_REG8(0x3783), 0x25 },
+ { CCI_REG8(0x3798), 0x1b },
+
+ /* Timing control */
+ { CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */
+
+ /* OTP control */
+ { CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1,
+ * OTP_power_up_load_data_enable = 1,
+ * OTP_bist_select = 1 (compare with zero)
+ */
+ { CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */
+ { CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */
+
+ /* BLC registers*/
+ { CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */
+ { CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */
+ { CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */
+ { CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */
+
+ /* ADC sync control */
+ { CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */
+ { CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */
+
+ /* Temperature monitor */
+ { CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */
+ { CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */
+ { CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */
+ { CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */
+ { CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8] = 0xf5 */
+ { CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0] = 0xc1 */
+
+ /* pre-ISP control */
+ { CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */
+
+ /* OTP-DPC control */
+ { CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */
+ { CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */
};
static const struct ov4689_mode supported_modes[] = {
@@ -215,16 +261,13 @@ static const struct ov4689_mode supported_modes[] = {
.id = OV4689_MODE_2688_1520,
.width = 2688,
.height = 1520,
- .sensor_width = 2720,
- .sensor_height = 1536,
- .crop_top = 8,
- .crop_left = 16,
- .max_fps = 30,
.exp_def = 1536,
- .hts_def = 4 * 2574,
+ .hts_def = 10296,
+ .hts_min = 3432,
.vts_def = 1554,
.pixel_rate = 480000000,
.reg_list = ov4689_2688x1520_regs,
+ .num_regs = ARRAY_SIZE(ov4689_2688x1520_regs),
},
};
@@ -277,83 +320,6 @@ static const struct ov4689_gain_range ov4689_gain_ranges[] = {
},
};
-/* Write registers up to 4 at a time */
-static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
- u32 val)
-{
- u32 buf_i, val_i;
- __be32 val_be;
- u8 *val_p;
- u8 buf[6];
-
- if (len > 4)
- return -EINVAL;
-
- buf[0] = reg >> 8;
- buf[1] = reg & 0xff;
-
- val_be = cpu_to_be32(val);
- val_p = (u8 *)&val_be;
- buf_i = 2;
- val_i = 4 - len;
-
- while (val_i < 4)
- buf[buf_i++] = val_p[val_i++];
-
- if (i2c_master_send(client, buf, len + 2) != len + 2)
- return -EIO;
-
- return 0;
-}
-
-static int ov4689_write_array(struct i2c_client *client,
- const struct regval *regs)
-{
- int ret = 0;
- u32 i;
-
- for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
- ret = ov4689_write_reg(client, regs[i].addr,
- OV4689_REG_VALUE_08BIT, regs[i].val);
-
- return ret;
-}
-
-/* Read registers up to 4 at a time */
-static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
- u32 *val)
-{
- __be16 reg_addr_be = cpu_to_be16(reg);
- struct i2c_msg msgs[2];
- __be32 data_be = 0;
- u8 *data_be_p;
- int ret;
-
- if (len > 4 || !len)
- return -EINVAL;
-
- data_be_p = (u8 *)&data_be;
- /* Write register address */
- msgs[0].addr = client->addr;
- msgs[0].flags = 0;
- msgs[0].len = 2;
- msgs[0].buf = (u8 *)&reg_addr_be;
-
- /* Read data from register */
- msgs[1].addr = client->addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = &data_be_p[4 - len];
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs))
- return -EIO;
-
- *val = be32_to_cpu(data_be);
-
- return 0;
-}
-
static void ov4689_fill_fmt(const struct ov4689_mode *mode,
struct v4l2_mbus_framefmt *fmt)
{
@@ -376,19 +342,6 @@ static int ov4689_set_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int ov4689_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
- struct ov4689 *ov4689 = to_ov4689(sd);
-
- /* only one mode supported for now */
- ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
-
- return 0;
-}
-
static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -427,16 +380,14 @@ static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
else
val = OV4689_TEST_PATTERN_DISABLE;
- return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
- OV4689_REG_VALUE_08BIT, val);
+ return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN,
+ val, NULL);
}
static int ov4689_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
- const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode;
-
if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
@@ -444,63 +395,114 @@ static int ov4689_get_selection(struct v4l2_subdev *sd,
case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.top = 0;
sel->r.left = 0;
- sel->r.width = mode->sensor_width;
- sel->r.height = mode->sensor_height;
+ sel->r.width = OV4689_PIXEL_ARRAY_WIDTH;
+ sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT;
return 0;
case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_CROP_DEFAULT:
- sel->r.top = mode->crop_top;
- sel->r.left = mode->crop_left;
- sel->r.width = mode->width;
- sel->r.height = mode->height;
+ sel->r.top = OV4689_DUMMY_ROWS;
+ sel->r.left = OV4689_DUMMY_COLUMNS;
+ sel->r.width =
+ OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS;
+ sel->r.height =
+ OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS;
return 0;
}
return -EINVAL;
}
+static int ov4689_setup_timings(struct ov4689 *ov4689)
+{
+ const struct ov4689_mode *mode = ov4689->cur_mode;
+ struct regmap *rm = ov4689->regmap;
+ int ret = 0;
+
+ cci_write(rm, OV4689_REG_H_CROP_START, 8, &ret);
+ cci_write(rm, OV4689_REG_V_CROP_START, 8, &ret);
+ cci_write(rm, OV4689_REG_H_CROP_END, 2711, &ret);
+ cci_write(rm, OV4689_REG_V_CROP_END, 1531, &ret);
+
+ cci_write(rm, OV4689_REG_H_OUTPUT_SIZE, mode->width, &ret);
+ cci_write(rm, OV4689_REG_V_OUTPUT_SIZE, mode->height, &ret);
+
+ cci_write(rm, OV4689_REG_H_WIN_OFF, 8, &ret);
+ cci_write(rm, OV4689_REG_V_WIN_OFF, 4, &ret);
+
+ cci_write(rm, OV4689_REG_VFIFO_CTRL_01, 167, &ret);
+
+ return ret;
+}
+
+static int ov4689_setup_blc_anchors(struct ov4689 *ov4689)
+{
+ struct regmap *rm = ov4689->regmap;
+ int ret = 0;
+
+ cci_write(rm, OV4689_REG_ANCHOR_LEFT_START, 16, &ret);
+ cci_write(rm, OV4689_REG_ANCHOR_LEFT_END, 1999, &ret);
+ cci_write(rm, OV4689_REG_ANCHOR_RIGHT_START, 2400, &ret);
+ cci_write(rm, OV4689_REG_ANCHOR_RIGHT_END, 2415, &ret);
+
+ return ret;
+}
+
static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
{
struct ov4689 *ov4689 = to_ov4689(sd);
- struct i2c_client *client = ov4689->client;
+ struct v4l2_subdev_state *sd_state;
+ struct device *dev = ov4689->dev;
int ret = 0;
- mutex_lock(&ov4689->mutex);
+ sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev);
if (on) {
- ret = pm_runtime_resume_and_get(&client->dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
goto unlock_and_return;
- ret = ov4689_write_array(ov4689->client,
- ov4689->cur_mode->reg_list);
+ ret = cci_multi_reg_write(ov4689->regmap,
+ ov4689->cur_mode->reg_list,
+ ov4689->cur_mode->num_regs,
+ NULL);
+ if (ret) {
+ pm_runtime_put(dev);
+ goto unlock_and_return;
+ }
+
+ ret = ov4689_setup_timings(ov4689);
if (ret) {
- pm_runtime_put(&client->dev);
+ pm_runtime_put(dev);
+ goto unlock_and_return;
+ }
+
+ ret = ov4689_setup_blc_anchors(ov4689);
+ if (ret) {
+ pm_runtime_put(dev);
goto unlock_and_return;
}
ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
if (ret) {
- pm_runtime_put(&client->dev);
+ pm_runtime_put(dev);
goto unlock_and_return;
}
- ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
- OV4689_REG_VALUE_08BIT,
- OV4689_MODE_STREAMING);
+ ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
+ OV4689_MODE_STREAMING, NULL);
if (ret) {
- pm_runtime_put(&client->dev);
+ pm_runtime_put(dev);
goto unlock_and_return;
}
} else {
- ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
- OV4689_REG_VALUE_08BIT,
- OV4689_MODE_SW_STANDBY);
- pm_runtime_put(&client->dev);
+ cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
+ OV4689_MODE_SW_STANDBY, NULL);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
}
unlock_and_return:
- mutex_unlock(&ov4689->mutex);
+ v4l2_subdev_unlock_state(sd_state);
return ret;
}
@@ -563,18 +565,13 @@ static int __maybe_unused ov4689_power_off(struct device *dev)
return 0;
}
-static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+static int ov4689_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
{
- struct ov4689 *ov4689 = to_ov4689(sd);
- struct v4l2_mbus_framefmt *try_fmt;
-
- mutex_lock(&ov4689->mutex);
-
- try_fmt = v4l2_subdev_state_get_format(fh->state, 0);
- /* Initialize try_fmt */
- ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(sd_state, 0);
- mutex_unlock(&ov4689->mutex);
+ ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt);
return 0;
}
@@ -583,10 +580,6 @@ static const struct dev_pm_ops ov4689_pm_ops = {
SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL)
};
-static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
- .open = ov4689_open,
-};
-
static const struct v4l2_subdev_video_ops ov4689_video_ops = {
.s_stream = ov4689_s_stream,
};
@@ -594,11 +587,15 @@ static const struct v4l2_subdev_video_ops ov4689_video_ops = {
static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
.enum_mbus_code = ov4689_enum_mbus_code,
.enum_frame_size = ov4689_enum_frame_sizes,
- .get_fmt = ov4689_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ov4689_set_fmt,
.get_selection = ov4689_get_selection,
};
+static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
+ .init_state = ov4689_init_state,
+};
+
static const struct v4l2_subdev_ops ov4689_subdev_ops = {
.video = &ov4689_video_ops,
.pad = &ov4689_pad_ops,
@@ -610,7 +607,6 @@ static const struct v4l2_subdev_ops ov4689_subdev_ops = {
*/
static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
{
- const struct device *dev = &ov4689->client->dev;
const struct ov4689_gain_range *range;
unsigned int n;
@@ -621,7 +617,8 @@ static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
}
if (n == ARRAY_SIZE(ov4689_gain_ranges)) {
- dev_warn_ratelimited(dev, "no mapping found for gain %d\n",
+ dev_warn_ratelimited(ov4689->dev,
+ "no mapping found for gain %d\n",
logical_gain);
return -EINVAL;
}
@@ -637,10 +634,11 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov4689 *ov4689 =
container_of(ctrl->handler, struct ov4689, ctrl_handler);
- struct i2c_client *client = ov4689->client;
- int sensor_gain;
+ struct regmap *regmap = ov4689->regmap;
+ struct device *dev = ov4689->dev;
+ int sensor_gain = 0;
s64 max_expo;
- int ret;
+ int ret = 0;
/* Propagate change of current control to all related controls */
switch (ctrl->id) {
@@ -654,44 +652,58 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
break;
}
- if (!pm_runtime_get_if_in_use(&client->dev))
+ if (!pm_runtime_get_if_in_use(dev))
return 0;
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
- /* 4 least significant bits of expsoure are fractional part */
- ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
- OV4689_REG_VALUE_24BIT, ctrl->val << 4);
+ /* 4 least significant bits of exposure are fractional part */
+ cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret);
break;
case V4L2_CID_ANALOGUE_GAIN:
ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain);
-
- ret = ret ?:
- ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
- OV4689_REG_VALUE_08BIT,
- (sensor_gain >> OV4689_GAIN_H_SHIFT) &
- OV4689_GAIN_H_MASK);
- ret = ret ?:
- ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
- OV4689_REG_VALUE_08BIT,
- sensor_gain & OV4689_GAIN_L_MASK);
+ cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret);
break;
case V4L2_CID_VBLANK:
- ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
- OV4689_REG_VALUE_16BIT,
- ctrl->val + ov4689->cur_mode->height);
+ cci_write(regmap, OV4689_REG_VTS,
+ ctrl->val + ov4689->cur_mode->height, &ret);
break;
case V4L2_CID_TEST_PATTERN:
ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
break;
+ case V4L2_CID_HBLANK:
+ cci_write(regmap, OV4689_REG_HTS,
+ (ctrl->val + ov4689->cur_mode->width) /
+ OV4689_HTS_DIVIDER, &ret);
+ break;
+ case V4L2_CID_VFLIP:
+ cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT1,
+ OV4689_TIMING_FLIP_MASK,
+ ctrl->val ? OV4689_TIMING_FLIP_BOTH : 0, &ret);
+ break;
+ case V4L2_CID_HFLIP:
+ cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT2,
+ OV4689_TIMING_FLIP_MASK,
+ ctrl->val ? 0 : OV4689_TIMING_FLIP_BOTH, &ret);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ cci_write(regmap, OV4689_REG_DIG_GAIN, ctrl->val, &ret);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ cci_write(regmap, OV4689_REG_WB_GAIN_RED, ctrl->val, &ret);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ cci_write(regmap, OV4689_REG_WB_GAIN_BLUE, ctrl->val, &ret);
+ break;
default:
- dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n",
__func__, ctrl->id, ctrl->val);
ret = -EINVAL;
break;
}
- pm_runtime_put(&client->dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
@@ -707,16 +719,15 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
struct v4l2_ctrl_handler *handler;
const struct ov4689_mode *mode;
s64 exposure_max, vblank_def;
+ s64 hblank_def, hblank_min;
struct v4l2_ctrl *ctrl;
- s64 h_blank_def;
int ret;
handler = &ov4689->ctrl_handler;
mode = ov4689->cur_mode;
- ret = v4l2_ctrl_handler_init(handler, 10);
+ ret = v4l2_ctrl_handler_init(handler, 15);
if (ret)
return ret;
- handler->lock = &ov4689->mutex;
ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
link_freq_menu_items);
@@ -726,11 +737,11 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
mode->pixel_rate, 1, mode->pixel_rate);
- h_blank_def = mode->hts_def - mode->width;
- ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def,
- h_blank_def, 1, h_blank_def);
- if (ctrl)
- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ hblank_def = mode->hts_def - mode->width;
+ hblank_min = mode->hts_min - mode->width;
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK,
+ hblank_min, OV4689_HTS_MAX - mode->width,
+ OV4689_HTS_DIVIDER, hblank_def);
vblank_def = mode->vts_def - mode->height;
v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
@@ -754,10 +765,24 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689)
ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
0, 0, ov4689_test_pattern_menu);
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV4689_DIG_GAIN_MIN, OV4689_DIG_GAIN_MAX,
+ OV4689_DIG_GAIN_STEP, OV4689_DIG_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_RED_BALANCE,
+ OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
+ OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_BLUE_BALANCE,
+ OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
+ OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
+
if (handler->error) {
ret = handler->error;
- dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
- ret);
+ dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret);
goto err_free_handler;
}
@@ -783,19 +808,18 @@ err_free_handler:
static int ov4689_check_sensor_id(struct ov4689 *ov4689,
struct i2c_client *client)
{
- struct device *dev = &ov4689->client->dev;
- u32 id = 0;
+ struct device *dev = ov4689->dev;
+ u64 id = 0;
int ret;
- ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
- OV4689_REG_VALUE_16BIT, &id);
+ ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL);
if (ret) {
dev_err(dev, "Cannot read sensor ID\n");
return ret;
}
if (id != CHIP_ID) {
- dev_err(dev, "Unexpected sensor ID %06x, expected %06x\n",
+ dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n",
id, CHIP_ID);
return -ENODEV;
}
@@ -812,7 +836,7 @@ static int ov4689_configure_regulators(struct ov4689 *ov4689)
for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++)
ov4689->supplies[i].supply = ov4689_supply_names[i];
- return devm_regulator_bulk_get(&ov4689->client->dev,
+ return devm_regulator_bulk_get(ov4689->dev,
ARRAY_SIZE(ov4689_supply_names),
ov4689->supplies);
}
@@ -881,7 +905,8 @@ static int ov4689_probe(struct i2c_client *client)
if (!ov4689)
return -ENOMEM;
- ov4689->client = client;
+ ov4689->dev = dev;
+
ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];
ov4689->xvclk = devm_clk_get_optional(dev, NULL);
@@ -905,6 +930,13 @@ static int ov4689_probe(struct i2c_client *client)
return -EINVAL;
}
+ ov4689->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(ov4689->regmap)) {
+ ret = PTR_ERR(ov4689->regmap);
+ dev_err(dev, "failed to initialize CCI: %d\n", ret);
+ return ret;
+ }
+
ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(ov4689->reset_gpio)) {
@@ -923,13 +955,15 @@ static int ov4689_probe(struct i2c_client *client)
return dev_err_probe(dev, ret,
"Failed to get power regulators\n");
- mutex_init(&ov4689->mutex);
-
sd = &ov4689->subdev;
v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
+ sd->internal_ops = &ov4689_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ret = ov4689_initialize_controls(ov4689);
- if (ret)
- goto err_destroy_mutex;
+ if (ret) {
+ dev_err(dev, "Failed to initialize controls\n");
+ return ret;
+ }
ret = ov4689_power_on(dev);
if (ret)
@@ -939,35 +973,47 @@ static int ov4689_probe(struct i2c_client *client)
if (ret)
goto err_power_off;
- sd->internal_ops = &ov4689_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
if (ret < 0)
goto err_power_off;
- ret = v4l2_async_register_subdev_sensor(sd);
+ sd->state_lock = ov4689->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(sd);
if (ret) {
- dev_err(dev, "v4l2 async register subdev failed\n");
+ dev_err(dev, "Could not register v4l2 device\n");
goto err_clean_entity;
}
pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);
- pm_runtime_idle(dev);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = v4l2_async_register_subdev_sensor(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto err_clean_subdev_pm;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return 0;
+err_clean_subdev_pm:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ v4l2_subdev_cleanup(sd);
err_clean_entity:
media_entity_cleanup(&sd->entity);
err_power_off:
ov4689_power_off(dev);
err_free_handler:
v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
-err_destroy_mutex:
- mutex_destroy(&ov4689->mutex);
return ret;
}
@@ -979,9 +1025,8 @@ static void ov4689_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
-
+ v4l2_subdev_cleanup(sd);
v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
- mutex_destroy(&ov4689->mutex);
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev))
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
index b4647bda8c21..b8bd8354d100 100644
--- a/drivers/media/i2c/rdacm20.c
+++ b/drivers/media/i2c/rdacm20.c
@@ -463,8 +463,8 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
return ret;
/*
- * Ensure that we have a good link configuration before attempting to
- * identify the device.
+ * Ensure that we have a good link configuration before attempting to
+ * identify the device.
*/
ret = max9271_configure_i2c(&dev->serializer,
MAX9271_I2CSLVSH_469NS_234NS |
diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
index f250640729ca..b947a55281f0 100644
--- a/drivers/media/i2c/st-mipid02.c
+++ b/drivers/media/i2c/st-mipid02.c
@@ -326,7 +326,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge,
}
dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq);
- do_div(ui_4, link_freq);
+ ui_4 = div64_u64(ui_4, link_freq);
bridge->r.clk_lane_reg1 |= ui_4 << 2;
return 0;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 3192a334aaab..0307fee3cce9 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -1521,11 +1521,14 @@ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status)
return 0;
}
-static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
+static int tc358743_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct tc358743_state *state = to_state(sd);
+ if (pad != 0)
+ return -EINVAL;
+
if (!timings)
return -EINVAL;
@@ -1553,11 +1556,14 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int tc358743_g_dv_timings(struct v4l2_subdev *sd,
+static int tc358743_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct tc358743_state *state = to_state(sd);
+ if (pad != 0)
+ return -EINVAL;
+
*timings = state->timings;
return 0;
@@ -1573,11 +1579,14 @@ static int tc358743_enum_dv_timings(struct v4l2_subdev *sd,
&tc358743_timings_cap, NULL, NULL);
}
-static int tc358743_query_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int tc358743_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
int ret;
+ if (pad != 0)
+ return -EINVAL;
+
ret = tc358743_get_detected_timings(sd, timings);
if (ret)
return ret;
@@ -1822,9 +1831,6 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = {
static const struct v4l2_subdev_video_ops tc358743_video_ops = {
.g_input_status = tc358743_g_input_status,
- .s_dv_timings = tc358743_s_dv_timings,
- .g_dv_timings = tc358743_g_dv_timings,
- .query_dv_timings = tc358743_query_dv_timings,
.s_stream = tc358743_s_stream,
};
@@ -1834,6 +1840,9 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = {
.get_fmt = tc358743_get_fmt,
.get_edid = tc358743_g_edid,
.set_edid = tc358743_s_edid,
+ .s_dv_timings = tc358743_s_dv_timings,
+ .g_dv_timings = tc358743_g_dv_timings,
+ .query_dv_timings = tc358743_query_dv_timings,
.enum_dv_timings = tc358743_enum_dv_timings,
.dv_timings_cap = tc358743_dv_timings_cap,
.get_mbus_config = tc358743_get_mbus_config,
@@ -2110,7 +2119,7 @@ static int tc358743_probe(struct i2c_client *client)
tc358743_initial_setup(sd);
- tc358743_s_dv_timings(sd, &default_timing);
+ tc358743_s_dv_timings(sd, 0, &default_timing);
tc358743_set_csi_color_space(sd);
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
index d676adc4401b..edf79107adc5 100644
--- a/drivers/media/i2c/tc358746.c
+++ b/drivers/media/i2c/tc358746.c
@@ -844,8 +844,7 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746,
continue;
tmp = fout * postdiv;
- do_div(tmp, fin);
- mul = tmp;
+ mul = div64_ul(tmp, fin);
if (mul > 511)
continue;
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 8e4a0718c4b6..58ce8fec3041 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -1669,8 +1669,8 @@ tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status)
return 0;
};
-static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct tda1997x_state *state = to_state(sd);
@@ -1694,7 +1694,7 @@ static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
+static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct tda1997x_state *state = to_state(sd);
@@ -1707,7 +1707,7 @@ static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
+static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct tda1997x_state *state = to_state(sd);
@@ -1724,9 +1724,6 @@ static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
.g_input_status = tda1997x_g_input_status,
- .s_dv_timings = tda1997x_s_dv_timings,
- .g_dv_timings = tda1997x_g_dv_timings,
- .query_dv_timings = tda1997x_query_dv_timings,
};
@@ -1930,6 +1927,9 @@ static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = {
.set_fmt = tda1997x_set_format,
.get_edid = tda1997x_get_edid,
.set_edid = tda1997x_set_edid,
+ .s_dv_timings = tda1997x_s_dv_timings,
+ .g_dv_timings = tda1997x_g_dv_timings,
+ .query_dv_timings = tda1997x_query_dv_timings,
.dv_timings_cap = tda1997x_get_dv_timings_cap,
.enum_dv_timings = tda1997x_enum_dv_timings,
};
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index ea70c1c13872..49ed83a0ac94 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -193,8 +193,8 @@ static int ths7303_s_stream(struct v4l2_subdev *sd, int enable)
}
/* for setting filter for HD output */
-static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *dv_timings)
+static int ths7303_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *dv_timings)
{
struct ths7303_state *state = to_state(sd);
@@ -210,7 +210,6 @@ static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops ths7303_video_ops = {
.s_stream = ths7303_s_stream,
.s_std_output = ths7303_s_std_output,
- .s_dv_timings = ths7303_s_dv_timings,
};
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -317,9 +316,14 @@ static const struct v4l2_subdev_core_ops ths7303_core_ops = {
#endif
};
+static const struct v4l2_subdev_pad_ops ths7303_pad_ops = {
+ .s_dv_timings = ths7303_s_dv_timings,
+};
+
static const struct v4l2_subdev_ops ths7303_ops = {
.core = &ths7303_core_ops,
.video = &ths7303_video_ops,
+ .pad = &ths7303_pad_ops,
};
static int ths7303_probe(struct i2c_client *client)
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 0e0f676cd221..ce0a7f809f19 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -358,13 +358,16 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
bt->hsync, bt->vsync);
}
-static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
+static int ths8200_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct ths8200_state *state = to_state(sd);
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap,
NULL, NULL))
return -EINVAL;
@@ -385,13 +388,16 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
+static int ths8200_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_dv_timings *timings)
{
struct ths8200_state *state = to_state(sd);
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+ if (pad != 0)
+ return -EINVAL;
+
*timings = state->dv_timings;
return 0;
@@ -420,11 +426,11 @@ static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
/* Specific video subsystem operation handlers */
static const struct v4l2_subdev_video_ops ths8200_video_ops = {
.s_stream = ths8200_s_stream,
- .s_dv_timings = ths8200_s_dv_timings,
- .g_dv_timings = ths8200_g_dv_timings,
};
static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
+ .s_dv_timings = ths8200_s_dv_timings,
+ .g_dv_timings = ths8200_g_dv_timings,
.enum_dv_timings = ths8200_enum_dv_timings,
.dv_timings_cap = ths8200_dv_timings_cap,
};
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 6a04ffae5343..ea01bd86450e 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -546,13 +546,16 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd,
return error;
}
-static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *dv_timings)
+static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *dv_timings)
{
struct tvp7002 *device = to_tvp7002(sd);
const struct v4l2_bt_timings *bt = &dv_timings->bt;
int i;
+ if (pad != 0)
+ return -EINVAL;
+
if (dv_timings->type != V4L2_DV_BT_656_1120)
return -EINVAL;
for (i = 0; i < NUM_TIMINGS; i++) {
@@ -566,11 +569,14 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
return -EINVAL;
}
-static int tvp7002_g_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *dv_timings)
+static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *dv_timings)
{
struct tvp7002 *device = to_tvp7002(sd);
+ if (pad != 0)
+ return -EINVAL;
+
*dv_timings = device->current_timings->timings;
return 0;
}
@@ -659,12 +665,16 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)
return 0;
}
-static int tvp7002_query_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
int index;
- int err = tvp7002_query_dv(sd, &index);
+ int err;
+
+ if (pad != 0)
+ return -EINVAL;
+ err = tvp7002_query_dv(sd, &index);
if (err)
return err;
*timings = tvp7002_timings[index].timings;
@@ -861,9 +871,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
/* Specific video subsystem operation handlers */
static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
- .g_dv_timings = tvp7002_g_dv_timings,
- .s_dv_timings = tvp7002_s_dv_timings,
- .query_dv_timings = tvp7002_query_dv_timings,
.s_stream = tvp7002_s_stream,
};
@@ -872,6 +879,9 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {
.enum_mbus_code = tvp7002_enum_mbus_code,
.get_fmt = tvp7002_get_pad_format,
.set_fmt = tvp7002_set_pad_format,
+ .g_dv_timings = tvp7002_g_dv_timings,
+ .s_dv_timings = tvp7002_s_dv_timings,
+ .query_dv_timings = tvp7002_query_dv_timings,
.enum_dv_timings = tvp7002_enum_dv_timings,
};
@@ -1001,7 +1011,7 @@ static int tvp7002_probe(struct i2c_client *c)
/* Set registers according to default video mode */
timings = device->current_timings->timings;
- error = tvp7002_s_dv_timings(sd, &timings);
+ error = tvp7002_s_dv_timings(sd, 0, &timings);
#if defined(CONFIG_MEDIA_CONTROLLER)
device->pad.flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 7f67825c8757..318e267e798e 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -245,15 +245,14 @@ int __must_check media_devnode_register(struct media_device *mdev,
kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
/* Part 3: Add the media and char device */
+ set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
ret = cdev_device_add(&devnode->cdev, &devnode->dev);
if (ret < 0) {
+ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
pr_err("%s: cdev_device_add failed\n", __func__);
goto cdev_add_error;
}
- /* Part 4: Activate this minor. The char device can now be used. */
- set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
-
return 0;
cdev_add_error:
diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
index 0e28b9a7936e..96dd0f6ccd0d 100644
--- a/drivers/media/mc/mc-entity.c
+++ b/drivers/media/mc/mc-entity.c
@@ -619,6 +619,12 @@ static int media_pipeline_explore_next_link(struct media_pipeline *pipe,
link = list_entry(entry->links, typeof(*link), list);
last_link = media_pipeline_walk_pop(walk);
+ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {
+ dev_dbg(walk->mdev->dev,
+ "media pipeline: skipping link (not data-link)\n");
+ return 0;
+ }
+
dev_dbg(walk->mdev->dev,
"media pipeline: exploring link '%s':%u -> '%s':%u\n",
link->source->entity->name, link->source->index,
diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c
index 065b572e0272..8199077faf36 100644
--- a/drivers/media/mmc/siano/smssdio.c
+++ b/drivers/media/mmc/siano/smssdio.c
@@ -344,30 +344,7 @@ static struct sdio_driver smssdio_driver = {
.probe = smssdio_probe,
.remove = smssdio_remove,
};
-
-/*******************************************************************/
-/* Module functions */
-/*******************************************************************/
-
-static int __init smssdio_module_init(void)
-{
- int ret = 0;
-
- printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
- printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
-
- ret = sdio_register_driver(&smssdio_driver);
-
- return ret;
-}
-
-static void __exit smssdio_module_exit(void)
-{
- sdio_unregister_driver(&smssdio_driver);
-}
-
-module_init(smssdio_module_init);
-module_exit(smssdio_module_exit);
+module_sdio_driver(smssdio_driver);
MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
MODULE_AUTHOR("Pierre Ossman");
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
index 77ba08ace29f..d4d7b264c965 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.c
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -633,7 +633,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh,
return -EBUSY;
err = v4l2_subdev_call(s->sd,
- video, s_dv_timings, timings);
+ pad, s_dv_timings, 0, timings);
if (!err) {
s->timings = *timings;
s->width = timings->bt.width;
@@ -653,7 +653,7 @@ static int cobalt_g_dv_timings(struct file *file, void *priv_fh,
return 0;
}
return v4l2_subdev_call(s->sd,
- video, g_dv_timings, timings);
+ pad, g_dv_timings, 0, timings);
}
static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
@@ -666,7 +666,7 @@ static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
return 0;
}
return v4l2_subdev_call(s->sd,
- video, query_dv_timings, timings);
+ pad, query_dv_timings, 0, timings);
}
static int cobalt_dv_timings_cap(struct file *file, void *priv_fh,
@@ -1080,7 +1080,7 @@ static int cobalt_g_pixelaspect(struct file *file, void *fh,
if (s->input == 1)
timings = cea1080p60;
else
- err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
+ err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
if (!err)
*f = v4l2_dv_timings_aspect_ratio(&timings);
return err;
@@ -1099,7 +1099,7 @@ static int cobalt_g_selection(struct file *file, void *fh,
if (s->input == 1)
timings = cea1080p60;
else
- err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
+ err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
if (err)
return err;
@@ -1243,7 +1243,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node)
if (s->sd)
vdev->ctrl_handler = s->sd->ctrl_handler;
s->timings = dv1080p60;
- v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings);
+ v4l2_subdev_call(s->sd, pad, s_dv_timings, 0, &s->timings);
if (!s->is_output && s->sd)
cobalt_enable_input(s);
vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops :
diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig
index ee4684159d3d..d9fcddce028b 100644
--- a/drivers/media/pci/intel/Kconfig
+++ b/drivers/media/pci/intel/Kconfig
@@ -1,11 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/media/pci/intel/ipu3/Kconfig"
+source "drivers/media/pci/intel/ipu6/Kconfig"
source "drivers/media/pci/intel/ivsc/Kconfig"
config IPU_BRIDGE
tristate "Intel IPU Bridge"
- depends on I2C && ACPI
+ depends on ACPI || COMPILE_TEST
+ depends on I2C
help
The IPU bridge is a helper library for Intel IPU drivers to
function on systems shipped with Windows.
diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile
index f199a97e1d78..3a2cc6567159 100644
--- a/drivers/media/pci/intel/Makefile
+++ b/drivers/media/pci/intel/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o
obj-y += ipu3/
obj-y += ivsc/
+obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index e994db4f4d91..61750cc98d70 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -15,6 +15,8 @@
#include <media/ipu-bridge.h>
#include <media/v4l2-fwnode.h>
+#define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev))
+
/*
* 92335fcf-3203-4472-af93-7b4453ac29da
*
@@ -87,6 +89,7 @@ static const char * const ipu_vcm_types[] = {
"lc898212axb",
};
+#if IS_ENABLED(CONFIG_ACPI)
/*
* Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev()
* instead of device and driver match to probe IVSC device.
@@ -100,13 +103,13 @@ static const struct acpi_device_id ivsc_acpi_ids[] = {
static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
{
- acpi_handle handle = acpi_device_handle(adev);
- struct acpi_device *consumer, *ivsc_adev;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) {
const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i];
+ struct acpi_device *consumer, *ivsc_adev;
+ acpi_handle handle = acpi_device_handle(adev);
for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1)
/* camera sensor depends on IVSC in DSDT if exist */
for_each_acpi_consumer_dev(ivsc_adev, consumer)
@@ -118,6 +121,12 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev
return NULL;
}
+#else
+static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
+{
+ return NULL;
+}
+#endif
static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev)
{
@@ -163,7 +172,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
if (!csi_dev) {
acpi_dev_put(adev);
- dev_err(&adev->dev, "Failed to find MEI CSI dev\n");
+ dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n");
return -ENODEV;
}
@@ -182,24 +191,25 @@ static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
acpi_status status;
int ret = 0;
- status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
+ status = acpi_evaluate_object(ACPI_PTR(adev->handle),
+ id, NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
obj = buffer.pointer;
if (!obj) {
- dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
+ dev_err(ADEV_DEV(adev), "Couldn't locate ACPI buffer\n");
return -ENODEV;
}
if (obj->type != ACPI_TYPE_BUFFER) {
- dev_err(&adev->dev, "Not an ACPI buffer\n");
+ dev_err(ADEV_DEV(adev), "Not an ACPI buffer\n");
ret = -ENODEV;
goto out_free_buff;
}
if (obj->buffer.length > size) {
- dev_err(&adev->dev, "Given buffer is too small\n");
+ dev_err(ADEV_DEV(adev), "Given buffer is too small\n");
ret = -EINVAL;
goto out_free_buff;
}
@@ -220,7 +230,7 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
case IPU_SENSOR_ROTATION_INVERTED:
return 180;
default:
- dev_warn(&adev->dev,
+ dev_warn(ADEV_DEV(adev),
"Unknown rotation %d. Assume 0 degree rotation\n",
ssdb->degree);
return 0;
@@ -230,12 +240,14 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev)
{
enum v4l2_fwnode_orientation orientation;
- struct acpi_pld_info *pld;
- acpi_status status;
+ struct acpi_pld_info *pld = NULL;
+ acpi_status status = AE_ERROR;
+#if IS_ENABLED(CONFIG_ACPI)
status = acpi_get_physical_device_location(adev->handle, &pld);
+#endif
if (ACPI_FAILURE(status)) {
- dev_warn(&adev->dev, "_PLD call failed, using default orientation\n");
+ dev_warn(ADEV_DEV(adev), "_PLD call failed, using default orientation\n");
return V4L2_FWNODE_ORIENTATION_EXTERNAL;
}
@@ -253,7 +265,8 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev
orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
break;
default:
- dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel);
+ dev_warn(ADEV_DEV(adev), "Unknown _PLD panel val %d\n",
+ pld->panel);
orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
break;
}
@@ -272,12 +285,12 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
return ret;
if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) {
- dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype);
+ dev_warn(ADEV_DEV(adev), "Unknown VCM type %d\n", ssdb.vcmtype);
ssdb.vcmtype = 0;
}
if (ssdb.lanes > IPU_MAX_LANES) {
- dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
+ dev_err(ADEV_DEV(adev), "Number of lanes in SSDB is invalid\n");
return -EINVAL;
}
@@ -465,8 +478,14 @@ static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge,
sensor->ipu_properties);
if (sensor->csi_dev) {
+ const char *device_hid = "";
+
+#if IS_ENABLED(CONFIG_ACPI)
+ device_hid = acpi_device_hid(sensor->ivsc_adev);
+#endif
+
snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u",
- acpi_device_hid(sensor->ivsc_adev), sensor->link);
+ device_hid, sensor->link);
nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name,
sensor->ivsc_properties);
@@ -631,11 +650,15 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
{
struct fwnode_handle *fwnode, *primary;
struct ipu_sensor *sensor;
- struct acpi_device *adev;
+ struct acpi_device *adev = NULL;
int ret;
+#if IS_ENABLED(CONFIG_ACPI)
for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
- if (!adev->status.enabled)
+#else
+ while (true) {
+#endif
+ if (!ACPI_PTR(adev->status.enabled))
continue;
if (bridge->n_sensors >= IPU_MAX_PORTS) {
@@ -671,7 +694,7 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
goto err_free_swnodes;
}
- sensor->adev = acpi_dev_get(adev);
+ sensor->adev = ACPI_PTR(acpi_dev_get(adev));
primary = acpi_fwnode_handle(adev);
primary->secondary = fwnode;
@@ -727,11 +750,16 @@ static int ipu_bridge_ivsc_is_ready(void)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) {
+#if IS_ENABLED(CONFIG_ACPI)
const struct ipu_sensor_config *cfg =
&ipu_supported_sensors[i];
for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) {
- if (!sensor_adev->status.enabled)
+#else
+ while (true) {
+ sensor_adev = NULL;
+#endif
+ if (!ACPI_PTR(sensor_adev->status.enabled))
continue;
adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index c42adc5a408d..81ec8630453b 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -4,9 +4,9 @@
*
* Based partially on Intel IPU4 driver written by
* Sakari Ailus <sakari.ailus@linux.intel.com>
- * Samu Onkalo <samu.onkalo@intel.com>
+ * Samu Onkalo
* Jouni Högander <jouni.hogander@intel.com>
- * Jouni Ukkonen <jouni.ukkonen@intel.com>
+ * Jouni Ukkonen
* Antti Laakso <antti.laakso@intel.com>
* et al.
*/
@@ -1752,11 +1752,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
- /* Register notifier for subdevices we care */
- r = cio2_parse_firmware(cio2);
- if (r)
- goto fail_clean_notifier;
-
r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
CIO2_NAME, cio2);
if (r) {
@@ -1764,6 +1759,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
goto fail_clean_notifier;
}
+ /* Register notifier for subdevices we care */
+ r = cio2_parse_firmware(cio2);
+ if (r)
+ goto fail_clean_notifier;
+
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);
@@ -1807,16 +1807,10 @@ static int __maybe_unused cio2_runtime_suspend(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
void __iomem *const base = cio2->base;
- u16 pm;
writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C);
dev_dbg(dev, "cio2 runtime suspend.\n");
- pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
- pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
- pm |= CIO2_PMCSR_D3;
- pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
-
return 0;
}
@@ -1825,15 +1819,10 @@ static int __maybe_unused cio2_runtime_resume(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
void __iomem *const base = cio2->base;
- u16 pm;
writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C);
dev_dbg(dev, "cio2 runtime resume.\n");
- pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
- pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
- pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
-
return 0;
}
@@ -2006,10 +1995,10 @@ static struct pci_driver cio2_pci_driver = {
module_pci_driver(cio2_pci_driver);
-MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
+MODULE_AUTHOR("Tuukka Toivonen");
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_AUTHOR("Jian Xu Zheng");
-MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
+MODULE_AUTHOR("Yuning Pu");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IPU3 CIO2 driver");
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index d731ce8adbe3..d7cb7dae665b 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -320,10 +320,6 @@ struct pci_dev;
#define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4
#define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570
-#define CIO2_PMCSR_OFFSET 4U
-#define CIO2_PMCSR_D0D3_SHIFT 2U
-#define CIO2_PMCSR_D3 0x3
-
struct cio2_csi2_timing {
s32 clk_termen;
s32 clk_settle;
diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig
new file mode 100644
index 000000000000..154343080c82
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_INTEL_IPU6
+ tristate "Intel IPU6 driver"
+ depends on ACPI || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on X86 && X86_64 && HAS_DMA
+ select DMA_OPS
+ select IOMMU_IOVA
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ select IPU_BRIDGE
+ help
+ This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs
+ and used for capturing images and video from camera sensors.
+
+ To compile this driver, say Y here! It contains 2 modules -
+ intel_ipu6 and intel_ipu6_isys.
diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile
new file mode 100644
index 000000000000..a821b0a1567f
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+intel-ipu6-y := ipu6.o \
+ ipu6-bus.o \
+ ipu6-dma.o \
+ ipu6-mmu.o \
+ ipu6-buttress.o \
+ ipu6-cpd.o \
+ ipu6-fw-com.o
+
+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o
+
+intel-ipu6-isys-y := ipu6-isys.o \
+ ipu6-isys-csi2.o \
+ ipu6-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
+
+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o
diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.c b/drivers/media/pci/intel/ipu6/ipu6-bus.c
new file mode 100644
index 000000000000..149ec098cdbf
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-bus.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 Intel Corporation
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-buttress.h"
+#include "ipu6-dma.h"
+
+static int bus_pm_runtime_suspend(struct device *dev)
+{
+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+ int ret;
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ ret = ipu6_buttress_power(dev, adev->ctrl, false);
+ if (!ret)
+ return 0;
+
+ dev_err(dev, "power down failed!\n");
+
+ /* Powering down failed, attempt to resume device now */
+ ret = pm_generic_runtime_resume(dev);
+ if (!ret)
+ return -EBUSY;
+
+ return -EIO;
+}
+
+static int bus_pm_runtime_resume(struct device *dev)
+{
+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+ int ret;
+
+ ret = ipu6_buttress_power(dev, adev->ctrl, true);
+ if (ret)
+ return ret;
+
+ ret = pm_generic_runtime_resume(dev);
+ if (ret)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ ipu6_buttress_power(dev, adev->ctrl, false);
+
+ return -EBUSY;
+}
+
+static struct dev_pm_domain ipu6_bus_pm_domain = {
+ .ops = {
+ .runtime_suspend = bus_pm_runtime_suspend,
+ .runtime_resume = bus_pm_runtime_resume,
+ },
+};
+
+static DEFINE_MUTEX(ipu6_bus_mutex);
+
+static void ipu6_bus_release(struct device *dev)
+{
+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+
+ kfree(adev->pdata);
+ kfree(adev);
+}
+
+struct ipu6_bus_device *
+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
+ void *pdata, struct ipu6_buttress_ctrl *ctrl,
+ char *name)
+{
+ struct auxiliary_device *auxdev;
+ struct ipu6_bus_device *adev;
+ struct ipu6_device *isp = pci_get_drvdata(pdev);
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return ERR_PTR(-ENOMEM);
+
+ adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
+ IPU6_MMU_ADDR_BITS_NON_SECURE);
+ adev->isp = isp;
+ adev->ctrl = ctrl;
+ adev->pdata = pdata;
+ auxdev = &adev->auxdev;
+ auxdev->name = name;
+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
+ PCI_DEVID(pdev->bus->number, pdev->devfn);
+
+ auxdev->dev.parent = parent;
+ auxdev->dev.release = ipu6_bus_release;
+ auxdev->dev.dma_ops = &ipu6_dma_ops;
+ auxdev->dev.dma_mask = &adev->dma_mask;
+ auxdev->dev.dma_parms = pdev->dev.dma_parms;
+ auxdev->dev.coherent_dma_mask = adev->dma_mask;
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
+ ret);
+ kfree(adev);
+ return ERR_PTR(ret);
+ }
+
+ dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
+
+ pm_runtime_forbid(&adev->auxdev.dev);
+ pm_runtime_enable(&adev->auxdev.dev);
+
+ return adev;
+}
+
+int ipu6_bus_add_device(struct ipu6_bus_device *adev)
+{
+ struct auxiliary_device *auxdev = &adev->auxdev;
+ int ret;
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret) {
+ auxiliary_device_uninit(auxdev);
+ return ret;
+ }
+
+ mutex_lock(&ipu6_bus_mutex);
+ list_add(&adev->list, &adev->isp->devices);
+ mutex_unlock(&ipu6_bus_mutex);
+
+ pm_runtime_allow(&auxdev->dev);
+
+ return 0;
+}
+
+void ipu6_bus_del_devices(struct pci_dev *pdev)
+{
+ struct ipu6_device *isp = pci_get_drvdata(pdev);
+ struct ipu6_bus_device *adev, *save;
+
+ mutex_lock(&ipu6_bus_mutex);
+
+ list_for_each_entry_safe(adev, save, &isp->devices, list) {
+ pm_runtime_disable(&adev->auxdev.dev);
+ list_del(&adev->list);
+ auxiliary_device_delete(&adev->auxdev);
+ auxiliary_device_uninit(&adev->auxdev);
+ }
+
+ mutex_unlock(&ipu6_bus_mutex);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h
new file mode 100644
index 000000000000..b26c6aee1621
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013 - 2024 Intel Corporation */
+
+#ifndef IPU6_BUS_H
+#define IPU6_BUS_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+
+struct firmware;
+struct pci_dev;
+
+#define IPU6_BUS_NAME IPU6_NAME "-bus"
+
+struct ipu6_buttress_ctrl;
+
+struct ipu6_bus_device {
+ struct auxiliary_device auxdev;
+ struct auxiliary_driver *auxdrv;
+ const struct ipu6_auxdrv_data *auxdrv_data;
+ struct list_head list;
+ void *pdata;
+ struct ipu6_mmu *mmu;
+ struct ipu6_device *isp;
+ struct ipu6_buttress_ctrl *ctrl;
+ u64 dma_mask;
+ const struct firmware *fw;
+ struct sg_table fw_sgt;
+ u64 *pkg_dir;
+ dma_addr_t pkg_dir_dma_addr;
+ unsigned int pkg_dir_size;
+};
+
+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;
+};
+
+#define to_ipu6_bus_device(_dev) \
+ container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev)
+#define auxdev_to_adev(_auxdev) \
+ container_of(_auxdev, struct ipu6_bus_device, auxdev)
+#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
+
+struct ipu6_bus_device *
+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
+ void *pdata, struct ipu6_buttress_ctrl *ctrl,
+ char *name);
+int ipu6_bus_add_device(struct ipu6_bus_device *adev);
+void ipu6_bus_del_devices(struct pci_dev *pdev);
+
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
new file mode 100644
index 000000000000..23c537e7ce1e
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
@@ -0,0 +1,917 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pfn.h>
+#include <linux/pm_runtime.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/time64.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-buttress.h"
+#include "ipu6-platform-buttress-regs.h"
+
+#define BOOTLOADER_STATUS_OFFSET 0x15c
+
+#define BOOTLOADER_MAGIC_KEY 0xb00710ad
+
+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
+
+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10
+
+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC)
+
+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC)
+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC)
+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC)
+
+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC
+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC
+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC)
+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC)
+
+#define BUTTRESS_IPC_RESET_RETRY 2000
+#define BUTTRESS_CSE_IPC_RESET_RETRY 4
+#define BUTTRESS_IPC_CMD_SEND_RETRY 1
+
+#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)
+{
+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
+ struct ipu6_buttress *b = &isp->buttress;
+ u32 val = 0, csr_in_clr;
+
+ if (!isp->secure_mode) {
+ dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
+ return 0;
+ }
+
+ mutex_lock(&b->ipc_mutex);
+
+ /* Clear-by-1 CSR (all bits), corresponding internal states. */
+ val = readl(isp->base + ipc->csr_in);
+ writel(val, isp->base + ipc->csr_in);
+
+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
+ writel(ENTRY, isp->base + ipc->csr_out);
+ /*
+ * Clear-by-1 all CSR bits EXCEPT following
+ * bits:
+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * C. Possibly custom bits, depending on
+ * their role.
+ */
+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
+
+ do {
+ usleep_range(400, 500);
+ val = readl(isp->base + ipc->csr_in);
+ switch (val) {
+ case ENTRY | EXIT:
+ case ENTRY | EXIT | QUERY:
+ /*
+ * 1) Clear-by-1 CSR bits
+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
+ * 2) Set peer CSR bit
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
+ */
+ writel(ENTRY | EXIT, isp->base + ipc->csr_in);
+ writel(QUERY, isp->base + ipc->csr_out);
+ break;
+ case ENTRY:
+ case ENTRY | QUERY:
+ /*
+ * 1) Clear-by-1 CSR bits
+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
+ * 2) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ */
+ writel(ENTRY | QUERY, isp->base + ipc->csr_in);
+ writel(ENTRY, isp->base + ipc->csr_out);
+ break;
+ case EXIT:
+ case EXIT | QUERY:
+ /*
+ * Clear-by-1 CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * 1) Clear incoming doorbell.
+ * 2) Clear-by-1 all CSR bits EXCEPT following
+ * bits:
+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * C. Possibly custom bits, depending on
+ * their role.
+ * 3) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ */
+ writel(EXIT, isp->base + ipc->csr_in);
+ writel(0, isp->base + ipc->db0_in);
+ writel(csr_in_clr, isp->base + ipc->csr_in);
+ writel(EXIT, isp->base + ipc->csr_out);
+
+ /*
+ * Read csr_in again to make sure if RST_PHASE2 is done.
+ * If csr_in is QUERY, it should be handled again.
+ */
+ usleep_range(200, 300);
+ val = readl(isp->base + ipc->csr_in);
+ if (val & QUERY) {
+ dev_dbg(&isp->pdev->dev,
+ "RST_PHASE2 retry csr_in = %x\n", val);
+ break;
+ }
+ mutex_unlock(&b->ipc_mutex);
+ return 0;
+ case QUERY:
+ /*
+ * 1) Clear-by-1 CSR bit
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
+ * 2) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1
+ */
+ writel(QUERY, isp->base + ipc->csr_in);
+ writel(ENTRY, isp->base + ipc->csr_out);
+ break;
+ default:
+ dev_warn_ratelimited(&isp->pdev->dev,
+ "Unexpected CSR 0x%x\n", val);
+ break;
+ }
+ } while (retries--);
+
+ mutex_unlock(&b->ipc_mutex);
+ dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
+
+ return -ETIMEDOUT;
+}
+
+static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
+ struct ipu6_buttress_ipc *ipc)
+{
+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
+ isp->base + ipc->csr_out);
+}
+
+static int
+ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
+ struct ipu6_buttress_ipc *ipc)
+{
+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
+ void __iomem *addr;
+ int ret;
+ u32 val;
+
+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
+ isp->base + ipc->csr_out);
+
+ addr = isp->base + ipc->csr_in;
+ ret = readl_poll_timeout(addr, val, val & mask, 200,
+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
+ ipu6_buttress_ipc_validity_close(isp, ipc);
+ }
+
+ return ret;
+}
+
+static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
+ struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
+{
+ if (ipc_msg)
+ *ipc_msg = readl(isp->base + ipc->data0_in);
+ writel(0, isp->base + ipc->db0_in);
+}
+
+static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
+ enum ipu6_buttress_ipc_domain ipc_domain,
+ struct ipu6_ipc_buttress_bulk_msg *msgs,
+ u32 size)
+{
+ 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;
+ u32 val;
+ int ret;
+ int tout;
+
+ ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
+
+ mutex_lock(&b->ipc_mutex);
+
+ ret = ipu6_buttress_ipc_validity_open(isp, ipc);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "IPC validity open failed\n");
+ goto out;
+ }
+
+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
+
+ for (i = 0; i < size; i++) {
+ reinit_completion(&ipc->send_complete);
+ if (msgs[i].require_resp)
+ reinit_completion(&ipc->recv_complete);
+
+ dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
+ msgs[i].cmd);
+ writel(msgs[i].cmd, isp->base + ipc->data0_out);
+ val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
+ writel(val, isp->base + ipc->db0_out);
+
+ tout = wait_for_completion_timeout(&ipc->send_complete,
+ tx_timeout_jiffies);
+ if (!tout) {
+ dev_err(&isp->pdev->dev, "send IPC response timeout\n");
+ if (!retry--) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* Try again if CSE is not responding on first try */
+ writel(0, isp->base + ipc->db0_out);
+ i--;
+ continue;
+ }
+
+ retry = BUTTRESS_IPC_CMD_SEND_RETRY;
+
+ if (!msgs[i].require_resp)
+ continue;
+
+ tout = wait_for_completion_timeout(&ipc->recv_complete,
+ rx_timeout_jiffies);
+ if (!tout) {
+ dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (ipc->nack_mask &&
+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
+ dev_err(&isp->pdev->dev,
+ "IPC NACK for cmd 0x%x\n", msgs[i].cmd);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (ipc->recv_data != msgs[i].expected_resp) {
+ dev_err(&isp->pdev->dev,
+ "expected resp: 0x%x, IPC response: 0x%x ",
+ msgs[i].expected_resp, ipc->recv_data);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
+
+out:
+ ipu6_buttress_ipc_validity_close(isp, ipc);
+ mutex_unlock(&b->ipc_mutex);
+ return ret;
+}
+
+static int
+ipu6_buttress_ipc_send(struct ipu6_device *isp,
+ enum ipu6_buttress_ipc_domain ipc_domain,
+ u32 ipc_msg, u32 size, bool require_resp,
+ u32 expected_resp)
+{
+ struct ipu6_ipc_buttress_bulk_msg msg = {
+ .cmd = ipc_msg,
+ .cmd_size = size,
+ .require_resp = require_resp,
+ .expected_resp = expected_resp,
+ };
+
+ return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
+}
+
+static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
+{
+ irqreturn_t ret = IRQ_WAKE_THREAD;
+
+ if (!adev || !adev->auxdrv || !adev->auxdrv_data)
+ return IRQ_NONE;
+
+ if (adev->auxdrv_data->isr)
+ ret = adev->auxdrv_data->isr(adev);
+
+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
+ ret = IRQ_NONE;
+
+ return ret;
+}
+
+irqreturn_t 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;
+ irqreturn_t ret = IRQ_NONE;
+ u32 disable_irqs = 0;
+ u32 irq_status;
+ u32 i, count = 0;
+
+ pm_runtime_get_noresume(&isp->pdev->dev);
+
+ irq_status = readl(isp->base + reg_irq_sts);
+ if (!irq_status) {
+ pm_runtime_put_noidle(&isp->pdev->dev);
+ return IRQ_NONE;
+ }
+
+ do {
+ writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
+
+ for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
+ irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
+
+ if (!(irq_status & ipu6_adev_irq_mask[i]))
+ continue;
+
+ if (r == IRQ_WAKE_THREAD) {
+ ret = IRQ_WAKE_THREAD;
+ disable_irqs |= ipu6_adev_irq_mask[i];
+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
+ ret = IRQ_HANDLED;
+
+ if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
+ 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);
+ }
+
+ if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
+ dev_dbg(&isp->pdev->dev,
+ "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
+ ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
+ complete(&b->ish.recv_complete);
+ }
+
+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
+ dev_dbg(&isp->pdev->dev,
+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
+ complete(&b->cse.send_complete);
+ }
+
+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
+ dev_dbg(&isp->pdev->dev,
+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
+ complete(&b->ish.send_complete);
+ }
+
+ if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
+ ipu6_buttress_get_secure_mode(isp))
+ dev_err(&isp->pdev->dev,
+ "BUTTRESS_ISR_SAI_VIOLATION\n");
+
+ if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
+ BUTTRESS_ISR_PS_FATAL_MEM_ERR))
+ dev_err(&isp->pdev->dev,
+ "BUTTRESS_ISR_FATAL_MEM_ERR\n");
+
+ if (irq_status & BUTTRESS_ISR_UFI_ERROR)
+ dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
+
+ if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
+ dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
+ ret = IRQ_NONE;
+ break;
+ }
+
+ irq_status = readl(isp->base + reg_irq_sts);
+ } while (irq_status);
+
+ if (disable_irqs)
+ writel(BUTTRESS_IRQS & ~disable_irqs,
+ isp->base + BUTTRESS_REG_ISR_ENABLE);
+
+ pm_runtime_put(&isp->pdev->dev);
+
+ return ret;
+}
+
+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
+{
+ struct ipu6_device *isp = isp_ptr;
+ 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++) {
+ drv_data = adev[i]->auxdrv_data;
+ if (!drv_data)
+ continue;
+
+ if (drv_data->wake_isr_thread &&
+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
+
+ return ret;
+}
+
+int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
+ bool on)
+{
+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
+ u32 pwr_sts, val;
+ int ret;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+
+ 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;
+
+ 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,
+ 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);
+
+ ctrl->started = !ret && on;
+
+ mutex_unlock(&isp->buttress.power_mutex);
+
+ return ret;
+}
+
+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+
+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
+}
+
+bool ipu6_buttress_auth_done(struct ipu6_device *isp)
+{
+ u32 val;
+
+ if (!isp->secure_mode)
+ return true;
+
+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
+
+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
+
+int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
+{
+ int ret;
+ u32 val;
+
+ if (!isp->secure_mode) {
+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
+ return 0;
+ }
+
+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
+ BUTTRESS_REG_FW_RESET_CTL);
+
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
+ val & BUTTRESS_FW_RESET_CTL_DONE, 500,
+ BUTTRESS_CSE_FWRESET_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev,
+ "Time out while resetting authentication state\n");
+ return ret;
+ }
+
+ dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
+ /* leave some time for HW restore */
+ usleep_range(800, 1000);
+
+ return 0;
+}
+
+int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
+ const struct firmware *fw, struct sg_table *sgt)
+{
+ bool is_vmalloc = is_vmalloc_addr(fw->data);
+ struct page **pages;
+ const void *addr;
+ unsigned long n_pages;
+ unsigned int i;
+ int ret;
+
+ if (!is_vmalloc && !virt_addr_valid(fw->data))
+ return -EDOM;
+
+ n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
+
+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
+ 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);
+
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pages[i] = p;
+ addr += PAGE_SIZE;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
+ GFP_KERNEL);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ sg_free_table(sgt);
+ goto out;
+ }
+
+ dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
+
+out:
+ kfree(pages);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
+
+void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
+ struct sg_table *sgt)
+{
+ dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
+ sg_free_table(sgt);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
+
+int ipu6_buttress_authenticate(struct ipu6_device *isp)
+{
+ struct ipu6_buttress *b = &isp->buttress;
+ struct ipu6_psys_pdata *psys_pdata;
+ u32 data, mask, done, fail;
+ int ret;
+
+ if (!isp->secure_mode) {
+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
+ return 0;
+ }
+
+ psys_pdata = isp->psys->pdata;
+
+ mutex_lock(&b->auth_mutex);
+
+ if (ipu6_buttress_auth_done(isp)) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ /*
+ * 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);
+
+ 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
+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
+ */
+ dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
+
+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
+ BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
+ 1, true,
+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
+ goto out_unlock;
+ }
+
+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ((data & mask) == done ||
+ (data & mask) == fail), 500,
+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
+ goto out_unlock;
+ }
+
+ if ((data & mask) == fail) {
+ dev_err(&isp->pdev->dev, "CSE auth failed\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
+ data, data == BOOTLOADER_MAGIC_KEY, 500,
+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
+ data);
+ goto out_unlock;
+ }
+
+ /*
+ * Write authenticate_run into IU2CSEDATA0
+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
+ */
+ dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
+ BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
+ 1, true,
+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
+ goto out_unlock;
+ }
+
+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ((data & mask) == done ||
+ (data & mask) == fail), 500,
+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
+ goto out_unlock;
+ }
+
+ if ((data & mask) == fail) {
+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
+
+out_unlock:
+ mutex_unlock(&b->auth_mutex);
+
+ return ret;
+}
+
+static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
+{
+ 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);
+
+ val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
+ val = FIELD_GET(mask, val);
+ if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
+ return -EINVAL;
+ }
+
+ done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
+ FIELD_GET(mask, val) == done, 500,
+ BUTTRESS_TSC_SYNC_TIMEOUT_US);
+ if (ret)
+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
+
+ return ret;
+}
+
+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
+{
+ unsigned int i;
+
+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ u32 val;
+ int ret;
+
+ ret = ipu6_buttress_send_tsc_request(isp);
+ if (ret != -ETIMEDOUT)
+ return ret;
+
+ val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
+ val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
+ val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
+ }
+
+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
+
+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
+{
+ 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);
+ if (tsc_hi_1 == tsc_hi_2) {
+ *val = (u64)tsc_hi_1 << 32 | tsc_lo;
+ } else {
+ /* Check if TSC has rolled over */
+ if (tsc_lo & BIT(31))
+ *val = (u64)tsc_hi_1 << 32 | tsc_lo;
+ else
+ *val = (u64)tsc_hi_2 << 32 | tsc_lo;
+ }
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
+
+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
+{
+ u64 ns = ticks * 10000;
+
+ /*
+ * converting TSC tick count to ns is calculated by:
+ * Example (TSC clock frequency is 19.2MHz):
+ * ns = ticks * 1000 000 000 / 19.2Mhz
+ * = ticks * 1000 000 000 / 19200000Hz
+ * = ticks * 10000 / 192 ns
+ */
+ return div_u64(ns, isp->buttress.ref_clk);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
+
+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);
+}
+
+int ipu6_buttress_init(struct ipu6_device *isp)
+{
+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
+ struct ipu6_buttress *b = &isp->buttress;
+ u32 val;
+
+ mutex_init(&b->power_mutex);
+ mutex_init(&b->auth_mutex);
+ mutex_init(&b->cons_mutex);
+ mutex_init(&b->ipc_mutex);
+ init_completion(&b->ish.send_complete);
+ init_completion(&b->cse.send_complete);
+ init_completion(&b->ish.recv_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;
+
+ /* no ISH on IPU6 */
+ memset(&b->ish, 0, sizeof(b->ish));
+ INIT_LIST_HEAD(&b->constraints);
+
+ isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
+ dev_info(&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:
+ 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;
+ }
+
+ /* Retry couple of times in case of CSE initialization is delayed */
+ do {
+ ret = ipu6_buttress_ipc_reset(isp, &b->cse);
+ if (ret) {
+ dev_warn(&isp->pdev->dev,
+ "IPC reset protocol failed, retrying\n");
+ } else {
+ dev_dbg(&isp->pdev->dev, "IPC reset done\n");
+ return 0;
+ }
+ } while (ipc_reset_retry--);
+
+ dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
+
+ mutex_destroy(&b->power_mutex);
+ mutex_destroy(&b->auth_mutex);
+ mutex_destroy(&b->cons_mutex);
+ mutex_destroy(&b->ipc_mutex);
+
+ return ret;
+}
+
+void ipu6_buttress_exit(struct ipu6_device *isp)
+{
+ struct ipu6_buttress *b = &isp->buttress;
+
+ writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
+
+ mutex_destroy(&b->power_mutex);
+ mutex_destroy(&b->auth_mutex);
+ mutex_destroy(&b->cons_mutex);
+ mutex_destroy(&b->ipc_mutex);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
new file mode 100644
index 000000000000..9b6f56958be7
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_BUTTRESS_H
+#define IPU6_BUTTRESS_H
+
+#include <linux/completion.h>
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct device;
+struct firmware;
+struct ipu6_device;
+struct ipu6_bus_device;
+
+#define BUTTRESS_PS_FREQ_STEP 25U
+#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8)
+#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32)
+
+#define BUTTRESS_IS_FREQ_STEP 25U
+#define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8)
+#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22)
+
+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;
+ bool started;
+};
+
+struct ipu6_buttress_ipc {
+ struct completion send_complete;
+ struct completion recv_complete;
+ u32 nack;
+ u32 nack_mask;
+ u32 recv_data;
+ u32 csr_out;
+ u32 csr_in;
+ u32 db0_in;
+ u32 db0_out;
+ u32 data0_out;
+ u32 data0_in;
+};
+
+struct ipu6_buttress {
+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
+ struct ipu6_buttress_ipc cse;
+ struct ipu6_buttress_ipc ish;
+ struct list_head constraints;
+ u32 wdt_cached_value;
+ bool force_suspend;
+ u32 ref_clk;
+};
+
+enum ipu6_buttress_ipc_domain {
+ IPU6_BUTTRESS_IPC_CSE,
+ IPU6_BUTTRESS_IPC_ISH,
+};
+
+struct ipu6_ipc_buttress_bulk_msg {
+ u32 cmd;
+ u32 expected_resp;
+ bool require_resp;
+ 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_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
+ bool on);
+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
+int ipu6_buttress_authenticate(struct ipu6_device *isp);
+int ipu6_buttress_reset_authentication(struct ipu6_device *isp);
+bool ipu6_buttress_auth_done(struct ipu6_device *isp);
+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp);
+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val);
+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp);
+
+irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr);
+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr);
+int ipu6_buttress_init(struct ipu6_device *isp);
+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);
+#endif /* IPU6_BUTTRESS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
new file mode 100644
index 000000000000..715b21ab4b8e
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp_types.h>
+#include <linux/math64.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-cpd.h"
+
+/* 15 entries + header*/
+#define MAX_PKG_DIR_ENT_CNT 16
+/* 2 qword per entry/header */
+#define PKG_DIR_ENT_LEN 2
+/* PKG_DIR size in bytes */
+#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \
+ (PKG_DIR_ENT_LEN) * sizeof(u64))
+/* _IUPKDR_ */
+#define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL
+
+/* $CPD */
+#define CPD_HDR_MARK 0x44504324
+
+#define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32))
+#define MAX_METADATA_SIZE SZ_64K
+
+#define MAX_COMPONENT_ID 127
+#define MAX_COMPONENT_VERSION 0xffff
+
+#define MANIFEST_IDX 0
+#define METADATA_IDX 1
+#define MODULEDATA_IDX 2
+/*
+ * PKG_DIR Entry (type == id)
+ * 63:56 55 54:48 47:32 31:24 23:0
+ * Rsvd Rsvd Type Version Rsvd Size
+ */
+#define PKG_DIR_SIZE_MASK GENMASK(23, 0)
+#define PKG_DIR_VERSION_MASK GENMASK(47, 32)
+#define PKG_DIR_TYPE_MASK GENMASK(54, 48)
+
+static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
+ u8 idx)
+{
+ const struct ipu6_cpd_hdr *cpd_hdr = cpd;
+ const struct ipu6_cpd_ent *ent;
+
+ ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
+ return ent + idx;
+}
+
+#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
+#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)
+
+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)
+{
+ size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
+ size_t cmpnt_count = metadata_size - extn_size;
+
+ cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
+
+ if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
+ dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
+ idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
+}
+
+static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
+ const void *metadata,
+ unsigned int metadata_size, u8 idx)
+{
+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
+
+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
+ if (IS_ERR(cmpnt))
+ return PTR_ERR(cmpnt);
+
+ return cmpnt->ver;
+}
+
+static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
+ const void *metadata,
+ unsigned int metadata_size, u8 idx)
+{
+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
+
+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
+ if (IS_ERR(cmpnt))
+ return PTR_ERR(cmpnt);
+
+ return cmpnt->id;
+}
+
+static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
+ const void *module_data,
+ unsigned int module_data_size,
+ dma_addr_t dma_addr_module_data,
+ u64 *pkg_dir, const void *metadata,
+ unsigned int metadata_size)
+{
+ const struct ipu6_cpd_module_data_hdr *module_data_hdr;
+ const struct ipu6_cpd_hdr *dir_hdr;
+ const struct ipu6_cpd_ent *dir_ent;
+ unsigned int i;
+ u8 len;
+
+ if (!module_data)
+ return -EINVAL;
+
+ module_data_hdr = module_data;
+ dir_hdr = module_data + module_data_hdr->hdr_len;
+ len = dir_hdr->hdr_len;
+ dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
+
+ pkg_dir[0] = PKG_DIR_HDR_MARK;
+ /* pkg_dir entry count = component count + pkg_dir header */
+ pkg_dir[1] = dir_hdr->ent_cnt + 1;
+
+ for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
+ u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)];
+ int ver, id;
+
+ *p++ = dma_addr_module_data + dir_ent->offset;
+ id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
+ metadata_size, i);
+ if (id < 0 || id > MAX_COMPONENT_ID) {
+ dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
+ return -EINVAL;
+ }
+
+ ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
+ metadata_size, i);
+ if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
+ dev_err(&isp->pdev->dev,
+ "Invalid CPD component version\n");
+ return -EINVAL;
+ }
+
+ *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
+ FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
+ FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
+ }
+
+ return 0;
+}
+
+int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
+{
+ dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
+ const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu6_device *isp = adev->isp;
+ unsigned int man_sz, met_sz;
+ void *pkg_dir_pos;
+ int ret;
+
+ man_ent = ipu6_cpd_get_manifest(src);
+ man_sz = man_ent->len;
+
+ met_ent = ipu6_cpd_get_metadata(src);
+ met_sz = met_ent->len;
+
+ adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
+ adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,
+ &adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
+ if (!adev->pkg_dir)
+ return -ENOMEM;
+
+ /*
+ * pkg_dir entry/header:
+ * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0
+ * N Address/Offset/"_IUPKDR_"
+ * N + 1 | rsvd | rsvd | type | ver | rsvd | size
+ *
+ * We can ignore other fields that size in N + 1 qword as they
+ * are 0 anyway. Just setting size for now.
+ */
+
+ ent = ipu6_cpd_get_moduledata(src);
+
+ ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
+ ent->len, dma_addr_src + ent->offset,
+ adev->pkg_dir, src + met_ent->offset,
+ met_ent->len);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "Failed to parse module data\n");
+ dma_free_attrs(dev, adev->pkg_dir_size,
+ adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
+ return ret;
+ }
+
+ /* Copy manifest after pkg_dir */
+ pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
+ memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
+
+ /* Copy metadata after manifest */
+ pkg_dir_pos += man_sz;
+ memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
+
+ dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,
+ 0, adev->pkg_dir_size, DMA_TO_DEVICE);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
+
+void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
+{
+ dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,
+ adev->pkg_dir_dma_addr, 0);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
+
+static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
+ unsigned long cpd_size,
+ unsigned long data_size)
+{
+ const struct ipu6_cpd_hdr *cpd_hdr = cpd;
+ const struct ipu6_cpd_ent *ent;
+ unsigned int i;
+ u8 len;
+
+ len = cpd_hdr->hdr_len;
+
+ /* Ensure cpd hdr is within moduledata */
+ if (cpd_size < len) {
+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
+ return -EINVAL;
+ }
+
+ /* Sanity check for CPD header */
+ if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
+ dev_err(&isp->pdev->dev, "Invalid CPD header\n");
+ return -EINVAL;
+ }
+
+ /* Ensure that all entries are within moduledata */
+ ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
+ for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
+ if (data_size < ent->offset ||
+ data_size - ent->offset < ent->len) {
+ dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
+ const void *moduledata,
+ 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) ||
+ moduledata_size < mod_hdr->hdr_len) {
+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
+ return -EINVAL;
+ }
+
+ dev_info(&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;
+}
+
+static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
+ const void *metadata, u32 meta_size)
+{
+ const struct ipu6_cpd_metadata_extn *extn = metadata;
+
+ /* Sanity check for metadata size */
+ if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
+ dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
+ return -EINVAL;
+ }
+
+ /* Validate extension and image types */
+ if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
+ extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
+ dev_err(&isp->pdev->dev,
+ "Invalid CPD metadata descriptor img_type (%d)\n",
+ extn->img_type);
+ return -EINVAL;
+ }
+
+ /* Validate metadata size multiple of metadata components */
+ if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
+ dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
+ const struct ipu6_cpd_ent *ent;
+ 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 in file\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;
+ }
+
+ /* Sanity check for manifest size */
+ 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;
+ }
+
+ /* Validate metadata */
+ 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");
+ return ret;
+ }
+
+ /* Validate moduledata */
+ ent = ipu6_cpd_get_moduledata(cpd_file);
+ ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
+ ent->len);
+ if (ret)
+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
+
+ return ret;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
new file mode 100644
index 000000000000..e0e4fdeca902
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2015--2024 Intel Corporation */
+
+#ifndef IPU6_CPD_H
+#define IPU6_CPD_H
+
+struct ipu6_device;
+struct ipu6_bus_device;
+
+#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION 7
+#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION 11
+#define IPU6_CPD_SIZE_OF_COMPONENT_NAME 12
+
+#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT 0x10
+
+#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED 0
+#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1
+#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2
+
+#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX 0
+#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX 1
+
+#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE 3
+
+#define IPU6_CPD_METADATA_HASH_KEY_SIZE 48
+#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE 32
+
+struct ipu6_cpd_module_data_hdr {
+ u32 hdr_len;
+ u32 endian;
+ u32 fw_pkg_date;
+ u32 hive_sdk_date;
+ u32 compiler_date;
+ u32 target_platform_type;
+ u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION];
+ u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION];
+ u8 rsvd[2];
+} __packed;
+
+/*
+ * ipu6_cpd_hdr structure updated as the chksum and
+ * sub_partition_name is unused on host side
+ * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10)
+ * CSE layout version 1.7 for IPU6 (hdr_len = 0x14)
+ */
+struct ipu6_cpd_hdr {
+ u32 hdr_mark;
+ u32 ent_cnt;
+ u8 hdr_ver;
+ u8 ent_ver;
+ u8 hdr_len;
+} __packed;
+
+struct ipu6_cpd_ent {
+ u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME];
+ u32 offset;
+ u32 len;
+ u8 rsvd[4];
+} __packed;
+
+struct ipu6_cpd_metadata_cmpnt_hdr {
+ u32 id;
+ u32 size;
+ u32 ver;
+} __packed;
+
+struct ipu6_cpd_metadata_cmpnt {
+ struct ipu6_cpd_metadata_cmpnt_hdr hdr;
+ u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE];
+ u32 entry_point;
+ u32 icache_base_offs;
+ u8 attrs[16];
+} __packed;
+
+struct ipu6se_cpd_metadata_cmpnt {
+ struct ipu6_cpd_metadata_cmpnt_hdr hdr;
+ u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE];
+ u32 entry_point;
+ u32 icache_base_offs;
+ u8 attrs[16];
+} __packed;
+
+struct ipu6_cpd_metadata_extn {
+ u32 extn_type;
+ u32 len;
+ u32 img_type;
+ u8 rsvd[16];
+} __packed;
+
+struct ipu6_cpd_client_pkg_hdr {
+ u32 prog_list_offs;
+ u32 prog_list_size;
+ u32 prog_desc_offs;
+ u32 prog_desc_size;
+ u32 pg_manifest_offs;
+ u32 pg_manifest_size;
+ u32 prog_bin_offs;
+ u32 prog_bin_size;
+} __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);
+#endif /* IPU6_CPD_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c
new file mode 100644
index 000000000000..92530a1cc90f
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/dma-mapping.h>
+#include <linux/iova.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-dma.h"
+#include "ipu6-mmu.h"
+
+struct vm_info {
+ struct list_head list;
+ struct page **pages;
+ dma_addr_t ipu6_iova;
+ void *vaddr;
+ unsigned long size;
+};
+
+static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova)
+{
+ struct vm_info *info, *save;
+
+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
+ if (iova >= info->ipu6_iova &&
+ iova < (info->ipu6_iova + info->size))
+ return info;
+ }
+
+ return NULL;
+}
+
+static void __dma_clear_buffer(struct page *page, size_t size,
+ unsigned long attrs)
+{
+ void *ptr;
+
+ if (!page)
+ return;
+ /*
+ * Ensure that the allocated pages are zeroed, and that any data
+ * lurking in the kernel direct-mapped region is invalidated.
+ */
+ ptr = page_address(page);
+ memset(ptr, 0, size);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ clflush_cache_range(ptr, size);
+}
+
+static struct page **__dma_alloc_buffer(struct device *dev, size_t size,
+ gfp_t gfp, unsigned long attrs)
+{
+ int count = PHYS_PFN(size);
+ int array_size = count * sizeof(struct page *);
+ struct page **pages;
+ int i = 0;
+
+ pages = kvzalloc(array_size, GFP_KERNEL);
+ if (!pages)
+ return NULL;
+
+ gfp |= __GFP_NOWARN;
+
+ while (count) {
+ int j, order = __fls(count);
+
+ pages[i] = alloc_pages(gfp, order);
+ while (!pages[i] && order)
+ pages[i] = alloc_pages(gfp, --order);
+ if (!pages[i])
+ goto error;
+
+ if (order) {
+ split_page(pages[i], order);
+ j = 1 << order;
+ while (j--)
+ pages[i + j] = pages[i] + j;
+ }
+
+ __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
+ i += 1 << order;
+ count -= 1 << order;
+ }
+
+ return pages;
+error:
+ while (i--)
+ if (pages[i])
+ __free_pages(pages[i], 0);
+ kvfree(pages);
+ return NULL;
+}
+
+static void __dma_free_buffer(struct device *dev, struct page **pages,
+ size_t size, unsigned long attrs)
+{
+ int count = PHYS_PFN(size);
+ unsigned int i;
+
+ for (i = 0; i < count && pages[i]; i++) {
+ __dma_clear_buffer(pages[i], PAGE_SIZE, attrs);
+ __free_pages(pages[i], 0);
+ }
+
+ kvfree(pages);
+}
+
+static void ipu6_dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size,
+ enum dma_data_direction dir)
+{
+ void *vaddr;
+ u32 offset;
+ struct vm_info *info;
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+
+ info = get_vm_info(mmu, dma_handle);
+ if (WARN_ON(!info))
+ return;
+
+ offset = dma_handle - info->ipu6_iova;
+ if (WARN_ON(size > (info->size - offset)))
+ return;
+
+ vaddr = info->vaddr + offset;
+ clflush_cache_range(vaddr, size);
+}
+
+static void ipu6_dma_sync_sg_for_cpu(struct device *dev,
+ struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sglist, sg, nents, i)
+ clflush_cache_range(page_to_virt(sg_page(sg)), sg->length);
+}
+
+static void *ipu6_dma_alloc(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp,
+ unsigned long attrs)
+{
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
+ dma_addr_t pci_dma_addr, ipu6_iova;
+ struct vm_info *info;
+ unsigned long count;
+ struct page **pages;
+ struct iova *iova;
+ unsigned int i;
+ int ret;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+
+ size = PAGE_ALIGN(size);
+ count = PHYS_PFN(size);
+
+ iova = alloc_iova(&mmu->dmap->iovad, count,
+ PHYS_PFN(dma_get_mask(dev)), 0);
+ if (!iova)
+ goto out_kfree;
+
+ pages = __dma_alloc_buffer(dev, size, gfp, attrs);
+ if (!pages)
+ goto out_free_iova;
+
+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
+ size, iova->pfn_lo, iova->pfn_hi);
+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
+ PAGE_SIZE, DMA_BIDIRECTIONAL,
+ attrs);
+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
+ &pci_dma_addr);
+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
+ goto out_unmap;
+ }
+
+ ret = ipu6_mmu_map(mmu->dmap->mmu_info,
+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
+ PAGE_SIZE);
+ if (ret) {
+ dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed",
+ i, &pci_dma_addr);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
+ PAGE_SIZE, DMA_BIDIRECTIONAL,
+ attrs);
+ goto out_unmap;
+ }
+ }
+
+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
+ if (!info->vaddr)
+ goto out_unmap;
+
+ *dma_handle = PFN_PHYS(iova->pfn_lo);
+
+ info->pages = pages;
+ info->ipu6_iova = *dma_handle;
+ info->size = size;
+ list_add(&info->list, &mmu->vma_list);
+
+ return info->vaddr;
+
+out_unmap:
+ while (i--) {
+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ ipu6_iova);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL, attrs);
+
+ ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE);
+ }
+
+ __dma_free_buffer(dev, pages, size, attrs);
+
+out_free_iova:
+ __free_iova(&mmu->dmap->iovad, iova);
+out_kfree:
+ kfree(info);
+
+ return NULL;
+}
+
+static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_handle,
+ unsigned long attrs)
+{
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
+ dma_addr_t pci_dma_addr, ipu6_iova;
+ struct vm_info *info;
+ struct page **pages;
+ unsigned int i;
+
+ if (WARN_ON(!iova))
+ return;
+
+ info = get_vm_info(mmu, dma_handle);
+ if (WARN_ON(!info))
+ return;
+
+ if (WARN_ON(!info->vaddr))
+ return;
+
+ if (WARN_ON(!info->pages))
+ return;
+
+ list_del(&info->list);
+
+ size = PAGE_ALIGN(size);
+
+ pages = info->pages;
+
+ vunmap(vaddr);
+
+ for (i = 0; i < PHYS_PFN(size); i++) {
+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ ipu6_iova);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL, attrs);
+ }
+
+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+
+ __dma_free_buffer(dev, pages, size, attrs);
+
+ mmu->tlb_invalidate(mmu);
+
+ __free_iova(&mmu->dmap->iovad, iova);
+
+ kfree(info);
+}
+
+static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *addr, dma_addr_t iova, size_t size,
+ unsigned long attrs)
+{
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ size_t count = PHYS_PFN(PAGE_ALIGN(size));
+ struct vm_info *info;
+ size_t i;
+ int ret;
+
+ info = get_vm_info(mmu, iova);
+ if (!info)
+ return -EFAULT;
+
+ if (!info->vaddr)
+ return -EFAULT;
+
+ if (vma->vm_start & ~PAGE_MASK)
+ return -EINVAL;
+
+ if (size > info->size)
+ return -EFAULT;
+
+ for (i = 0; i < count; i++) {
+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
+ info->pages[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ipu6_dma_unmap_sg(struct device *dev,
+ struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ struct iova *iova = find_iova(&mmu->dmap->iovad,
+ PHYS_PFN(sg_dma_address(sglist)));
+ int i, npages, count;
+ struct scatterlist *sg;
+ dma_addr_t pci_dma_addr;
+
+ if (!nents)
+ return;
+
+ if (WARN_ON(!iova))
+ return;
+
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
+
+ /* get the nents as orig_nents given by caller */
+ count = 0;
+ npages = iova_size(iova);
+ for_each_sg(sglist, sg, nents, i) {
+ if (sg_dma_len(sg) == 0 ||
+ sg_dma_address(sg) == DMA_MAPPING_ERROR)
+ break;
+
+ npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
+ count++;
+ if (npages <= 0)
+ break;
+ }
+
+ /*
+ * Before IPU6 mmu unmap, return the pci dma address back to sg
+ * assume the nents is less than orig_nents as the least granule
+ * is 1 SZ_4K page
+ */
+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", count);
+ for_each_sg(sglist, sg, count, i) {
+ dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg));
+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ sg_dma_address(sg));
+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
+ &pci_dma_addr, i);
+ sg_dma_address(sg) = pci_dma_addr;
+ }
+
+ dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n",
+ iova->pfn_lo, iova->pfn_hi);
+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+
+ mmu->tlb_invalidate(mmu);
+
+ dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
+
+ __free_iova(&mmu->dmap->iovad, iova);
+}
+
+static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
+ struct scatterlist *sg;
+ struct iova *iova;
+ size_t npages = 0;
+ unsigned long iova_addr;
+ int i, count;
+
+ for_each_sg(sglist, sg, nents, i) {
+ if (sg->offset) {
+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
+ i, sg->offset);
+ return -EFAULT;
+ }
+ }
+
+ dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents);
+ count = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
+ if (count <= 0) {
+ dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents);
+ return 0;
+ }
+
+ dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count);
+
+ for_each_sg(sglist, sg, count, i)
+ npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
+
+ iova = alloc_iova(&mmu->dmap->iovad, npages,
+ PHYS_PFN(dma_get_mask(dev)), 0);
+ if (!iova)
+ return 0;
+
+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
+ iova->pfn_hi);
+
+ iova_addr = iova->pfn_lo;
+ for_each_sg(sglist, sg, count, i) {
+ int ret;
+
+ dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n",
+ i, PFN_PHYS(iova_addr), &sg_dma_address(sg),
+ sg_dma_len(sg));
+
+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
+ sg_dma_address(sg),
+ PAGE_ALIGN(sg_dma_len(sg)));
+ if (ret)
+ goto out_fail;
+
+ sg_dma_address(sg) = PFN_PHYS(iova_addr);
+
+ iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
+ }
+
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
+
+ return count;
+
+out_fail:
+ ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs);
+
+ return 0;
+}
+
+/*
+ * Create scatter-list for the already allocated DMA buffer
+ */
+static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+ void *cpu_addr, dma_addr_t handle, size_t size,
+ unsigned long attrs)
+{
+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
+ struct vm_info *info;
+ int n_pages;
+ int ret = 0;
+
+ info = get_vm_info(mmu, handle);
+ if (!info)
+ return -EFAULT;
+
+ if (!info->vaddr)
+ return -EFAULT;
+
+ if (WARN_ON(!info->pages))
+ return -ENOMEM;
+
+ n_pages = PHYS_PFN(PAGE_ALIGN(size));
+
+ ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size,
+ GFP_KERNEL);
+ if (ret)
+ dev_warn(dev, "IPU6 get sgt table failed\n");
+
+ return ret;
+}
+
+const struct dma_map_ops ipu6_dma_ops = {
+ .alloc = ipu6_dma_alloc,
+ .free = ipu6_dma_free,
+ .mmap = ipu6_dma_mmap,
+ .map_sg = ipu6_dma_map_sg,
+ .unmap_sg = ipu6_dma_unmap_sg,
+ .sync_single_for_cpu = ipu6_dma_sync_single_for_cpu,
+ .sync_single_for_device = ipu6_dma_sync_single_for_cpu,
+ .sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu,
+ .sync_sg_for_device = ipu6_dma_sync_sg_for_cpu,
+ .get_sgtable = ipu6_dma_get_sgtable,
+};
diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h
new file mode 100644
index 000000000000..847ea5b7c925
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_DMA_H
+#define IPU6_DMA_H
+
+#include <linux/dma-map-ops.h>
+#include <linux/iova.h>
+
+struct ipu6_mmu_info;
+
+struct ipu6_dma_mapping {
+ struct ipu6_mmu_info *mmu_info;
+ struct iova_domain iovad;
+};
+
+extern const struct dma_map_ops ipu6_dma_ops;
+
+#endif /* IPU6_DMA_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.c b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
new file mode 100644
index 000000000000..0b33fe9e703d
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-fw-com.h"
+
+/*
+ * FWCOM layer is a shared resource between FW and driver. It consist
+ * of token queues to both send and receive directions. Queue is simply
+ * an array of structures with read and write indexes to the queue.
+ * There are 1...n queues to both directions. Queues locates in
+ * system RAM and are mapped to ISP MMU so that both CPU and ISP can
+ * see the same buffer. Indexes are located in ISP DMEM so that FW code
+ * can poll those with very low latency and cost. CPU access to indexes is
+ * more costly but that happens only at message sending time and
+ * interrupt triggered message handling. CPU doesn't need to poll indexes.
+ * wr_reg / rd_reg are offsets to those dmem location. They are not
+ * the indexes itself.
+ */
+
+/* Shared structure between driver and FW - do not modify */
+struct ipu6_fw_sys_queue {
+ u64 host_address;
+ u32 vied_address;
+ u32 size;
+ u32 token_size;
+ u32 wr_reg; /* reg number in subsystem's regmem */
+ u32 rd_reg;
+ u32 _align;
+} __packed;
+
+struct ipu6_fw_sys_queue_res {
+ u64 host_address;
+ u32 vied_address;
+ u32 reg;
+} __packed;
+
+enum syscom_state {
+ /* Program load or explicit host setting should init to this */
+ SYSCOM_STATE_UNINIT = 0x57a7e000,
+ /* SP Syscom sets this when it is ready for use */
+ SYSCOM_STATE_READY = 0x57a7e001,
+ /* SP Syscom sets this when no more syscom accesses will happen */
+ SYSCOM_STATE_INACTIVE = 0x57a7e002,
+};
+
+enum syscom_cmd {
+ /* Program load or explicit host setting should init to this */
+ SYSCOM_COMMAND_UNINIT = 0x57a7f000,
+ /* Host Syscom requests syscom to become inactive */
+ SYSCOM_COMMAND_INACTIVE = 0x57a7f001,
+};
+
+/* firmware config: data that sent from the host to SP via DDR */
+/* Cell copies data into a context */
+
+struct ipu6_fw_syscom_config {
+ u32 firmware_address;
+
+ u32 num_input_queues;
+ u32 num_output_queues;
+
+ /* ISP pointers to an array of ipu6_fw_sys_queue structures */
+ u32 input_queue;
+ u32 output_queue;
+
+ /* ISYS / PSYS private data */
+ u32 specific_addr;
+ u32 specific_size;
+};
+
+struct ipu6_fw_com_context {
+ struct ipu6_bus_device *adev;
+ void __iomem *dmem_addr;
+ int (*cell_ready)(struct ipu6_bus_device *adev);
+ void (*cell_start)(struct ipu6_bus_device *adev);
+
+ void *dma_buffer;
+ dma_addr_t dma_addr;
+ unsigned int dma_size;
+ unsigned long attrs;
+
+ struct ipu6_fw_sys_queue *input_queue; /* array of host to SP queues */
+ struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */
+
+ u32 config_vied_addr;
+
+ unsigned int buttress_boot_offset;
+ void __iomem *base_addr;
+};
+
+#define FW_COM_WR_REG 0
+#define FW_COM_RD_REG 4
+
+#define REGMEM_OFFSET 0
+#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
+
+enum regmem_id {
+ /* pass pkg_dir address to SPC in non-secure mode */
+ PKG_DIR_ADDR_REG = 0,
+ /* Tunit CFG blob for secure - provided by host.*/
+ TUNIT_CFG_DWR_REG = 1,
+ /* syscom commands - modified by the host */
+ SYSCOM_COMMAND_REG = 2,
+ /* Store interrupt status - updated by SP */
+ SYSCOM_IRQ_REG = 3,
+ /* first syscom queue pointer register */
+ SYSCOM_QPR_BASE_REG = 4
+};
+
+#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
+#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id) \
+ ((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
+
+enum buttress_syscom_id {
+ /* pass syscom configuration to SPC */
+ SYSCOM_CONFIG_ID = 0,
+ /* syscom state - modified by SP */
+ SYSCOM_STATE_ID = 1,
+ /* syscom vtl0 addr mask */
+ SYSCOM_VTL0_ADDR_MASK_ID = 2,
+ SYSCOM_ID_MAX
+};
+
+static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
+ unsigned int token_size,
+ struct ipu6_fw_sys_queue_res *res)
+{
+ unsigned int buf_size = (size + 1) * token_size;
+
+ q->size = size + 1;
+ q->token_size = token_size;
+
+ /* acquire the shared buffer space */
+ q->host_address = res->host_address;
+ res->host_address += buf_size;
+ q->vied_address = res->vied_address;
+ res->vied_address += buf_size;
+
+ /* acquire the shared read and writer pointers */
+ q->wr_reg = res->reg;
+ res->reg++;
+ q->rd_reg = res->reg;
+ res->reg++;
+}
+
+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
+ struct ipu6_bus_device *adev, void __iomem *base)
+{
+ size_t conf_size, inq_size, outq_size, specific_size;
+ struct ipu6_fw_syscom_config *config_host_addr;
+ unsigned int sizeinput = 0, sizeoutput = 0;
+ struct ipu6_fw_sys_queue_res res;
+ struct ipu6_fw_com_context *ctx;
+ struct device *dev = &adev->auxdev.dev;
+ size_t sizeall, offset;
+ unsigned long attrs = 0;
+ void *specific_host_addr;
+ unsigned int i;
+
+ if (!cfg || !cfg->cell_start || !cfg->cell_ready)
+ return NULL;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+ ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
+ ctx->adev = adev;
+ ctx->cell_start = cfg->cell_start;
+ ctx->cell_ready = cfg->cell_ready;
+ ctx->buttress_boot_offset = cfg->buttress_boot_offset;
+ ctx->base_addr = base;
+
+ /*
+ * Allocate DMA mapped memory. Allocate one big chunk.
+ */
+ /* Base cfg for FW */
+ conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
+ /* Descriptions of the queues */
+ inq_size = size_mul(cfg->num_input_queues,
+ sizeof(struct ipu6_fw_sys_queue));
+ outq_size = size_mul(cfg->num_output_queues,
+ sizeof(struct ipu6_fw_sys_queue));
+ /* FW specific information structure */
+ specific_size = roundup(cfg->specific_size, 8);
+
+ sizeall = conf_size + inq_size + outq_size + specific_size;
+
+ for (i = 0; i < cfg->num_input_queues; i++)
+ sizeinput += size_mul(cfg->input[i].queue_size + 1,
+ cfg->input[i].token_size);
+
+ for (i = 0; i < cfg->num_output_queues; i++)
+ sizeoutput += size_mul(cfg->output[i].queue_size + 1,
+ cfg->output[i].token_size);
+
+ sizeall += sizeinput + sizeoutput;
+
+ ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr,
+ GFP_KERNEL, attrs);
+ ctx->attrs = attrs;
+ if (!ctx->dma_buffer) {
+ dev_err(dev, "failed to allocate dma memory\n");
+ kfree(ctx);
+ return NULL;
+ }
+
+ ctx->dma_size = sizeall;
+
+ config_host_addr = ctx->dma_buffer;
+ ctx->config_vied_addr = ctx->dma_addr;
+
+ offset = conf_size;
+ ctx->input_queue = ctx->dma_buffer + offset;
+ config_host_addr->input_queue = ctx->dma_addr + offset;
+ config_host_addr->num_input_queues = cfg->num_input_queues;
+
+ offset += inq_size;
+ ctx->output_queue = ctx->dma_buffer + offset;
+ config_host_addr->output_queue = ctx->dma_addr + offset;
+ config_host_addr->num_output_queues = cfg->num_output_queues;
+
+ /* copy firmware specific data */
+ offset += outq_size;
+ specific_host_addr = ctx->dma_buffer + offset;
+ config_host_addr->specific_addr = ctx->dma_addr + offset;
+ config_host_addr->specific_size = cfg->specific_size;
+ if (cfg->specific_addr && cfg->specific_size)
+ memcpy(specific_host_addr, cfg->specific_addr,
+ cfg->specific_size);
+
+ /* initialize input queues */
+ offset += specific_size;
+ res.reg = SYSCOM_QPR_BASE_REG;
+ res.host_address = (u64)(ctx->dma_buffer + offset);
+ res.vied_address = ctx->dma_addr + offset;
+ for (i = 0; i < cfg->num_input_queues; i++)
+ ipu6_sys_queue_init(ctx->input_queue + i,
+ cfg->input[i].queue_size,
+ cfg->input[i].token_size, &res);
+
+ /* initialize output queues */
+ offset += sizeinput;
+ res.host_address = (u64)(ctx->dma_buffer + offset);
+ res.vied_address = ctx->dma_addr + offset;
+ for (i = 0; i < cfg->num_output_queues; i++) {
+ ipu6_sys_queue_init(ctx->output_queue + i,
+ cfg->output[i].queue_size,
+ cfg->output[i].token_size, &res);
+ }
+
+ return ctx;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
+
+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
+{
+ /* write magic pattern to disable the tunit trace */
+ writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4);
+ /* Check if SP is in valid state */
+ if (!ctx->cell_ready(ctx->adev))
+ return -EIO;
+
+ /* store syscom uninitialized command */
+ writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
+
+ /* store syscom uninitialized state */
+ writel(SYSCOM_STATE_UNINIT,
+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
+ ctx->buttress_boot_offset,
+ SYSCOM_STATE_ID));
+
+ /* store firmware configuration address */
+ writel(ctx->config_vied_addr,
+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
+ ctx->buttress_boot_offset,
+ SYSCOM_CONFIG_ID));
+ ctx->cell_start(ctx->adev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
+
+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
+{
+ int state;
+
+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
+ ctx->buttress_boot_offset,
+ SYSCOM_STATE_ID));
+ if (state != SYSCOM_STATE_READY)
+ return -EBUSY;
+
+ /* set close request flag */
+ writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
+ SYSCOM_COMMAND_REG * 4);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
+
+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
+{
+ /* check if release is forced, an verify cell state if it is not */
+ if (!force && !ctx->cell_ready(ctx->adev))
+ return -EBUSY;
+
+ dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size,
+ ctx->dma_buffer, ctx->dma_addr, ctx->attrs);
+ kfree(ctx);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
+
+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
+{
+ int state;
+
+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
+ ctx->buttress_boot_offset,
+ SYSCOM_STATE_ID));
+
+ return state == SYSCOM_STATE_READY;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
+
+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
+{
+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
+ unsigned int wr, rd;
+ unsigned int packets;
+ unsigned int index;
+
+ wr = readl(q_dmem + FW_COM_WR_REG);
+ rd = readl(q_dmem + FW_COM_RD_REG);
+
+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
+ return NULL;
+
+ if (wr < rd)
+ packets = rd - wr - 1;
+ else
+ packets = q->size - (wr - rd + 1);
+
+ if (!packets)
+ return NULL;
+
+ index = readl(q_dmem + FW_COM_WR_REG);
+
+ return (void *)(q->host_address + index * q->token_size);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
+
+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
+{
+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
+ unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
+
+ if (wr >= q->size)
+ wr = 0;
+
+ writel(wr, q_dmem + FW_COM_WR_REG);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
+
+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
+{
+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
+ unsigned int wr, rd;
+ unsigned int packets;
+
+ wr = readl(q_dmem + FW_COM_WR_REG);
+ rd = readl(q_dmem + FW_COM_RD_REG);
+
+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
+ return NULL;
+
+ if (wr < rd)
+ wr += q->size;
+
+ packets = wr - rd;
+ if (!packets)
+ return NULL;
+
+ return (void *)(q->host_address + rd * q->token_size);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
+
+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
+{
+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
+ unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1;
+
+ if (rd >= q->size)
+ rd = 0;
+
+ writel(rd, q_dmem + FW_COM_RD_REG);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.h b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
new file mode 100644
index 000000000000..b02285a3e43e
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_FW_COM_H
+#define IPU6_FW_COM_H
+
+struct ipu6_fw_com_context;
+struct ipu6_bus_device;
+
+struct ipu6_fw_syscom_queue_config {
+ unsigned int queue_size; /* tokens per queue */
+ unsigned int token_size; /* bytes per token */
+};
+
+#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET 0
+
+struct ipu6_fw_com_cfg {
+ unsigned int num_input_queues;
+ unsigned int num_output_queues;
+ struct ipu6_fw_syscom_queue_config *input;
+ struct ipu6_fw_syscom_queue_config *output;
+
+ unsigned int dmem_addr;
+
+ /* firmware-specific configuration data */
+ void *specific_addr;
+ unsigned int specific_size;
+ int (*cell_ready)(struct ipu6_bus_device *adev);
+ void (*cell_start)(struct ipu6_bus_device *adev);
+
+ unsigned int buttress_boot_offset;
+};
+
+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
+ struct ipu6_bus_device *adev, void __iomem *base);
+
+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx);
+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx);
+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx);
+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force);
+
+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
+
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
new file mode 100644
index 000000000000..62ed92ff1d30
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-fw-com.h"
+#include "ipu6-isys.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+#include "ipu6-platform-regs.h"
+
+static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {
+ "STREAM_OPEN",
+ "STREAM_START",
+ "STREAM_START_AND_CAPTURE",
+ "STREAM_CAPTURE",
+ "STREAM_STOP",
+ "STREAM_FLUSH",
+ "STREAM_CLOSE"
+};
+
+static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu6_fw_isys_proxy_resp_info_abi *resp;
+ int ret;
+
+ resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
+ if (!resp)
+ return 1;
+
+ dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n",
+ resp->request_id, resp->error_info.error,
+ resp->error_info.error_details);
+
+ ret = req_id == resp->request_id ? 0 : -EIO;
+
+ ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
+
+ return ret;
+}
+
+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
+ unsigned int req_id,
+ unsigned int index,
+ unsigned int offset, u32 value)
+{
+ struct ipu6_fw_com_context *ctx = isys->fwcom;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu6_fw_proxy_send_queue_token *token;
+ unsigned int timeout = 1000;
+ int ret;
+
+ dev_dbg(dev,
+ "proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n",
+ req_id, index, offset, value);
+
+ token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
+ if (!token)
+ return -EBUSY;
+
+ token->request_id = req_id;
+ token->region_index = index;
+ token->offset = offset;
+ token->value = value;
+ ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
+
+ do {
+ usleep_range(100, 110);
+ ret = handle_proxy_response(isys, req_id);
+ if (!ret)
+ break;
+ if (ret == -EIO) {
+ dev_err(dev, "Proxy respond with unexpected id\n");
+ break;
+ }
+ timeout--;
+ } while (ret && timeout);
+
+ if (!timeout)
+ dev_err(dev, "Proxy response timed out\n");
+
+ 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)
+{
+ struct ipu6_fw_com_context *ctx = isys->fwcom;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu6_fw_send_queue_token *token;
+
+ if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE)
+ return -EINVAL;
+
+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
+
+ /*
+ * Time to flush cache in case we have some payload. Not all messages
+ * have that
+ */
+ if (cpu_mapped_buf)
+ clflush_cache_range(cpu_mapped_buf, size);
+
+ token = ipu6_send_get_token(ctx,
+ stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
+ if (!token)
+ return -EBUSY;
+
+ token->payload = dma_mapped_buf;
+ token->buf_handle = (unsigned long)cpu_mapped_buf;
+ token->send_type = send_type;
+
+ ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
+
+ 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)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ int retry = IPU6_ISYS_CLOSE_RETRY;
+ unsigned long flags;
+ void *fwcom;
+ int ret;
+
+ /*
+ * Stop the isys fw. Actual close takes
+ * some time as the FW must stop its actions including code fetch
+ * 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);
+ 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);
+ 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);
+ }
+
+ return ret;
+}
+
+void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
+{
+ int ret;
+
+ ret = ipu6_fw_com_release(isys->fwcom, 1);
+ if (ret < 0)
+ dev_warn(&isys->adev->auxdev.dev,
+ "Device busy, fw_com release failed.");
+ isys->fwcom = NULL;
+}
+
+static void start_sp(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ void __iomem *spc_regs_base = isys->pdata->base +
+ isys->pdata->ipdata->hw_variant.spc_offset;
+ u32 val = IPU6_ISYS_SPC_STATUS_START |
+ IPU6_ISYS_SPC_STATUS_RUN |
+ IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
+
+ val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0;
+
+ writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
+}
+
+static int query_sp(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ void __iomem *spc_regs_base = isys->pdata->base +
+ isys->pdata->ipdata->hw_variant.spc_offset;
+ u32 val;
+
+ val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
+ /* return true when READY == 1, START == 0 */
+ val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START;
+
+ return val == IPU6_ISYS_SPC_STATUS_READY;
+}
+
+static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
+ struct ipu6_fw_com_cfg *fwcom,
+ unsigned int num_streams)
+{
+ unsigned int max_send_queues, max_sram_blocks, max_devq_size;
+ struct ipu6_fw_syscom_queue_config *input_queue_cfg;
+ struct ipu6_fw_syscom_queue_config *output_queue_cfg;
+ struct device *dev = &isys->adev->auxdev.dev;
+ int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY;
+ int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV;
+ int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG;
+ int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES;
+ int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES;
+ int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES;
+ struct ipu6_fw_isys_fw_config *isys_fw_cfg;
+ u32 num_in_message_queues;
+ unsigned int max_streams;
+ unsigned int size;
+ unsigned int i;
+
+ max_streams = isys->pdata->ipdata->max_streams;
+ max_send_queues = isys->pdata->ipdata->max_send_queues;
+ max_sram_blocks = isys->pdata->ipdata->max_sram_blocks;
+ max_devq_size = isys->pdata->ipdata->max_devq_size;
+ num_in_message_queues = clamp(num_streams, 1U, max_streams);
+ isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL);
+ if (!isys_fw_cfg)
+ return -ENOMEM;
+
+ isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES;
+ isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES;
+ isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues;
+ isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES;
+ /* Common msg/dev return queue */
+ isys_fw_cfg->num_recv_queues[type_dev] = 0;
+ isys_fw_cfg->num_recv_queues[type_msg] = 1;
+
+ size = sizeof(*input_queue_cfg) * max_send_queues;
+ input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!input_queue_cfg)
+ return -ENOMEM;
+
+ size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES;
+ output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!output_queue_cfg)
+ return -ENOMEM;
+
+ fwcom->input = input_queue_cfg;
+ fwcom->output = output_queue_cfg;
+
+ fwcom->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] +
+ isys_fw_cfg->num_recv_queues[type_dev] +
+ isys_fw_cfg->num_recv_queues[type_msg];
+
+ /* SRAM partitioning. Equal partitioning is set. */
+ for (i = 0; i < max_sram_blocks; i++) {
+ if (i < num_in_message_queues)
+ isys_fw_cfg->buffer_partition.num_gda_pages[i] =
+ (IPU6_DEVICE_GDA_NR_PAGES *
+ IPU6_DEVICE_GDA_VIRT_FACTOR) /
+ num_in_message_queues;
+ else
+ isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;
+ }
+
+ /* FW assumes proxy interface at fwcom 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);
+ input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE;
+ }
+
+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) {
+ input_queue_cfg[base_dev_send + i].token_size =
+ sizeof(struct ipu6_fw_send_queue_token);
+ input_queue_cfg[base_dev_send + i].queue_size = max_devq_size;
+ }
+
+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) {
+ input_queue_cfg[base_msg_send + i].token_size =
+ sizeof(struct ipu6_fw_send_queue_token);
+ input_queue_cfg[base_msg_send + i].queue_size =
+ IPU6_ISYS_SIZE_SEND_QUEUE;
+ }
+
+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) {
+ output_queue_cfg[i].token_size =
+ sizeof(struct ipu6_fw_proxy_resp_queue_token);
+ output_queue_cfg[i].queue_size =
+ IPU6_ISYS_SIZE_PROXY_RECV_QUEUE;
+ }
+ /* There is no recv DEV queue */
+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) {
+ output_queue_cfg[base_msg_recv + i].token_size =
+ sizeof(struct ipu6_fw_resp_queue_token);
+ output_queue_cfg[base_msg_recv + i].queue_size =
+ 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);
+
+ return 0;
+}
+
+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 = {
+ .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);
+
+ isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,
+ isys->pdata->base);
+ if (!isys->fwcom) {
+ dev_err(dev, "isys fw com prepare failed\n");
+ return -EIO;
+ }
+
+ ret = ipu6_fw_com_open(isys->fwcom);
+ if (ret) {
+ dev_err(dev, "isys fw com open failed %d\n", ret);
+ return ret;
+ }
+
+ do {
+ usleep_range(400, 500);
+ if (ipu6_fw_com_ready(isys->fwcom))
+ break;
+ retry--;
+ } while (retry > 0);
+
+ if (!retry) {
+ dev_err(dev, "isys port open ready failed %d\n", ret);
+ ipu6_fw_isys_close(isys);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+struct ipu6_fw_isys_resp_info_abi *
+ipu6_fw_isys_get_resp(void *context, unsigned int queue)
+{
+ return ipu6_recv_get_token(context, queue);
+}
+
+void ipu6_fw_isys_put_resp(void *context, unsigned int queue)
+{
+ ipu6_recv_put_token(context, queue);
+}
+
+void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
+{
+ unsigned int i;
+
+ dev_dbg(dev, "-----------------------------------------------------\n");
+ dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n");
+
+ dev_dbg(dev, "compfmt = %d\n", cfg->vc);
+ dev_dbg(dev, "src = %d\n", cfg->src);
+ dev_dbg(dev, "vc = %d\n", cfg->vc);
+ dev_dbg(dev, "isl_use = %d\n", cfg->isl_use);
+ dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type);
+
+ dev_dbg(dev, "send_irq_sof_discarded = %d\n",
+ cfg->send_irq_sof_discarded);
+ dev_dbg(dev, "send_irq_eof_discarded = %d\n",
+ cfg->send_irq_eof_discarded);
+ dev_dbg(dev, "send_resp_sof_discarded = %d\n",
+ cfg->send_resp_sof_discarded);
+ dev_dbg(dev, "send_resp_eof_discarded = %d\n",
+ cfg->send_resp_eof_discarded);
+
+ dev_dbg(dev, "crop:\n");
+ dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset,
+ cfg->crop.top_offset);
+ dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset,
+ cfg->crop.bottom_offset);
+
+ dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins);
+ 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.mipi_store_mode = %d\n",
+ cfg->input_pins[i].mipi_store_mode);
+ dev_dbg(dev, "\t.bits_per_pix = %d\n",
+ cfg->input_pins[i].bits_per_pix);
+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
+ cfg->input_pins[i].mapped_dt);
+ dev_dbg(dev, "\t.input_res = %dx%d\n",
+ cfg->input_pins[i].input_res.width,
+ cfg->input_pins[i].input_res.height);
+ dev_dbg(dev, "\t.mipi_decompression = %d\n",
+ cfg->input_pins[i].mipi_decompression);
+ dev_dbg(dev, "\t.capture_mode = %d\n",
+ cfg->input_pins[i].capture_mode);
+ }
+
+ dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins);
+ 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.output_res = %dx%d\n",
+ cfg->output_pins[i].output_res.width,
+ cfg->output_pins[i].output_res.height);
+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
+ dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt);
+ dev_dbg(dev, "\t.payload_buf_size = %d\n",
+ cfg->output_pins[i].payload_buf_size);
+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
+ dev_dbg(dev, "\t.watermark_in_lines = %d\n",
+ cfg->output_pins[i].watermark_in_lines);
+ dev_dbg(dev, "\t.send_irq = %d\n",
+ cfg->output_pins[i].send_irq);
+ dev_dbg(dev, "\t.reserve_compression = %d\n",
+ cfg->output_pins[i].reserve_compression);
+ dev_dbg(dev, "\t.snoopable = %d\n",
+ cfg->output_pins[i].snoopable);
+ dev_dbg(dev, "\t.error_handling_enable = %d\n",
+ cfg->output_pins[i].error_handling_enable);
+ dev_dbg(dev, "\t.sensor_type = %d\n",
+ cfg->output_pins[i].sensor_type);
+ }
+ 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)
+{
+ unsigned int i;
+
+ dev_dbg(dev, "-----------------------------------------------------\n");
+ dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");
+
+ for (i = 0; i < outputs; i++) {
+ dev_dbg(dev, "output_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.out_buf_id = %llu\n",
+ buf->output_pins[i].out_buf_id);
+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
+ dev_dbg(dev, "\t.compress = %d\n",
+ buf->output_pins[i].compress);
+ }
+
+ dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof);
+ dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof);
+ dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof);
+ dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof);
+ dev_dbg(dev, "send_irq_capture_ack = 0x%x\n",
+ buf->send_irq_capture_ack);
+ dev_dbg(dev, "send_irq_capture_done = 0x%x\n",
+ buf->send_irq_capture_done);
+ dev_dbg(dev, "send_resp_capture_ack = 0x%x\n",
+ buf->send_resp_capture_ack);
+ dev_dbg(dev, "send_resp_capture_done = 0x%x\n",
+ buf->send_resp_capture_done);
+
+ dev_dbg(dev, "-----------------------------------------------------\n");
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
new file mode 100644
index 000000000000..b60f02076d8a
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
@@ -0,0 +1,596 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_FW_ISYS_H
+#define IPU6_FW_ISYS_H
+
+#include <linux/types.h>
+
+struct device;
+struct ipu6_isys;
+
+/* Max number of Input/Output Pins */
+#define IPU6_MAX_IPINS 4
+
+#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1)
+
+#define IPU6_STREAM_ID_MAX 16
+#define IPU6_NONSECURE_STREAM_ID_MAX 12
+#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX)
+#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX)
+#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX)
+#define IPU6SE_STREAM_ID_MAX 8
+#define IPU6SE_NONSECURE_STREAM_ID_MAX 4
+#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX)
+#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX)
+#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX)
+
+/* Single return queue for all streams/commands type */
+#define IPU6_N_MAX_MSG_RECV_QUEUES 1
+/* Single device queue for high priority commands (bypass in-order queue) */
+#define IPU6_N_MAX_DEV_SEND_QUEUES 1
+/* Single dedicated send queue for proxy interface */
+#define IPU6_N_MAX_PROXY_SEND_QUEUES 1
+/* Single dedicated recv queue for proxy interface */
+#define IPU6_N_MAX_PROXY_RECV_QUEUES 1
+/* Send queues layout */
+#define IPU6_BASE_PROXY_SEND_QUEUES 0
+#define IPU6_BASE_DEV_SEND_QUEUES \
+ (IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES)
+#define IPU6_BASE_MSG_SEND_QUEUES \
+ (IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES)
+/* Recv queues layout */
+#define IPU6_BASE_PROXY_RECV_QUEUES 0
+#define IPU6_BASE_MSG_RECV_QUEUES \
+ (IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES)
+#define IPU6_N_MAX_RECV_QUEUES \
+ (IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES)
+
+#define IPU6_N_MAX_SEND_QUEUES \
+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES)
+#define IPU6SE_N_MAX_SEND_QUEUES \
+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES)
+
+/* Max number of planes for frame formats supported by the FW */
+#define IPU6_PIN_PLANES_MAX 4
+
+#define IPU6_FW_ISYS_SENSOR_TYPE_START 14
+#define IPU6_FW_ISYS_SENSOR_TYPE_END 19
+#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6
+#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11
+/*
+ * Device close takes some time from last ack message to actual stopping
+ * of the SP processor. As long as the SP processor runs we can't proceed with
+ * clean up of resources.
+ */
+#define IPU6_ISYS_OPEN_RETRY 2000
+#define IPU6_ISYS_CLOSE_RETRY 2000
+#define IPU6_FW_CALL_TIMEOUT_JIFFIES msecs_to_jiffies(2000)
+
+enum ipu6_fw_isys_resp_type {
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK,
+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY,
+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK,
+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF,
+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE,
+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED,
+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED,
+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED,
+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED,
+ IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY,
+ N_IPU6_FW_ISYS_RESP_TYPE
+};
+
+enum ipu6_fw_isys_send_type {
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE,
+ N_IPU6_FW_ISYS_SEND_TYPE
+};
+
+enum ipu6_fw_isys_queue_type {
+ IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0,
+ IPU6_FW_ISYS_QUEUE_TYPE_DEV,
+ IPU6_FW_ISYS_QUEUE_TYPE_MSG,
+ N_IPU6_FW_ISYS_QUEUE_TYPE
+};
+
+enum ipu6_fw_isys_stream_source {
+ IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_1,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_2,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_3,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_4,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_5,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_6,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_7,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_8,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_9,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_10,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_11,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_12,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_13,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_14,
+ IPU6_FW_ISYS_STREAM_SRC_PORT_15,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8,
+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9,
+ N_IPU6_FW_ISYS_STREAM_SRC
+};
+
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3
+
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \
+ IPU6_FW_ISYS_STREAM_SRC_PORT_6
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \
+ IPU6_FW_ISYS_STREAM_SRC_PORT_7
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \
+ IPU6_FW_ISYS_STREAM_SRC_PORT_8
+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \
+ IPU6_FW_ISYS_STREAM_SRC_PORT_9
+
+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0
+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1
+
+/*
+ * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec
+ * supports up to 4 virtual per physical channel
+ */
+enum ipu6_fw_isys_mipi_vc {
+ IPU6_FW_ISYS_MIPI_VC_0 = 0,
+ IPU6_FW_ISYS_MIPI_VC_1,
+ IPU6_FW_ISYS_MIPI_VC_2,
+ IPU6_FW_ISYS_MIPI_VC_3,
+ N_IPU6_FW_ISYS_MIPI_VC
+};
+
+enum ipu6_fw_isys_frame_format_type {
+ IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */
+ /* 12 bit YUV 420, Intel proprietary tiled format */
+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY,
+
+ IPU6_FW_ISYS_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */
+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */
+ /* Internal format, 2 y lines followed by a uvinterleaved line */
+ IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */
+ /**
+ * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit
+ * value, 5 bits for R, 6 bits for G and 5 bits for B.
+ */
+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565,
+ IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */
+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */
+ IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */
+ IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */
+ N_IPU6_FW_ISYS_FRAME_FORMAT
+};
+
+enum ipu6_fw_isys_pin_type {
+ /* captured as MIPI packets */
+ IPU6_FW_ISYS_PIN_TYPE_MIPI = 0,
+ /* captured through the SoC path */
+ IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3,
+};
+
+/*
+ * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach
+ * MIPI SRAM with the long packet header or
+ * if not, then only option is to capture it with pin type MIPI.
+ */
+enum ipu6_fw_isys_mipi_store_mode {
+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0,
+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER,
+ N_IPU6_FW_ISYS_MIPI_STORE_MODE
+};
+
+enum ipu6_fw_isys_capture_mode {
+ IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0,
+ IPU6_FW_ISYS_CAPTURE_MODE_BURST,
+ N_IPU6_FW_ISYS_CAPTURE_MODE,
+};
+
+enum ipu6_fw_isys_sensor_mode {
+ IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0,
+ IPU6_FW_ISYS_SENSOR_MODE_TOBII,
+ N_IPU6_FW_ISYS_SENSOR_MODE,
+};
+
+enum ipu6_fw_isys_error {
+ IPU6_FW_ISYS_ERROR_NONE = 0,
+ IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY,
+ IPU6_FW_ISYS_ERROR_HW_CONSISTENCY,
+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE,
+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION,
+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION,
+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION,
+ IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES,
+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO,
+ IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO,
+ IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC,
+ IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION,
+ IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL,
+ N_IPU6_FW_ISYS_ERROR
+};
+
+enum ipu6_fw_proxy_error {
+ IPU6_FW_PROXY_ERROR_NONE = 0,
+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION,
+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET,
+ N_IPU6_FW_PROXY_ERROR
+};
+
+/* firmware ABI structure below are aligned in firmware, no need pack */
+struct ipu6_fw_isys_buffer_partition_abi {
+ u32 num_gda_pages[IPU6_STREAM_ID_MAX];
+};
+
+struct ipu6_fw_isys_fw_config {
+ struct ipu6_fw_isys_buffer_partition_abi buffer_partition;
+ u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
+ u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
+};
+
+/*
+ * struct ipu6_fw_isys_resolution_abi: Generic resolution structure.
+ */
+struct ipu6_fw_isys_resolution_abi {
+ u32 width;
+ u32 height;
+};
+
+/**
+ * struct ipu6_fw_isys_output_pin_payload_abi - ISYS output pin buffer
+ * @out_buf_id: Points to output pin buffer - buffer identifier
+ * @addr: Points to output pin buffer - CSS Virtual Address
+ * @compress: Request frame compression (1), or not (0)
+ */
+struct ipu6_fw_isys_output_pin_payload_abi {
+ u64 out_buf_id;
+ u32 addr;
+ u32 compress;
+};
+
+/**
+ * struct ipu6_fw_isys_output_pin_info_abi - ISYS output pin info
+ * @output_res: output pin resolution
+ * @stride: output stride in Bytes (not valid for statistics)
+ * @watermark_in_lines: pin watermark level in lines
+ * @payload_buf_size: minimum size in Bytes of all buffers that will be
+ * supplied for capture on this pin
+ * @ts_offsets: ts_offsets
+ * @s2m_pixel_soc_pixel_remapping: pixel soc remapping (see the definition of
+ * S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING)
+ * @csi_be_soc_pixel_remapping: see s2m_pixel_soc_pixel_remapping
+ * @send_irq: assert if pin event should trigger irq
+ * @input_pin_id: related input pin id
+ * @pt: pin type -real format "enum ipu6_fw_isys_pin_type"
+ * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type"
+ * @reserved: a reserved field
+ * @reserve_compression: reserve compression resources for pin
+ * @snoopable: snoopable
+ * @error_handling_enable: enable error handling
+ * @sensor_type: sensor_type
+ */
+struct ipu6_fw_isys_output_pin_info_abi {
+ struct ipu6_fw_isys_resolution_abi output_res;
+ u32 stride;
+ u32 watermark_in_lines;
+ u32 payload_buf_size;
+ u32 ts_offsets[IPU6_PIN_PLANES_MAX];
+ u32 s2m_pixel_soc_pixel_remapping;
+ u32 csi_be_soc_pixel_remapping;
+ u8 send_irq;
+ u8 input_pin_id;
+ u8 pt;
+ u8 ft;
+ u8 reserved;
+ u8 reserve_compression;
+ u8 snoopable;
+ u8 error_handling_enable;
+ u32 sensor_type;
+};
+
+/**
+ * struct ipu6_fw_isys_input_pin_info_abi - ISYS input pin info
+ * @input_res: input resolution
+ * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type)
+ * @mipi_store_mode: defines if legacy long packet header will be stored or
+ * discarded if discarded, output pin type for this
+ * input pin can only be MIPI
+ * (enum ipu6_fw_isys_mipi_store_mode)
+ * @bits_per_pix: native bits per pixel
+ * @mapped_dt: actual data type from sensor
+ * @mipi_decompression: defines which compression will be in mipi backend
+ * @crop_first_and_last_lines: Control whether to crop the first and last line
+ * of the input image. Crop done by HW device.
+ * @capture_mode: mode of capture, regular or burst, default value is regular
+ * @reserved: a reserved field
+ */
+struct ipu6_fw_isys_input_pin_info_abi {
+ struct ipu6_fw_isys_resolution_abi input_res;
+ u8 dt;
+ u8 mipi_store_mode;
+ u8 bits_per_pix;
+ u8 mapped_dt;
+ u8 mipi_decompression;
+ u8 crop_first_and_last_lines;
+ u8 capture_mode;
+ u8 reserved;
+};
+
+/**
+ * struct ipu6_fw_isys_cropping_abi - ISYS cropping coordinates
+ * @top_offset: Top offset
+ * @left_offset: Left offset
+ * @bottom_offset: Bottom offset
+ * @right_offset: Right offset
+ */
+struct ipu6_fw_isys_cropping_abi {
+ s32 top_offset;
+ s32 left_offset;
+ s32 bottom_offset;
+ s32 right_offset;
+};
+
+/**
+ * struct ipu6_fw_isys_stream_cfg_data_abi - ISYS stream configuration data
+ * ISYS stream configuration data structure
+ * @crop: for extended use and is not used in FW currently
+ * @input_pins: input pin descriptors
+ * @output_pins: output pin descriptors
+ * @compfmt: de-compression setting for User Defined Data
+ * @nof_input_pins: number of input pins
+ * @nof_output_pins: number of output pins
+ * @send_irq_sof_discarded: send irq on discarded frame sof response
+ * - if '1' it will override the send_resp_sof_discarded
+ * and send the response
+ * - if '0' the send_resp_sof_discarded will determine
+ * whether to send the response
+ * @send_irq_eof_discarded: send irq on discarded frame eof response
+ * - if '1' it will override the send_resp_eof_discarded
+ * and send the response
+ * - if '0' the send_resp_eof_discarded will determine
+ * whether to send the response
+ * @send_resp_sof_discarded: send response for discarded frame sof detected,
+ * used only when send_irq_sof_discarded is '0'
+ * @send_resp_eof_discarded: send response for discarded frame eof detected,
+ * used only when send_irq_eof_discarded is '0'
+ * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1
+ * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel)
+ * @isl_use: indicates whether stream requires ISL and how
+ * @sensor_type: type of connected sensor, tobii or others, default is 0
+ * @reserved: a reserved field
+ * @reserved2: a reserved field
+ */
+struct ipu6_fw_isys_stream_cfg_data_abi {
+ struct ipu6_fw_isys_cropping_abi crop;
+ struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS];
+ struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS];
+ u32 compfmt;
+ u8 nof_input_pins;
+ u8 nof_output_pins;
+ u8 send_irq_sof_discarded;
+ u8 send_irq_eof_discarded;
+ u8 send_resp_sof_discarded;
+ u8 send_resp_eof_discarded;
+ u8 src;
+ u8 vc;
+ u8 isl_use;
+ u8 sensor_type;
+ u8 reserved;
+ u8 reserved2;
+};
+
+/**
+ * struct ipu6_fw_isys_frame_buff_set_abi - ISYS frame buffer set (request)
+ * @output_pins: output pin addresses
+ * @send_irq_sof: send irq on frame sof response
+ * - if '1' it will override the send_resp_sof and
+ * send the response
+ * - if '0' the send_resp_sof will determine whether to
+ * send the response
+ * @send_irq_eof: send irq on frame eof response
+ * - if '1' it will override the send_resp_eof and
+ * send the response
+ * - if '0' the send_resp_eof will determine whether to
+ * send the response
+ * @send_irq_capture_ack: send irq on capture ack
+ * @send_irq_capture_done: send irq on capture done
+ * @send_resp_sof: send response for frame sof detected,
+ * used only when send_irq_sof is '0'
+ * @send_resp_eof: send response for frame eof detected,
+ * used only when send_irq_eof is '0'
+ * @send_resp_capture_ack: send response for capture ack event
+ * @send_resp_capture_done: send response for capture done event
+ * @reserved: a reserved field
+ */
+struct ipu6_fw_isys_frame_buff_set_abi {
+ struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS];
+ u8 send_irq_sof;
+ u8 send_irq_eof;
+ u8 send_irq_capture_ack;
+ u8 send_irq_capture_done;
+ u8 send_resp_sof;
+ u8 send_resp_eof;
+ u8 send_resp_capture_ack;
+ u8 send_resp_capture_done;
+ u8 reserved[8];
+};
+
+/**
+ * struct ipu6_fw_isys_error_info_abi - ISYS error information
+ * @error: error code if something went wrong
+ * @error_details: depending on error code, it may contain additional error info
+ */
+struct ipu6_fw_isys_error_info_abi {
+ u32 error;
+ u32 error_details;
+};
+
+/**
+ * struct ipu6_fw_isys_resp_info_abi - ISYS firmware response
+ * @buf_id: buffer ID
+ * @pin: this var is only valid for pin event related responses,
+ * contains pin addresses
+ * @error_info: error information from the FW
+ * @timestamp: Time information for event if available
+ * @stream_handle: stream id the response corresponds to
+ * @type: response type (enum ipu6_fw_isys_resp_type)
+ * @pin_id: pin id that the pin payload corresponds to
+ * @reserved: a reserved field
+ * @reserved2: a reserved field
+ */
+struct ipu6_fw_isys_resp_info_abi {
+ u64 buf_id;
+ struct ipu6_fw_isys_output_pin_payload_abi pin;
+ struct ipu6_fw_isys_error_info_abi error_info;
+ u32 timestamp[2];
+ u8 stream_handle;
+ u8 type;
+ u8 pin_id;
+ u8 reserved;
+ u32 reserved2;
+};
+
+/**
+ * struct ipu6_fw_isys_proxy_error_info_abi - ISYS proxy error
+ * @error: error code if something went wrong
+ * @error_details: depending on error code, it may contain additional error info
+ */
+struct ipu6_fw_isys_proxy_error_info_abi {
+ u32 error;
+ u32 error_details;
+};
+
+struct ipu6_fw_isys_proxy_resp_info_abi {
+ u32 request_id;
+ struct ipu6_fw_isys_proxy_error_info_abi error_info;
+};
+
+/**
+ * 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
+ */
+struct ipu6_fw_resp_queue_token {
+ struct ipu6_fw_isys_resp_info_abi resp_info;
+};
+
+/**
+ * struct ipu6_fw_send_queue_token - ISYS send queue token
+ * @buf_handle: buffer handle
+ * @payload: payload
+ * @send_type: send_type
+ * @stream_id: stream_id
+ */
+struct ipu6_fw_send_queue_token {
+ u64 buf_handle;
+ u32 payload;
+ u16 send_type;
+ u16 stream_id;
+};
+
+/**
+ * struct ipu6_fw_proxy_resp_queue_token - ISYS proxy response queue token
+ * @proxy_resp_info: proxy response info
+ */
+struct ipu6_fw_proxy_resp_queue_token {
+ struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info;
+};
+
+/**
+ * struct ipu6_fw_proxy_send_queue_token - SYS proxy send queue token
+ * @request_id: request_id
+ * @region_index: region_index
+ * @offset: offset
+ * @value: value
+ */
+struct ipu6_fw_proxy_send_queue_token {
+ u32 request_id;
+ u32 region_index;
+ u32 offset;
+ 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);
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
new file mode 100644
index 000000000000..b9ce4324996d
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/sprintf.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-isys-subdev.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+
+static const u32 csi2_supported_codes[] = {
+ MEDIA_BUS_FMT_RGB565_1X16,
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_META_8,
+ MEDIA_BUS_FMT_META_10,
+ MEDIA_BUS_FMT_META_12,
+ MEDIA_BUS_FMT_META_16,
+ MEDIA_BUS_FMT_META_24,
+ 0
+};
+
+/*
+ * Strings corresponding to CSI-2 receiver errors are here.
+ * Corresponding macros are defined in the header file.
+ */
+static const struct ipu6_csi2_error dphy_rx_errors[] = {
+ { "Single packet header error corrected", true },
+ { "Multiple packet header errors detected", true },
+ { "Payload checksum (CRC) error", true },
+ { "Transfer FIFO overflow", false },
+ { "Reserved short packet data type detected", true },
+ { "Reserved long packet data type detected", true },
+ { "Incomplete long packet detected", false },
+ { "Frame sync error", false },
+ { "Line sync error", false },
+ { "DPHY recoverable synchronization error", true },
+ { "DPHY fatal error", false },
+ { "DPHY elastic FIFO overflow", false },
+ { "Inter-frame short packet discarded", true },
+ { "Inter-frame long packet discarded", true },
+ { "MIPI pktgen overflow", false },
+ { "MIPI pktgen data loss", false },
+ { "FIFO overflow", false },
+ { "Lane deskew", false },
+ { "SOT sync error", false },
+ { "HSIDLE detected", false }
+};
+
+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
+{
+ struct media_pad *src_pad;
+ struct v4l2_subdev *ext_sd;
+ struct device *dev;
+
+ if (!csi2)
+ return -EINVAL;
+
+ dev = &csi2->isys->adev->auxdev.dev;
+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ if (IS_ERR(src_pad)) {
+ dev_err(dev, "can't get source pad of %s (%ld)\n",
+ csi2->asd.sd.name, PTR_ERR(src_pad));
+ return PTR_ERR(src_pad);
+ }
+
+ ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))
+ return -ENODEV;
+
+ return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0);
+}
+
+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
+ sub->type, sub->id);
+
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 10, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
+ .subscribe_event = csi2_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+/*
+ * The input system CSI2+ receiver has several
+ * parameters affecting the receiver timings. These depend
+ * on the MIPI bus frequency F in Hz (sensor transmitter rate)
+ * as follows:
+ * register value = (A/1e9 + B * UI) / COUNT_ACC
+ * where
+ * UI = 1 / (2 * F) in seconds
+ * COUNT_ACC = counter accuracy in seconds
+ * COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.
+ *
+ * A and B are coefficients from the table below,
+ * depending whether the register minimum or maximum value is
+ * calculated.
+ * Minimum Maximum
+ * Clock lane A B A B
+ * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0
+ * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16
+ * Data lanes
+ * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4
+ * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6
+ * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4
+ * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6
+ * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4
+ * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6
+ * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4
+ * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6
+ *
+ * We use the minimum values of both A and B.
+ */
+
+#define DIV_SHIFT 8
+#define CSI2_ACCINV 8
+
+static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)
+{
+ return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)
+ / (s32)(link_freq >> DIV_SHIFT));
+}
+
+static int
+ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,
+ struct ipu6_isys_csi2_timing *timing, s32 accinv)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ s64 link_freq;
+
+ link_freq = ipu6_isys_csi2_get_link_freq(csi2);
+ if (link_freq < 0)
+ return link_freq;
+
+ timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,
+ CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,
+ link_freq, accinv);
+ timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,
+ CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,
+ link_freq, accinv);
+ timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,
+ CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,
+ link_freq, accinv);
+ timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,
+ CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,
+ link_freq, accinv);
+
+ dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",
+ timing->ctermen, timing->csettle,
+ timing->dtermen, timing->dsettle);
+
+ return 0;
+}
+
+void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)
+{
+ u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
+ struct ipu6_isys *isys = csi2->isys;
+ u32 mask;
+
+ mask = isys->pdata->ipdata->csi2.irq_mask;
+ writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
+ csi2->receiver_errors |= irq & mask;
+}
+
+void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ const struct ipu6_csi2_error *errors;
+ u32 status;
+ u32 i;
+
+ /* register errors once more in case of interrupts are disabled */
+ ipu6_isys_register_errors(csi2);
+ status = csi2->receiver_errors;
+ csi2->receiver_errors = 0;
+ errors = dphy_rx_errors;
+
+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
+ if (status & BIT(i))
+ dev_err_ratelimited(dev, "csi2-%i error: %s\n",
+ csi2->port, errors[i].error_string);
+ }
+}
+
+static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
+ const struct ipu6_isys_csi2_timing *timing,
+ unsigned int nlanes, int enable)
+{
+ 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);
+
+ cfg.port = csi2->port;
+ cfg.nlanes = nlanes;
+
+ mask = isys->pdata->ipdata->csi2.irq_mask;
+ nports = isys->pdata->ipdata->csi2.nports;
+
+ 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));
+
+ return ret;
+ }
+
+ /* reset port reset */
+ writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
+ usleep_range(100, 200);
+ writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
+
+ /* enable port clock */
+ for (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));
+ }
+
+ /* enable all error related irq */
+ writel(mask,
+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
+ writel(mask,
+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
+ writel(mask,
+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
+ writel(mask,
+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
+ writel(mask,
+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
+
+ /*
+ * Using event from firmware instead of irq to handle CSI2 sync event
+ * which can reduce system wakeups. If CSI2 sync irq enabled, we need
+ * disable the firmware CSI2 sync event to avoid duplicate handling.
+ */
+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
+
+ /* configure to enable FE and PPI2CSI */
+ writel(0, csi2->base + CSI_REG_CSI_FE_MODE);
+ 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),
+ 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);
+ if (ret)
+ dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
+ ret);
+
+ return ret;
+}
+
+static int set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct ipu6_isys_csi2_timing timing = { };
+ unsigned int nlanes;
+ int ret;
+
+ dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off");
+
+ if (!enable) {
+ csi2->stream_count--;
+ if (csi2->stream_count)
+ return 0;
+
+ ipu6_isys_csi2_set_stream(sd, &timing, 0, enable);
+ return 0;
+ }
+
+ if (csi2->stream_count) {
+ csi2->stream_count++;
+ return 0;
+ }
+
+ nlanes = csi2->nlanes;
+
+ ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
+ if (ret)
+ return ret;
+
+ ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable);
+ if (ret)
+ return ret;
+
+ csi2->stream_count++;
+
+ return 0;
+}
+
+static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ struct device *dev = &asd->isys->adev->auxdev.dev;
+ struct v4l2_mbus_framefmt *sink_ffmt;
+ struct v4l2_mbus_framefmt *src_ffmt;
+ struct v4l2_rect *crop;
+
+ if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ sel->pad,
+ sel->stream);
+ if (!sink_ffmt)
+ return -EINVAL;
+
+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+ if (!src_ffmt)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ /* Only vertical cropping is supported */
+ sel->r.left = 0;
+ sel->r.width = sink_ffmt->width;
+ /* Non-bayer formats can't be single line cropped */
+ if (!ipu6_isys_is_bayer_format(sink_ffmt->code))
+ sel->r.top &= ~1;
+ sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,
+ sink_ffmt->height - sel->r.top);
+ *crop = sel->r;
+
+ /* update source pad format */
+ src_ffmt->width = sel->r.width;
+ src_ffmt->height = sel->r.height;
+ if (ipu6_isys_is_bayer_format(sink_ffmt->code))
+ src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,
+ sel->r.left,
+ sel->r.top);
+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ src_ffmt->code);
+
+ return 0;
+}
+
+static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *sink_ffmt;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
+ return -EINVAL;
+
+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ sel->pad,
+ sel->stream);
+ if (!sink_ffmt)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = sink_ffmt->width;
+ sel->r.height = sink_ffmt->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *crop;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
+ .s_stream = set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ipu6_isys_subdev_set_fmt,
+ .get_selection = ipu6_isys_csi2_get_sel,
+ .set_selection = ipu6_isys_csi2_set_sel,
+ .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,
+ .set_routing = ipu6_isys_subdev_set_routing,
+};
+
+static const struct v4l2_subdev_ops csi2_sd_ops = {
+ .core = &csi2_sd_core_ops,
+ .video = &csi2_sd_video_ops,
+ .pad = &csi2_sd_pad_ops,
+};
+
+static const struct media_entity_operations csi2_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)
+{
+ if (!csi2->isys)
+ return;
+
+ v4l2_device_unregister_subdev(&csi2->asd.sd);
+ v4l2_subdev_cleanup(&csi2->asd.sd);
+ ipu6_isys_subdev_cleanup(&csi2->asd);
+ csi2->isys = NULL;
+}
+
+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
+ struct ipu6_isys *isys,
+ void __iomem *base, unsigned int index)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ int ret;
+
+ csi2->isys = isys;
+ csi2->base = base;
+ csi2->port = index;
+
+ csi2->asd.sd.entity.ops = &csi2_entity_ops;
+ csi2->asd.isys = isys;
+ ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
+ NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
+ if (ret)
+ goto fail;
+
+ 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),
+ IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);
+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
+ if (ret) {
+ dev_err(dev, "failed to init v4l2 subdev\n");
+ goto fail;
+ }
+
+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
+ if (ret) {
+ dev_err(dev, "failed to register v4l2 subdev\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ipu6_isys_csi2_cleanup(csi2);
+
+ return ret;
+}
+
+void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)
+{
+ struct video_device *vdev = stream->asd->sd.devnode;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+ struct v4l2_event ev = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
+ v4l2_event_queue(vdev, &ev);
+
+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
+}
+
+void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
+{
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+ u32 frame_sequence = atomic_read(&stream->sequence);
+
+ 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;
+}
+
+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status)
+{
+ struct ipu6_isys_stream *stream = av->stream;
+ struct v4l2_subdev *sd = &stream->asd->sd;
+ struct v4l2_subdev_state *state;
+ struct media_pad *r_pad;
+ unsigned int i;
+ u32 r_stream;
+
+ r_pad = media_pad_remote_pad_first(&av->pad);
+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ for (i = 0; i < state->stream_configs.num_configs; i++) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == r_pad->index && r_stream == cfg->stream) {
+ dev_dbg(&av->isys->adev->auxdev.dev,
+ "%s: pad:%u, stream:%u, status:%u\n",
+ sd->entity.name, r_pad->index, r_stream,
+ status);
+ cfg->enabled = status;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
new file mode 100644
index 000000000000..eba6b29386ea
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_ISYS_CSI2_H
+#define IPU6_ISYS_CSI2_H
+
+#include <linux/container_of.h>
+
+#include "ipu6-isys-subdev.h"
+#include "ipu6-isys-video.h"
+
+struct media_entity;
+struct v4l2_mbus_frame_desc_entry;
+
+struct ipu6_isys_video;
+struct ipu6_isys;
+struct ipu6_isys_csi2_pdata;
+struct ipu6_isys_stream;
+
+#define NR_OF_CSI2_VC 16
+#define INVALID_VC_ID -1
+#define NR_OF_CSI2_SINK_PADS 1
+#define CSI2_PAD_SINK 0
+#define NR_OF_CSI2_SRC_PADS 8
+#define CSI2_PAD_SRC 1
+#define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS)
+
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8
+
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2
+
+struct ipu6_isys_csi2 {
+ struct ipu6_isys_subdev asd;
+ struct ipu6_isys_csi2_pdata *pdata;
+ struct ipu6_isys *isys;
+ struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS];
+
+ void __iomem *base;
+ u32 receiver_errors;
+ unsigned int nlanes;
+ unsigned int port;
+ unsigned int stream_count;
+};
+
+struct ipu6_isys_csi2_timing {
+ u32 ctermen;
+ u32 csettle;
+ u32 dtermen;
+ u32 dsettle;
+};
+
+struct ipu6_csi2_error {
+ const char *error_string;
+ bool is_info_only;
+};
+
+#define ipu6_isys_subdev_to_csi2(__sd) \
+ container_of(__sd, struct ipu6_isys_csi2, asd)
+
+#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd)
+
+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2);
+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys,
+ void __iomem *base, unsigned int index);
+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2);
+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);
+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status);
+
+#endif /* IPU6_ISYS_CSI2_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
new file mode 100644
index 000000000000..1715275e6776
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+
+#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i))
+#define IPU6_DWC_DPHY_RSTZ 0x00
+#define IPU6_DWC_DPHY_SHUTDOWNZ 0x04
+#define IPU6_DWC_DPHY_HSFREQRANGE 0x08
+#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c
+#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10
+#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x14
+#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18
+#define IPU6_DWC_DPHY_DFT_CTRL0 0x28
+#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c
+#define IPU6_DWC_DPHY_DFT_CTRL2 0x30
+
+/*
+ * test IFC request definition:
+ * - req: 0 for read, 1 for write
+ * - 12 bits address
+ * - 8bits data (will ignore for read)
+ * --24----16------4-----0
+ * --|-data-|-addr-|-req-|
+ */
+#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
+ FIELD_PREP(GENMASK(15, 4), addr) | \
+ FIELD_PREP(GENMASK(1, 0), req))
+
+#define TEST_IFC_REQ_READ 0
+#define TEST_IFC_REQ_WRITE 1
+#define TEST_IFC_REQ_RESET 2
+
+#define TEST_IFC_ACCESS_MODE_FSM 0
+#define TEST_IFC_ACCESS_MODE_IFC_CTL 1
+
+enum phy_fsm_state {
+ PHY_FSM_STATE_POWERON = 0,
+ PHY_FSM_STATE_BGPON = 1,
+ PHY_FSM_STATE_CAL_TYPE = 2,
+ PHY_FSM_STATE_BURNIN_CAL = 3,
+ PHY_FSM_STATE_TERMCAL = 4,
+ PHY_FSM_STATE_OFFSETCAL = 5,
+ PHY_FSM_STATE_OFFSET_LANE = 6,
+ PHY_FSM_STATE_IDLE = 7,
+ PHY_FSM_STATE_ULP = 8,
+ PHY_FSM_STATE_DDLTUNNING = 9,
+ PHY_FSM_STATE_SKEW_BACKWARD = 10,
+ PHY_FSM_STATE_INVALID,
+};
+
+static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
+ u32 data)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
+
+ dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,
+ data);
+ writel(data, base + addr);
+}
+
+static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
+ u32 data;
+
+ data = readl(base + addr);
+ dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,
+ data);
+
+ return data;
+}
+
+static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
+ u32 data, u8 shift, u8 width)
+{
+ u32 temp;
+ u32 mask;
+
+ mask = (1 << width) - 1;
+ temp = dwc_dphy_read(isys, phy_id, addr);
+ temp &= ~(mask << shift);
+ temp |= (data & mask) << shift;
+ dwc_dphy_write(isys, phy_id, addr, temp);
+}
+
+static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
+ u32 addr, u8 shift, u8 width)
+{
+ u32 val;
+
+ val = dwc_dphy_read(isys, phy_id, addr) >> shift;
+ return val & ((1 << width) - 1);
+}
+
+#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
+static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
+ u32 *val)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
+ void __iomem *reg;
+ u32 completion;
+ int ret;
+
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
+ IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
+ 10, DWC_DPHY_TIMEOUT);
+ if (ret) {
+ dev_err(dev, "DWC ifc request read timeout\n");
+ return ret;
+ }
+
+ *val = completion >> 8 & 0xff;
+ *val = FIELD_GET(GENMASK(15, 8), completion);
+ dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
+
+ return 0;
+}
+
+static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
+ u32 data)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
+ void __iomem *reg;
+ u32 completion;
+ int ret;
+
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
+ IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
+ completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
+ 10, DWC_DPHY_TIMEOUT);
+ if (ret)
+ dev_err(dev, "DWC ifc request write timeout\n");
+
+ return ret;
+}
+
+static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
+ u32 addr, u32 data, u8 shift, u8 width)
+{
+ u32 temp, mask;
+ int ret;
+
+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
+ if (ret)
+ return;
+
+ mask = (1 << width) - 1;
+ temp &= ~(mask << shift);
+ temp |= (data & mask) << shift;
+ dwc_dphy_ifc_write(isys, phy_id, addr, temp);
+}
+
+static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
+ u8 shift, u8 width)
+{
+ int ret;
+ u32 val;
+
+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
+ if (ret)
+ return 0;
+
+ return ((val >> shift) & ((1 << width) - 1));
+}
+
+static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 fsm_state;
+ int ret;
+
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
+ usleep_range(10, 20);
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
+
+ ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
+ (fsm_state == PHY_FSM_STATE_IDLE ||
+ fsm_state == PHY_FSM_STATE_ULP),
+ 100, DWC_DPHY_TIMEOUT, false, isys,
+ phy_id, 0x1e, 0, 4);
+
+ if (ret)
+ dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
+ fsm_state);
+
+ return ret;
+}
+
+struct dwc_dphy_freq_range {
+ u8 hsfreq;
+ u16 min;
+ u16 max;
+ u16 default_mbps;
+ u16 osc_freq_target;
+};
+
+#define DPHY_FREQ_RANGE_NUM (63)
+#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff)
+static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
+ {0x00, 80, 97, 80, 335},
+ {0x10, 80, 107, 90, 335},
+ {0x20, 84, 118, 100, 335},
+ {0x30, 93, 128, 110, 335},
+ {0x01, 103, 139, 120, 335},
+ {0x11, 112, 149, 130, 335},
+ {0x21, 122, 160, 140, 335},
+ {0x31, 131, 170, 150, 335},
+ {0x02, 141, 181, 160, 335},
+ {0x12, 150, 191, 170, 335},
+ {0x22, 160, 202, 180, 335},
+ {0x32, 169, 212, 190, 335},
+ {0x03, 183, 228, 205, 335},
+ {0x13, 198, 244, 220, 335},
+ {0x23, 212, 259, 235, 335},
+ {0x33, 226, 275, 250, 335},
+ {0x04, 250, 301, 275, 335},
+ {0x14, 274, 328, 300, 335},
+ {0x25, 297, 354, 325, 335},
+ {0x35, 321, 380, 350, 335},
+ {0x05, 369, 433, 400, 335},
+ {0x16, 416, 485, 450, 335},
+ {0x26, 464, 538, 500, 335},
+ {0x37, 511, 590, 550, 335},
+ {0x07, 559, 643, 600, 335},
+ {0x18, 606, 695, 650, 335},
+ {0x28, 654, 748, 700, 335},
+ {0x39, 701, 800, 750, 335},
+ {0x09, 749, 853, 800, 335},
+ {0x19, 796, 905, 850, 335},
+ {0x29, 844, 958, 900, 335},
+ {0x3a, 891, 1010, 950, 335},
+ {0x0a, 939, 1063, 1000, 335},
+ {0x1a, 986, 1115, 1050, 335},
+ {0x2a, 1034, 1168, 1100, 335},
+ {0x3b, 1081, 1220, 1150, 335},
+ {0x0b, 1129, 1273, 1200, 335},
+ {0x1b, 1176, 1325, 1250, 335},
+ {0x2b, 1224, 1378, 1300, 335},
+ {0x3c, 1271, 1430, 1350, 335},
+ {0x0c, 1319, 1483, 1400, 335},
+ {0x1c, 1366, 1535, 1450, 335},
+ {0x2c, 1414, 1588, 1500, 335},
+ {0x3d, 1461, 1640, 1550, 208},
+ {0x0d, 1509, 1693, 1600, 214},
+ {0x1d, 1556, 1745, 1650, 221},
+ {0x2e, 1604, 1798, 1700, 228},
+ {0x3e, 1651, 1850, 1750, 234},
+ {0x0e, 1699, 1903, 1800, 241},
+ {0x1e, 1746, 1955, 1850, 248},
+ {0x2f, 1794, 2008, 1900, 255},
+ {0x3f, 1841, 2060, 1950, 261},
+ {0x0f, 1889, 2113, 2000, 268},
+ {0x40, 1936, 2165, 2050, 275},
+ {0x41, 1984, 2218, 2100, 281},
+ {0x42, 2031, 2270, 2150, 288},
+ {0x43, 2079, 2323, 2200, 294},
+ {0x44, 2126, 2375, 2250, 302},
+ {0x45, 2174, 2428, 2300, 308},
+ {0x46, 2221, 2480, 2350, 315},
+ {0x47, 2269, 2500, 2400, 321},
+ {0x48, 2316, 2500, 2450, 328},
+ {0x49, 2364, 2500, 2500, 335}
+};
+
+static u16 get_hsfreq_by_mbps(u32 mbps)
+{
+ unsigned int i = DPHY_FREQ_RANGE_NUM;
+
+ while (i--) {
+ if (freqranges[i].default_mbps == mbps ||
+ (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
+ return i;
+ }
+
+ return DPHY_FREQ_RANGE_INVALID_INDEX;
+}
+
+static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
+ u32 phy_id, u32 mbps)
+{
+ struct ipu6_bus_device *adev = isys->adev;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu6_device *isp = adev->isp;
+ u32 cfg_clk_freqrange;
+ u32 osc_freq_target;
+ u32 index;
+
+ dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
+
+ index = get_hsfreq_by_mbps(mbps);
+ if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
+ dev_err(dev, "link freq not found for mbps %u", mbps);
+ return -EINVAL;
+ }
+
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
+ freqranges[index].hsfreq, 0, 7);
+
+ /* Force termination Calibration */
+ if (isys->phy_termcal_val) {
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
+ isys->phy_termcal_val, 4, 4);
+ }
+
+ /*
+ * Enable override to configure the DDL target oscillation
+ * frequency on bit 0 of register 0xe4
+ */
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
+ /*
+ * configure registers 0xe2, 0xe3 with the
+ * appropriate DDL target oscillation frequency
+ * 0x1cc(460)
+ */
+ osc_freq_target = freqranges[index].osc_freq_target;
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
+ osc_freq_target & 0xff, 0, 8);
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
+ (osc_freq_target >> 8) & 0xf, 0, 4);
+
+ if (mbps < 1500) {
+ /* deskew_polarity_rw, for < 1.5Gbps */
+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
+ }
+
+ /*
+ * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
+ * (38.4 - 17) * 4 = ~85 (0x55)
+ */
+ cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
+ dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
+ isp->buttress.ref_clk, cfg_clk_freqrange);
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
+ cfg_clk_freqrange, 0, 8);
+
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
+
+ return 0;
+}
+
+static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
+ u32 slave, u32 mbps)
+{
+ /* Config mastermacro */
+ dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
+
+ /* Config master PHY clk lane to drive long channel clk */
+ dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
+
+ /* Config both PHYs data lanes to get clk from long channel */
+ dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
+ dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
+
+ /* Config slave PHY clk lane to bypass long channel clk to DDR clk */
+ dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
+
+ /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
+ dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
+
+ /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
+ dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
+
+ /* Turn off slave PHY LP-RX clk lane */
+ dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
+ dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
+}
+
+#define PHY_E 4
+static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 rescal_done;
+ int ret;
+
+ ret = dwc_dphy_pwr_up(isys, phy_id);
+ if (ret != 0) {
+ dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
+ return ret;
+ }
+
+ /* reset forcerxmode */
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
+
+ dev_dbg(dev, "Dphy %u is ready!", phy_id);
+
+ if (phy_id != PHY_E || isys->phy_termcal_val)
+ return 0;
+
+ usleep_range(100, 200);
+ rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
+ if (rescal_done) {
+ isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
+ 0x220, 2, 4);
+ dev_dbg(dev, "termcal done with value = %u",
+ isys->phy_termcal_val);
+ }
+
+ return 0;
+}
+
+static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
+{
+ dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
+
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
+ TEST_IFC_ACCESS_MODE_FSM);
+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
+ TEST_IFC_REQ_RESET);
+}
+
+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ u32 phy_id, primary, secondary;
+ u32 nlanes, port, mbps;
+ s64 link_freq;
+ int ret;
+
+ port = cfg->port;
+
+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
+ dev_warn(dev, "invalid port ID %d\n", port);
+ return -EINVAL;
+ }
+
+ nlanes = cfg->nlanes;
+ /* only port 0, 2 and 4 support 4 lanes */
+ if (nlanes == 4 && port % 2) {
+ dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
+ nlanes);
+ return -EINVAL;
+ }
+
+ link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
+ if (link_freq < 0) {
+ dev_err(dev, "get link freq failed(%lld).\n", link_freq);
+ return link_freq;
+ }
+
+ mbps = div_u64(link_freq, 500000);
+
+ phy_id = port;
+ primary = port & ~1;
+ secondary = primary + 1;
+ if (on) {
+ if (nlanes == 4) {
+ dev_dbg(dev, "config phy %u and %u in aggr mode\n",
+ primary, secondary);
+
+ ipu6_isys_dwc_phy_reset(isys, primary);
+ ipu6_isys_dwc_phy_reset(isys, secondary);
+ ipu6_isys_dwc_phy_aggr_setup(isys, primary,
+ secondary, mbps);
+
+ ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
+ if (ret)
+ return ret;
+ ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
+ if (ret)
+ return ret;
+
+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
+ if (ret)
+ return ret;
+
+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
+ return ret;
+ }
+
+ dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
+ phy_id, nlanes);
+
+ ipu6_isys_dwc_phy_reset(isys, phy_id);
+ ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
+ if (ret)
+ return ret;
+
+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
+ return ret;
+ }
+
+ if (nlanes == 4) {
+ dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
+ primary, secondary, port);
+ ipu6_isys_dwc_phy_reset(isys, secondary);
+ ipu6_isys_dwc_phy_reset(isys, primary);
+
+ return 0;
+ }
+
+ dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
+
+ ipu6_isys_dwc_phy_reset(isys, phy_id);
+
+ return 0;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
new file mode 100644
index 000000000000..c804291cfae9
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/io.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+
+/* only use BB0, BB2, BB4, and BB6 on PHY0 */
+#define IPU6SE_ISYS_PHY_BB_NUM 4
+#define IPU6SE_ISYS_PHY_0_BASE 0x10000
+
+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x))
+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x))
+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x))
+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x))
+
+/*
+ * use port_cfg to configure that which data lanes used
+ * +---------+ +------+ +-----+
+ * | port0 x4<-----| | | |
+ * | | | port | | |
+ * | port1 x2<-----| | | |
+ * | | | <-| PHY |
+ * | port2 x4<-----| | | |
+ * | | |config| | |
+ * | port3 x2<-----| | | |
+ * +---------+ +------+ +-----+
+ */
+static const unsigned int csi2_port_cfg[][3] = {
+ {0, 0, 0x1f}, /* no link */
+ {4, 0, 0x10}, /* x4 + x4 config */
+ {2, 0, 0x12}, /* x2 + x2 config */
+ {1, 0, 0x13}, /* x1 + x1 config */
+ {2, 1, 0x15}, /* x2x1 + x2x1 config */
+ {1, 1, 0x16}, /* x1x1 + x1x1 config */
+ {2, 2, 0x18}, /* x2x2 + x2x2 config */
+ {1, 2, 0x19} /* x1x2 + x1x2 config */
+};
+
+/* port, nlanes, bbindex, portcfg */
+static const unsigned int phy_port_cfg[][4] = {
+ /* sip0 */
+ {0, 1, 0, 0x15},
+ {0, 2, 0, 0x15},
+ {0, 4, 0, 0x15},
+ {0, 4, 2, 0x22},
+ /* sip1 */
+ {2, 1, 4, 0x15},
+ {2, 2, 4, 0x15},
+ {2, 4, 4, 0x15},
+ {2, 4, 6, 0x22}
+};
+
+static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys,
+ unsigned int port,
+ unsigned int nlanes)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *base = isys->adev->isp->base;
+ unsigned int bbnum;
+ u32 val, reg, i;
+
+ dev_dbg(dev, "port %u with %u lanes", port, nlanes);
+
+ /* only support <1.5Gbps */
+ for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) {
+ /* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */
+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
+ val = readl(base + reg);
+ val |= FIELD_PREP(GENMASK(6, 1), 13);
+ writel(val, base + reg);
+
+ /* cphy_rx_control1.en_crc1 = 1 */
+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i);
+ val = readl(base + reg);
+ val |= BIT(31);
+ writel(val, base + reg);
+
+ /* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */
+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i);
+ val = readl(base + reg);
+ val |= BIT(25) | BIT(26);
+ writel(val, base + reg);
+
+ /* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */
+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
+ val = readl(base + reg);
+ val |= BIT(0);
+ writel(val, base + reg);
+ }
+
+ /* Front end config, use minimal channel loss */
+ for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) {
+ if (phy_port_cfg[i][0] == port &&
+ phy_port_cfg[i][1] == nlanes) {
+ bbnum = phy_port_cfg[i][2] / 2;
+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum);
+ val = readl(base + reg);
+ val |= phy_port_cfg[i][3];
+ writel(val, base + reg);
+ }
+ }
+}
+
+static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys)
+{
+ void __iomem *base = isys->adev->isp->base;
+ u32 val, reg;
+
+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL;
+ val = readl(base + reg);
+ val |= BIT(0);
+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL);
+
+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL;
+ val = readl(base + reg);
+ val |= BIT(0);
+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL);
+
+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL;
+ val = readl(base + reg);
+ val |= BIT(0);
+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL);
+
+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL;
+ val = readl(base + reg);
+ val |= BIT(0);
+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL);
+}
+
+static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys,
+ unsigned int port, unsigned int nlanes)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int sip = port / 2;
+ unsigned int index;
+
+ switch (nlanes) {
+ case 1:
+ index = 5;
+ break;
+ case 2:
+ index = 6;
+ break;
+ case 4:
+ index = 1;
+ break;
+ default:
+ dev_err(dev, "lanes nr %u is unsupported\n", nlanes);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "port config for port %u with %u lanes\n", port, nlanes);
+
+ writel(csi2_port_cfg[index][2],
+ isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip));
+
+ return 0;
+}
+
+static void
+ipu6_isys_csi2_set_timing(struct ipu6_isys *isys,
+ const struct ipu6_isys_csi2_timing *timing,
+ unsigned int port, unsigned int nlanes)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *reg;
+ u32 port_base;
+ u32 i;
+
+ port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) :
+ CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port);
+
+ dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes);
+
+ reg = isys->pdata->base + port_base;
+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE;
+
+ writel(timing->ctermen, reg);
+
+ reg = isys->pdata->base + port_base;
+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE;
+ writel(timing->csettle, reg);
+
+ for (i = 0; i < nlanes; i++) {
+ reg = isys->pdata->base + port_base;
+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i);
+ writel(timing->dtermen, reg);
+
+ reg = isys->pdata->base + port_base;
+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i);
+ writel(timing->dsettle, reg);
+ }
+}
+
+#define DPHY_TIMER_INCR 0x28
+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ int ret = 0;
+ u32 nlanes;
+ u32 port;
+
+ if (!on)
+ return 0;
+
+ port = cfg->port;
+ nlanes = cfg->nlanes;
+
+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
+ dev_warn(dev, "invalid port ID %d\n", port);
+ return -EINVAL;
+ }
+
+ ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes);
+
+ writel(DPHY_TIMER_INCR,
+ isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR);
+
+ /* set port cfg and rx timing */
+ ipu6_isys_csi2_set_timing(isys, timing, port, nlanes);
+
+ ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes);
+ if (ret)
+ return ret;
+
+ ipu6_isys_csi2_rx_control(isys);
+
+ return 0;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
new file mode 100644
index 000000000000..71aa5009512a
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/refcount.h>
+#include <linux/time64.h>
+
+#include <media/v4l2-async.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+
+#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8)
+#define CSI_REG_HUB_GPREG_PHY_CTL_RESET BIT(4)
+#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN BIT(0)
+#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8)
+#define CSI_REG_HUB_GPREG_PHY_POWER_ACK BIT(0)
+#define CSI_REG_HUB_GPREG_PHY_READY BIT(4)
+
+#define MCD_PHY_POWER_STATUS_TIMEOUT (200 * USEC_PER_MSEC)
+
+/*
+ * bridge to phy in buttress reg map, each phy has 16 kbytes
+ * only 2 phys for TGL U and Y
+ */
+#define IPU6_ISYS_MCD_PHY_BASE(i) (0x10000 + (i) * 0x4000)
+
+/*
+ * There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL.
+ * Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes.
+ * CSI port 1, 3 (5, 7) can support max 2 data lanes.
+ * CSI port 0, 2 (4, 6) can support max 4 data lanes.
+ * PHY configurations are PPI based instead of port.
+ * Left:
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | PPI | PPI5 | PPI4 | PPI3 | PPI2 | PPI1 | PPI0 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x4 | unused | D3 | D2 | C0 | D0 | D1 |
+ * |---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x2x2 | C1 | D0 | D1 | C0 | D0 | D1 |
+ * ----------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x2x1 | C1 | D0 | unused | C0 | D0 | D1 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x1x1 | C1 | D0 | unused | C0 | D0 | unused |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x1x2 | C1 | D0 | D1 | C0 | D0 | unused |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ *
+ * Right:
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | PPI | PPI6 | PPI7 | PPI8 | PPI9 | PPI10 | PPI11 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x4 | D1 | D0 | C2 | D2 | D3 | unused |
+ * |---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x2x2 | D1 | D0 | C2 | D1 | D0 | C3 |
+ * ----------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x2x1 | D1 | D0 | C2 | unused | D0 | C3 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x1x1 | unused | D0 | C2 | unused | D0 | C3 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ * | | | | | | | |
+ * | x1x2 | unused | D0 | C2 | D1 | D0 | C3 |
+ * +---------+---------+---------+---------+--------+---------+----------+
+ *
+ * ppi mapping per phy :
+ *
+ * x4 + x4:
+ * Left : port0 - PPI range {0, 1, 2, 3, 4}
+ * Right: port2 - PPI range {6, 7, 8, 9, 10}
+ *
+ * x4 + x2x2:
+ * Left: port0 - PPI range {0, 1, 2, 3, 4}
+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
+ *
+ * x2x2 + x4:
+ * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
+ * Right: port2 - PPI range {6, 7, 8, 9, 10}
+ *
+ * x2x2 + x2x2:
+ * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
+ */
+
+struct phy_reg {
+ u32 reg;
+ u32 val;
+};
+
+static const struct phy_reg common_init_regs[] = {
+ /* for TGL-U, use 0x80000000 */
+ {0x00000040, 0x80000000},
+ {0x00000044, 0x00a80880},
+ {0x00000044, 0x00b80880},
+ {0x00000010, 0x0000078c},
+ {0x00000344, 0x2f4401e2},
+ {0x00000544, 0x924401e2},
+ {0x00000744, 0x594401e2},
+ {0x00000944, 0x624401e2},
+ {0x00000b44, 0xfc4401e2},
+ {0x00000d44, 0xc54401e2},
+ {0x00000f44, 0x034401e2},
+ {0x00001144, 0x8f4401e2},
+ {0x00001344, 0x754401e2},
+ {0x00001544, 0xe94401e2},
+ {0x00001744, 0xcb4401e2},
+ {0x00001944, 0xfa4401e2}
+};
+
+static const struct phy_reg x1_port0_config_regs[] = {
+ {0x00000694, 0xc80060fa},
+ {0x00000680, 0x3d4f78ea},
+ {0x00000690, 0x10a0140b},
+ {0x000006a8, 0xdf04010a},
+ {0x00000700, 0x57050060},
+ {0x00000710, 0x0030001c},
+ {0x00000738, 0x5f004444},
+ {0x0000073c, 0x78464204},
+ {0x00000748, 0x7821f940},
+ {0x0000074c, 0xb2000433},
+ {0x00000494, 0xfe6030fa},
+ {0x00000480, 0x29ef5ed0},
+ {0x00000490, 0x10a0540b},
+ {0x000004a8, 0x7a01010a},
+ {0x00000500, 0xef053460},
+ {0x00000510, 0xe030101c},
+ {0x00000538, 0xdf808444},
+ {0x0000053c, 0xc8422204},
+ {0x00000540, 0x0180088c},
+ {0x00000574, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x1_port1_config_regs[] = {
+ {0x00000c94, 0xc80060fa},
+ {0x00000c80, 0xcf47abea},
+ {0x00000c90, 0x10a0840b},
+ {0x00000ca8, 0xdf04010a},
+ {0x00000d00, 0x57050060},
+ {0x00000d10, 0x0030001c},
+ {0x00000d38, 0x5f004444},
+ {0x00000d3c, 0x78464204},
+ {0x00000d48, 0x7821f940},
+ {0x00000d4c, 0xb2000433},
+ {0x00000a94, 0xc91030fa},
+ {0x00000a80, 0x5a166ed0},
+ {0x00000a90, 0x10a0540b},
+ {0x00000aa8, 0x5d060100},
+ {0x00000b00, 0xef053460},
+ {0x00000b10, 0xa030101c},
+ {0x00000b38, 0xdf808444},
+ {0x00000b3c, 0xc8422204},
+ {0x00000b40, 0x0180088c},
+ {0x00000b74, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x1_port2_config_regs[] = {
+ {0x00001294, 0x28f000fa},
+ {0x00001280, 0x08130cea},
+ {0x00001290, 0x10a0140b},
+ {0x000012a8, 0xd704010a},
+ {0x00001300, 0x8d050060},
+ {0x00001310, 0x0030001c},
+ {0x00001338, 0xdf008444},
+ {0x0000133c, 0x78422204},
+ {0x00001348, 0x7821f940},
+ {0x0000134c, 0x5a000433},
+ {0x00001094, 0x2d20b0fa},
+ {0x00001080, 0xade75dd0},
+ {0x00001090, 0x10a0540b},
+ {0x000010a8, 0xb101010a},
+ {0x00001100, 0x33053460},
+ {0x00001110, 0x0030101c},
+ {0x00001138, 0xdf808444},
+ {0x0000113c, 0xc8422204},
+ {0x00001140, 0x8180088c},
+ {0x00001174, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x1_port3_config_regs[] = {
+ {0x00001894, 0xc80060fa},
+ {0x00001880, 0x0f90fd6a},
+ {0x00001890, 0x10a0840b},
+ {0x000018a8, 0xdf04010a},
+ {0x00001900, 0x57050060},
+ {0x00001910, 0x0030001c},
+ {0x00001938, 0x5f004444},
+ {0x0000193c, 0x78464204},
+ {0x00001948, 0x7821f940},
+ {0x0000194c, 0xb2000433},
+ {0x00001694, 0x3050d0fa},
+ {0x00001680, 0x0ef6d050},
+ {0x00001690, 0x10a0540b},
+ {0x000016a8, 0xe301010a},
+ {0x00001700, 0x69053460},
+ {0x00001710, 0xa030101c},
+ {0x00001738, 0xdf808444},
+ {0x0000173c, 0xc8422204},
+ {0x00001740, 0x0180088c},
+ {0x00001774, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x2_port0_config_regs[] = {
+ {0x00000694, 0xc80060fa},
+ {0x00000680, 0x3d4f78ea},
+ {0x00000690, 0x10a0140b},
+ {0x000006a8, 0xdf04010a},
+ {0x00000700, 0x57050060},
+ {0x00000710, 0x0030001c},
+ {0x00000738, 0x5f004444},
+ {0x0000073c, 0x78464204},
+ {0x00000748, 0x7821f940},
+ {0x0000074c, 0xb2000433},
+ {0x00000494, 0xc80060fa},
+ {0x00000480, 0x29ef5ed8},
+ {0x00000490, 0x10a0540b},
+ {0x000004a8, 0x7a01010a},
+ {0x00000500, 0xef053460},
+ {0x00000510, 0xe030101c},
+ {0x00000538, 0xdf808444},
+ {0x0000053c, 0xc8422204},
+ {0x00000540, 0x0180088c},
+ {0x00000574, 0x00000000},
+ {0x00000294, 0xc80060fa},
+ {0x00000280, 0xcb45b950},
+ {0x00000290, 0x10a0540b},
+ {0x000002a8, 0x8c01010a},
+ {0x00000300, 0xef053460},
+ {0x00000310, 0x8030101c},
+ {0x00000338, 0x41808444},
+ {0x0000033c, 0x32422204},
+ {0x00000340, 0x0180088c},
+ {0x00000374, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x2_port1_config_regs[] = {
+ {0x00000c94, 0xc80060fa},
+ {0x00000c80, 0xcf47abea},
+ {0x00000c90, 0x10a0840b},
+ {0x00000ca8, 0xdf04010a},
+ {0x00000d00, 0x57050060},
+ {0x00000d10, 0x0030001c},
+ {0x00000d38, 0x5f004444},
+ {0x00000d3c, 0x78464204},
+ {0x00000d48, 0x7821f940},
+ {0x00000d4c, 0xb2000433},
+ {0x00000a94, 0xc80060fa},
+ {0x00000a80, 0x5a166ed8},
+ {0x00000a90, 0x10a0540b},
+ {0x00000aa8, 0x7a01010a},
+ {0x00000b00, 0xef053460},
+ {0x00000b10, 0xa030101c},
+ {0x00000b38, 0xdf808444},
+ {0x00000b3c, 0xc8422204},
+ {0x00000b40, 0x0180088c},
+ {0x00000b74, 0x00000000},
+ {0x00000894, 0xc80060fa},
+ {0x00000880, 0x4d4f21d0},
+ {0x00000890, 0x10a0540b},
+ {0x000008a8, 0x5601010a},
+ {0x00000900, 0xef053460},
+ {0x00000910, 0x8030101c},
+ {0x00000938, 0xdf808444},
+ {0x0000093c, 0xc8422204},
+ {0x00000940, 0x0180088c},
+ {0x00000974, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x2_port2_config_regs[] = {
+ {0x00001294, 0xc80060fa},
+ {0x00001280, 0x08130cea},
+ {0x00001290, 0x10a0140b},
+ {0x000012a8, 0xd704010a},
+ {0x00001300, 0x8d050060},
+ {0x00001310, 0x0030001c},
+ {0x00001338, 0xdf008444},
+ {0x0000133c, 0x78422204},
+ {0x00001348, 0x7821f940},
+ {0x0000134c, 0x5a000433},
+ {0x00001094, 0xc80060fa},
+ {0x00001080, 0xade75dd8},
+ {0x00001090, 0x10a0540b},
+ {0x000010a8, 0xb101010a},
+ {0x00001100, 0x33053460},
+ {0x00001110, 0x0030101c},
+ {0x00001138, 0xdf808444},
+ {0x0000113c, 0xc8422204},
+ {0x00001140, 0x8180088c},
+ {0x00001174, 0x00000000},
+ {0x00000e94, 0xc80060fa},
+ {0x00000e80, 0x0fbf16d0},
+ {0x00000e90, 0x10a0540b},
+ {0x00000ea8, 0x7a01010a},
+ {0x00000f00, 0xf5053460},
+ {0x00000f10, 0xc030101c},
+ {0x00000f38, 0xdf808444},
+ {0x00000f3c, 0xc8422204},
+ {0x00000f40, 0x8180088c},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x2_port3_config_regs[] = {
+ {0x00001894, 0xc80060fa},
+ {0x00001880, 0x0f90fd6a},
+ {0x00001890, 0x10a0840b},
+ {0x000018a8, 0xdf04010a},
+ {0x00001900, 0x57050060},
+ {0x00001910, 0x0030001c},
+ {0x00001938, 0x5f004444},
+ {0x0000193c, 0x78464204},
+ {0x00001948, 0x7821f940},
+ {0x0000194c, 0xb2000433},
+ {0x00001694, 0xc80060fa},
+ {0x00001680, 0x0ef6d058},
+ {0x00001690, 0x10a0540b},
+ {0x000016a8, 0x7a01010a},
+ {0x00001700, 0x69053460},
+ {0x00001710, 0xa030101c},
+ {0x00001738, 0xdf808444},
+ {0x0000173c, 0xc8422204},
+ {0x00001740, 0x0180088c},
+ {0x00001774, 0x00000000},
+ {0x00001494, 0xc80060fa},
+ {0x00001480, 0xf9d34bd0},
+ {0x00001490, 0x10a0540b},
+ {0x000014a8, 0x7a01010a},
+ {0x00001500, 0x1b053460},
+ {0x00001510, 0x0030101c},
+ {0x00001538, 0xdf808444},
+ {0x0000153c, 0xc8422204},
+ {0x00001540, 0x8180088c},
+ {0x00001574, 0x00000000},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x4_port0_config_regs[] = {
+ {0x00000694, 0xc80060fa},
+ {0x00000680, 0x3d4f78fa},
+ {0x00000690, 0x10a0140b},
+ {0x000006a8, 0xdf04010a},
+ {0x00000700, 0x57050060},
+ {0x00000710, 0x0030001c},
+ {0x00000738, 0x5f004444},
+ {0x0000073c, 0x78464204},
+ {0x00000748, 0x7821f940},
+ {0x0000074c, 0xb2000433},
+ {0x00000494, 0xfe6030fa},
+ {0x00000480, 0x29ef5ed8},
+ {0x00000490, 0x10a0540b},
+ {0x000004a8, 0x7a01010a},
+ {0x00000500, 0xef053460},
+ {0x00000510, 0xe030101c},
+ {0x00000538, 0xdf808444},
+ {0x0000053c, 0xc8422204},
+ {0x00000540, 0x0180088c},
+ {0x00000574, 0x00000004},
+ {0x00000294, 0x23e030fa},
+ {0x00000280, 0xcb45b950},
+ {0x00000290, 0x10a0540b},
+ {0x000002a8, 0x8c01010a},
+ {0x00000300, 0xef053460},
+ {0x00000310, 0x8030101c},
+ {0x00000338, 0x41808444},
+ {0x0000033c, 0x32422204},
+ {0x00000340, 0x0180088c},
+ {0x00000374, 0x00000004},
+ {0x00000894, 0x5620b0fa},
+ {0x00000880, 0x4d4f21dc},
+ {0x00000890, 0x10a0540b},
+ {0x000008a8, 0x5601010a},
+ {0x00000900, 0xef053460},
+ {0x00000910, 0x8030101c},
+ {0x00000938, 0xdf808444},
+ {0x0000093c, 0xc8422204},
+ {0x00000940, 0x0180088c},
+ {0x00000974, 0x00000004},
+ {0x00000a94, 0xc91030fa},
+ {0x00000a80, 0x5a166ecc},
+ {0x00000a90, 0x10a0540b},
+ {0x00000aa8, 0x5d01010a},
+ {0x00000b00, 0xef053460},
+ {0x00000b10, 0xa030101c},
+ {0x00000b38, 0xdf808444},
+ {0x00000b3c, 0xc8422204},
+ {0x00000b40, 0x0180088c},
+ {0x00000b74, 0x00000004},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x4_port1_config_regs[] = {
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x4_port2_config_regs[] = {
+ {0x00001294, 0x28f000fa},
+ {0x00001280, 0x08130cfa},
+ {0x00001290, 0x10c0140b},
+ {0x000012a8, 0xd704010a},
+ {0x00001300, 0x8d050060},
+ {0x00001310, 0x0030001c},
+ {0x00001338, 0xdf008444},
+ {0x0000133c, 0x78422204},
+ {0x00001348, 0x7821f940},
+ {0x0000134c, 0x5a000433},
+ {0x00001094, 0x2d20b0fa},
+ {0x00001080, 0xade75dd8},
+ {0x00001090, 0x10a0540b},
+ {0x000010a8, 0xb101010a},
+ {0x00001100, 0x33053460},
+ {0x00001110, 0x0030101c},
+ {0x00001138, 0xdf808444},
+ {0x0000113c, 0xc8422204},
+ {0x00001140, 0x8180088c},
+ {0x00001174, 0x00000004},
+ {0x00000e94, 0xd308d0fa},
+ {0x00000e80, 0x0fbf16d0},
+ {0x00000e90, 0x10a0540b},
+ {0x00000ea8, 0x2c01010a},
+ {0x00000f00, 0xf5053460},
+ {0x00000f10, 0xc030101c},
+ {0x00000f38, 0xdf808444},
+ {0x00000f3c, 0xc8422204},
+ {0x00000f40, 0x8180088c},
+ {0x00000f74, 0x00000004},
+ {0x00001494, 0x136850fa},
+ {0x00001480, 0xf9d34bdc},
+ {0x00001490, 0x10a0540b},
+ {0x000014a8, 0x5a01010a},
+ {0x00001500, 0x1b053460},
+ {0x00001510, 0x0030101c},
+ {0x00001538, 0xdf808444},
+ {0x0000153c, 0xc8422204},
+ {0x00001540, 0x8180088c},
+ {0x00001574, 0x00000004},
+ {0x00001694, 0x3050d0fa},
+ {0x00001680, 0x0ef6d04c},
+ {0x00001690, 0x10a0540b},
+ {0x000016a8, 0xe301010a},
+ {0x00001700, 0x69053460},
+ {0x00001710, 0xa030101c},
+ {0x00001738, 0xdf808444},
+ {0x0000173c, 0xc8422204},
+ {0x00001740, 0x0180088c},
+ {0x00001774, 0x00000004},
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg x4_port3_config_regs[] = {
+ {0x00000000, 0x00000000}
+};
+
+static const struct phy_reg *x1_config_regs[4] = {
+ x1_port0_config_regs,
+ x1_port1_config_regs,
+ x1_port2_config_regs,
+ x1_port3_config_regs
+};
+
+static const struct phy_reg *x2_config_regs[4] = {
+ x2_port0_config_regs,
+ x2_port1_config_regs,
+ x2_port2_config_regs,
+ x2_port3_config_regs
+};
+
+static const struct phy_reg *x4_config_regs[4] = {
+ x4_port0_config_regs,
+ x4_port1_config_regs,
+ x4_port2_config_regs,
+ x4_port3_config_regs
+};
+
+static const struct phy_reg **config_regs[3] = {
+ x1_config_regs,
+ x2_config_regs,
+ x4_config_regs
+};
+
+static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ u32 val;
+ int ret;
+
+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
+ val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN;
+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
+
+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
+ val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK,
+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
+ if (ret)
+ dev_err(dev, "PHY%d powerup ack timeout", id);
+
+ return ret;
+}
+
+static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ u32 val;
+ int ret;
+
+ writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
+ val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK),
+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
+ if (ret)
+ dev_err(dev, "PHY%d powerdown ack timeout", id);
+
+ return ret;
+}
+
+static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ u32 val;
+
+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
+ if (assert)
+ val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET;
+ else
+ val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET);
+
+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
+}
+
+static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ u32 val;
+ int ret;
+
+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
+ val, val & CSI_REG_HUB_GPREG_PHY_READY,
+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
+ if (ret)
+ dev_err(dev, "PHY%d ready ack timeout", id);
+
+ return ret;
+}
+
+static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys)
+{
+ struct ipu6_bus_device *adev = isys->adev;
+ struct ipu6_device *isp = adev->isp;
+ void __iomem *isp_base = isp->base;
+ struct sensor_async_sd *s_asd;
+ struct v4l2_async_connection *asc;
+ void __iomem *phy_base;
+ unsigned int i;
+ u8 phy_id;
+
+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
+ s_asd = container_of(asc, struct sensor_async_sd, asc);
+ phy_id = s_asd->csi2.port / 4;
+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
+
+ for (i = 0; i < ARRAY_SIZE(common_init_regs); i++)
+ writel(common_init_regs[i].val,
+ phy_base + common_init_regs[i].reg);
+ }
+}
+
+static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg)
+{
+ int phy_port;
+ int ret;
+
+ if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1))
+ return -EINVAL;
+
+ /* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */
+ /* normalize driver port number */
+ phy_port = cfg->port % 4;
+
+ /* swap port number only for A and B */
+ if (phy_port == 0)
+ phy_port = 1;
+ else if (phy_port == 1)
+ phy_port = 0;
+
+ ret = phy_port;
+
+ /* check validity per lane configuration */
+ if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2))
+ ret = -EINVAL;
+ else if ((cfg->nlanes == 2 || cfg->nlanes == 1) &&
+ !(phy_port >= 0 && phy_port <= 3))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu6_bus_device *adev = isys->adev;
+ const struct phy_reg **phy_config_regs;
+ struct ipu6_device *isp = adev->isp;
+ void __iomem *isp_base = isp->base;
+ struct sensor_async_sd *s_asd;
+ struct ipu6_isys_csi2_config cfg;
+ struct v4l2_async_connection *asc;
+ int phy_port, phy_id;
+ unsigned int i;
+ void __iomem *phy_base;
+
+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
+ s_asd = container_of(asc, struct sensor_async_sd, asc);
+ cfg.port = s_asd->csi2.port;
+ cfg.nlanes = s_asd->csi2.nlanes;
+ phy_port = ipu6_isys_driver_port_to_phy_port(&cfg);
+ if (phy_port < 0) {
+ dev_err(dev, "invalid port %d for lane %d", cfg.port,
+ cfg.nlanes);
+ return -ENXIO;
+ }
+
+ phy_id = cfg.port / 4;
+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
+ dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id,
+ cfg.nlanes);
+
+ phy_config_regs = config_regs[cfg.nlanes / 2];
+ cfg.port = phy_port;
+ for (i = 0; phy_config_regs[cfg.port][i].reg; i++)
+ writel(phy_config_regs[cfg.port][i].val,
+ phy_base + phy_config_regs[cfg.port][i].reg);
+ }
+
+ return 0;
+}
+
+#define CSI_MCD_PHY_NUM 2
+static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM];
+
+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ u8 port, phy_id;
+ refcount_t *ref;
+ int ret;
+
+ port = cfg->port;
+ phy_id = port / 4;
+
+ ref = &phy_power_ref_count[phy_id];
+
+ dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port,
+ cfg->nlanes);
+
+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
+ dev_warn(dev, "invalid port ID %d\n", port);
+ return -EINVAL;
+ }
+
+ if (on) {
+ if (refcount_read(ref)) {
+ dev_dbg(dev, "for phy %d is already UP", phy_id);
+ refcount_inc(ref);
+ return 0;
+ }
+
+ ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id);
+ if (ret)
+ return ret;
+
+ ipu6_isys_mcd_phy_reset(isys, phy_id, 0);
+ ipu6_isys_mcd_phy_common_init(isys);
+
+ ret = ipu6_isys_mcd_phy_config(isys);
+ if (ret)
+ return ret;
+
+ ipu6_isys_mcd_phy_reset(isys, phy_id, 1);
+ ret = ipu6_isys_mcd_phy_ready(isys, phy_id);
+ if (ret)
+ return ret;
+
+ refcount_set(ref, 1);
+ return 0;
+ }
+
+ if (!refcount_dec_and_test(ref))
+ return 0;
+
+ return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
new file mode 100644
index 000000000000..40a8ebfcfce2
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
@@ -0,0 +1,810 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-fw-isys.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-video.h"
+
+static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ u32 size = ipu6_isys_get_data_size(av);
+
+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */
+ if (!*num_planes) {
+ sizes[0] = size;
+ } else if (sizes[0] < size) {
+ dev_dbg(dev, "%s: queue setup: size %u < %u\n",
+ av->vdev.name, sizes[0], size);
+ return -EINVAL;
+ }
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int ipu6_isys_buf_prepare(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 device *dev = &av->isys->adev->auxdev.dev;
+ u32 bytesperline = ipu6_isys_get_bytes_per_line(av);
+ u32 height = ipu6_isys_get_frame_height(av);
+ u32 size = ipu6_isys_get_data_size(av);
+
+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
+ av->vdev.name, size, vb2_plane_size(vb, 0));
+
+ if (size > vb2_plane_size(vb, 0))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, bytesperline * height);
+
+ return 0;
+}
+
+/*
+ * Queue a buffer list back to incoming or active queues. The buffers
+ * are removed from the buffer list.
+ */
+void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
+ unsigned long op_flags,
+ enum vb2_buffer_state state)
+{
+ struct ipu6_isys_buffer *ib, *ib_safe;
+ unsigned long flags;
+ bool first = true;
+
+ if (!bl)
+ return;
+
+ WARN_ON_ONCE(!bl->nbufs);
+ WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE &&
+ op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING);
+
+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
+ struct ipu6_isys_video *av;
+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+ struct ipu6_isys_queue *aq =
+ vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct device *dev;
+
+ av = ipu6_isys_queue_to_video(aq);
+ dev = &av->isys->adev->auxdev.dev;
+ spin_lock_irqsave(&aq->lock, flags);
+ list_del(&ib->head);
+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE)
+ list_add(&ib->head, &aq->active);
+ else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING)
+ list_add_tail(&ib->head, &aq->incoming);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE)
+ vb2_buffer_done(vb, state);
+
+ if (first) {
+ dev_dbg(dev,
+ "queue buf list %p flags %lx, s %d, %d bufs\n",
+ bl, op_flags, state, bl->nbufs);
+ first = false;
+ }
+
+ bl->nbufs--;
+ }
+
+ WARN_ON(bl->nbufs);
+}
+
+/*
+ * flush_firmware_streamon_fail() - Flush in cases where requests may
+ * have been queued to firmware and the *firmware streamon fails for a
+ * reason or another.
+ */
+static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream)
+{
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_queue *aq;
+ unsigned long flags;
+
+ lockdep_assert_held(&stream->mutex);
+
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ struct ipu6_isys_buffer *ib, *ib_safe;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
+ struct vb2_buffer *vb =
+ ipu6_isys_buffer_to_vb2_buffer(ib);
+
+ list_del(&ib->head);
+ if (av->streaming) {
+ dev_dbg(dev,
+ "%s: queue buffer %u back to incoming\n",
+ av->vdev.name, vb->index);
+ /* Queue already streaming, return to driver. */
+ list_add(&ib->head, &aq->incoming);
+ continue;
+ }
+ /* Queue not yet streaming, return to user. */
+ dev_dbg(dev, "%s: return %u back to videobuf2\n",
+ av->vdev.name, vb->index);
+ vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib),
+ VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&aq->lock, flags);
+ }
+}
+
+/*
+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
+ * that contains one entry from each video buffer queue. If a buffer can't be
+ * obtained from every queue, the buffers are returned back to the queue.
+ */
+static int buffer_list_get(struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl)
+{
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_queue *aq;
+ unsigned long flags;
+ unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING;
+
+ bl->nbufs = 0;
+ INIT_LIST_HEAD(&bl->head);
+
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu6_isys_buffer *ib;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ if (list_empty(&aq->incoming)) {
+ spin_unlock_irqrestore(&aq->lock, flags);
+ if (!list_empty(&bl->head))
+ ipu6_isys_buffer_list_queue(bl, buf_flag, 0);
+ return -ENODATA;
+ }
+
+ ib = list_last_entry(&aq->incoming,
+ struct ipu6_isys_buffer, head);
+
+ dev_dbg(dev, "buffer: %s: buffer %u\n",
+ ipu6_isys_queue_to_video(aq)->vdev.name,
+ ipu6_isys_buffer_to_vb2_buffer(ib)->index);
+ list_del(&ib->head);
+ list_add(&ib->head, &bl->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ bl->nbufs++;
+ }
+
+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
+
+ return 0;
+}
+
+static void
+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);
+
+ set->output_pins[aq->fw_output].addr =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ 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, bool error)
+{
+ struct ipu6_isys_stream *stream = av->stream;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_buffer_list __bl;
+ int ret;
+
+ mutex_lock(&stream->isys->stream_mutex);
+ ret = ipu6_isys_video_set_streaming(av, 1, bl);
+ mutex_unlock(&stream->isys->stream_mutex);
+ if (ret)
+ goto out_requeue;
+
+ stream->streaming = 1;
+
+ bl = &__bl;
+
+ do {
+ struct 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);
+ if (ret < 0)
+ break;
+
+ 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));
+
+ return 0;
+
+out_requeue:
+ if (bl && bl->nbufs)
+ ipu6_isys_buffer_list_queue(bl,
+ (IPU6_ISYS_BUFFER_LIST_FL_INCOMING |
+ error) ?
+ IPU6_ISYS_BUFFER_LIST_FL_SET_STATE :
+ 0, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ flush_firmware_streamon_fail(stream);
+
+ return ret;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ 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 media_pipeline *media_pipe =
+ media_entity_pipeline(&av->vdev.entity);
+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
+ struct ipu6_isys_stream *stream = av->stream;
+ struct ipu6_isys_buffer_list bl;
+ struct isys_fw_msgs *msg;
+ unsigned long flags;
+ dma_addr_t dma;
+ int ret;
+
+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
+
+ dma = vb2_dma_contig_plane_dma_addr(vb, 0);
+ dev_dbg(dev, "iova: iova %pad\n", &dma);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ list_add(&ib->head, &aq->incoming);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
+ dev_dbg(dev, "media pipeline is not ready for %s\n",
+ av->vdev.name);
+ return;
+ }
+
+ mutex_lock(&stream->mutex);
+
+ if (stream->nr_streaming != stream->nr_queues) {
+ dev_dbg(dev, "not streaming yet, adding to incoming\n");
+ goto out;
+ }
+
+ /*
+ * We just put one buffer to the incoming list of this queue
+ * (above). Let's see whether all queues in the pipeline would
+ * have a buffer.
+ */
+ ret = buffer_list_get(stream, &bl);
+ if (ret < 0) {
+ dev_dbg(dev, "No buffers available\n");
+ goto out;
+ }
+
+ msg = ipu6_get_fw_msg_buf(stream);
+ if (!msg) {
+ ret = -ENOMEM;
+ 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);
+
+ if (!stream->streaming) {
+ ret = ipu6_isys_stream_start(av, &bl, true);
+ if (ret)
+ dev_err(dev, "stream start failed.\n");
+ goto out;
+ }
+
+ /*
+ * We must queue the buffers in the buffer list to the
+ * appropriate video buffer queues BEFORE passing them to the
+ * firmware since we could get a buffer event back before we
+ * have queued them ourselves to the active queue.
+ */
+ 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);
+ if (ret < 0)
+ dev_err(dev, "send stream capture failed\n");
+
+out:
+ mutex_unlock(&stream->mutex);
+}
+
+static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
+{
+ struct v4l2_mbus_framefmt format;
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct media_pad *remote_pad =
+ media_pad_remote_pad_first(av->vdev.entity.pads);
+ struct v4l2_subdev *sd;
+ u32 r_stream, code;
+ int ret;
+
+ if (!remote_pad)
+ return -ENOTCONN;
+
+ sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
+
+ ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
+ &format);
+
+ if (ret) {
+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
+ sd->entity.name, remote_pad->index, r_stream);
+ return ret;
+ }
+
+ if (format.width != ipu6_isys_get_frame_width(av) ||
+ format.height != ipu6_isys_get_frame_height(av)) {
+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
+ ipu6_isys_get_frame_width(av),
+ ipu6_isys_get_frame_height(av), format.width,
+ format.height);
+ return -EINVAL;
+ }
+
+ code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;
+ if (format.code != code) {
+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
+ code, format.code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void return_buffers(struct ipu6_isys_queue *aq,
+ enum vb2_buffer_state state)
+{
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ struct ipu6_isys_buffer *ib;
+ bool need_reset = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ while (!list_empty(&aq->incoming)) {
+ struct vb2_buffer *vb;
+
+ ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer,
+ head);
+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ vb2_buffer_done(vb, state);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ }
+
+ /*
+ * Something went wrong (FW crash / HW hang / not all buffers
+ * returned from isys) if there are still buffers queued in active
+ * queue. We have to clean up places a bit.
+ */
+ while (!list_empty(&aq->active)) {
+ struct vb2_buffer *vb;
+
+ ib = list_first_entry(&aq->active, struct ipu6_isys_buffer,
+ head);
+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ vb2_buffer_done(vb, state);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ need_reset = true;
+ }
+
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (need_reset) {
+ mutex_lock(&av->isys->mutex);
+ av->isys->need_reset = true;
+ mutex_unlock(&av->isys->mutex);
+ }
+}
+
+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;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ 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_entity *source_entity = NULL;
+ int nr_queues, ret;
+
+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
+ av->vdev.name, ipu6_isys_get_frame_width(av),
+ ipu6_isys_get_frame_height(av), pfmt->css_pixelformat);
+
+ ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
+ if (ret < 0) {
+ dev_dbg(dev, "failed to setup video\n");
+ goto out_return_buffers;
+ }
+
+ ret = ipu6_isys_link_fmt_validate(aq);
+ if (ret) {
+ dev_dbg(dev,
+ "%s: link format validation failed (%d)\n",
+ av->vdev.name, ret);
+ goto out_pipeline_stop;
+ }
+
+ ret = ipu6_isys_fw_open(av->isys);
+ 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_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_set_csi2_streams_status(av, true);
+ ipu6_isys_configure_stream_watermark(av, true);
+ 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, false);
+ 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);
+
+out_return_buffers:
+ return_buffers(aq, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+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;
+
+ ipu6_isys_set_csi2_streams_status(av, false);
+
+ 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);
+ mutex_unlock(&av->isys->stream_mutex);
+
+ stream->nr_streaming--;
+ list_del(&aq->node);
+ 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
+get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream,
+ struct ipu6_fw_isys_resp_info_abi *info)
+{
+ u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0];
+ struct ipu6_isys *isys = stream->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i;
+
+ /*
+ * The timestamp is invalid as no TSC in some FPGA platform,
+ * so get the sequence from pipeline directly in this case.
+ */
+ if (time == 0)
+ return atomic_read(&stream->sequence) - 1;
+
+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
+ if (time == stream->seq[i].timestamp) {
+ dev_dbg(dev, "sof: using seq nr %u for ts %llu\n",
+ stream->seq[i].sequence, time);
+ return stream->seq[i].sequence;
+ }
+
+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
+ dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n",
+ stream->seq[i].sequence, stream->seq[i].timestamp);
+
+ return 0;
+}
+
+static u64 get_sof_ns_delta(struct ipu6_isys_video *av,
+ struct ipu6_fw_isys_resp_info_abi *info)
+{
+ struct ipu6_bus_device *adev = av->isys->adev;
+ struct ipu6_device *isp = adev->isp;
+ u64 delta, tsc_now;
+
+ ipu6_buttress_tsc_read(isp, &tsc_now);
+ if (!tsc_now)
+ return 0;
+
+ delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
+
+ return ipu6_buttress_tsc_ticks_to_ns(delta, isp);
+}
+
+void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
+ struct ipu6_fw_isys_resp_info_abi *info)
+{
+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_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 device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu6_isys_stream *stream = av->stream;
+ u64 ns;
+ u32 sequence;
+
+ ns = ktime_get_ns() - get_sof_ns_delta(av, info);
+ sequence = get_sof_sequence_by_timestamp(stream, info);
+
+ vbuf->vb2_buf.timestamp = ns;
+ vbuf->sequence = sequence;
+
+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
+ av->vdev.name, ktime_get_ns(), sequence);
+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
+ vbuf->vb2_buf.timestamp);
+}
+
+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib)
+{
+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+
+ if (atomic_read(&ib->str2mmio_flag)) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ /*
+ * Operation on buffer is ended with error and will be reported
+ * to the userspace when it is de-queued
+ */
+ atomic_set(&ib->str2mmio_flag, 0);
+ } else {
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+}
+
+void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
+ struct ipu6_fw_isys_resp_info_abi *info)
+{
+ struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq;
+ struct ipu6_isys *isys = stream->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu6_isys_buffer *ib;
+ struct vb2_buffer *vb;
+ unsigned long flags;
+ bool first = true;
+ struct vb2_v4l2_buffer *buf;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ if (list_empty(&aq->active)) {
+ spin_unlock_irqrestore(&aq->lock, flags);
+ dev_err(dev, "active queue empty\n");
+ return;
+ }
+
+ list_for_each_entry_reverse(ib, &aq->active, head) {
+ dma_addr_t addr;
+
+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (info->pin.addr != addr) {
+ if (first)
+ dev_err(dev, "Unexpected buffer address %pad\n",
+ &addr);
+ first = false;
+ continue;
+ }
+
+ if (info->error_info.error ==
+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) {
+ /*
+ * Check for error message:
+ * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO'
+ */
+ atomic_set(&ib->str2mmio_flag, 1);
+ }
+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
+
+ buf = to_vb2_v4l2_buffer(vb);
+ buf->field = V4L2_FIELD_NONE;
+
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ ipu6_isys_buf_calc_sequence_time(ib, info);
+
+ ipu6_isys_queue_buf_done(ib);
+
+ return;
+ }
+
+ dev_err(dev, "Failed to find a matching video buffer");
+
+ spin_unlock_irqrestore(&aq->lock, flags);
+}
+
+static const struct vb2_ops ipu6_isys_queue_ops = {
+ .queue_setup = queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_prepare = ipu6_isys_buf_prepare,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .buf_queue = buf_queue,
+};
+
+int ipu6_isys_queue_init(struct ipu6_isys_queue *aq)
+{
+ struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys;
+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ int ret;
+
+ /* no support for userptr */
+ if (!aq->vbq.io_modes)
+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
+
+ aq->vbq.drv_priv = aq;
+ aq->vbq.ops = &ipu6_isys_queue_ops;
+ aq->vbq.lock = &av->mutex;
+ aq->vbq.mem_ops = &vb2_dma_contig_memops;
+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ aq->vbq.min_queued_buffers = 1;
+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ ret = vb2_queue_init(&aq->vbq);
+ if (ret)
+ return ret;
+
+ aq->dev = &isys->adev->auxdev.dev;
+ aq->vbq.dev = &isys->adev->auxdev.dev;
+ spin_lock_init(&aq->lock);
+ INIT_LIST_HEAD(&aq->active);
+ INIT_LIST_HEAD(&aq->incoming);
+
+ return 0;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
new file mode 100644
index 000000000000..95cfd4869d93
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_ISYS_QUEUE_H
+#define IPU6_ISYS_QUEUE_H
+
+#include <linux/container_of.h>
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+
+#include <media/videobuf2-v4l2.h>
+
+#include "ipu6-fw-isys.h"
+#include "ipu6-isys-video.h"
+
+struct ipu6_isys_stream;
+
+struct ipu6_isys_queue {
+ struct vb2_queue vbq;
+ struct list_head node;
+ struct device *dev;
+ /*
+ * @lock: serialise access to queued and pre_streamon_queued
+ */
+ spinlock_t lock;
+ struct list_head active;
+ struct list_head incoming;
+ unsigned int fw_output;
+};
+
+struct ipu6_isys_buffer {
+ struct list_head head;
+ atomic_t str2mmio_flag;
+};
+
+struct ipu6_isys_video_buffer {
+ struct vb2_v4l2_buffer vb_v4l2;
+ struct ipu6_isys_buffer ib;
+};
+
+#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0)
+#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1)
+#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2)
+
+struct ipu6_isys_buffer_list {
+ struct list_head head;
+ unsigned int nbufs;
+};
+
+#define vb2_queue_to_isys_queue(__vb2) \
+ container_of(__vb2, struct ipu6_isys_queue, vbq)
+
+#define ipu6_isys_to_isys_video_buffer(__ib) \
+ container_of(__ib, struct ipu6_isys_video_buffer, ib)
+
+#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \
+ container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2)
+
+#define ipu6_isys_buffer_to_vb2_buffer(__ib) \
+ (&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
+
+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,
+ struct ipu6_isys_buffer_list *bl);
+void
+ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
+ struct ipu6_fw_isys_resp_info_abi *info);
+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib);
+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);
+#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
new file mode 100644
index 000000000000..0a06de5c739c
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+
+#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-subdev.h"
+
+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_META_24:
+ return 24;
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_META_16:
+ return 16;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_META_12:
+ return 12;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_META_10:
+ return 10;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_META_8:
+ return 8;
+ default:
+ WARN_ON(1);
+ return 8;
+ }
+}
+
+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ return MIPI_CSI2_DT_RGB565;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return MIPI_CSI2_DT_RGB888;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ return MIPI_CSI2_DT_YUV422_8B;
+ case MEDIA_BUS_FMT_SBGGR16_1X16:
+ case MEDIA_BUS_FMT_SGBRG16_1X16:
+ case MEDIA_BUS_FMT_SGRBG16_1X16:
+ case MEDIA_BUS_FMT_SRGGB16_1X16:
+ return MIPI_CSI2_DT_RAW16;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ return MIPI_CSI2_DT_RAW12;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ return MIPI_CSI2_DT_RAW10;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ return MIPI_CSI2_DT_RAW8;
+ default:
+ /* return unavailable MIPI data type - 0x3f */
+ WARN_ON(1);
+ return 0x3f;
+ }
+}
+
+bool ipu6_isys_is_bayer_format(u32 code)
+{
+ switch (ipu6_isys_mbus_code_to_mipi(code)) {
+ case MIPI_CSI2_DT_RAW8:
+ case MIPI_CSI2_DT_RAW10:
+ case MIPI_CSI2_DT_RAW12:
+ case MIPI_CSI2_DT_RAW14:
+ case MIPI_CSI2_DT_RAW16:
+ case MIPI_CSI2_DT_RAW20:
+ case MIPI_CSI2_DT_RAW24:
+ case MIPI_CSI2_DT_RAW28:
+ return true;
+ default:
+ return false;
+ }
+}
+
+u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
+{
+ static const u32 code_map[] = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SRGGB16_1X16,
+ MEDIA_BUS_FMT_SGRBG16_1X16,
+ MEDIA_BUS_FMT_SGBRG16_1X16,
+ MEDIA_BUS_FMT_SBGGR16_1X16,
+ };
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(code_map); i++)
+ if (code_map[i] == code)
+ break;
+
+ if (WARN_ON(i == ARRAY_SIZE(code_map)))
+ return code;
+
+ return code_map[i ^ (((y & 1) << 1) | (x & 1))];
+}
+
+int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ u32 code = asd->supported_codes[0];
+ u32 other_pad, other_stream;
+ unsigned int i;
+ int ret;
+
+ /* No transcoding, source and sink formats must match. */
+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
+ sd->entity.num_pads > 1)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,
+ IPU6_ISYS_MAX_WIDTH);
+ format->format.height = clamp(format->format.height,
+ IPU6_ISYS_MIN_HEIGHT,
+ IPU6_ISYS_MAX_HEIGHT);
+
+ for (i = 0; asd->supported_codes[i]; i++) {
+ if (asd->supported_codes[i] == format->format.code) {
+ code = asd->supported_codes[i];
+ break;
+ }
+ }
+ format->format.code = code;
+ format->format.field = V4L2_FIELD_NONE;
+
+ /* Store the format and propagate it to the source pad. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
+ return 0;
+
+ /* propagate format to following source pad */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ format->pad,
+ format->stream,
+ &other_pad,
+ &other_stream);
+ if (ret)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+ /* reset crop */
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->width;
+ crop->height = fmt->height;
+
+ return 0;
+}
+
+int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ const u32 *supported_codes = asd->supported_codes;
+ u32 index;
+
+ for (index = 0; supported_codes[index]; index++) {
+ if (index == code->index) {
+ code->code = supported_codes[index];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 4096,
+ .height = 3072,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .field = V4L2_FIELD_NONE,
+ };
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+}
+
+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_mbus_framefmt *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev_state *state;
+
+ if (!sd || !format)
+ return -EINVAL;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (fmt)
+ *format = *fmt;
+ v4l2_subdev_unlock_state(state);
+
+ return fmt ? 0 : -EINVAL;
+}
+
+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_rect *crop)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_rect *rect;
+
+ if (!sd || !crop)
+ return -EINVAL;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ rect = v4l2_subdev_state_get_crop(state, pad, stream);
+ if (rect)
+ *crop = *rect;
+ v4l2_subdev_unlock_state(state);
+
+ return rect ? 0 : -EINVAL;
+}
+
+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, 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) {
+ source_stream = routes[i].source_stream;
+ break;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return source_stream;
+}
+
+static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route route = {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = 1,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ };
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = &route,
+ };
+
+ return subdev_set_routing(sd, state, &routing);
+}
+
+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ return subdev_set_routing(sd, state, routing);
+}
+
+static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {
+ .init_state = ipu6_isys_subdev_init_state,
+};
+
+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ const struct v4l2_subdev_ops *ops,
+ unsigned int nr_ctrls,
+ unsigned int num_sink_pads,
+ unsigned int num_source_pads)
+{
+ unsigned int num_pads = num_sink_pads + num_source_pads;
+ unsigned int i;
+ int ret;
+
+ v4l2_subdev_init(&asd->sd, ops);
+
+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS |
+ V4L2_SUBDEV_FL_STREAMS;
+ asd->sd.owner = THIS_MODULE;
+ asd->sd.dev = &asd->isys->adev->auxdev.dev;
+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;
+
+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
+ sizeof(*asd->pad), GFP_KERNEL);
+ if (!asd->pad)
+ return -ENOMEM;
+
+ for (i = 0; i < num_sink_pads; i++)
+ asd->pad[i].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ for (i = num_sink_pads; i < num_pads; i++)
+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
+ if (ret)
+ return ret;
+
+ if (asd->ctrl_init) {
+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
+ if (ret)
+ goto out_media_entity_cleanup;
+
+ asd->ctrl_init(&asd->sd);
+ if (asd->ctrl_handler.error) {
+ ret = asd->ctrl_handler.error;
+ goto out_v4l2_ctrl_handler_free;
+ }
+
+ asd->sd.ctrl_handler = &asd->ctrl_handler;
+ }
+
+ asd->source = -1;
+
+ return 0;
+
+out_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
+
+out_media_entity_cleanup:
+ media_entity_cleanup(&asd->sd.entity);
+
+ return ret;
+}
+
+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)
+{
+ media_entity_cleanup(&asd->sd.entity);
+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
new file mode 100644
index 000000000000..9ef8d95464f5
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_ISYS_SUBDEV_H
+#define IPU6_ISYS_SUBDEV_H
+
+#include <linux/container_of.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+struct ipu6_isys;
+
+struct ipu6_isys_subdev {
+ struct v4l2_subdev sd;
+ struct ipu6_isys *isys;
+ u32 const *supported_codes;
+ struct media_pad *pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ void (*ctrl_init)(struct v4l2_subdev *sd);
+ int source; /* SSI stream source; -1 if unset */
+};
+
+#define to_ipu6_isys_subdev(__sd) \
+ container_of(__sd, struct ipu6_isys_subdev, sd)
+
+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code);
+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code);
+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,
+ 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);
+int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt);
+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_mbus_framefmt *format);
+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_rect *crop);
+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing);
+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ const struct v4l2_subdev_ops *ops,
+ unsigned int nr_ctrls,
+ unsigned int num_sink_pads,
+ unsigned int num_source_pads);
+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd);
+#endif /* IPU6_ISYS_SUBDEV_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
new file mode 100644
index 000000000000..c8a33e1e910c
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
@@ -0,0 +1,1420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/align.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-cpd.h"
+#include "ipu6-fw-isys.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-isys-queue.h"
+#include "ipu6-isys-video.h"
+#include "ipu6-platform-regs.h"
+
+const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
+ { V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
+ { V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
+ { V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
+ { V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
+ { V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
+ { V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
+ { V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
+ { V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+ { V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+ { V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+ { V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+ { V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
+ { V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
+ { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565 },
+ { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 },
+ { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8, true },
+ { V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10, true },
+ { V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true },
+ { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true },
+};
+
+static int video_open(struct file *file)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+ struct ipu6_isys *isys = av->isys;
+ struct ipu6_bus_device *adev = isys->adev;
+
+ mutex_lock(&isys->mutex);
+ if (isys->need_reset) {
+ mutex_unlock(&isys->mutex);
+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n");
+ return -EIO;
+ }
+ mutex_unlock(&isys->mutex);
+
+ return v4l2_fh_open(file);
+}
+
+const struct ipu6_isys_pixelformat *
+ipu6_isys_get_isys_format(u32 pixelformat, u32 type)
+{
+ const struct ipu6_isys_pixelformat *default_pfmt = NULL;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+ const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
+
+ if (type && ((!pfmt->is_meta &&
+ type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (pfmt->is_meta &&
+ type != V4L2_BUF_TYPE_META_CAPTURE)))
+ continue;
+
+ if (!default_pfmt)
+ default_pfmt = pfmt;
+
+ if (pfmt->pixelformat != pixelformat)
+ continue;
+
+ return pfmt;
+ }
+
+ return default_pfmt;
+}
+
+static int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));
+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int i, num_found;
+
+ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+ if ((ipu6_isys_pfmts[i].is_meta &&
+ f->type != V4L2_BUF_TYPE_META_CAPTURE) ||
+ (!ipu6_isys_pfmts[i].is_meta &&
+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ continue;
+
+ if (f->mbus_code && f->mbus_code != ipu6_isys_pfmts[i].code)
+ continue;
+
+ if (num_found < f->index) {
+ num_found++;
+ continue;
+ }
+
+ f->flags = 0;
+ f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ unsigned int i;
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+ if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat)
+ continue;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;
+ fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;
+ fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;
+ fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ f->fmt.pix = av->pix_fmt;
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_g_fmt_meta_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ f->fmt.meta = av->meta_fmt;
+
+ return 0;
+}
+
+static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type,
+ u32 *format, u32 *width, u32 *height,
+ u32 *bytesperline, u32 *sizeimage)
+{
+ const struct ipu6_isys_pixelformat *pfmt =
+ ipu6_isys_get_isys_format(*format, type);
+
+ *format = pfmt->pixelformat;
+ *width = clamp(*width, IPU6_ISYS_MIN_WIDTH, IPU6_ISYS_MAX_WIDTH);
+ *height = clamp(*height, IPU6_ISYS_MIN_HEIGHT, IPU6_ISYS_MAX_HEIGHT);
+
+ if (pfmt->bpp != pfmt->bpp_packed)
+ *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
+ else
+ *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);
+
+ *bytesperline = ALIGN(*bytesperline, av->isys->line_align);
+
+ /*
+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit
+ * is a power of two, and a line should be transferred as few units
+ * as possible. The result is that up to line length more data than
+ * the image size may be transferred to memory after the image.
+ * Another limitation is the GDA allocation unit size. For low
+ * resolution it gives a bigger number. Use larger one to avoid
+ * memory corruption.
+ */
+ *sizeimage = *bytesperline * *height +
+ max(*bytesperline,
+ av->isys->pdata->ipdata->isys_dma_overshoot);
+}
+
+static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av,
+ struct v4l2_format *f)
+{
+ ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,
+ &f->fmt.pix.width, &f->fmt.pix.height,
+ &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
+ f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+ f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ if (vb2_is_busy(&av->aq.vbq))
+ return -EBUSY;
+
+ __ipu6_isys_vidioc_try_fmt_vid_cap(av, f);
+
+ return 0;
+}
+
+static int __ipu6_isys_vidioc_try_fmt_meta_cap(struct ipu6_isys_video *av,
+ struct v4l2_format *f)
+{
+ ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.meta.dataformat,
+ &f->fmt.meta.width, &f->fmt.meta.height,
+ &f->fmt.meta.bytesperline,
+ &f->fmt.meta.buffersize);
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_try_fmt_meta_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ __ipu6_isys_vidioc_try_fmt_meta_cap(av, f);
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f);
+ av->pix_fmt = f->fmt.pix;
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_s_fmt_meta_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+
+ if (vb2_is_busy(&av->aq.vbq))
+ return -EBUSY;
+
+ ipu6_isys_vidioc_try_fmt_meta_cap(file, fh, f);
+ av->meta_fmt = f->fmt.meta;
+
+ return 0;
+}
+
+static int ipu6_isys_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+ int ret;
+
+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
+
+ ret = vb2_queue_change_type(&av->aq.vbq, p->type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_reqbufs(file, priv, p);
+}
+
+static int ipu6_isys_vidioc_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct ipu6_isys_video *av = video_drvdata(file);
+ int ret;
+
+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
+
+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_create_bufs(file, priv, p);
+}
+
+static int link_validate(struct media_link *link)
+{
+ struct ipu6_isys_video *av =
+ container_of(link->sink, struct ipu6_isys_video, pad);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct v4l2_subdev_state *s_state;
+ struct v4l2_subdev *s_sd;
+ struct v4l2_mbus_framefmt *s_fmt;
+ struct media_pad *s_pad;
+ u32 s_stream, code;
+ int ret = -EPIPE;
+
+ if (!link->source->entity)
+ return ret;
+
+ s_sd = media_entity_to_v4l2_subdev(link->source->entity);
+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
+ if (!s_state)
+ return ret;
+
+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name);
+
+ s_pad = media_pad_remote_pad_first(&av->pad);
+ s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
+
+ v4l2_subdev_lock_state(s_state);
+
+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
+ if (!s_fmt) {
+ dev_err(dev, "failed to get source pad format\n");
+ goto unlock;
+ }
+
+ code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;
+
+ if (s_fmt->width != ipu6_isys_get_frame_width(av) ||
+ s_fmt->height != ipu6_isys_get_frame_height(av) ||
+ s_fmt->code != code) {
+ dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
+ s_fmt->width, s_fmt->height, s_fmt->code,
+ ipu6_isys_get_frame_width(av),
+ ipu6_isys_get_frame_height(av), code);
+ goto unlock;
+ }
+
+ v4l2_subdev_unlock_state(s_state);
+
+ return 0;
+unlock:
+ v4l2_subdev_unlock_state(s_state);
+
+ return ret;
+}
+
+static void get_stream_opened(struct 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);
+}
+
+static void put_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);
+}
+
+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 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;
+ struct device *dev = &isys->adev->auxdev.dev;
+ int input_pins = cfg->nof_input_pins++;
+ int output_pins;
+ u32 src_stream;
+ int ret;
+
+ src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
+ ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
+ &fmt);
+ if (ret < 0) {
+ dev_err(dev, "can't get stream format (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
+ &v4l2_crop);
+ if (ret < 0) {
+ dev_err(dev, "can't get stream crop (%d)\n", ret);
+ return ret;
+ }
+
+ input_pin = &cfg->input_pins[input_pins];
+ input_pin->input_res.width = fmt.width;
+ input_pin->input_res.height = fmt.height;
+ input_pin->dt = av->dt;
+ input_pin->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[output_pins].pin_ready = ipu6_isys_queue_buf_ready;
+ stream->output_pins[output_pins].aq = 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 start_stream_firmware(struct ipu6_isys_video *av,
+ struct ipu6_isys_buffer_list *bl)
+{
+ 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 isys_fw_msgs *msg = NULL;
+ struct ipu6_isys_queue *aq;
+ int ret, retout, tout;
+ u16 send_type;
+
+ 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, (u64)stream_cfg);
+ 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);
+ if (ret < 0) {
+ dev_err(dev, "can't open stream (%d)\n", ret);
+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
+ 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, (u64)stream_cfg);
+
+ if (!tout) {
+ dev_err(dev, "stream open time out\n");
+ ret = -ETIMEDOUT;
+ goto out_put_stream_opened;
+ }
+ if (stream->error) {
+ dev_err(dev, "stream open error: %d\n", stream->error);
+ ret = -EIO;
+ goto out_put_stream_opened;
+ }
+ dev_dbg(dev, "start stream: open complete\n");
+
+ 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);
+ }
+
+ reinit_completion(&stream->stream_start_completion);
+
+ 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);
+ }
+
+ if (ret < 0) {
+ dev_err(dev, "can't start streaming (%d)\n", ret);
+ goto out_stream_close;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_start_completion,
+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout) {
+ dev_err(dev, "stream start time out\n");
+ ret = -ETIMEDOUT;
+ goto out_stream_close;
+ }
+ if (stream->error) {
+ dev_err(dev, "stream start error: %d\n", stream->error);
+ ret = -EIO;
+ goto out_stream_close;
+ }
+ dev_dbg(dev, "start stream: complete\n");
+
+ return 0;
+
+out_stream_close:
+ reinit_completion(&stream->stream_close_completion);
+
+ retout = ipu6_fw_isys_simple_cmd(av->isys,
+ stream->stream_handle,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
+ if (retout < 0) {
+ dev_dbg(dev, "can't close stream (%d)\n", retout);
+ goto out_put_stream_opened;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_err(dev, "stream close time out\n");
+ else if (stream->error)
+ dev_err(dev, "stream close error: %d\n", stream->error);
+ else
+ dev_dbg(dev, "stream close complete\n");
+
+out_put_stream_opened:
+ put_stream_opened(av);
+
+ return ret;
+}
+
+static void stop_streaming_firmware(struct ipu6_isys_video *av)
+{
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu6_isys_stream *stream = av->stream;
+ 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);
+
+ if (ret < 0) {
+ dev_err(dev, "can't stop stream (%d)\n", ret);
+ return;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_stop_completion,
+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_warn(dev, "stream stop time out\n");
+ else if (stream->error)
+ dev_warn(dev, "stream stop error: %d\n", stream->error);
+ else
+ dev_dbg(dev, "stop stream: complete\n");
+}
+
+static void close_streaming_firmware(struct ipu6_isys_video *av)
+{
+ struct ipu6_isys_stream *stream = av->stream;
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ 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);
+ if (ret < 0) {
+ dev_err(dev, "can't close stream (%d)\n", ret);
+ return;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_warn(dev, "stream close time out\n");
+ else if (stream->error)
+ dev_warn(dev, "stream close error: %d\n", stream->error);
+ else
+ dev_dbg(dev, "close stream: complete\n");
+
+ put_stream_opened(av);
+}
+
+int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
+ struct media_entity *source_entity,
+ int nr_queues)
+{
+ struct ipu6_isys_stream *stream = av->stream;
+ struct ipu6_isys_csi2 *csi2;
+
+ if (WARN_ON(stream->nr_streaming))
+ return -EINVAL;
+
+ 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;
+ stream->source_entity = source_entity;
+
+ dev_dbg(&av->isys->adev->auxdev.dev,
+ "prepare stream: external entity %s\n",
+ stream->source_entity->name);
+
+ return 0;
+}
+
+void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
+ bool state)
+{
+ 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;
+
+ if (!state)
+ return;
+
+ esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
+
+ 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) {
+ lanes = csi2->nlanes;
+ ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
+ av->source_stream, &format);
+ if (!ret) {
+ bpp = ipu6_isys_mbus_code_to_bpp(format.code);
+ pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
+ }
+ }
+
+ av->watermark.pixel_rate = pixel_rate;
+
+ 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",
+ av->stream->source_entity->name);
+ }
+}
+
+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;
+}
+
+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;
+
+ 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);
+ }
+
+ update_watermark_setting(av->isys);
+}
+
+void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
+{
+ struct device *dev;
+ unsigned int i;
+ unsigned long flags;
+
+ if (!stream) {
+ pr_err("ipu6-isys: no available stream\n");
+ return;
+ }
+
+ 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);
+}
+
+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;
+
+ if (!isys)
+ return NULL;
+
+ 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];
+ 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;
+ }
+ }
+ }
+ 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 (source < 0) {
+ dev_err(&stream->isys->adev->auxdev.dev,
+ "query stream with invalid port number\n");
+ return NULL;
+ }
+
+ spin_lock_irqsave(&isys->streams_lock, flags);
+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
+ if (!isys->streams_ref_count[i])
+ continue;
+
+ if (isys->streams[i].stream_source == source &&
+ isys->streams[i].vc == vc) {
+ stream = &isys->streams[i];
+ isys->streams_ref_count[i]++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+ return stream;
+}
+
+static 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];
+
+ if (pipeline == media_entity_pipeline(&av->vdev.entity))
+ stream_mask |= BIT_ULL(av->source_stream);
+ }
+
+ return stream_mask;
+}
+
+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
+ struct ipu6_isys_buffer_list *bl)
+{
+ 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 v4l2_subdev *ssd;
+ struct media_pad *r_pad;
+ struct media_pad *s_pad;
+ u32 sink_pad, sink_stream;
+ u64 r_stream;
+ u64 stream_mask = 0;
+ int ret = 0;
+
+ dev_dbg(dev, "set stream: %d\n", state);
+
+ if (WARN(!stream->source_entity, "No source entity for stream\n"))
+ return -ENODEV;
+
+ ssd = media_entity_to_v4l2_subdev(stream->source_entity);
+ sd = &stream->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;
+
+ s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]);
+
+ stream_mask = get_stream_mask_by_pipeline(av);
+ if (!state) {
+ stop_streaming_firmware(av);
+
+ /* stop external sub-device now. */
+ dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask,
+ ssd->name);
+ ret = v4l2_subdev_disable_streams(ssd, s_pad->index,
+ stream_mask);
+ if (ret) {
+ dev_err(dev, "disable streams of %s failed with %d\n",
+ ssd->name, ret);
+ return ret;
+ }
+
+ /* stop sub-device which connects with video */
+ dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name,
+ r_pad->index);
+ ret = v4l2_subdev_call(sd, video, s_stream, state);
+ if (ret) {
+ dev_err(dev, "stream off %s failed with %d\n", sd->name,
+ ret);
+ return ret;
+ }
+ close_streaming_firmware(av);
+ } else {
+ ret = start_stream_firmware(av, bl);
+ if (ret) {
+ dev_err(dev, "start stream of firmware failed\n");
+ return ret;
+ }
+
+ /* start sub-device which connects with video */
+ dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index);
+ ret = v4l2_subdev_call(sd, video, s_stream, state);
+ if (ret) {
+ dev_err(dev, "stream on %s failed with %d\n", sd->name,
+ ret);
+ goto out_media_entity_stop_streaming_firmware;
+ }
+
+ /* start external sub-device now. */
+ dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask,
+ ssd->name);
+ ret = v4l2_subdev_enable_streams(ssd, s_pad->index,
+ stream_mask);
+ if (ret) {
+ dev_err(dev,
+ "enable streams 0x%llx of %s failed with %d\n",
+ stream_mask, stream->source_entity->name, ret);
+ goto out_media_entity_stop_streaming;
+ }
+ }
+
+ av->streaming = state;
+
+ return 0;
+
+out_media_entity_stop_streaming:
+ v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream));
+
+out_media_entity_stop_streaming_firmware:
+ stop_streaming_firmware(av);
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = {
+ .vidioc_querycap = ipu6_isys_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
+ .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,
+ .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = ipu6_isys_vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = ipu6_isys_vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = ipu6_isys_vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_meta_cap = ipu6_isys_vidioc_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = ipu6_isys_vidioc_s_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = ipu6_isys_vidioc_try_fmt_meta_cap,
+ .vidioc_reqbufs = ipu6_isys_vidioc_reqbufs,
+ .vidioc_create_bufs = ipu6_isys_vidioc_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct media_entity_operations entity_ops = {
+ .link_validate = link_validate,
+};
+
+static const struct v4l2_file_operations isys_fops = {
+ .owner = THIS_MODULE,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .open = video_open,
+ .release = vb2_fop_release,
+};
+
+int 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_entity **source_entity, 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 ipu6_isys_subdev *asd;
+ struct v4l2_subdev *remote_sd;
+ struct media_pipeline *pipeline;
+ struct media_pad *source_pad, *remote_pad;
+ int ret = -EINVAL;
+
+ *nr_queues = 0;
+
+ remote_pad = media_pad_remote_pad_unique(&av->pad);
+ if (IS_ERR(remote_pad)) {
+ dev_dbg(dev, "failed to get remote pad\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ asd = to_ipu6_isys_subdev(remote_sd);
+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
+ if (!source_pad) {
+ dev_dbg(dev, "No external source entity\n");
+ return -ENODEV;
+ }
+
+ *source_entity = source_pad->entity;
+
+ /* 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_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;
+ }
+
+ pipeline = media_entity_pipeline(&av->vdev.entity);
+ if (!pipeline)
+ ret = video_device_pipeline_alloc_start(&av->vdev);
+ else
+ ret = video_device_pipeline_start(&av->vdev, pipeline);
+ if (ret < 0) {
+ dev_dbg(dev, "media pipeline start failed\n");
+ return ret;
+ }
+
+ av->stream = 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
+ * is expected to assign isys field and set the name of the video
+ * device.
+ */
+int ipu6_isys_video_init(struct ipu6_isys_video *av)
+{
+ struct v4l2_format format = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = 1920,
+ .height = 1080,
+ },
+ };
+ struct v4l2_format format_meta = {
+ .type = V4L2_BUF_TYPE_META_CAPTURE,
+ .fmt.meta = {
+ .width = 1920,
+ .height = 4,
+ },
+ };
+ int ret;
+
+ mutex_init(&av->mutex);
+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
+ av->vdev.vfl_dir = VFL_DIR_RX;
+
+ ret = ipu6_isys_queue_init(&av->aq);
+ if (ret)
+ goto out_free_watermark;
+
+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
+ if (ret)
+ goto out_vb2_queue_release;
+
+ av->vdev.entity.ops = &entity_ops;
+ av->vdev.release = video_device_release_empty;
+ av->vdev.fops = &isys_fops;
+ av->vdev.v4l2_dev = &av->isys->v4l2_dev;
+ if (!av->vdev.ioctl_ops)
+ av->vdev.ioctl_ops = &ipu6_v4l2_ioctl_ops;
+ av->vdev.queue = &av->aq.vbq;
+ av->vdev.lock = &av->mutex;
+
+ __ipu6_isys_vidioc_try_fmt_vid_cap(av, &format);
+ av->pix_fmt = format.fmt.pix;
+ __ipu6_isys_vidioc_try_fmt_meta_cap(av, &format_meta);
+ av->meta_fmt = format_meta.fmt.meta;
+
+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
+ video_set_drvdata(&av->vdev, av);
+
+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto out_media_entity_cleanup;
+
+ return ret;
+
+out_media_entity_cleanup:
+ vb2_video_unregister_device(&av->vdev);
+ media_entity_cleanup(&av->vdev.entity);
+
+out_vb2_queue_release:
+ vb2_queue_release(&av->aq.vbq);
+
+out_free_watermark:
+ mutex_destroy(&av->mutex);
+
+ return ret;
+}
+
+void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
+{
+ vb2_video_unregister_device(&av->vdev);
+ media_entity_cleanup(&av->vdev.entity);
+ mutex_destroy(&av->mutex);
+}
+
+u32 ipu6_isys_get_format(struct ipu6_isys_video *av)
+{
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return av->pix_fmt.pixelformat;
+
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
+ return av->meta_fmt.dataformat;
+
+ return 0;
+}
+
+u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av)
+{
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return av->pix_fmt.sizeimage;
+
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
+ return av->meta_fmt.buffersize;
+
+ return 0;
+}
+
+u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av)
+{
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return av->pix_fmt.bytesperline;
+
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
+ return av->meta_fmt.bytesperline;
+
+ return 0;
+}
+
+u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av)
+{
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return av->pix_fmt.width;
+
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
+ return av->meta_fmt.width;
+
+ return 0;
+}
+
+u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av)
+{
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return av->pix_fmt.height;
+
+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
+ return av->meta_fmt.height;
+
+ return 0;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
new file mode 100644
index 000000000000..1d945be2b879
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_ISYS_VIDEO_H
+#define IPU6_ISYS_VIDEO_H
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+
+#include "ipu6-isys-queue.h"
+
+#define IPU6_ISYS_OUTPUT_PINS 11
+#define IPU6_ISYS_MAX_PARALLEL_SOF 2
+
+struct file;
+struct ipu6_isys;
+struct ipu6_isys_csi2;
+struct ipu6_isys_subdev;
+
+struct ipu6_isys_pixelformat {
+ u32 pixelformat;
+ u32 bpp;
+ u32 bpp_packed;
+ u32 code;
+ u32 css_pixelformat;
+ bool is_meta;
+};
+
+struct sequence_info {
+ unsigned int sequence;
+ u64 timestamp;
+};
+
+struct output_pin_data {
+ void (*pin_ready)(struct ipu6_isys_stream *stream,
+ struct ipu6_fw_isys_resp_info_abi *info);
+ struct ipu6_isys_queue *aq;
+};
+
+/*
+ * Align with firmware stream. Each stream represents a CSI virtual channel.
+ * May map to multiple video devices
+ */
+struct ipu6_isys_stream {
+ struct mutex mutex;
+ struct media_entity *source_entity;
+ atomic_t sequence;
+ 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 completion stream_open_completion;
+ struct completion stream_close_completion;
+ struct completion stream_start_completion;
+ struct completion stream_stop_completion;
+ struct ipu6_isys *isys;
+
+ struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS];
+ int error;
+ 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;
+ /* Serialise access to other fields in the struct. */
+ struct mutex mutex;
+ struct media_pad pad;
+ struct video_device vdev;
+ struct v4l2_pix_format pix_fmt;
+ struct v4l2_meta_format meta_fmt;
+ struct ipu6_isys *isys;
+ 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) \
+ container_of(__aq, struct ipu6_isys_video, aq)
+
+extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[];
+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_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_entity **source_entity, int *nr_queues);
+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,
+ bool state);
+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);
+u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av);
+u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av);
+u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av);
+
+#endif /* IPU6_ISYS_VIDEO_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c
new file mode 100644
index 000000000000..5992138c7290
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c
@@ -0,0 +1,1367 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <media/ipu-bridge.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-cpd.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-mmu.h"
+#include "ipu6-platform-buttress-regs.h"
+#include "ipu6-platform-isys-csi2-reg.h"
+#include "ipu6-platform-regs.h"
+
+#define IPU6_BUTTRESS_FABIC_CONTROL 0x68
+#define GDA_ENABLE_IWAKE_INDEX 2
+#define GDA_IWAKE_THRESHOLD_INDEX 1
+#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX 0
+#define GDA_MEMOPEN_THRESHOLD_INDEX 3
+#define DEFAULT_DID_RATIO 90
+#define DEFAULT_IWAKE_THRESHOLD 0x42
+#define DEFAULT_MEM_OPEN_TIME 10
+#define ONE_THOUSAND_MICROSECOND 1000
+/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */
+#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE 0x800
+
+/* LTR & DID value are 10 bit at most */
+#define LTR_DID_VAL_MAX 1023
+#define LTR_DEFAULT_VALUE 0x70503c19
+#define FILL_TIME_DEFAULT_VALUE 0xfff0783c
+#define LTR_DID_PKGC_2R 20
+#define LTR_SCALE_DEFAULT 5
+#define LTR_SCALE_1024NS 2
+#define DID_SCALE_1US 2
+#define DID_SCALE_32US 3
+#define REG_PKGC_PMON_CFG 0xb00
+
+#define VAL_PKGC_PMON_CFG_RESET 0x38
+#define VAL_PKGC_PMON_CFG_START 0x7
+
+#define IS_PIXEL_BUFFER_PAGES 0x80
+/*
+ * when iwake mode is disabled, the critical threshold is statically set
+ * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4
+ */
+#define CRITICAL_THRESHOLD_IWAKE_DISABLE (IS_PIXEL_BUFFER_PAGES * 3 / 4)
+
+union fabric_ctrl {
+ struct {
+ u16 ltr_val : 10;
+ u16 ltr_scale : 3;
+ u16 reserved : 3;
+ u16 did_val : 10;
+ u16 did_scale : 3;
+ u16 reserved2 : 1;
+ u16 keep_power_in_D0 : 1;
+ u16 keep_power_override : 1;
+ } bits;
+ u32 value;
+};
+
+enum ltr_did_type {
+ LTR_IWAKE_ON,
+ LTR_IWAKE_OFF,
+ LTR_ISYS_ON,
+ LTR_ISYS_OFF,
+ LTR_ENHANNCE_IWAKE,
+ LTR_TYPE_MAX
+};
+
+#define ISYS_PM_QOS_VALUE 300
+
+static int isys_isr_one(struct ipu6_bus_device *adev);
+
+static int
+isys_complete_ext_device_registration(struct ipu6_isys *isys,
+ struct v4l2_subdev *sd,
+ struct ipu6_isys_csi2_config *csi2)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+
+ if (i == sd->entity.num_pads) {
+ dev_warn(dev, "no src pad in external entity\n");
+ ret = -ENOENT;
+ goto unregister_subdev;
+ }
+
+ ret = media_create_pad_link(&sd->entity, i,
+ &isys->csi2[csi2->port].asd.sd.entity,
+ 0, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_warn(dev, "can't create link\n");
+ goto unregister_subdev;
+ }
+
+ isys->csi2[csi2->port].nlanes = csi2->nlanes;
+
+ return 0;
+
+unregister_subdev:
+ v4l2_device_unregister_subdev(sd);
+
+ 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 =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i;
+
+ for (i = 0; i < csi2->nports; i++)
+ ipu6_isys_csi2_cleanup(&isys->csi2[i]);
+}
+
+static int isys_csi2_register_subdevices(struct ipu6_isys *isys)
+{
+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i;
+ 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);
+ if (ret)
+ goto fail;
+
+ isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
+ }
+
+ return 0;
+
+fail:
+ while (i--)
+ ipu6_isys_csi2_cleanup(&isys->csi2[i]);
+
+ return ret;
+}
+
+static int isys_csi2_create_media_links(struct ipu6_isys *isys)
+{
+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < csi2_pdata->nports; i++) {
+ struct media_entity *sd = &isys->csi2[i].asd.sd.entity;
+
+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) {
+ struct ipu6_isys_video *av = &isys->csi2[i].av[j];
+
+ ret = media_create_pad_link(sd, CSI2_PAD_SRC + j,
+ &av->vdev.entity, 0, 0);
+ if (ret) {
+ dev_err(dev, "CSI2 can't create link\n");
+ return ret;
+ }
+
+ av->csi2 = &isys->csi2[i];
+ }
+ }
+
+ return 0;
+}
+
+static void isys_unregister_video_devices(struct ipu6_isys *isys)
+{
+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i, j;
+
+ for (i = 0; i < csi2_pdata->nports; i++)
+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++)
+ ipu6_isys_video_cleanup(&isys->csi2[i].av[j]);
+}
+
+static int isys_register_video_devices(struct ipu6_isys *isys)
+{
+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < csi2_pdata->nports; i++) {
+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) {
+ struct ipu6_isys_video *av = &isys->csi2[i].av[j];
+
+ snprintf(av->vdev.name, sizeof(av->vdev.name),
+ IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u",
+ i * NR_OF_CSI2_SRC_PADS + j);
+ av->isys = isys;
+ av->aq.vbq.buf_struct_size =
+ sizeof(struct ipu6_isys_video_buffer);
+
+ ret = ipu6_isys_video_init(av);
+ if (ret)
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ while (i--) {
+ while (j--)
+ ipu6_isys_video_cleanup(&isys->csi2[i].av[j]);
+ j = NR_OF_CSI2_SRC_PADS;
+ }
+
+ return ret;
+}
+
+void isys_setup_hw(struct ipu6_isys *isys)
+{
+ void __iomem *base = isys->pdata->base;
+ const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
+ u32 irqs = 0;
+ unsigned int i, nports;
+
+ nports = isys->pdata->ipdata->csi2.nports;
+
+ /* Enable irqs for all MIPI ports */
+ for (i = 0; i < nports; i++)
+ irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
+
+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge);
+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp);
+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask);
+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable);
+ writel(GENMASK(19, 0),
+ base + isys->pdata->ipdata->csi2.ctrl0_irq_clear);
+
+ irqs = ISYS_UNISPART_IRQS;
+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE);
+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE);
+ writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE);
+
+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG);
+
+ /* Write CDC FIFO threshold values for isys */
+ for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++)
+ 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);
+ }
+ }
+ }
+}
+
+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;
+ u32 req_id = index;
+ u32 offset = 0;
+ int ret;
+
+ ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value);
+ if (ret)
+ dev_err(dev, "write %d failed %d", index, ret);
+
+ return ret;
+}
+
+/*
+ * When input system is powered up and before enabling any new sensor capture,
+ * or after disabling any sensor capture the following values need to be set:
+ * LTR_value = LTR(usec) from calculation;
+ * LTR_scale = 2;
+ * DID_value = DID(usec) from calculation;
+ * DID_scale = 2;
+ *
+ * When input system is powered down, the LTR and DID values
+ * must be returned to the default values:
+ * LTR_value = 1023;
+ * LTR_scale = 5;
+ * DID_value = 1023;
+ * DID_scale = 2;
+ */
+static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did,
+ enum ltr_did_type use)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ u16 ltr_val, ltr_scale = LTR_SCALE_1024NS;
+ u16 did_val, did_scale = DID_SCALE_1US;
+ struct ipu6_device *isp = isys->adev->isp;
+ union fabric_ctrl fc;
+
+ switch (use) {
+ case LTR_IWAKE_ON:
+ ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX);
+ did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX);
+ ltr_scale = (ltr == LTR_DID_VAL_MAX &&
+ did == LTR_DID_VAL_MAX) ?
+ LTR_SCALE_DEFAULT : LTR_SCALE_1024NS;
+ break;
+ case LTR_ISYS_ON:
+ case LTR_IWAKE_OFF:
+ ltr_val = LTR_DID_PKGC_2R;
+ did_val = LTR_DID_PKGC_2R;
+ break;
+ case LTR_ISYS_OFF:
+ ltr_val = LTR_DID_VAL_MAX;
+ did_val = LTR_DID_VAL_MAX;
+ ltr_scale = LTR_SCALE_DEFAULT;
+ break;
+ case LTR_ENHANNCE_IWAKE:
+ if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) {
+ ltr_val = LTR_DID_VAL_MAX;
+ did_val = LTR_DID_VAL_MAX;
+ ltr_scale = LTR_SCALE_DEFAULT;
+ } else if (did < ONE_THOUSAND_MICROSECOND) {
+ ltr_val = ltr;
+ did_val = did;
+ } else {
+ ltr_val = ltr;
+ /* div 90% value by 32 to account for scale change */
+ did_val = did / 32;
+ did_scale = DID_SCALE_32US;
+ }
+ break;
+ default:
+ ltr_val = LTR_DID_VAL_MAX;
+ did_val = LTR_DID_VAL_MAX;
+ ltr_scale = LTR_SCALE_DEFAULT;
+ break;
+ }
+
+ fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
+ fc.bits.ltr_val = ltr_val;
+ fc.bits.ltr_scale = ltr_scale;
+ fc.bits.did_val = did_val;
+ fc.bits.did_scale = did_scale;
+
+ dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n",
+ ltr_val, ltr_scale, did_val, did_scale);
+ writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
+}
+
+/*
+ * Driver may clear register GDA_ENABLE_IWAKE before FW configures the
+ * stream for debug purpose. Otherwise driver should not access this register.
+ */
+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);
+ return;
+ }
+
+ ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable);
+ if (!ret)
+ iwake_watermark->iwake_enabled = enable;
+
+ mutex_unlock(&iwake_watermark->mutex);
+}
+
+void 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;
+
+ 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) {
+ 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;
+ }
+ }
+ mutex_unlock(&iwake_watermark->mutex);
+
+ if (!isys_pb_datarate_mbs) {
+ 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;
+ }
+
+ enable_iwake(isys, true);
+ calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs;
+
+ if (isys->pdata->ipdata->enhanced_iwake) {
+ ltr = isys->pdata->ipdata->ltr;
+ did = calc_fill_time_us * DEFAULT_DID_RATIO / 100;
+ ltr_did_type = LTR_ENHANNCE_IWAKE;
+ } else {
+ get_lut_ltrdid(isys, &ltrdid);
+
+ if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0)
+ ltr = 0;
+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1)
+ ltr = ltrdid.lut_ltr.bits.val0;
+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2)
+ ltr = ltrdid.lut_ltr.bits.val1;
+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3)
+ ltr = ltrdid.lut_ltr.bits.val2;
+ else
+ ltr = ltrdid.lut_ltr.bits.val3;
+
+ did = calc_fill_time_us - ltr;
+ ltr_did_type = LTR_IWAKE_ON;
+ }
+
+ set_iwake_ltrdid(isys, ltr, did, ltr_did_type);
+
+ /* calculate iwake threshold with 2KB granularity pages */
+ threshold_bytes = did * isys_pb_datarate_mbs;
+ 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);
+ /* calculate number of pages that will be filled in 10 usec */
+ page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) /
+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE;
+ page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) %
+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0;
+ mem_open_threshold = isys->pdata->ipdata->memopen_threshold;
+ mem_open_threshold = max_t(u32, mem_open_threshold, page_num);
+ dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold);
+ set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX,
+ mem_open_threshold);
+ } else {
+ set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
+ iwake_threshold);
+ }
+
+ iwake_critical_threshold = iwake_threshold +
+ (IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2;
+
+ dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold,
+ iwake_critical_threshold);
+
+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
+ iwake_critical_threshold);
+
+ writel(VAL_PKGC_PMON_CFG_RESET,
+ 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 */
+static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct ipu6_isys *isys =
+ container_of(notifier, struct ipu6_isys, notifier);
+ struct sensor_async_sd *s_asd =
+ container_of(asc, struct sensor_async_sd, asc);
+ int ret;
+
+ ret = ipu_bridge_instantiate_vcm(sd->dev);
+ if (ret) {
+ dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n");
+ return ret;
+ }
+
+ dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n",
+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
+ ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
+ if (ret)
+ return ret;
+
+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct ipu6_isys *isys =
+ container_of(notifier, struct ipu6_isys, notifier);
+
+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations isys_async_ops = {
+ .bound = isys_notifier_bound,
+ .complete = isys_notifier_complete,
+};
+
+#define ISYS_MAX_PORTS 8
+static int isys_notifier_init(struct ipu6_isys *isys)
+{
+ struct ipu6_device *isp = isys->adev->isp;
+ struct device *dev = &isp->pdev->dev;
+ unsigned int i;
+ int ret;
+
+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
+
+ for (i = 0; i < ISYS_MAX_PORTS; i++) {
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct sensor_async_sd *s_asd;
+ struct fwnode_handle *ep;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ continue;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret) {
+ dev_err(dev, "fwnode endpoint parse failed: %d\n", ret);
+ goto err_parse;
+ }
+
+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
+ struct sensor_async_sd);
+ if (IS_ERR(s_asd)) {
+ ret = PTR_ERR(s_asd);
+ dev_err(dev, "add remove fwnode failed: %d\n", ret);
+ goto err_parse;
+ }
+
+ s_asd->csi2.port = vep.base.port;
+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
+
+ dev_dbg(dev, "remote endpoint port %d with %d lanes added\n",
+ s_asd->csi2.port, s_asd->csi2.nlanes);
+
+ fwnode_handle_put(ep);
+
+ continue;
+
+err_parse:
+ fwnode_handle_put(ep);
+ return ret;
+ }
+
+ isys->notifier.ops = &isys_async_ops;
+ ret = v4l2_async_nf_register(&isys->notifier);
+ if (ret) {
+ dev_err(dev, "failed to register async notifier : %d\n", ret);
+ v4l2_async_nf_cleanup(&isys->notifier);
+ }
+
+ return ret;
+}
+
+static void isys_notifier_cleanup(struct ipu6_isys *isys)
+{
+ v4l2_async_nf_unregister(&isys->notifier);
+ v4l2_async_nf_cleanup(&isys->notifier);
+}
+
+static int isys_register_devices(struct ipu6_isys *isys)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct pci_dev *pdev = isys->adev->isp->pdev;
+ int ret;
+
+ isys->media_dev.dev = dev;
+ media_device_pci_init(&isys->media_dev,
+ pdev, IPU6_MEDIA_DEV_MODEL_NAME);
+
+ strscpy(isys->v4l2_dev.name, isys->media_dev.model,
+ sizeof(isys->v4l2_dev.name));
+
+ ret = media_device_register(&isys->media_dev);
+ if (ret < 0)
+ goto out_media_device_unregister;
+
+ isys->v4l2_dev.mdev = &isys->media_dev;
+ isys->v4l2_dev.ctrl_handler = NULL;
+
+ ret = v4l2_device_register(&pdev->dev, &isys->v4l2_dev);
+ if (ret < 0)
+ goto out_media_device_unregister;
+
+ ret = isys_register_video_devices(isys);
+ if (ret)
+ goto out_v4l2_device_unregister;
+
+ ret = isys_csi2_register_subdevices(isys);
+ if (ret)
+ goto out_isys_unregister_video_device;
+
+ ret = isys_csi2_create_media_links(isys);
+ if (ret)
+ goto out_isys_unregister_subdevices;
+
+ ret = isys_notifier_init(isys);
+ if (ret)
+ goto out_isys_unregister_subdevices;
+
+ return 0;
+
+out_isys_unregister_subdevices:
+ isys_csi2_unregister_subdevices(isys);
+
+out_isys_unregister_video_device:
+ isys_unregister_video_devices(isys);
+
+out_v4l2_device_unregister:
+ v4l2_device_unregister(&isys->v4l2_dev);
+
+out_media_device_unregister:
+ media_device_unregister(&isys->media_dev);
+ media_device_cleanup(&isys->media_dev);
+
+ dev_err(dev, "failed to register isys devices\n");
+
+ return ret;
+}
+
+static void isys_unregister_devices(struct ipu6_isys *isys)
+{
+ isys_unregister_video_devices(isys);
+ isys_csi2_unregister_subdevices(isys);
+ v4l2_device_unregister(&isys->v4l2_dev);
+ media_device_unregister(&isys->media_dev);
+ media_device_cleanup(&isys->media_dev);
+}
+
+static int isys_runtime_pm_resume(struct device *dev)
+{
+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu6_device *isp = adev->isp;
+ unsigned long flags;
+ int ret;
+
+ if (!isys)
+ return 0;
+
+ ret = ipu6_mmu_hw_init(adev->mmu);
+ if (ret)
+ return ret;
+
+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
+
+ ret = ipu6_buttress_start_tsc_sync(isp);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&isys->power_lock, flags);
+ isys->power = 1;
+ spin_unlock_irqrestore(&isys->power_lock, flags);
+
+ isys_setup_hw(isys);
+
+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
+
+ return 0;
+}
+
+static int isys_runtime_pm_suspend(struct device *dev)
+{
+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+ struct ipu6_isys *isys;
+ unsigned long flags;
+
+ isys = dev_get_drvdata(dev);
+ if (!isys)
+ return 0;
+
+ spin_lock_irqsave(&isys->power_lock, flags);
+ isys->power = 0;
+ spin_unlock_irqrestore(&isys->power_lock, flags);
+
+ mutex_lock(&isys->mutex);
+ isys->need_reset = false;
+ mutex_unlock(&isys->mutex);
+
+ 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);
+
+ ipu6_mmu_hw_cleanup(adev->mmu);
+
+ return 0;
+}
+
+static int isys_suspend(struct device *dev)
+{
+ struct ipu6_isys *isys = dev_get_drvdata(dev);
+
+ /* If stream is open, refuse to suspend */
+ if (isys->stream_opened)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int isys_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops isys_pm_ops = {
+ .runtime_suspend = isys_runtime_pm_suspend,
+ .runtime_resume = isys_runtime_pm_resume,
+ .suspend = isys_suspend,
+ .resume = isys_resume,
+};
+
+static void isys_remove(struct auxiliary_device *auxdev)
+{
+ struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
+ struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev);
+ struct ipu6_device *isp = adev->isp;
+ struct isys_fw_msgs *fwmsg, *safe;
+ unsigned int i;
+
+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head)
+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
+ fwmsg, fwmsg->dma_addr, 0);
+
+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head)
+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
+ fwmsg, fwmsg->dma_addr, 0);
+
+ isys_unregister_devices(isys);
+ isys_notifier_cleanup(isys);
+
+ 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);
+}
+
+static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct isys_fw_msgs *addr;
+ dma_addr_t dma_addr;
+ unsigned long flags;
+ unsigned int i;
+
+ for (i = 0; i < amount; i++) {
+ addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs),
+ &dma_addr, GFP_KERNEL, 0);
+ if (!addr)
+ break;
+ addr->dma_addr = dma_addr;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ list_add(&addr->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ }
+
+ if (i == amount)
+ return 0;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ while (!list_empty(&isys->framebuflist)) {
+ addr = list_first_entry(&isys->framebuflist,
+ struct isys_fw_msgs, head);
+ list_del(&addr->head);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr,
+ addr->dma_addr, 0);
+ spin_lock_irqsave(&isys->listlock, flags);
+ }
+ spin_unlock_irqrestore(&isys->listlock, flags);
+
+ return -ENOMEM;
+}
+
+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream)
+{
+ struct ipu6_isys *isys = stream->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct isys_fw_msgs *msg;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ if (list_empty(&isys->framebuflist)) {
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ dev_dbg(dev, "Frame list empty\n");
+
+ ret = alloc_fw_msg_bufs(isys, 5);
+ if (ret < 0)
+ return NULL;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ if (list_empty(&isys->framebuflist)) {
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ dev_err(dev, "Frame list empty\n");
+ return NULL;
+ }
+ }
+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
+ list_move(&msg->head, &isys->framebuflist_fw);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
+
+ return msg;
+}
+
+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys)
+{
+ struct isys_fw_msgs *fwmsg, *fwmsg0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head)
+ list_move(&fwmsg->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+}
+
+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data)
+{
+ struct isys_fw_msgs *msg;
+ unsigned long flags;
+ u64 *ptr = (u64 *)data;
+
+ if (!ptr)
+ return;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
+ list_move(&msg->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+}
+
+static int isys_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *auxdev_id)
+{
+ const struct 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)
+ return -EPROBE_DEFER;
+
+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
+ if (!isys)
+ return -ENOMEM;
+
+ adev->auxdrv_data =
+ (const struct ipu6_auxdrv_data *)auxdev_id->driver_data;
+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
+ isys->adev = adev;
+ isys->pdata = adev->pdata;
+ csi2_pdata = &isys->pdata->ipdata->csi2;
+
+ isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports,
+ sizeof(*isys->csi2), GFP_KERNEL);
+ if (!isys->csi2)
+ return -ENOMEM;
+
+ ret = ipu6_mmu_hw_init(adev->mmu);
+ if (ret)
+ return ret;
+
+ /* initial sensor type */
+ 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);
+ mutex_init(&isys->stream_mutex);
+
+ spin_lock_init(&isys->listlock);
+ INIT_LIST_HEAD(&isys->framebuflist);
+ INIT_LIST_HEAD(&isys->framebuflist_fw);
+
+ isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN;
+ isys->icache_prefetch = 0;
+
+ 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;
+ }
+
+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+
+ ret = alloc_fw_msg_bufs(isys, 20);
+ if (ret < 0)
+ goto out_remove_pkg_dir_shared_buffer;
+
+ isys_iwake_watermark_init(isys);
+
+ if (is_ipu6se(adev->isp->hw_ver))
+ isys->phy_set_power = ipu6_isys_jsl_phy_set_power;
+ else if (is_ipu6ep_mtl(adev->isp->hw_ver))
+ isys->phy_set_power = ipu6_isys_dwc_phy_set_power;
+ else
+ isys->phy_set_power = ipu6_isys_mcd_phy_set_power;
+
+ ret = isys_register_devices(isys);
+ if (ret)
+ goto out_remove_pkg_dir_shared_buffer;
+
+ ipu6_mmu_hw_cleanup(adev->mmu);
+
+ return 0;
+
+out_remove_pkg_dir_shared_buffer:
+ 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);
+
+ ipu6_mmu_hw_cleanup(adev->mmu);
+
+ return ret;
+}
+
+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;
+}
+
+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[resp->pin_id].pin_ready)
+ stream->output_pins[resp->pin_id].pin_ready(stream,
+ resp);
+ else
+ dev_warn(&adev->auxdev.dev,
+ "%d:No data pin ready handler 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,
+ .isr_threaded = NULL,
+ .wake_isr_thread = false,
+};
+
+static const struct auxiliary_device_id ipu6_isys_id_table[] = {
+ {
+ .name = "intel_ipu6.isys",
+ .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table);
+
+static struct auxiliary_driver isys_driver = {
+ .name = IPU6_ISYS_NAME,
+ .probe = isys_probe,
+ .remove = isys_remove,
+ .id_table = ipu6_isys_id_table,
+ .driver = {
+ .pm = &isys_pm_ops,
+ },
+};
+
+module_auxiliary_driver(isys_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
+MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
+MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel IPU6 input system driver");
+MODULE_IMPORT_NS(INTEL_IPU6);
+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
new file mode 100644
index 000000000000..86dfc2eff5d0
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 Intel Corporation */
+
+#ifndef IPU6_ISYS_H
+#define IPU6_ISYS_H
+
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+
+#include "ipu6.h"
+#include "ipu6-fw-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu6-isys-video.h"
+
+struct ipu6_bus_device;
+
+#define IPU6_ISYS_ENTITY_PREFIX "Intel IPU6"
+/* FW support max 16 streams */
+#define IPU6_ISYS_MAX_STREAMS 16
+#define ISYS_UNISPART_IRQS (IPU6_ISYS_UNISPART_IRQ_SW | \
+ IPU6_ISYS_UNISPART_IRQ_CSI0 | \
+ IPU6_ISYS_UNISPART_IRQ_CSI1)
+
+#define IPU6_ISYS_2600_MEM_LINE_ALIGN 64
+
+/*
+ * Current message queue configuration. These must be big enough
+ * so that they never gets full. Queues are located in system memory
+ */
+#define IPU6_ISYS_SIZE_RECV_QUEUE 40
+#define IPU6_ISYS_SIZE_SEND_QUEUE 40
+#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5
+#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5
+#define IPU6_ISYS_NUM_RECV_QUEUE 1
+
+#define IPU6_ISYS_MIN_WIDTH 2U
+#define IPU6_ISYS_MIN_HEIGHT 2U
+#define IPU6_ISYS_MAX_WIDTH 4672U
+#define IPU6_ISYS_MAX_HEIGHT 3416U
+
+/* the threshold granularity is 2KB on IPU6 */
+#define IPU6_SRAM_GRANULARITY_SHIFT 11
+#define IPU6_SRAM_GRANULARITY_SIZE 2048
+/* the threshold granularity is 1KB on IPU6SE */
+#define IPU6SE_SRAM_GRANULARITY_SHIFT 10
+#define IPU6SE_SRAM_GRANULARITY_SIZE 1024
+/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */
+#define IPU6_MAX_SRAM_SIZE (200 << 10)
+/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */
+#define IPU6SE_MAX_SRAM_SIZE (96 << 10)
+
+#define IPU6EP_LTR_VALUE 200
+#define IPU6EP_MIN_MEMOPEN_TH 0x4
+#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;
+};
+
+struct sensor_async_sd {
+ struct v4l2_async_connection asc;
+ struct ipu6_isys_csi2_config csi2;
+};
+
+/*
+ * struct ipu6_isys
+ *
+ * @media_dev: Media device
+ * @v4l2_dev: V4L2 device
+ * @adev: ISYS bus device
+ * @power: Is ISYS powered on or not?
+ * @isr_bits: Which bits does the ISR handle?
+ * @power_lock: Serialise access to power (power state in general)
+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
+ * @streams_lock: serialise access to streams
+ * @streams: streams per firmware stream ID
+ * @fwcom: fw communication layer private pointer
+ * or optional external library private pointer
+ * @line_align: line alignment in memory
+ * @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
+ * @csi2: CSI-2 receivers
+ */
+struct ipu6_isys {
+ struct media_device media_dev;
+ 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;
+ unsigned int line_align;
+ u32 phy_termcal_val;
+ bool need_reset;
+ bool icache_prefetch;
+ bool csi2_cse_ipc_not_supported;
+ unsigned int ref_count;
+ unsigned int stream_opened;
+ unsigned int sensor_type;
+
+ struct mutex mutex;
+ struct mutex stream_mutex;
+
+ struct ipu6_isys_pdata *pdata;
+
+ int (*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;
+
+ struct pm_qos_request pm_qos;
+ spinlock_t listlock; /* Protect framebuflist */
+ struct list_head framebuflist;
+ struct list_head framebuflist_fw;
+ struct v4l2_async_notifier notifier;
+ struct isys_iwake_watermark iwake_watermark;
+};
+
+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;
+ struct list_head head;
+ dma_addr_t dma_addr;
+};
+
+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream);
+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data);
+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
+
+extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
+
+void isys_setup_hw(struct ipu6_isys *isys);
+irqreturn_t isys_isr(struct ipu6_bus_device *adev);
+void update_watermark_setting(struct ipu6_isys *isys);
+
+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on);
+
+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on);
+
+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on);
+#endif /* IPU6_ISYS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
new file mode 100644
index 000000000000..c3a20507d6db
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+#include <asm/barrier.h>
+
+#include <linux/align.h>
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/cacheflush.h>
+#include <linux/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>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.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 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)
+{
+ u32 l1_idx;
+
+ dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n");
+
+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
+ u32 l2_idx;
+ u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT;
+
+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval)
+ continue;
+ dev_dbg(mmu_info->dev,
+ "l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n",
+ l1_idx, iova, iova + ISP_PAGE_SIZE,
+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]));
+
+ for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) {
+ u32 *l2_pt = mmu_info->l2_pts[l1_idx];
+ u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT);
+
+ if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval)
+ continue;
+
+ dev_dbg(mmu_info->dev,
+ "\tl2 entry %u; iova 0x%8.8x, phys %pa\n",
+ l2_idx, iova2,
+ TBL_PHYS_ADDR(l2_pt[l2_idx]));
+ }
+ }
+
+ dev_dbg(mmu_info->dev, "end IOMMU page table dump\n");
+}
+#endif /* DEBUG */
+
+static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr)
+{
+ dma_addr_t dma;
+
+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mmu_info->dev, dma))
+ return 0;
+
+ return dma;
+}
+
+static int get_dummy_page(struct ipu6_mmu_info *mmu_info)
+{
+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+
+ if (!pt)
+ return -ENOMEM;
+
+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt);
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map dummy page\n");
+ goto err_free_page;
+ }
+
+ mmu_info->dummy_page = pt;
+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT;
+
+ return 0;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return -ENOMEM;
+}
+
+static void free_dummy_page(struct ipu6_mmu_info *mmu_info)
+{
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_page);
+}
+
+static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+ unsigned int i;
+
+ if (!pt)
+ return -ENOMEM;
+
+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt);
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map l2pt page\n");
+ goto err_free_page;
+ }
+
+ for (i = 0; i < ISP_L2PT_PTES; i++)
+ pt[i] = mmu_info->dummy_page_pteval;
+
+ mmu_info->dummy_l2_pt = pt;
+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT;
+
+ return 0;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return -ENOMEM;
+}
+
+static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
+{
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_l2_pt);
+}
+
+static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+ unsigned int i;
+
+ if (!pt)
+ return NULL;
+
+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt);
+
+ for (i = 0; i < ISP_L1PT_PTES; i++)
+ pt[i] = mmu_info->dummy_l2_pteval;
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map l1pt page\n");
+ goto err_free_page;
+ }
+
+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT;
+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma);
+
+ return pt;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return NULL;
+}
+
+static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ unsigned int i;
+
+ if (!pt)
+ return NULL;
+
+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
+
+ for (i = 0; i < ISP_L1PT_PTES; i++)
+ pt[i] = mmu_info->dummy_page_pteval;
+
+ return pt;
+}
+
+static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ u32 l1_idx = iova >> ISP_L1PT_SHIFT;
+ u32 iova_start = iova;
+ u32 *l2_pt, *l2_virt;
+ unsigned int l2_idx;
+ unsigned long flags;
+ dma_addr_t dma;
+ u32 l1_entry;
+
+ dev_dbg(mmu_info->dev,
+ "mapping l2 page table for l1 index %u (iova %8.8x)\n",
+ l1_idx, (u32)iova);
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+ l1_entry = mmu_info->l1_pt[l1_idx];
+ if (l1_entry == mmu_info->dummy_l2_pteval) {
+ l2_virt = mmu_info->l2_pts[l1_idx];
+ if (likely(!l2_virt)) {
+ l2_virt = alloc_l2_pt(mmu_info);
+ if (!l2_virt) {
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+ return -ENOMEM;
+ }
+ }
+
+ dma = map_single(mmu_info, l2_virt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map l2pt page\n");
+ free_page((unsigned long)l2_virt);
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+ return -EINVAL;
+ }
+
+ l1_entry = dma >> ISP_PADDR_SHIFT;
+
+ dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n",
+ l1_idx, l2_virt);
+ mmu_info->l1_pt[l1_idx] = l1_entry;
+ mmu_info->l2_pts[l1_idx] = l2_virt;
+ clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx],
+ sizeof(mmu_info->l1_pt[l1_idx]));
+ }
+
+ l2_pt = mmu_info->l2_pts[l1_idx];
+
+ dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry);
+
+ paddr = ALIGN(paddr, ISP_PAGE_SIZE);
+
+ l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
+
+ dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx,
+ l2_pt[l2_idx]);
+ if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) {
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+ return -EINVAL;
+ }
+
+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT;
+
+ clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx]));
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+
+ dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx,
+ l2_pt[l2_idx]);
+
+ return 0;
+}
+
+static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE);
+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE);
+
+ dev_dbg(mmu_info->dev,
+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n",
+ iova_start, iova_end, size, paddr);
+
+ return l2_map(mmu_info, iova_start, paddr, size);
+}
+
+static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t dummy, size_t size)
+{
+ u32 l1_idx = iova >> ISP_L1PT_SHIFT;
+ u32 iova_start = iova;
+ unsigned int l2_idx;
+ size_t unmapped = 0;
+ unsigned long flags;
+ u32 *l2_pt;
+
+ dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n",
+ l1_idx, iova);
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) {
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+ dev_err(mmu_info->dev,
+ "unmap iova 0x%8.8lx l1 idx %u which was not mapped\n",
+ iova, l1_idx);
+ return 0;
+ }
+
+ for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
+ (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT)
+ < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) {
+ l2_pt = mmu_info->l2_pts[l1_idx];
+ dev_dbg(mmu_info->dev,
+ "unmap l2 index %u with pteval 0x%10.10llx\n",
+ l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx]));
+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval;
+
+ clflush_cache_range((void *)&l2_pt[l2_idx],
+ sizeof(l2_pt[l2_idx]));
+ unmapped++;
+ }
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+
+ return unmapped << ISP_PAGE_SHIFT;
+}
+
+static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info,
+ unsigned long iova, size_t size)
+{
+ return l2_unmap(mmu_info, iova, 0, size);
+}
+
+static int allocate_trash_buffer(struct ipu6_mmu *mmu)
+{
+ unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE));
+ struct iova *iova;
+ unsigned int i;
+ dma_addr_t dma;
+ unsigned long iova_addr;
+ int ret;
+
+ /* Allocate 8MB in iova range */
+ iova = alloc_iova(&mmu->dmap->iovad, n_pages,
+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
+ if (!iova) {
+ dev_err(mmu->dev, "cannot allocate iova range for trash\n");
+ return -ENOMEM;
+ }
+
+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) {
+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n");
+ ret = -ENOMEM;
+ goto out_free_iova;
+ }
+
+ mmu->pci_trash_page = dma;
+
+ /*
+ * Map the 8MB iova address range to the same physical trash page
+ * mmu->trash_page which is already reserved at the probe
+ */
+ iova_addr = iova->pfn_lo;
+ for (i = 0; i < n_pages; i++) {
+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
+ mmu->pci_trash_page, PAGE_SIZE);
+ if (ret) {
+ dev_err(mmu->dev,
+ "mapping trash buffer range failed\n");
+ goto out_unmap;
+ }
+
+ iova_addr++;
+ }
+
+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo);
+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n",
+ mmu->mmid, (unsigned int)mmu->iova_trash_page);
+ return 0;
+
+out_unmap:
+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+out_free_iova:
+ __free_iova(&mmu->dmap->iovad, iova);
+ return ret;
+}
+
+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;
+ }
+
+ writel(block_addr, mmu_hw->base +
+ mmu_hw->l2_stream_id_reg_offset + 4 * j);
+ }
+ }
+
+ if (!mmu->trash_page) {
+ int ret;
+
+ mmu->trash_page = alloc_page(GFP_KERNEL);
+ if (!mmu->trash_page) {
+ dev_err(mmu->dev, "insufficient memory for trash buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = allocate_trash_buffer(mmu);
+ if (ret) {
+ __free_page(mmu->trash_page);
+ mmu->trash_page = NULL;
+ dev_err(mmu->dev, "trash buffer allocation failed\n");
+ return ret;
+ }
+ }
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ mmu->ready = true;
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6);
+
+static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
+{
+ struct ipu6_mmu_info *mmu_info;
+ int ret;
+
+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL);
+ if (!mmu_info)
+ return NULL;
+
+ mmu_info->aperture_start = 0;
+ mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ?
+ IPU6_MMU_ADDR_BITS :
+ IPU6_MMU_ADDR_BITS_NON_SECURE);
+ mmu_info->pgsize_bitmap = SZ_4K;
+ mmu_info->dev = &isp->pdev->dev;
+
+ ret = get_dummy_page(mmu_info);
+ if (ret)
+ goto err_free_info;
+
+ ret = alloc_dummy_l2_pt(mmu_info);
+ if (ret)
+ goto err_free_dummy_page;
+
+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts));
+ if (!mmu_info->l2_pts)
+ goto err_free_dummy_l2_pt;
+
+ /*
+ * We always map the L1 page table (a single page as well as
+ * the L2 page tables).
+ */
+ mmu_info->l1_pt = alloc_l1_pt(mmu_info);
+ if (!mmu_info->l1_pt)
+ goto err_free_l2_pts;
+
+ spin_lock_init(&mmu_info->lock);
+
+ dev_dbg(mmu_info->dev, "domain initialised\n");
+
+ return mmu_info;
+
+err_free_l2_pts:
+ vfree(mmu_info->l2_pts);
+err_free_dummy_l2_pt:
+ free_dummy_l2_pt(mmu_info);
+err_free_dummy_page:
+ free_dummy_page(mmu_info);
+err_free_info:
+ kfree(mmu_info);
+
+ return NULL;
+}
+
+void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ mmu->ready = false;
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6);
+
+static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
+{
+ struct ipu6_dma_mapping *dmap;
+
+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL);
+ if (!dmap)
+ return NULL;
+
+ dmap->mmu_info = ipu6_mmu_alloc(isp);
+ if (!dmap->mmu_info) {
+ kfree(dmap);
+ return NULL;
+ }
+
+ init_iova_domain(&dmap->iovad, SZ_4K, 1);
+ dmap->mmu_info->dmap = dmap;
+
+ dev_dbg(&isp->pdev->dev, "alloc mapping\n");
+
+ iova_cache_get();
+
+ return dmap;
+}
+
+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
+ dma_addr_t iova)
+{
+ phys_addr_t phy_addr;
+ unsigned long flags;
+ u32 *l2_pt;
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT];
+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT];
+ phy_addr <<= ISP_PAGE_SHIFT;
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+
+ return phy_addr;
+}
+
+static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap,
+ unsigned long addr_merge, size_t size)
+{
+ unsigned int pgsize_idx;
+ size_t pgsize;
+
+ /* Max page size that still fits into 'size' */
+ pgsize_idx = __fls(size);
+
+ if (likely(addr_merge)) {
+ /* Max page size allowed by address */
+ unsigned int align_pgsize_idx = __ffs(addr_merge);
+
+ pgsize_idx = min(pgsize_idx, align_pgsize_idx);
+ }
+
+ pgsize = (1UL << (pgsize_idx + 1)) - 1;
+ pgsize &= pgsize_bitmap;
+
+ WARN_ON(!pgsize);
+
+ /* pick the biggest page */
+ pgsize_idx = __fls(pgsize);
+ pgsize = 1UL << pgsize_idx;
+
+ return pgsize;
+}
+
+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ size_t size)
+{
+ size_t unmapped_page, unmapped = 0;
+ unsigned int min_pagesz;
+
+ /* find out the minimum page size supported */
+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
+
+ /*
+ * The virtual address and the size of the mapping must be
+ * aligned (at least) to the size of the smallest page supported
+ * by the hardware
+ */
+ if (!IS_ALIGNED(iova | size, min_pagesz)) {
+ dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
+ iova, size, min_pagesz);
+ return -EINVAL;
+ }
+
+ /*
+ * Keep iterating until we either unmap 'size' bytes (or more)
+ * or we hit an area that isn't mapped.
+ */
+ while (unmapped < size) {
+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
+ iova, size - unmapped);
+
+ unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize);
+ if (!unmapped_page)
+ break;
+
+ dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n",
+ iova, unmapped_page);
+
+ iova += unmapped_page;
+ unmapped += unmapped_page;
+ }
+
+ return unmapped;
+}
+
+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ unsigned long orig_iova = iova;
+ unsigned int min_pagesz;
+ size_t orig_size = size;
+ int ret = 0;
+
+ if (mmu_info->pgsize_bitmap == 0UL)
+ return -ENODEV;
+
+ /* find out the minimum page size supported */
+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
+
+ /*
+ * both the virtual address and the physical one, as well as
+ * the size of the mapping, must be aligned (at least) to the
+ * size of the smallest page supported by the hardware
+ */
+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
+ dev_err(mmu_info->dev,
+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n",
+ iova, &paddr, size, min_pagesz);
+ return -EINVAL;
+ }
+
+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
+ iova, &paddr, size);
+
+ while (size) {
+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
+ iova | paddr, size);
+
+ dev_dbg(mmu_info->dev,
+ "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
+ iova, &paddr, pgsize);
+
+ ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize);
+ if (ret)
+ break;
+
+ iova += pgsize;
+ paddr += pgsize;
+ size -= pgsize;
+ }
+
+ /* unroll mapping in case something went wrong */
+ if (ret)
+ ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size);
+
+ return ret;
+}
+
+static void ipu6_mmu_destroy(struct ipu6_mmu *mmu)
+{
+ struct ipu6_dma_mapping *dmap = mmu->dmap;
+ struct ipu6_mmu_info *mmu_info = dmap->mmu_info;
+ struct iova *iova;
+ u32 l1_idx;
+
+ if (mmu->iova_trash_page) {
+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page));
+ if (iova) {
+ /* unmap and free the trash buffer iova */
+ ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+ __free_iova(&dmap->iovad, iova);
+ } else {
+ dev_err(mmu->dev, "trash buffer iova not found.\n");
+ }
+
+ mmu->iova_trash_page = 0;
+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ mmu->pci_trash_page = 0;
+ __free_page(mmu->trash_page);
+ }
+
+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) {
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]);
+ }
+ }
+
+ vfree(mmu_info->l2_pts);
+ free_dummy_page(mmu_info);
+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_l2_pt);
+ free_page((unsigned long)mmu_info->l1_pt);
+ kfree(mmu_info);
+}
+
+struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
+ void __iomem *base, int mmid,
+ const struct ipu6_hw_variants *hw)
+{
+ 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;
+ }
+
+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
+ if (!mmu)
+ return ERR_PTR(-ENOMEM);
+
+ mmu->mmid = mmid;
+ mmu->mmu_hw = pdata->mmu_hw;
+ mmu->nr_mmus = hw->nr_mmus;
+ mmu->tlb_invalidate = tlb_invalidate;
+ mmu->ready = false;
+ INIT_LIST_HEAD(&mmu->vma_list);
+ spin_lock_init(&mmu->ready_lock);
+
+ mmu->dmap = alloc_dma_mapping(isp);
+ if (!mmu->dmap) {
+ dev_err(dev, "can't alloc dma mapping\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return mmu;
+}
+
+void ipu6_mmu_cleanup(struct ipu6_mmu *mmu)
+{
+ struct ipu6_dma_mapping *dmap = mmu->dmap;
+
+ ipu6_mmu_destroy(mmu);
+ mmu->dmap = NULL;
+ iova_cache_put();
+ put_iova_domain(&dmap->iovad);
+ kfree(dmap);
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
new file mode 100644
index 000000000000..21cdb0f146eb
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2024 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>
+
+struct device;
+struct page;
+struct ipu6_hw_variants;
+
+struct ipu6_mmu_info {
+ struct device *dev;
+
+ u32 *l1_pt;
+ u32 l1_pt_dma;
+ u32 **l2_pts;
+
+ u32 *dummy_l2_pt;
+ u32 dummy_l2_pteval;
+ void *dummy_page;
+ u32 dummy_page_pteval;
+
+ dma_addr_t aperture_start;
+ dma_addr_t aperture_end;
+ unsigned long pgsize_bitmap;
+
+ spinlock_t lock; /* Serialize access to users */
+ struct ipu6_dma_mapping *dmap;
+};
+
+struct ipu6_mmu {
+ struct list_head node;
+
+ struct ipu6_mmu_hw *mmu_hw;
+ unsigned int nr_mmus;
+ unsigned int mmid;
+
+ phys_addr_t pgtbl;
+ struct device *dev;
+
+ struct ipu6_dma_mapping *dmap;
+ struct list_head vma_list;
+
+ struct page *trash_page;
+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */
+ dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */
+
+ bool ready;
+ spinlock_t ready_lock; /* Serialize access to bool ready */
+
+ void (*tlb_invalidate)(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 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);
+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size);
+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
+ size_t size);
+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
+ dma_addr_t iova);
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
new file mode 100644
index 000000000000..20f27011df43
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2023--2024 Intel Corporation */
+
+#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H
+#define IPU6_PLATFORM_BUTTRESS_REGS_H
+
+#include <linux/bits.h>
+
+/* IS_WORKPOINT_REQ */
+#define IPU6_BUTTRESS_REG_IS_FREQ_CTL 0x34
+/* PS_WORKPOINT_REQ */
+#define IPU6_BUTTRESS_REG_PS_FREQ_CTL 0x38
+
+/* should be tuned for real silicon */
+#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO 0x08
+#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO 0x0a
+#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO 0x0d
+
+#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x10
+#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x0708
+
+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3
+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK GENMASK(4, 3)
+
+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6
+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK GENMASK(7, 6)
+
+#define IPU6_BUTTRESS_PWR_STATE_DN_DONE 0x0
+#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS 0x1
+#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS 0x2
+#define IPU6_BUTTRESS_PWR_STATE_UP_DONE 0x3
+
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0 0x270
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1 0x274
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2 0x278
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3 0x27c
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4 0x280
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5 0x284
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6 0x288
+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7 0x28c
+
+#define BUTTRESS_REG_WDT 0x8
+#define BUTTRESS_REG_BTRS_CTRL 0xc
+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0 BIT(0)
+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1 BIT(1)
+#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND GENMASK(9, 8)
+
+#define BUTTRESS_REG_FW_RESET_CTL 0x30
+#define BUTTRESS_FW_RESET_CTL_START BIT(0)
+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1)
+
+#define BUTTRESS_REG_IS_FREQ_CTL 0x34
+#define BUTTRESS_REG_PS_FREQ_CTL 0x38
+
+#define BUTTRESS_FREQ_CTL_START BIT(31)
+#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL GENMASK(19, 16)
+#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK GENMASK(15, 8)
+#define BUTTRESS_FREQ_CTL_RATIO_MASK GENMASK(7, 0)
+
+#define BUTTRESS_REG_PWR_STATE 0x5c
+
+#define BUTTRESS_PWR_STATE_RESET 0x0
+#define BUTTRESS_PWR_STATE_PWR_ON_DONE 0x1
+#define BUTTRESS_PWR_STATE_PWR_RDY 0x3
+#define BUTTRESS_PWR_STATE_PWR_IDLE 0x4
+
+#define BUTTRESS_PWR_STATE_HH_STATUS_MASK GENMASK(12, 11)
+
+enum {
+ BUTTRESS_PWR_STATE_HH_STATE_IDLE,
+ BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS,
+ BUTTRESS_PWR_STATE_HH_STATE_DONE,
+ BUTTRESS_PWR_STATE_HH_STATE_ERR,
+};
+
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK GENMASK(23, 19)
+
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP 0x1
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK 0x2
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK 0x3
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES 0x4
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1 0x5
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2 0x6
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES 0x7
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP 0x8
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT 0x9
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xa
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED 0xb
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3 0xc
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD 0xd
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT 0xe
+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0 0xf
+
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK GENMASK(28, 24)
+
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY 0x1
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH 0x2
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD 0x3
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH 0x4
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO 0x5
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP 0x6
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK 0x7
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES 0x8
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1 0x9
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2 0xa
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES 0xb
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT 0xc
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT 0xd
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0xf
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED 0x10
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3 0x11
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK 0x12
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND 0x13
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4 0x14
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP 0x15
+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK 0x16
+
+#define BUTTRESS_REG_SECURITY_CTL 0x300
+#define BUTTRESS_REG_SKU 0x314
+#define BUTTRESS_REG_SECURITY_TOUCH 0x318
+#define BUTTRESS_REG_CAMERA_MASK 0x84
+
+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16)
+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0)
+
+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0)
+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1)
+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3)
+
+#define BUTTRESS_REG_FW_SOURCE_BASE_LO 0x78
+#define BUTTRESS_REG_FW_SOURCE_BASE_HI 0x7C
+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x80
+
+#define BUTTRESS_REG_ISR_STATUS 0x90
+#define BUTTRESS_REG_ISR_ENABLED_STATUS 0x94
+#define BUTTRESS_REG_ISR_ENABLE 0x98
+#define BUTTRESS_REG_ISR_CLEAR 0x9C
+
+#define BUTTRESS_ISR_IS_IRQ BIT(0)
+#define BUTTRESS_ISR_PS_IRQ BIT(1)
+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE BIT(2)
+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH BIT(3)
+#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING BIT(4)
+#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING BIT(5)
+#define BUTTRESS_ISR_CSE_CSR_SET BIT(6)
+#define BUTTRESS_ISR_ISH_CSR_SET BIT(7)
+#define BUTTRESS_ISR_SPURIOUS_CMP BIT(8)
+#define BUTTRESS_ISR_WATCHDOG_EXPIRED BIT(9)
+#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ BIT(10)
+#define BUTTRESS_ISR_SAI_VIOLATION BIT(11)
+#define BUTTRESS_ISR_HW_ASSERTION BIT(12)
+#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR BIT(13)
+#define BUTTRESS_ISR_IS_FATAL_MEM_ERR BIT(14)
+#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR BIT(15)
+#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR BIT(16)
+#define BUTTRESS_ISR_PS_FATAL_MEM_ERR BIT(17)
+#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR BIT(18)
+#define BUTTRESS_ISR_PS_FAST_THROTTLE BIT(19)
+#define BUTTRESS_ISR_UFI_ERROR BIT(20)
+
+#define BUTTRESS_REG_IU2CSEDB0 0x100
+
+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31)
+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2
+
+#define BUTTRESS_REG_IU2CSEDATA0 0x104
+
+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1
+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2
+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3
+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16
+
+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0)
+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1)
+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2)
+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4)
+
+#define BUTTRESS_REG_IU2CSECSR 0x108
+
+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5)
+
+#define BUTTRESS_REG_CSE2IUDB0 0x304
+#define BUTTRESS_REG_CSE2IUCSR 0x30C
+#define BUTTRESS_REG_CSE2IUDATA0 0x308
+
+/* 0x20 == NACK, 0xf == unknown command */
+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20
+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0)
+
+#define BUTTRESS_REG_ISH2IUCSR 0x50
+#define BUTTRESS_REG_ISH2IUDB0 0x54
+#define BUTTRESS_REG_ISH2IUDATA0 0x58
+
+#define BUTTRESS_REG_IU2ISHDB0 0x10C
+#define BUTTRESS_REG_IU2ISHDATA0 0x110
+#define BUTTRESS_REG_IU2ISHDATA1 0x114
+#define BUTTRESS_REG_IU2ISHCSR 0x118
+
+#define BUTTRESS_REG_FABRIC_CMD 0x88
+
+#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC BIT(0)
+#define BUTTRESS_FABRIC_CMD_IS_DRAIN BIT(4)
+
+#define BUTTRESS_REG_TSW_CTL 0x120
+#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8)
+
+#define BUTTRESS_REG_TSC_LO 0x164
+#define BUTTRESS_REG_TSC_HI 0x168
+
+#define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
+ BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ)
+
+#define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
+ BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \
+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | \
+ BUTTRESS_ISR_SAI_VIOLATION)
+#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
new file mode 100644
index 000000000000..cc58377534dc
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2023--2024 Intel Corporation */
+
+#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H
+#define IPU6_PLATFORM_ISYS_CSI2_REG_H
+
+#include <linux/bits.h>
+
+#define CSI_REG_BASE 0x220000
+#define CSI_REG_PORT_BASE(id) (CSI_REG_BASE + (id) * 0x1000)
+
+/* CSI Port Genral Purpose Registers */
+#define CSI_REG_PORT_GPREG_SRST 0x0
+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4
+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8
+
+/*
+ * Port IRQs mapping events:
+ * IRQ0 - CSI_FE event
+ * IRQ1 - CSI_SYNC
+ * IRQ2 - S2M_SIDS0TO7
+ * IRQ3 - S2M_SIDS8TO15
+ */
+#define CSI_PORT_REG_BASE_IRQ_CSI 0x80
+#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC 0xA0
+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7 0xC0
+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15 0xE0
+
+#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET 0x0
+#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET 0x4
+#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET 0x8
+#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET 0xc
+#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET 0x10
+#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET 0x14
+
+#define IPU6SE_CSI_RX_ERROR_IRQ_MASK GENMASK(18, 0)
+#define IPU6_CSI_RX_ERROR_IRQ_MASK GENMASK(19, 0)
+
+#define CSI_RX_NUM_ERRORS_IN_IRQ 20
+#define CSI_RX_NUM_IRQ 32
+
+#define IPU_CSI_RX_IRQ_FS_VC(chn) (1 << ((chn) * 2))
+#define IPU_CSI_RX_IRQ_FE_VC(chn) (2 << ((chn) * 2))
+
+/* PPI2CSI */
+#define CSI_REG_PPI2CSI_ENABLE 0x200
+#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF 0x204
+#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK GENMASK(4, 3)
+#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE 0x208
+
+enum CSI_PPI2CSI_CTRL {
+ CSI_PPI2CSI_DISABLE = 0,
+ CSI_PPI2CSI_ENABLE = 1,
+};
+
+/* CSI_FE */
+#define CSI_REG_CSI_FE_ENABLE 0x280
+#define CSI_REG_CSI_FE_MODE 0x284
+#define CSI_REG_CSI_FE_MUX_CTRL 0x288
+#define CSI_REG_CSI_FE_SYNC_CNTR_SEL 0x290
+
+enum CSI_FE_ENABLE_TYPE {
+ CSI_FE_DISABLE = 0,
+ CSI_FE_ENABLE = 1,
+};
+
+enum CSI_FE_MODE_TYPE {
+ CSI_FE_DPHY_MODE = 0,
+ CSI_FE_CPHY_MODE = 1,
+};
+
+enum CSI_FE_INPUT_SELECTOR {
+ CSI_SENSOR_INPUT = 0,
+ CSI_MIPIGEN_INPUT = 1,
+};
+
+enum CSI_FE_SYNC_CNTR_SEL_TYPE {
+ CSI_CNTR_SENSOR_LINE_ID = BIT(0),
+ CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID,
+ CSI_CNTR_SENSOR_FRAME_ID = BIT(1),
+ CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID,
+};
+
+/* CSI HUB General Purpose Registers */
+#define CSI_REG_HUB_GPREG_SRST (CSI_REG_BASE + 0x18000)
+#define CSI_REG_HUB_GPREG_SLV_REG_SRST (CSI_REG_BASE + 0x18004)
+
+#define CSI_REG_HUB_DRV_ACCESS_PORT(id) (CSI_REG_BASE + 0x18018 + (id) * 4)
+#define CSI_REG_HUB_FW_ACCESS_PORT_OFS 0x17000
+#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS 0x16000
+#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id) \
+ (CSI_REG_BASE + (ofs) + (id) * 4)
+
+enum CSI_PORT_CLK_GATING_SWITCH {
+ CSI_PORT_CLK_GATING_OFF = 0,
+ CSI_PORT_CLK_GATING_ON = 1,
+};
+
+#define CSI_REG_BASE_HUB_IRQ 0x18200
+
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238200
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238204
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238208
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23820c
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238210
+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238214
+
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238220
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238224
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238228
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23822c
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238230
+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238234
+
+/* MTL IPU6V6 irq ctrl0 & ctrl1 */
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238700
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238704
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238708
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23870c
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238710
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238714
+
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238720
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238724
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238728
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23872c
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238730
+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238734
+
+/*
+ * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits)
+ * [0] CSI_PORT.IRQ_CTRL0_csi
+ * [1] CSI_PORT.IRQ_CTRL1_csi_sync
+ * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7
+ * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15
+ */
+#define IPU6_ISYS_UNISPART_IRQ_CSI2(port) \
+ (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE))
+
+/*
+ * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3
+ * sip0 - 0, 1
+ * sip1 - 2, 3
+ * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes
+ * all offset are base from isys base address
+ */
+
+#define CSI2_HUB_GPREG_SIP_SRST(sip) (0x238038 + (sip) * 4)
+#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip) (0x238050 + (sip) * 4)
+
+#define CSI2_HUB_GPREG_DPHY_TIMER_INCR 0x238040
+#define CSI2_HUB_GPREG_HPLL_FREQ 0x238044
+#define CSI2_HUB_GPREG_IS_CLK_RATIO 0x238048
+#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE 0x23804c
+#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE 0x238058
+#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL 0x23805c
+#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL 0x238088
+#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL 0x2380a4
+#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL 0x2380d0
+
+#define CSI2_SIP_TOP_CSI_RX_BASE(sip) (0x23805c + (sip) * 0x48)
+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port) (0x23805c + ((port) / 2) * 0x48)
+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) (0x238088 + ((port) / 2) * 0x48)
+
+/* offset from port base */
+#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL 0x0
+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE 0x4
+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE 0x8
+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane) (0xc + (lane) * 8)
+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane) (0x10 + (lane) * 8)
+
+#endif /* IPU6_ISYS_CSI2_REG_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
new file mode 100644
index 000000000000..b883385adb44
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2018 - 2024 Intel Corporation */
+
+#ifndef IPU6_PLATFORM_REGS_H
+#define IPU6_PLATFORM_REGS_H
+
+#include <linux/bits.h>
+
+/*
+ * IPU6 uses uniform address within IPU6, therefore all subsystem registers
+ * locates in one single space starts from 0 but in different sctions with
+ * different addresses, the subsystem offsets are defined to 0 as the
+ * register definition will have the address offset to 0.
+ */
+#define IPU6_UNIFIED_OFFSET 0
+
+#define IPU6_ISYS_IOMMU0_OFFSET 0x2e0000
+#define IPU6_ISYS_IOMMU1_OFFSET 0x2e0500
+#define IPU6_ISYS_IOMMUI_OFFSET 0x2e0a00
+
+#define IPU6_PSYS_IOMMU0_OFFSET 0x1b0000
+#define IPU6_PSYS_IOMMU1_OFFSET 0x1b0700
+#define IPU6_PSYS_IOMMU1R_OFFSET 0x1b0e00
+#define IPU6_PSYS_IOMMUI_OFFSET 0x1b1500
+
+/* the offset from IOMMU base register */
+#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET 0x0c
+#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET 0x4c
+#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET 0x8c
+
+#define IPU6_MMU_INFO_OFFSET 0x8
+
+#define IPU6_ISYS_SPC_OFFSET 0x210000
+
+#define IPU6SE_PSYS_SPC_OFFSET 0x110000
+#define IPU6_PSYS_SPC_OFFSET 0x118000
+
+#define IPU6_ISYS_DMEM_OFFSET 0x200000
+#define IPU6_PSYS_DMEM_OFFSET 0x100000
+
+#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE 0x27c000
+#define IPU6_REG_ISYS_UNISPART_IRQ_MASK 0x27c004
+#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS 0x27c008
+#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR 0x27c00c
+#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE 0x27c010
+#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x27c014
+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG 0x27c414
+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x27c418
+#define IPU6_ISYS_UNISPART_IRQ_CSI0 BIT(2)
+#define IPU6_ISYS_UNISPART_IRQ_CSI1 BIT(3)
+#define IPU6_ISYS_UNISPART_IRQ_SW BIT(22)
+
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE 0x2b0200
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK 0x2b0204
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS 0x2b0208
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR 0x2b020c
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE 0x2b0210
+#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE 0x2b0214
+
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE 0x2d2100
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK 0x2d2104
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS 0x2d2108
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR 0x2d210c
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE 0x2d2110
+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE 0x2d2114
+
+/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */
+#define IPU6_REG_ISYS_CDC_THRESHOLD(i) (0x27c400 + ((i) * 4))
+
+#define IPU6_CSI_IRQ_NUM_PER_PIPE 4
+#define IPU6SE_ISYS_CSI_PORT_NUM 4
+#define IPU6_ISYS_CSI_PORT_NUM 8
+
+#define IPU6_ISYS_CSI_PORT_IRQ(irq_num) BIT(irq_num)
+
+/* PKG DIR OFFSET in IMR in secure mode */
+#define IPU6_PKG_DIR_IMR_OFFSET 0x40
+
+#define IPU6_ISYS_REG_SPC_STATUS_CTRL 0x0
+
+#define IPU6_ISYS_SPC_STATUS_START BIT(1)
+#define IPU6_ISYS_SPC_STATUS_RUN BIT(3)
+#define IPU6_ISYS_SPC_STATUS_READY BIT(5)
+#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12)
+#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH BIT(13)
+
+#define IPU6_PSYS_REG_SPC_STATUS_CTRL 0x0
+#define IPU6_PSYS_REG_SPC_START_PC 0x4
+#define IPU6_PSYS_REG_SPC_ICACHE_BASE 0x10
+#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14
+
+#define IPU6_PSYS_SPC_STATUS_START BIT(1)
+#define IPU6_PSYS_SPC_STATUS_RUN BIT(3)
+#define IPU6_PSYS_SPC_STATUS_READY BIT(5)
+#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12)
+#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH BIT(13)
+
+#define IPU6_PSYS_REG_SPP0_STATUS_CTRL 0x20000
+
+#define IPU6_INFO_ENABLE_SNOOP BIT(0)
+#define IPU6_INFO_DEC_FORCE_FLUSH BIT(1)
+#define IPU6_INFO_DEC_PASS_THROUGH BIT(2)
+#define IPU6_INFO_ZLW BIT(3)
+#define IPU6_INFO_REQUEST_DESTINATION_IOSF BIT(9)
+#define IPU6_INFO_IMR_BASE BIT(10)
+#define IPU6_INFO_IMR_DESTINED BIT(11)
+
+#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF
+
+/*
+ * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the
+ * pixel s2m remp ability.Remap here means that s2m rearange the order
+ * of the pixels in each 4 pixels group.
+ * For examle, mirroring remping means that if input's 4 first pixels
+ * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels.
+ * 0xE4 is from s2m MAS document. It means no remapping.
+ */
+#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
+/*
+ * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping.
+ * This remapping is exactly like the stream2mmio remapping.
+ */
+#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
+
+#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR 0x1ae000
+#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR 0x1af000
+#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n) (0x4 + (n) * 0xc)
+#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n) (0x8 + (n) * 0xc)
+#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n) (0xc + (n) * 0xc)
+
+enum ipu6_device_ab_group1_target_id {
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER,
+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB,
+};
+
+enum nci_ab_access_mode {
+ NCI_AB_ACCESS_MODE_RW, /* read & write */
+ NCI_AB_ACCESS_MODE_RO, /* read only */
+ NCI_AB_ACCESS_MODE_WO, /* write only */
+ NCI_AB_ACCESS_MODE_NA, /* No access at all */
+};
+
+/* IRQ-related registers in PSYS */
+#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE 0x1aa200
+#define IPU6_REG_PSYS_GPDEV_IRQ_MASK 0x1aa204
+#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS 0x1aa208
+#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR 0x1aa20c
+#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE 0x1aa210
+#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE 0x1aa214
+/* There are 8 FW interrupts, n = 0..7 */
+#define IPU6_PSYS_GPDEV_FWIRQ0 5
+#define IPU6_PSYS_GPDEV_FWIRQ1 6
+#define IPU6_PSYS_GPDEV_FWIRQ2 7
+#define IPU6_PSYS_GPDEV_FWIRQ3 8
+#define IPU6_PSYS_GPDEV_FWIRQ4 9
+#define IPU6_PSYS_GPDEV_FWIRQ5 10
+#define IPU6_PSYS_GPDEV_FWIRQ6 11
+#define IPU6_PSYS_GPDEV_FWIRQ7 12
+#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n) BIT(n)
+#define IPU6_REG_PSYS_GPDEV_FWIRQ(n) (4 * (n) + 0x1aa100)
+
+#endif /* IPU6_PLATFORM_REGS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
new file mode 100644
index 000000000000..d2bebd208461
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6.c
@@ -0,0 +1,856 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013--2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/pci-ats.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <media/ipu-bridge.h>
+#include <media/ipu6-pci-table.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-buttress.h"
+#include "ipu6-cpd.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"
+
+#define IPU6_PCI_BAR 0
+
+struct ipu6_cell_program {
+ u32 magic_number;
+
+ u32 blob_offset;
+ u32 blob_size;
+
+ u32 start[3];
+
+ u32 icache_source;
+ u32 icache_target;
+ u32 icache_size;
+
+ u32 pmem_source;
+ u32 pmem_target;
+ u32 pmem_size;
+
+ u32 data_source;
+ u32 data_target;
+ u32 data_size;
+
+ u32 bss_target;
+ u32 bss_size;
+
+ u32 cell_id;
+ u32 regs_addr;
+
+ u32 cell_pmem_data_bus_address;
+ u32 cell_dmem_data_bus_address;
+ u32 cell_pmem_control_bus_address;
+ u32 cell_dmem_control_bus_address;
+
+ u32 next;
+ u32 dummy[2];
+};
+
+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,
+ .spc_offset = IPU6_ISYS_SPC_OFFSET,
+ },
+ .isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN,
+};
+
+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 = {
+ .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,
+ .pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
+ .pwr_sts_mask = IPU6_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 psys_buttress_ctrl = {
+ .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,
+ .pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
+ .pwr_sts_mask = IPU6_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 void
+ipu6_pkg_dir_configure_spc(struct ipu6_device *isp,
+ const struct ipu6_hw_variants *hw_variant,
+ int pkg_dir_idx, void __iomem *base,
+ u64 *pkg_dir, dma_addr_t pkg_dir_vied_address)
+{
+ struct ipu6_cell_program *prog;
+ void __iomem *spc_base;
+ u32 server_fw_addr;
+ dma_addr_t dma_addr;
+ u32 pg_offset;
+
+ server_fw_addr = lower_32_bits(*(pkg_dir + (pkg_dir_idx + 1) * 2));
+ if (pkg_dir_idx == IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX)
+ dma_addr = sg_dma_address(isp->isys->fw_sgt.sgl);
+ else
+ dma_addr = sg_dma_address(isp->psys->fw_sgt.sgl);
+
+ pg_offset = server_fw_addr - dma_addr;
+ prog = (struct ipu6_cell_program *)((u64)isp->cpd_fw->data + pg_offset);
+ spc_base = base + prog->regs_addr;
+ if (spc_base != (base + hw_variant->spc_offset))
+ dev_warn(&isp->pdev->dev,
+ "SPC reg addr %p not matching value from CPD %p\n",
+ base + hw_variant->spc_offset, spc_base);
+ writel(server_fw_addr + prog->blob_offset +
+ prog->icache_source, spc_base + IPU6_PSYS_REG_SPC_ICACHE_BASE);
+ writel(IPU6_INFO_REQUEST_DESTINATION_IOSF,
+ spc_base + IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER);
+ writel(prog->start[1], spc_base + IPU6_PSYS_REG_SPC_START_PC);
+ writel(pkg_dir_vied_address, base + hw_variant->dmem_offset);
+}
+
+void ipu6_configure_spc(struct ipu6_device *isp,
+ const struct ipu6_hw_variants *hw_variant,
+ 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;
+ u32 val;
+
+ 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);
+
+ if (isp->secure_mode)
+ writel(IPU6_PKG_DIR_IMR_OFFSET, dmem_base);
+ else
+ ipu6_pkg_dir_configure_spc(isp, hw_variant, pkg_dir_idx, base,
+ pkg_dir, pkg_dir_dma_addr);
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6);
+
+#define IPU6_ISYS_CSI2_NPORTS 4
+#define IPU6SE_ISYS_CSI2_NPORTS 4
+#define IPU6_TGL_ISYS_CSI2_NPORTS 8
+#define IPU6EP_MTL_ISYS_CSI2_NPORTS 4
+
+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;
+ isys_ipdata.max_sram_size = IPU6_MAX_SRAM_SIZE;
+ isys_ipdata.sensor_type_start = IPU6_FW_ISYS_SENSOR_TYPE_START;
+ isys_ipdata.sensor_type_end = IPU6_FW_ISYS_SENSOR_TYPE_END;
+ isys_ipdata.max_streams = IPU6_ISYS_NUM_STREAMS;
+ isys_ipdata.max_send_queues = IPU6_N_MAX_SEND_QUEUES;
+ isys_ipdata.max_sram_blocks = IPU6_NOF_SRAM_BLOCKS_MAX;
+ isys_ipdata.max_devq_size = IPU6_DEV_SEND_QUEUE_SIZE;
+ isys_ipdata.csi2.nports = IPU6_ISYS_CSI2_NPORTS;
+ isys_ipdata.csi2.irq_mask = IPU6_CSI_RX_ERROR_IRQ_MASK;
+ isys_ipdata.csi2.ctrl0_irq_edge = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
+ isys_ipdata.csi2.ctrl0_irq_clear =
+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
+ isys_ipdata.csi2.ctrl0_irq_mask = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
+ isys_ipdata.csi2.ctrl0_irq_enable =
+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
+ isys_ipdata.csi2.ctrl0_irq_status =
+ 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);
+ 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)) {
+ isys_ipdata.ltr = IPU6EP_LTR_VALUE;
+ isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH;
+ }
+
+ if (is_ipu6_tgl(hw_ver))
+ isys_ipdata.csi2.nports = IPU6_TGL_ISYS_CSI2_NPORTS;
+
+ if (is_ipu6ep_mtl(hw_ver)) {
+ isys_ipdata.csi2.nports = IPU6EP_MTL_ISYS_CSI2_NPORTS;
+
+ isys_ipdata.csi2.ctrl0_irq_edge =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
+ isys_ipdata.csi2.ctrl0_irq_clear =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
+ isys_ipdata.csi2.ctrl0_irq_mask =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
+ isys_ipdata.csi2.ctrl0_irq_enable =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
+ isys_ipdata.csi2.ctrl0_irq_lnp =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
+ isys_ipdata.csi2.ctrl0_irq_status =
+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
+ isys_ipdata.csi2.fw_access_port_ofs =
+ CSI_REG_HUB_FW_ACCESS_PORT_V6OFS;
+ isys_ipdata.ltr = IPU6EP_MTL_LTR_VALUE;
+ isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH;
+ }
+
+ if (is_ipu6se(hw_ver)) {
+ 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;
+ isys_ipdata.sram_gran_shift = IPU6SE_SRAM_GRANULARITY_SHIFT;
+ isys_ipdata.sram_gran_size = IPU6SE_SRAM_GRANULARITY_SIZE;
+ isys_ipdata.max_sram_size = IPU6SE_MAX_SRAM_SIZE;
+ isys_ipdata.sensor_type_start =
+ IPU6SE_FW_ISYS_SENSOR_TYPE_START;
+ isys_ipdata.sensor_type_end = IPU6SE_FW_ISYS_SENSOR_TYPE_END;
+ isys_ipdata.max_streams = IPU6SE_ISYS_NUM_STREAMS;
+ isys_ipdata.max_send_queues = IPU6SE_N_MAX_SEND_QUEUES;
+ isys_ipdata.max_sram_blocks = IPU6SE_NOF_SRAM_BLOCKS_MAX;
+ isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE;
+ psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET;
+ }
+}
+
+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_isys_internal_pdata *ipdata)
+{
+ struct device *dev = &pdev->dev;
+ struct ipu6_bus_device *isys_adev;
+ struct ipu6_isys_pdata *pdata;
+ int ret;
+
+ ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb);
+ if (ret) {
+ dev_err_probe(dev, ret, "IPU6 bridge init failed\n");
+ return ERR_PTR(ret);
+ }
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ pdata->ipdata = ipdata;
+
+ isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
+ IPU6_ISYS_NAME);
+ if (IS_ERR(isys_adev)) {
+ dev_err_probe(dev, PTR_ERR(isys_adev),
+ "ipu6_bus_initialize_device isys failed\n");
+ kfree(pdata);
+ return ERR_CAST(isys_adev);
+ }
+
+ isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID,
+ &ipdata->hw_variant);
+ if (IS_ERR(isys_adev->mmu)) {
+ dev_err_probe(dev, PTR_ERR(isys_adev->mmu),
+ "ipu6_mmu_init(isys_adev->mmu) failed\n");
+ put_device(&isys_adev->auxdev.dev);
+ kfree(pdata);
+ return ERR_CAST(isys_adev->mmu);
+ }
+
+ isys_adev->mmu->dev = &isys_adev->auxdev.dev;
+
+ ret = ipu6_bus_add_device(isys_adev);
+ if (ret) {
+ kfree(pdata);
+ return ERR_PTR(ret);
+ }
+
+ return isys_adev;
+}
+
+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_psys_internal_pdata *ipdata)
+{
+ struct ipu6_bus_device *psys_adev;
+ struct ipu6_psys_pdata *pdata;
+ int ret;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ pdata->ipdata = ipdata;
+
+ psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
+ IPU6_PSYS_NAME);
+ if (IS_ERR(psys_adev)) {
+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
+ "ipu6_bus_initialize_device psys failed\n");
+ kfree(pdata);
+ return ERR_CAST(psys_adev);
+ }
+
+ psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID,
+ &ipdata->hw_variant);
+ if (IS_ERR(psys_adev->mmu)) {
+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu),
+ "ipu6_mmu_init(psys_adev->mmu) failed\n");
+ put_device(&psys_adev->auxdev.dev);
+ kfree(pdata);
+ return ERR_CAST(psys_adev->mmu);
+ }
+
+ psys_adev->mmu->dev = &psys_adev->auxdev.dev;
+
+ ret = ipu6_bus_add_device(psys_adev);
+ if (ret) {
+ kfree(pdata);
+ return ERR_PTR(ret);
+ }
+
+ return psys_adev;
+}
+
+static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver)
+{
+ int ret;
+
+ /* disable IPU6 PCI ATS on mtl ES2 */
+ if (is_ipu6ep_mtl(hw_ver) && boot_cpu_data.x86_stepping == 0x2 &&
+ pci_ats_supported(dev))
+ pci_disable_ats(dev);
+
+ /* No PCI msi capability for IPU6EP */
+ if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) {
+ /* likely do nothing as msi not enabled by default */
+ pci_disable_msi(dev);
+ return 0;
+ }
+
+ ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_MSI);
+ if (ret < 0)
+ return dev_err_probe(&dev->dev, ret, "Request msi failed");
+
+ return 0;
+}
+
+static void ipu6_configure_vc_mechanism(struct ipu6_device *isp)
+{
+ u32 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;
+ else
+ val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
+
+ if (IPU6_BTRS_ARB_STALL_MODE_VC1 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
+ val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
+ else
+ val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
+
+ writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL);
+}
+
+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;
+ 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;
+ int ret;
+
+ 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);
+
+ ret = pcim_iomap_regions(pdev, 1 << IPU6_PCI_BAR, pci_name(pdev));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to I/O mem remapping\n");
+
+ isp->base = pcim_iomap_table(pdev)[IPU6_PCI_BAR];
+ pci_set_drvdata(pdev, isp);
+ pci_set_master(pdev);
+
+ isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt);
+ switch (id->device) {
+ case PCI_DEVICE_ID_INTEL_IPU6:
+ isp->hw_ver = IPU6_VER_6;
+ isp->cpd_fw_name = IPU6_FIRMWARE_NAME;
+ break;
+ case PCI_DEVICE_ID_INTEL_IPU6SE:
+ isp->hw_ver = IPU6_VER_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->cpd_fw_name = IPU6EP_FIRMWARE_NAME;
+ break;
+ case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN:
+ isp->hw_ver = IPU6_VER_6EP;
+ isp->cpd_fw_name = IPU6EPADLN_FIRMWARE_NAME;
+ break;
+ case PCI_DEVICE_ID_INTEL_IPU6EP_MTL:
+ isp->hw_ver = IPU6_VER_6EP_MTL;
+ isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME;
+ break;
+ default:
+ return dev_err_probe(dev, -ENODEV,
+ "Unsupported IPU6 device %x\n",
+ id->device);
+ }
+
+ ipu6_internal_pdata_init(isp);
+
+ isys_base = isp->base + isys_ipdata.hw_variant.offset;
+ psys_base = isp->base + psys_ipdata.hw_variant.offset;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(39));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set DMA mask\n");
+
+ ret = dma_set_max_seg_size(dev, UINT_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set max_seg_size\n");
+
+ ret = ipu6_pci_config_setup(pdev, isp->hw_ver);
+ if (ret)
+ return ret;
+
+ ret = ipu6_buttress_init(isp);
+ if (ret)
+ return ret;
+
+ ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Requesting signed firmware %s failed\n",
+ isp->cpd_fw_name);
+ goto buttress_exit;
+ }
+
+ ret = ipu6_cpd_validate_cpd_file(isp, isp->cpd_fw->data,
+ isp->cpd_fw->size);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Failed to validate cpd\n");
+ 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)) {
+ ret = PTR_ERR(isp->isys);
+ 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)) {
+ ret = PTR_ERR(isp->psys);
+ goto out_ipu6_bus_del_devices;
+ }
+
+ ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
+ if (ret < 0)
+ goto out_ipu6_bus_del_devices;
+
+ ret = ipu6_mmu_hw_init(isp->psys->mmu);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Failed to set MMU hardware\n");
+ goto out_ipu6_bus_del_devices;
+ }
+
+ 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_bus_del_devices;
+ }
+
+ 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");
+ goto out_ipu6_bus_del_devices;
+ }
+
+ ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr,
+ ipu6_buttress_isr_threaded,
+ IRQF_SHARED, IPU6_NAME, isp);
+ if (ret) {
+ dev_err_probe(dev, ret, "Requesting irq failed\n");
+ goto out_ipu6_bus_del_devices;
+ }
+
+ ret = ipu6_buttress_authenticate(isp);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "FW authentication failed\n");
+ goto out_free_irq;
+ }
+
+ ipu6_mmu_hw_cleanup(isp->psys->mmu);
+ pm_runtime_put(&isp->psys->auxdev.dev);
+
+ /* Configure the arbitration mechanisms for VC requests */
+ ipu6_configure_vc_mechanism(isp);
+
+ val = readl(isp->base + BUTTRESS_REG_SKU);
+ 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,
+ pdev->device, isp->hw_ver);
+
+ pm_runtime_put_noidle(dev);
+ pm_runtime_allow(dev);
+
+ isp->bus_ready_to_probe = true;
+
+ return 0;
+
+out_free_irq:
+ devm_free_irq(dev, pdev->irq, isp);
+out_ipu6_bus_del_devices:
+ if (isp->psys) {
+ ipu6_cpd_free_pkg_dir(isp->psys);
+ ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
+ }
+ 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))
+ ipu6_mmu_cleanup(isp->isys->mmu);
+ ipu6_bus_del_devices(pdev);
+ release_firmware(isp->cpd_fw);
+buttress_exit:
+ ipu6_buttress_exit(isp);
+
+ return ret;
+}
+
+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;
+
+ devm_free_irq(&pdev->dev, pdev->irq, isp);
+ ipu6_cpd_free_pkg_dir(isp->psys);
+
+ ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
+ ipu6_buttress_exit(isp);
+
+ ipu6_bus_del_devices(pdev);
+
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ release_firmware(isp->cpd_fw);
+
+ ipu6_mmu_cleanup(psys_mmu);
+ ipu6_mmu_cleanup(isys_mmu);
+}
+
+static void ipu6_pci_reset_prepare(struct pci_dev *pdev)
+{
+ struct ipu6_device *isp = pci_get_drvdata(pdev);
+
+ pm_runtime_forbid(&isp->pdev->dev);
+}
+
+static void ipu6_pci_reset_done(struct pci_dev *pdev)
+{
+ struct ipu6_device *isp = pci_get_drvdata(pdev);
+
+ ipu6_buttress_restore(isp);
+ if (isp->secure_mode)
+ ipu6_buttress_reset_authentication(isp);
+
+ isp->need_ipc_reset = true;
+ pm_runtime_allow(&isp->pdev->dev);
+}
+
+/*
+ * PCI base driver code requires driver to provide these to enable
+ * PCI device level PM state transitions (D0<->D3)
+ */
+static int ipu6_suspend(struct device *dev)
+{
+ return 0;
+}
+
+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 */
+ ipu6_configure_vc_mechanism(isp);
+
+ isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
+ dev_info(dev, "IPU6 in %s mode\n",
+ isp->secure_mode ? "secure" : "non-secure");
+
+ ipu6_buttress_restore(isp);
+
+ ret = ipu6_buttress_ipc_reset(isp, &b->cse);
+ if (ret)
+ dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n");
+
+ ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
+ if (ret < 0) {
+ dev_err(&isp->psys->auxdev.dev, "Failed to get runtime PM\n");
+ return 0;
+ }
+
+ ret = ipu6_buttress_authenticate(isp);
+ if (ret)
+ dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", ret);
+
+ pm_runtime_put(&isp->psys->auxdev.dev);
+
+ return 0;
+}
+
+static int ipu6_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ipu6_device *isp = pci_get_drvdata(pdev);
+ int ret;
+
+ ipu6_configure_vc_mechanism(isp);
+ 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);
+ if (ret)
+ dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops ipu6_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(&ipu6_suspend, &ipu6_resume)
+ RUNTIME_PM_OPS(&ipu6_suspend, &ipu6_runtime_resume, NULL)
+};
+
+MODULE_DEVICE_TABLE(pci, ipu6_pci_tbl);
+
+static const struct pci_error_handlers pci_err_handlers = {
+ .reset_prepare = ipu6_pci_reset_prepare,
+ .reset_done = ipu6_pci_reset_done,
+};
+
+static struct pci_driver ipu6_pci_driver = {
+ .name = IPU6_NAME,
+ .id_table = ipu6_pci_tbl,
+ .probe = ipu6_pci_probe,
+ .remove = ipu6_pci_remove,
+ .driver = {
+ .pm = pm_ptr(&ipu6_pm_ops),
+ },
+ .err_handler = &pci_err_handlers,
+};
+
+module_pci_driver(ipu6_pci_driver);
+
+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
+MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
+MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
+MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel IPU6 PCI driver");
diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
new file mode 100644
index 000000000000..92e3c3414c91
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013 - 2024 Intel Corporation */
+
+#ifndef IPU6_H
+#define IPU6_H
+
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "ipu6-buttress.h"
+
+struct firmware;
+struct pci_dev;
+struct ipu6_bus_device;
+
+#define IPU6_NAME "intel-ipu6"
+#define IPU6_MEDIA_DEV_MODEL_NAME "ipu6"
+
+#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;
+}
+
+/*
+ * ISYS DMA can overshoot. For higher resolutions over allocation is one line
+ * but it must be at minimum 1024 bytes. Value could be different in
+ * different versions / generations thus provide it via platform data.
+ */
+#define IPU6_ISYS_OVERALLOC_MIN 1024
+
+/* Physical pages in GDA is 128, page size is 2K for IPU6, 1K for others */
+#define IPU6_DEVICE_GDA_NR_PAGES 128
+
+/* Virtualization factor to calculate the available virtual pages */
+#define IPU6_DEVICE_GDA_VIRT_FACTOR 32
+
+struct ipu6_device {
+ struct pci_dev *pdev;
+ struct list_head devices;
+ struct ipu6_bus_device *isys;
+ struct ipu6_bus_device *psys;
+ struct ipu6_buttress buttress;
+
+ const struct firmware *cpd_fw;
+ const char *cpd_fw_name;
+ u32 cpd_metadata_cmpnt_size;
+
+ void __iomem *base;
+ bool need_ipc_reset;
+ bool secure_mode;
+ u8 hw_ver;
+ bool bus_ready_to_probe;
+};
+
+#define IPU6_ISYS_NAME "isys"
+#define IPU6_PSYS_NAME "psys"
+
+#define IPU6_MMU_MAX_DEVICES 4
+#define IPU6_MMU_ADDR_BITS 32
+/* The firmware is accessible within the first 2 GiB only in non-secure mode. */
+#define IPU6_MMU_ADDR_BITS_NON_SECURE 31
+
+#define IPU6_MMU_MAX_TLB_L1_STREAMS 32
+#define IPU6_MMU_MAX_TLB_L2_STREAMS 32
+#define IPU6_MAX_LI_BLOCK_ADDR 128
+#define IPU6_MAX_L2_BLOCK_ADDR 64
+
+#define IPU6SE_ISYS_NUM_STREAMS IPU6SE_NONSECURE_STREAM_ID_MAX
+#define IPU6_ISYS_NUM_STREAMS IPU6_NONSECURE_STREAM_ID_MAX
+
+/*
+ * To maximize the IOSF utlization, IPU6 need to send requests in bursts.
+ * At the DMA interface with the buttress, there are CDC FIFOs with burst
+ * collection capability. CDC FIFO burst collectors have a configurable
+ * threshold and is configured based on the outcome of performance measurements.
+ *
+ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2
+ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2
+ *
+ * Threshold values are pre-defined and are arrived at after performance
+ * evaluations on a type of IPU6
+ */
+#define IPU6_MAX_VC_IOSF_PORTS 4
+
+/*
+ * IPU6 must configure correct arbitration mechanism related to the IOSF VC
+ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on
+ * stall and 1 means stall until the request is completed.
+ */
+#define IPU6_BTRS_ARB_MODE_TYPE_REARB 0
+#define IPU6_BTRS_ARB_MODE_TYPE_STALL 1
+
+/* Currently chosen arbitration mechanism for VC0 */
+#define IPU6_BTRS_ARB_STALL_MODE_VC0 \
+ IPU6_BTRS_ARB_MODE_TYPE_REARB
+
+/* Currently chosen arbitration mechanism for VC1 */
+#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;
+};
+
+struct ipu6_isys_internal_csi2_pdata {
+ u32 nports;
+ u32 irq_mask;
+ u32 ctrl0_irq_edge;
+ u32 ctrl0_irq_clear;
+ u32 ctrl0_irq_mask;
+ u32 ctrl0_irq_enable;
+ u32 ctrl0_irq_lnp;
+ u32 ctrl0_irq_status;
+ u32 fw_access_port_ofs;
+};
+
+struct ipu6_isys_internal_tpg_pdata {
+ u32 ntpgs;
+ u32 *offsets;
+ u32 *sels;
+};
+
+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;
+ u32 spc_offset;
+};
+
+struct ipu6_isys_internal_pdata {
+ struct ipu6_isys_internal_csi2_pdata csi2;
+ struct ipu6_hw_variants hw_variant;
+ u32 num_parallel_streams;
+ u32 isys_dma_overshoot;
+ u32 sram_gran_shift;
+ u32 sram_gran_size;
+ u32 max_sram_size;
+ u32 max_streams;
+ u32 max_send_queues;
+ u32 max_sram_blocks;
+ u32 max_devq_size;
+ u32 sensor_type_start;
+ u32 sensor_type_end;
+ u32 ltr;
+ u32 memopen_threshold;
+ bool enhanced_iwake;
+};
+
+struct ipu6_isys_pdata {
+ void __iomem *base;
+ const struct ipu6_isys_internal_pdata *ipdata;
+};
+
+struct ipu6_psys_internal_pdata {
+ struct ipu6_hw_variants hw_variant;
+};
+
+struct ipu6_psys_pdata {
+ void __iomem *base;
+ const struct ipu6_psys_internal_pdata *ipdata;
+};
+
+int ipu6_fw_authenticate(void *data, u64 val);
+void ipu6_configure_spc(struct ipu6_device *isp,
+ const struct ipu6_hw_variants *hw_variant,
+ int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
+ dma_addr_t pkg_dir_dma_addr);
+#endif /* IPU6_H */
diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
index 55e0c60c420c..89b582a221ab 100644
--- a/drivers/media/pci/intel/ivsc/mei_csi.c
+++ b/drivers/media/pci/intel/ivsc/mei_csi.c
@@ -19,12 +19,15 @@
#include <linux/mei_cl_bus.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/units.h>
#include <linux/uuid.h>
#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>
@@ -661,11 +664,23 @@ static int mei_csi_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id)
{
struct device *dev = &cldev->dev;
+ struct pci_dev *ipu;
struct mei_csi *csi;
+ unsigned int i;
int ret;
- if (!dev_fwnode(dev))
- return -EPROBE_DEFER;
+ 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);
+
+ if (!ipu)
+ return -ENODEV;
+
+ ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb);
+ if (ret < 0)
+ return ret;
+ if (WARN_ON(!dev_fwnode(dev)))
+ return -ENXIO;
csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL);
if (!csi)
@@ -784,6 +799,7 @@ static struct mei_cl_driver mei_csi_driver = {
module_mei_cl_driver(mei_csi_driver);
+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_DESCRIPTION("Device driver for IVSC CSI");
diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c
index 9bcf10a77fd3..60498a5abebf 100644
--- a/drivers/media/pci/mgb4/mgb4_core.c
+++ b/drivers/media/pci/mgb4/mgb4_core.c
@@ -493,13 +493,13 @@ static int mgb4_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct mgb4_dev *mgbdev;
struct resource video = {
.start = 0x0,
- .end = 0x100,
+ .end = 0xff,
.flags = IORESOURCE_MEM,
.name = "mgb4-video",
};
struct resource cmt = {
.start = 0x1000,
- .end = 0x1800,
+ .end = 0x17ff,
.flags = IORESOURCE_MEM,
.name = "mgb4-cmt",
};
diff --git a/drivers/media/pci/mgb4/mgb4_regs.c b/drivers/media/pci/mgb4/mgb4_regs.c
index 53d4e4503a74..31befd722d72 100644
--- a/drivers/media/pci/mgb4/mgb4_regs.c
+++ b/drivers/media/pci/mgb4/mgb4_regs.c
@@ -10,7 +10,7 @@
int mgb4_regs_map(struct resource *res, struct mgb4_regs *regs)
{
regs->mapbase = res->start;
- regs->mapsize = res->end - res->start;
+ regs->mapsize = resource_size(res);
if (!request_mem_region(regs->mapbase, regs->mapsize, res->name))
return -EINVAL;
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
index 46676f2c89c7..1c885d620b75 100644
--- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
@@ -135,7 +135,7 @@ static void netup_i2c_fifo_tx(struct netup_i2c *i2c)
(readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f);
u32 msg_length = i2c->msg->len - i2c->xmit_size;
- msg_length = (msg_length < fifo_space ? msg_length : fifo_space);
+ msg_length = min(msg_length, fifo_space);
while (msg_length--) {
data = i2c->msg->buf[i2c->xmit_size++];
writeb(data, &i2c->regs->tx_fifo.data8);
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index 7481f553f959..24ec576dc3bf 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -1488,7 +1488,9 @@ static int init_channel(struct ngene_channel *chan)
}
if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
- dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
+ ret = dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
+ if (ret != 0)
+ goto err;
set_transfer(chan, 1);
chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
set_transfer(&chan->dev->channel[2], 1);
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index d3cde05a6eba..dd2236c5c4a1 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -1096,9 +1096,6 @@ static void snd_saa7134_free(struct snd_card * card)
if (chip->dev->dmasound.priv_data == NULL)
return;
- if (chip->irq >= 0)
- free_irq(chip->irq, &chip->dev->dmasound);
-
chip->dev->dmasound.priv_data = NULL;
}
@@ -1147,10 +1144,8 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum)
chip->iobase = pci_resource_start(dev->pci, 0);
- err = request_irq(dev->pci->irq, saa7134_alsa_irq,
- IRQF_SHARED, dev->name,
- (void*) &dev->dmasound);
-
+ err = devm_request_irq(&dev->pci->dev, dev->pci->irq, saa7134_alsa_irq,
+ IRQF_SHARED, dev->name, &dev->dmasound);
if (err < 0) {
pr_err("%s: can't get IRQ %d for ALSA\n",
dev->name, dev->pci->irq);
diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c
index 6d87fbb0ee04..1a9e2bccc413 100644
--- a/drivers/media/pci/solo6x10/solo6x10-core.c
+++ b/drivers/media/pci/solo6x10/solo6x10-core.c
@@ -144,11 +144,8 @@ static void free_solo_dev(struct solo_dev *solo_dev)
/* Now cleanup the PCI device */
solo_irq_off(solo_dev, ~0);
- free_irq(pdev->irq, solo_dev);
- pci_iounmap(pdev, solo_dev->reg_base);
}
- pci_release_regions(pdev);
pci_disable_device(pdev);
v4l2_device_unregister(&solo_dev->v4l2_dev);
pci_set_drvdata(pdev, NULL);
@@ -480,15 +477,10 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_write_config_byte(pdev, 0x40, 0x00);
pci_write_config_byte(pdev, 0x41, 0x00);
- ret = pci_request_regions(pdev, SOLO6X10_NAME);
+ ret = pcim_iomap_regions(pdev, BIT(0), SOLO6X10_NAME);
if (ret)
goto fail_probe;
-
- solo_dev->reg_base = pci_ioremap_bar(pdev, 0);
- if (solo_dev->reg_base == NULL) {
- ret = -ENOMEM;
- goto fail_probe;
- }
+ solo_dev->reg_base = pcim_iomap_table(pdev)[0];
chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
SOLO_CHIP_ID_MASK;
@@ -551,8 +543,8 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* PLL locking time of 1ms */
mdelay(1);
- ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME,
- solo_dev);
+ ret = devm_request_irq(&pdev->dev, pdev->irq, solo_isr, IRQF_SHARED,
+ SOLO6X10_NAME, solo_dev);
if (ret)
goto fail_probe;
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index a47c5850ef87..2e62c938e2cb 100644
--- a/drivers/media/pci/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * budget-av.c: driver for the SAA7146 based Budget DVB cards
- * with analog video in
+ * budget-av.ko: driver for the SAA7146 based Budget DVB cards
+ * with analog video input (and optionally with CI)
*
* Compiled from various sources by Michael Hunold <michael@mihu.de>
*
@@ -16,7 +16,6 @@
* the project's page is at https://linuxtv.org
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "budget.h"
#include "stv0299.h"
@@ -63,8 +62,8 @@ struct budget_av {
static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
-
-/* GPIO Connections:
+/*
+ * GPIO Connections:
* 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*!
* 1 - CI memory select 0=>IO memory, 1=>Attribute Memory
* 2 - CI Card Enable (Active Low)
@@ -95,12 +94,12 @@ static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg)
return mm2[0];
}
-static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len)
+static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 *buf, u8 len)
{
u8 mm1[] = { reg };
struct i2c_msg msgs[2] = {
- {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1},
- {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len}
+ {.addr = id / 2, .flags = 0, .buf = mm1, .len = 1},
+ {.addr = id / 2, .flags = I2C_M_RD, .buf = buf, .len = len}
};
if (i2c_transfer(i2c, msgs, 2) != 2)
@@ -206,7 +205,7 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
if (slot != 0)
return -EINVAL;
- dprintk(1, "ciintf_slot_reset\n");
+ dprintk(1, "ci slot reset\n");
budget_av->slot_status = SLOTSTATUS_RESET;
saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */
@@ -235,7 +234,7 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
if (slot != 0)
return -EINVAL;
- dprintk(1, "ciintf_slot_shutdown\n");
+ dprintk(1, "ci slot shutdown\n");
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
budget_av->slot_status = SLOTSTATUS_NONE;
@@ -251,7 +250,7 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
if (slot != 0)
return -EINVAL;
- dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status);
+ dprintk(1, "ci slot status: %d\n", budget_av->slot_status);
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
@@ -267,8 +266,10 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
if (slot != 0)
return -EINVAL;
- /* test the card detect line - needs to be done carefully
- * since it never goes high for some CAMs on this interface (e.g. topuptv) */
+ /*
+ * test the card detect line - needs to be done carefully
+ * since it never goes high for some CAMs on this interface (e.g. topuptv)
+ */
if (budget_av->slot_status == SLOTSTATUS_NONE) {
saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
udelay(1);
@@ -281,12 +282,14 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
}
- /* We also try and read from IO memory to work round the above detection bug. If
+ /*
+ * We also try and read from IO memory to work round the above detection bug. If
* there is no CAM, we will get a timeout. Only done if there is no cam
* present, since this test actually breaks some cams :(
*
* if the CI interface is not open, we also do the above test since we
- * don't care if the cam has problems - we'll be resetting it on open() anyway */
+ * don't care if the cam has problems - we'll be resetting it on open() anyway
+ */
if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) {
saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
@@ -305,16 +308,14 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
/* read from attribute memory in reset/ready state to know when the CAM is ready */
if (budget_av->slot_status == SLOTSTATUS_RESET) {
result = ciintf_read_attribute_mem(ca, slot, 0);
- if (result == 0x1d) {
+ if (result == 0x1d)
budget_av->slot_status = SLOTSTATUS_READY;
- }
}
/* work out correct return code */
if (budget_av->slot_status != SLOTSTATUS_NONE) {
- if (budget_av->slot_status & SLOTSTATUS_READY) {
+ if (budget_av->slot_status & SLOTSTATUS_READY)
return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
- }
return DVB_CA_EN50221_POLL_CAM_PRESENT;
}
return 0;
@@ -349,8 +350,9 @@ static int ciintf_init(struct budget_av *budget_av)
budget_av->budget.ci_present = 1;
budget_av->slot_status = SLOTSTATUS_NONE;
- if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
- &budget_av->ca, 0, 1)) != 0) {
+ result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
+ &budget_av->ca, 0, 1);
+ if (result != 0) {
pr_err("ci initialisation failed\n");
goto error;
}
@@ -439,7 +441,7 @@ static int saa7113_setinput(struct budget_av *budget_av, int input)
{
struct budget *budget = &budget_av->budget;
- if (1 != budget_av->has_saa7113)
+ if (budget_av->has_saa7113 != 1)
return -ENODEV;
if (input == 1) {
@@ -448,8 +450,9 @@ static int saa7113_setinput(struct budget_av *budget_av, int input)
} else if (input == 0) {
i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0);
i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00);
- } else
+ } else {
return -EINVAL;
+ }
budget_av->cur_input = input;
return 0;
@@ -492,7 +495,7 @@ static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe)
u32 div;
u8 buf[4];
struct budget *budget = fe->dvb->priv;
- struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+ struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
if ((c->frequency < 950000) || (c->frequency > 2150000))
return -EINVAL;
@@ -606,7 +609,7 @@ static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe)
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct budget *budget = fe->dvb->priv;
u8 buf[6];
- struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+ struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
int i;
#define CU1216_IF 36125000
@@ -670,7 +673,7 @@ static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
{
struct budget *budget = fe->dvb->priv;
static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+ struct i2c_msg tuner_msg = {.addr = 0x60, .flags = 0, .buf = tu1216_init, .len = sizeof(tu1216_init) };
// setup PLL configuration
if (fe->ops.i2c_gate_ctrl)
@@ -687,7 +690,7 @@ static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe)
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct budget *budget = fe->dvb->priv;
u8 tuner_buf[4];
- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
+ struct i2c_msg tuner_msg = {.addr = 0x60, .flags = 0, .buf = tuner_buf, .len =
sizeof(tuner_buf) };
int tuner_frequency = 0;
u8 band, cp, filter;
@@ -865,7 +868,7 @@ static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe,
static const struct stv0299_config philips_sd1878_config = {
.demod_address = 0x68,
- .inittab = philips_sd1878_inittab,
+ .inittab = philips_sd1878_inittab,
.mclk = 88000000UL,
.invert = 0,
.skip_reinit = 0,
@@ -878,222 +881,222 @@ static const struct stv0299_config philips_sd1878_config = {
/* KNC1 DVB-S (STB0899) Inittab */
static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
- { STB0899_DEV_ID , 0x81 },
- { STB0899_DISCNTRL1 , 0x32 },
- { STB0899_DISCNTRL2 , 0x80 },
- { STB0899_DISRX_ST0 , 0x04 },
- { STB0899_DISRX_ST1 , 0x00 },
- { STB0899_DISPARITY , 0x00 },
- { STB0899_DISSTATUS , 0x20 },
- { STB0899_DISF22 , 0x8c },
- { STB0899_DISF22RX , 0x9a },
- { STB0899_SYSREG , 0x0b },
- { STB0899_ACRPRESC , 0x11 },
- { STB0899_ACRDIV1 , 0x0a },
- { STB0899_ACRDIV2 , 0x05 },
- { STB0899_DACR1 , 0x00 },
- { STB0899_DACR2 , 0x00 },
- { STB0899_OUTCFG , 0x00 },
- { STB0899_MODECFG , 0x00 },
- { STB0899_IRQSTATUS_3 , 0x30 },
- { STB0899_IRQSTATUS_2 , 0x00 },
- { STB0899_IRQSTATUS_1 , 0x00 },
- { STB0899_IRQSTATUS_0 , 0x00 },
- { STB0899_IRQMSK_3 , 0xf3 },
- { STB0899_IRQMSK_2 , 0xfc },
- { STB0899_IRQMSK_1 , 0xff },
- { STB0899_IRQMSK_0 , 0xff },
- { STB0899_IRQCFG , 0x00 },
- { STB0899_I2CCFG , 0x88 },
- { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */
- { STB0899_IOPVALUE5 , 0x00 },
- { STB0899_IOPVALUE4 , 0x20 },
- { STB0899_IOPVALUE3 , 0xc9 },
- { STB0899_IOPVALUE2 , 0x90 },
- { STB0899_IOPVALUE1 , 0x40 },
- { STB0899_IOPVALUE0 , 0x00 },
- { STB0899_GPIO00CFG , 0x82 },
- { STB0899_GPIO01CFG , 0x82 },
- { STB0899_GPIO02CFG , 0x82 },
- { STB0899_GPIO03CFG , 0x82 },
- { STB0899_GPIO04CFG , 0x82 },
- { STB0899_GPIO05CFG , 0x82 },
- { STB0899_GPIO06CFG , 0x82 },
- { STB0899_GPIO07CFG , 0x82 },
- { STB0899_GPIO08CFG , 0x82 },
- { STB0899_GPIO09CFG , 0x82 },
- { STB0899_GPIO10CFG , 0x82 },
- { STB0899_GPIO11CFG , 0x82 },
- { STB0899_GPIO12CFG , 0x82 },
- { STB0899_GPIO13CFG , 0x82 },
- { STB0899_GPIO14CFG , 0x82 },
- { STB0899_GPIO15CFG , 0x82 },
- { STB0899_GPIO16CFG , 0x82 },
- { STB0899_GPIO17CFG , 0x82 },
- { STB0899_GPIO18CFG , 0x82 },
- { STB0899_GPIO19CFG , 0x82 },
- { STB0899_GPIO20CFG , 0x82 },
- { STB0899_SDATCFG , 0xb8 },
- { STB0899_SCLTCFG , 0xba },
- { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */
- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
- { STB0899_DIRCLKCFG , 0x82 },
- { STB0899_CLKOUT27CFG , 0x7e },
- { STB0899_STDBYCFG , 0x82 },
- { STB0899_CS0CFG , 0x82 },
- { STB0899_CS1CFG , 0x82 },
- { STB0899_DISEQCOCFG , 0x20 },
- { STB0899_GPIO32CFG , 0x82 },
- { STB0899_GPIO33CFG , 0x82 },
- { STB0899_GPIO34CFG , 0x82 },
- { STB0899_GPIO35CFG , 0x82 },
- { STB0899_GPIO36CFG , 0x82 },
- { STB0899_GPIO37CFG , 0x82 },
- { STB0899_GPIO38CFG , 0x82 },
- { STB0899_GPIO39CFG , 0x82 },
- { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
- { STB0899_FILTCTRL , 0x00 },
- { STB0899_SYSCTRL , 0x00 },
- { STB0899_STOPCLK1 , 0x20 },
- { STB0899_STOPCLK2 , 0x00 },
- { STB0899_INTBUFSTATUS , 0x00 },
- { STB0899_INTBUFCTRL , 0x0a },
- { 0xffff , 0xff },
+ { STB0899_DEV_ID, 0x81 },
+ { STB0899_DISCNTRL1, 0x32 },
+ { STB0899_DISCNTRL2, 0x80 },
+ { STB0899_DISRX_ST0, 0x04 },
+ { STB0899_DISRX_ST1, 0x00 },
+ { STB0899_DISPARITY, 0x00 },
+ { STB0899_DISSTATUS, 0x20 },
+ { STB0899_DISF22, 0x8c },
+ { STB0899_DISF22RX, 0x9a },
+ { STB0899_SYSREG, 0x0b },
+ { STB0899_ACRPRESC, 0x11 },
+ { STB0899_ACRDIV1, 0x0a },
+ { STB0899_ACRDIV2, 0x05 },
+ { STB0899_DACR1, 0x00 },
+ { STB0899_DACR2, 0x00 },
+ { STB0899_OUTCFG, 0x00 },
+ { STB0899_MODECFG, 0x00 },
+ { STB0899_IRQSTATUS_3, 0x30 },
+ { STB0899_IRQSTATUS_2, 0x00 },
+ { STB0899_IRQSTATUS_1, 0x00 },
+ { STB0899_IRQSTATUS_0, 0x00 },
+ { STB0899_IRQMSK_3, 0xf3 },
+ { STB0899_IRQMSK_2, 0xfc },
+ { STB0899_IRQMSK_1, 0xff },
+ { STB0899_IRQMSK_0, 0xff },
+ { STB0899_IRQCFG, 0x00 },
+ { STB0899_I2CCFG, 0x88 },
+ { STB0899_I2CRPT, 0x58 }, /* Repeater=8, Stop=disabled */
+ { STB0899_IOPVALUE5, 0x00 },
+ { STB0899_IOPVALUE4, 0x20 },
+ { STB0899_IOPVALUE3, 0xc9 },
+ { STB0899_IOPVALUE2, 0x90 },
+ { STB0899_IOPVALUE1, 0x40 },
+ { STB0899_IOPVALUE0, 0x00 },
+ { STB0899_GPIO00CFG, 0x82 },
+ { STB0899_GPIO01CFG, 0x82 },
+ { STB0899_GPIO02CFG, 0x82 },
+ { STB0899_GPIO03CFG, 0x82 },
+ { STB0899_GPIO04CFG, 0x82 },
+ { STB0899_GPIO05CFG, 0x82 },
+ { STB0899_GPIO06CFG, 0x82 },
+ { STB0899_GPIO07CFG, 0x82 },
+ { STB0899_GPIO08CFG, 0x82 },
+ { STB0899_GPIO09CFG, 0x82 },
+ { STB0899_GPIO10CFG, 0x82 },
+ { STB0899_GPIO11CFG, 0x82 },
+ { STB0899_GPIO12CFG, 0x82 },
+ { STB0899_GPIO13CFG, 0x82 },
+ { STB0899_GPIO14CFG, 0x82 },
+ { STB0899_GPIO15CFG, 0x82 },
+ { STB0899_GPIO16CFG, 0x82 },
+ { STB0899_GPIO17CFG, 0x82 },
+ { STB0899_GPIO18CFG, 0x82 },
+ { STB0899_GPIO19CFG, 0x82 },
+ { STB0899_GPIO20CFG, 0x82 },
+ { STB0899_SDATCFG, 0xb8 },
+ { STB0899_SCLTCFG, 0xba },
+ { STB0899_AGCRFCFG, 0x08 }, /* 0x1c */
+ { STB0899_GPIO22, 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21, 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG, 0x82 },
+ { STB0899_CLKOUT27CFG, 0x7e },
+ { STB0899_STDBYCFG, 0x82 },
+ { STB0899_CS0CFG, 0x82 },
+ { STB0899_CS1CFG, 0x82 },
+ { STB0899_DISEQCOCFG, 0x20 },
+ { STB0899_GPIO32CFG, 0x82 },
+ { STB0899_GPIO33CFG, 0x82 },
+ { STB0899_GPIO34CFG, 0x82 },
+ { STB0899_GPIO35CFG, 0x82 },
+ { STB0899_GPIO36CFG, 0x82 },
+ { STB0899_GPIO37CFG, 0x82 },
+ { STB0899_GPIO38CFG, 0x82 },
+ { STB0899_GPIO39CFG, 0x82 },
+ { STB0899_NCOARSE, 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL, 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL, 0x00 },
+ { STB0899_SYSCTRL, 0x00 },
+ { STB0899_STOPCLK1, 0x20 },
+ { STB0899_STOPCLK2, 0x00 },
+ { STB0899_INTBUFSTATUS, 0x00 },
+ { STB0899_INTBUFCTRL, 0x0a },
+ { 0xffff, 0xff },
};
static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
- { STB0899_DEMOD , 0x00 },
- { STB0899_RCOMPC , 0xc9 },
- { STB0899_AGC1CN , 0x41 },
- { STB0899_AGC1REF , 0x08 },
- { STB0899_RTC , 0x7a },
- { STB0899_TMGCFG , 0x4e },
- { STB0899_AGC2REF , 0x33 },
- { STB0899_TLSR , 0x84 },
- { STB0899_CFD , 0xee },
- { STB0899_ACLC , 0x87 },
- { STB0899_BCLC , 0x94 },
- { STB0899_EQON , 0x41 },
- { STB0899_LDT , 0xdd },
- { STB0899_LDT2 , 0xc9 },
- { STB0899_EQUALREF , 0xb4 },
- { STB0899_TMGRAMP , 0x10 },
- { STB0899_TMGTHD , 0x30 },
- { STB0899_IDCCOMP , 0xfb },
- { STB0899_QDCCOMP , 0x03 },
- { STB0899_POWERI , 0x3b },
- { STB0899_POWERQ , 0x3d },
- { STB0899_RCOMP , 0x81 },
- { STB0899_AGCIQIN , 0x80 },
- { STB0899_AGC2I1 , 0x04 },
- { STB0899_AGC2I2 , 0xf5 },
- { STB0899_TLIR , 0x25 },
- { STB0899_RTF , 0x80 },
- { STB0899_DSTATUS , 0x00 },
- { STB0899_LDI , 0xca },
- { STB0899_CFRM , 0xf1 },
- { STB0899_CFRL , 0xf3 },
- { STB0899_NIRM , 0x2a },
- { STB0899_NIRL , 0x05 },
- { STB0899_ISYMB , 0x17 },
- { STB0899_QSYMB , 0xfa },
- { STB0899_SFRH , 0x2f },
- { STB0899_SFRM , 0x68 },
- { STB0899_SFRL , 0x40 },
- { STB0899_SFRUPH , 0x2f },
- { STB0899_SFRUPM , 0x68 },
- { STB0899_SFRUPL , 0x40 },
- { STB0899_EQUAI1 , 0xfd },
- { STB0899_EQUAQ1 , 0x04 },
- { STB0899_EQUAI2 , 0x0f },
- { STB0899_EQUAQ2 , 0xff },
- { STB0899_EQUAI3 , 0xdf },
- { STB0899_EQUAQ3 , 0xfa },
- { STB0899_EQUAI4 , 0x37 },
- { STB0899_EQUAQ4 , 0x0d },
- { STB0899_EQUAI5 , 0xbd },
- { STB0899_EQUAQ5 , 0xf7 },
- { STB0899_DSTATUS2 , 0x00 },
- { STB0899_VSTATUS , 0x00 },
- { STB0899_VERROR , 0xff },
- { STB0899_IQSWAP , 0x2a },
- { STB0899_ECNT1M , 0x00 },
- { STB0899_ECNT1L , 0x00 },
- { STB0899_ECNT2M , 0x00 },
- { STB0899_ECNT2L , 0x00 },
- { STB0899_ECNT3M , 0x00 },
- { STB0899_ECNT3L , 0x00 },
- { STB0899_FECAUTO1 , 0x06 },
- { STB0899_FECM , 0x01 },
- { STB0899_VTH12 , 0xf0 },
- { STB0899_VTH23 , 0xa0 },
- { STB0899_VTH34 , 0x78 },
- { STB0899_VTH56 , 0x4e },
- { STB0899_VTH67 , 0x48 },
- { STB0899_VTH78 , 0x38 },
- { STB0899_PRVIT , 0xff },
- { STB0899_VITSYNC , 0x19 },
- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
- { STB0899_TSULC , 0x42 },
- { STB0899_RSLLC , 0x40 },
- { STB0899_TSLPL , 0x12 },
- { STB0899_TSCFGH , 0x0c },
- { STB0899_TSCFGM , 0x00 },
- { STB0899_TSCFGL , 0x0c },
- { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
- { STB0899_RSSYNCDEL , 0x00 },
- { STB0899_TSINHDELH , 0x02 },
- { STB0899_TSINHDELM , 0x00 },
- { STB0899_TSINHDELL , 0x00 },
- { STB0899_TSLLSTKM , 0x00 },
- { STB0899_TSLLSTKL , 0x00 },
- { STB0899_TSULSTKM , 0x00 },
- { STB0899_TSULSTKL , 0xab },
- { STB0899_PCKLENUL , 0x00 },
- { STB0899_PCKLENLL , 0xcc },
- { STB0899_RSPCKLEN , 0xcc },
- { STB0899_TSSTATUS , 0x80 },
- { STB0899_ERRCTRL1 , 0xb6 },
- { STB0899_ERRCTRL2 , 0x96 },
- { STB0899_ERRCTRL3 , 0x89 },
- { STB0899_DMONMSK1 , 0x27 },
- { STB0899_DMONMSK0 , 0x03 },
- { STB0899_DEMAPVIT , 0x5c },
- { STB0899_PLPARM , 0x1f },
- { STB0899_PDELCTRL , 0x48 },
- { STB0899_PDELCTRL2 , 0x00 },
- { STB0899_BBHCTRL1 , 0x00 },
- { STB0899_BBHCTRL2 , 0x00 },
- { STB0899_HYSTTHRESH , 0x77 },
- { STB0899_MATCSTM , 0x00 },
- { STB0899_MATCSTL , 0x00 },
- { STB0899_UPLCSTM , 0x00 },
- { STB0899_UPLCSTL , 0x00 },
- { STB0899_DFLCSTM , 0x00 },
- { STB0899_DFLCSTL , 0x00 },
- { STB0899_SYNCCST , 0x00 },
- { STB0899_SYNCDCSTM , 0x00 },
- { STB0899_SYNCDCSTL , 0x00 },
- { STB0899_ISI_ENTRY , 0x00 },
- { STB0899_ISI_BIT_EN , 0x00 },
- { STB0899_MATSTRM , 0x00 },
- { STB0899_MATSTRL , 0x00 },
- { STB0899_UPLSTRM , 0x00 },
- { STB0899_UPLSTRL , 0x00 },
- { STB0899_DFLSTRM , 0x00 },
- { STB0899_DFLSTRL , 0x00 },
- { STB0899_SYNCSTR , 0x00 },
- { STB0899_SYNCDSTRM , 0x00 },
- { STB0899_SYNCDSTRL , 0x00 },
- { STB0899_CFGPDELSTATUS1 , 0x10 },
- { STB0899_CFGPDELSTATUS2 , 0x00 },
- { STB0899_BBFERRORM , 0x00 },
- { STB0899_BBFERRORL , 0x00 },
- { STB0899_UPKTERRORM , 0x00 },
- { STB0899_UPKTERRORL , 0x00 },
- { 0xffff , 0xff },
+ { STB0899_DEMOD, 0x00 },
+ { STB0899_RCOMPC, 0xc9 },
+ { STB0899_AGC1CN, 0x41 },
+ { STB0899_AGC1REF, 0x08 },
+ { STB0899_RTC, 0x7a },
+ { STB0899_TMGCFG, 0x4e },
+ { STB0899_AGC2REF, 0x33 },
+ { STB0899_TLSR, 0x84 },
+ { STB0899_CFD, 0xee },
+ { STB0899_ACLC, 0x87 },
+ { STB0899_BCLC, 0x94 },
+ { STB0899_EQON, 0x41 },
+ { STB0899_LDT, 0xdd },
+ { STB0899_LDT2, 0xc9 },
+ { STB0899_EQUALREF, 0xb4 },
+ { STB0899_TMGRAMP, 0x10 },
+ { STB0899_TMGTHD, 0x30 },
+ { STB0899_IDCCOMP, 0xfb },
+ { STB0899_QDCCOMP, 0x03 },
+ { STB0899_POWERI, 0x3b },
+ { STB0899_POWERQ, 0x3d },
+ { STB0899_RCOMP, 0x81 },
+ { STB0899_AGCIQIN, 0x80 },
+ { STB0899_AGC2I1, 0x04 },
+ { STB0899_AGC2I2, 0xf5 },
+ { STB0899_TLIR, 0x25 },
+ { STB0899_RTF, 0x80 },
+ { STB0899_DSTATUS, 0x00 },
+ { STB0899_LDI, 0xca },
+ { STB0899_CFRM, 0xf1 },
+ { STB0899_CFRL, 0xf3 },
+ { STB0899_NIRM, 0x2a },
+ { STB0899_NIRL, 0x05 },
+ { STB0899_ISYMB, 0x17 },
+ { STB0899_QSYMB, 0xfa },
+ { STB0899_SFRH, 0x2f },
+ { STB0899_SFRM, 0x68 },
+ { STB0899_SFRL, 0x40 },
+ { STB0899_SFRUPH, 0x2f },
+ { STB0899_SFRUPM, 0x68 },
+ { STB0899_SFRUPL, 0x40 },
+ { STB0899_EQUAI1, 0xfd },
+ { STB0899_EQUAQ1, 0x04 },
+ { STB0899_EQUAI2, 0x0f },
+ { STB0899_EQUAQ2, 0xff },
+ { STB0899_EQUAI3, 0xdf },
+ { STB0899_EQUAQ3, 0xfa },
+ { STB0899_EQUAI4, 0x37 },
+ { STB0899_EQUAQ4, 0x0d },
+ { STB0899_EQUAI5, 0xbd },
+ { STB0899_EQUAQ5, 0xf7 },
+ { STB0899_DSTATUS2, 0x00 },
+ { STB0899_VSTATUS, 0x00 },
+ { STB0899_VERROR, 0xff },
+ { STB0899_IQSWAP, 0x2a },
+ { STB0899_ECNT1M, 0x00 },
+ { STB0899_ECNT1L, 0x00 },
+ { STB0899_ECNT2M, 0x00 },
+ { STB0899_ECNT2L, 0x00 },
+ { STB0899_ECNT3M, 0x00 },
+ { STB0899_ECNT3L, 0x00 },
+ { STB0899_FECAUTO1, 0x06 },
+ { STB0899_FECM, 0x01 },
+ { STB0899_VTH12, 0xf0 },
+ { STB0899_VTH23, 0xa0 },
+ { STB0899_VTH34, 0x78 },
+ { STB0899_VTH56, 0x4e },
+ { STB0899_VTH67, 0x48 },
+ { STB0899_VTH78, 0x38 },
+ { STB0899_PRVIT, 0xff },
+ { STB0899_VITSYNC, 0x19 },
+ { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC, 0x42 },
+ { STB0899_RSLLC, 0x40 },
+ { STB0899_TSLPL, 0x12 },
+ { STB0899_TSCFGH, 0x0c },
+ { STB0899_TSCFGM, 0x00 },
+ { STB0899_TSCFGL, 0x0c },
+ { STB0899_TSOUT, 0x4d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL, 0x00 },
+ { STB0899_TSINHDELH, 0x02 },
+ { STB0899_TSINHDELM, 0x00 },
+ { STB0899_TSINHDELL, 0x00 },
+ { STB0899_TSLLSTKM, 0x00 },
+ { STB0899_TSLLSTKL, 0x00 },
+ { STB0899_TSULSTKM, 0x00 },
+ { STB0899_TSULSTKL, 0xab },
+ { STB0899_PCKLENUL, 0x00 },
+ { STB0899_PCKLENLL, 0xcc },
+ { STB0899_RSPCKLEN, 0xcc },
+ { STB0899_TSSTATUS, 0x80 },
+ { STB0899_ERRCTRL1, 0xb6 },
+ { STB0899_ERRCTRL2, 0x96 },
+ { STB0899_ERRCTRL3, 0x89 },
+ { STB0899_DMONMSK1, 0x27 },
+ { STB0899_DMONMSK0, 0x03 },
+ { STB0899_DEMAPVIT, 0x5c },
+ { STB0899_PLPARM, 0x1f },
+ { STB0899_PDELCTRL, 0x48 },
+ { STB0899_PDELCTRL2, 0x00 },
+ { STB0899_BBHCTRL1, 0x00 },
+ { STB0899_BBHCTRL2, 0x00 },
+ { STB0899_HYSTTHRESH, 0x77 },
+ { STB0899_MATCSTM, 0x00 },
+ { STB0899_MATCSTL, 0x00 },
+ { STB0899_UPLCSTM, 0x00 },
+ { STB0899_UPLCSTL, 0x00 },
+ { STB0899_DFLCSTM, 0x00 },
+ { STB0899_DFLCSTL, 0x00 },
+ { STB0899_SYNCCST, 0x00 },
+ { STB0899_SYNCDCSTM, 0x00 },
+ { STB0899_SYNCDCSTL, 0x00 },
+ { STB0899_ISI_ENTRY, 0x00 },
+ { STB0899_ISI_BIT_EN, 0x00 },
+ { STB0899_MATSTRM, 0x00 },
+ { STB0899_MATSTRL, 0x00 },
+ { STB0899_UPLSTRM, 0x00 },
+ { STB0899_UPLSTRL, 0x00 },
+ { STB0899_DFLSTRM, 0x00 },
+ { STB0899_DFLSTRL, 0x00 },
+ { STB0899_SYNCSTR, 0x00 },
+ { STB0899_SYNCDSTRM, 0x00 },
+ { STB0899_SYNCDSTRL, 0x00 },
+ { STB0899_CFGPDELSTATUS1, 0x10 },
+ { STB0899_CFGPDELSTATUS2, 0x00 },
+ { STB0899_BBFERRORM, 0x00 },
+ { STB0899_BBFERRORL, 0x00 },
+ { STB0899_UPKTERRORM, 0x00 },
+ { STB0899_UPKTERRORL, 0x00 },
+ { 0xffff, 0xff },
};
/* STB0899 demodulator config for the KNC1 and clones */
@@ -1153,8 +1156,8 @@ static u8 read_pwm(struct budget_av *budget_av)
{
u8 b = 0xff;
u8 pwm;
- struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
- {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
+ struct i2c_msg msg[] = { {.addr = 0x50, .flags = 0, .buf = &b, .len = 1},
+ {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1}
};
if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2)
@@ -1196,8 +1199,8 @@ static u8 read_pwm(struct budget_av *budget_av)
static void frontend_init(struct budget_av *budget_av)
{
- struct saa7146_dev * saa = budget_av->budget.dev;
- struct dvb_frontend * fe = NULL;
+ struct saa7146_dev *saa = budget_av->budget.dev;
+ struct dvb_frontend *fe = NULL;
/* Enable / PowerON Frontend */
saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
@@ -1207,16 +1210,16 @@ static void frontend_init(struct budget_av *budget_av)
/* additional setup necessary for the PLUS cards */
switch (saa->pci->subsystem_device) {
- case SUBID_DVBS_KNC1_PLUS:
- case SUBID_DVBC_KNC1_PLUS:
- case SUBID_DVBT_KNC1_PLUS:
- case SUBID_DVBC_EASYWATCH:
- case SUBID_DVBC_KNC1_PLUS_MK3:
- case SUBID_DVBS2_KNC1:
- case SUBID_DVBS2_KNC1_OEM:
- case SUBID_DVBS2_EASYWATCH:
- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
- break;
+ case SUBID_DVBS_KNC1_PLUS:
+ case SUBID_DVBC_KNC1_PLUS:
+ case SUBID_DVBT_KNC1_PLUS:
+ case SUBID_DVBC_EASYWATCH:
+ case SUBID_DVBC_KNC1_PLUS_MK3:
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
+ break;
}
switch (saa->pci->subsystem_device) {
@@ -1233,15 +1236,13 @@ static void frontend_init(struct budget_av *budget_av)
if (saa->pci->subsystem_vendor == 0x1894) {
fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config,
&budget_av->budget.i2c_adap);
- if (fe) {
+ if (fe)
dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap);
- }
} else {
fe = dvb_attach(stv0299_attach, &typhoon_config,
&budget_av->budget.i2c_adap);
- if (fe) {
+ if (fe)
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
- }
}
break;
@@ -1253,34 +1254,32 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBS_EASYWATCH_2:
fe = dvb_attach(stv0299_attach, &philips_sd1878_config,
&budget_av->budget.i2c_adap);
- if (fe) {
+ if (fe)
dvb_attach(dvb_pll_attach, fe, 0x60,
&budget_av->budget.i2c_adap,
DVB_PLL_PHILIPS_SD1878_TDA8261);
- }
break;
case SUBID_DVBS_TYPHOON:
fe = dvb_attach(stv0299_attach, &typhoon_config,
&budget_av->budget.i2c_adap);
- if (fe) {
+ if (fe)
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
- }
break;
case SUBID_DVBS2_KNC1:
case SUBID_DVBS2_KNC1_OEM:
case SUBID_DVBS2_EASYWATCH:
budget_av->reinitialise_demod = 1;
- if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap)))
+ fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap);
+ if (fe)
dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap);
break;
case SUBID_DVBS_CINERGY1200:
fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
&budget_av->budget.i2c_adap);
- if (fe) {
+ if (fe)
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
- }
break;
case SUBID_DVBC_KNC1:
@@ -1296,9 +1295,8 @@ static void frontend_init(struct budget_av *budget_av)
fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress,
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
- if (fe) {
+ if (fe)
fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
- }
break;
case SUBID_DVBC_EASYWATCH_MK3:
@@ -1312,9 +1310,8 @@ static void frontend_init(struct budget_av *budget_av)
&philips_cu1216_tda10023_config,
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
- if (fe) {
+ if (fe)
fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
- }
break;
case SUBID_DVBT_EASYWATCH:
@@ -1351,7 +1348,7 @@ static void frontend_init(struct budget_av *budget_av)
}
-static void budget_av_irq(struct saa7146_dev *dev, u32 * isr)
+static void budget_av_irq(struct saa7146_dev *dev, u32 *isr)
{
struct budget_av *budget_av = dev->ext_priv;
@@ -1368,7 +1365,7 @@ static int budget_av_detach(struct saa7146_dev *dev)
dprintk(2, "dev: %p\n", dev);
- if (1 == budget_av->has_saa7113) {
+ if (budget_av->has_saa7113 == 1) {
saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
msleep(200);
@@ -1439,7 +1436,8 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
dprintk(2, "dev: %p\n", dev);
- if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL)))
+ budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL);
+ if (!budget_av)
return -ENOMEM;
budget_av->has_saa7113 = 0;
@@ -1465,18 +1463,19 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if (err != 0) {
ttpci_budget_deinit(&budget_av->budget);
kfree(budget_av);
- ERR("cannot init vv subsystem\n");
+ pr_err("cannot init vv subsystem\n");
return err;
}
vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
- if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) {
+ err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO);
+ if (err) {
saa7146_vv_release(dev);
ttpci_budget_deinit(&budget_av->budget);
kfree(budget_av);
- ERR("cannot register capture v4l2 device\n");
+ pr_err("cannot register capture v4l2 device\n");
return err;
}
@@ -1510,15 +1509,15 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
}
static struct saa7146_standard standard[] = {
- {.name = "PAL",.id = V4L2_STD_PAL,
- .v_offset = 0x17,.v_field = 288,
- .h_offset = 0x14,.h_pixels = 680,
- .v_max_out = 576,.h_max_out = 768 },
-
- {.name = "NTSC",.id = V4L2_STD_NTSC,
- .v_offset = 0x16,.v_field = 240,
- .h_offset = 0x06,.h_pixels = 708,
- .v_max_out = 480,.h_max_out = 640, },
+ {.name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768 },
+
+ {.name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640, },
};
static struct saa7146_ext_vv vv_data = {
@@ -1532,8 +1531,8 @@ static struct saa7146_ext_vv vv_data = {
static struct saa7146_extension budget_extension;
MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
-MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2);
-MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(knc1s2, "KNC1 DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(sates2, "Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 66e1a004ee43..76de40e3c802 100644
--- a/drivers/media/pci/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ * budget-ci.ko: driver for the SAA7146 based Budget DVB cards
+ * with CI (but without analog video input)
*
* Compiled from various sources by Michael Hunold <michael@mihu.de>
*
@@ -123,7 +124,7 @@ static void msp430_ir_interrupt(struct tasklet_struct *t)
*/
if (ir_debug)
- printk("budget_ci: received byte 0x%02x\n", command);
+ pr_info("received byte 0x%02x\n", command);
/* Remove repeat bit, we use every command */
command = command & 0x7f;
@@ -164,7 +165,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
dev = rc_allocate_device(RC_DRIVER_SCANCODE);
if (!dev) {
- printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+ pr_err("IR interface initialisation failed\n");
return -ENOMEM;
}
@@ -223,7 +224,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
error = rc_register_device(dev);
if (error) {
- printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+ pr_err("could not init driver for IR device (code %d)\n", error);
rc_free_device(dev);
return error;
}
@@ -411,24 +412,21 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open
flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
if (flags & CICONTROL_CAMDETECT) {
// mark it as present if it wasn't before
- if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+ if (budget_ci->slot_status & SLOTSTATUS_NONE)
budget_ci->slot_status = SLOTSTATUS_PRESENT;
- }
// during a RESET, we check if we can read from IO memory to see when CAM is ready
if (budget_ci->slot_status & SLOTSTATUS_RESET) {
- if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) {
+ if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d)
budget_ci->slot_status = SLOTSTATUS_READY;
- }
}
} else {
budget_ci->slot_status = SLOTSTATUS_NONE;
}
if (budget_ci->slot_status != SLOTSTATUS_NONE) {
- if (budget_ci->slot_status & SLOTSTATUS_READY) {
+ if (budget_ci->slot_status & SLOTSTATUS_READY)
return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
- }
return DVB_CA_EN50221_POLL_CAM_PRESENT;
}
@@ -483,21 +481,21 @@ static int ciintf_init(struct budget_ci *budget_ci)
budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
budget_ci->ca.poll_slot_status = ciintf_poll_slot_status;
budget_ci->ca.data = budget_ci;
- if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
- &budget_ci->ca,
- ca_flags, 1)) != 0) {
- printk("budget_ci: CI interface detected, but initialisation failed.\n");
+
+ result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
+ &budget_ci->ca, ca_flags, 1);
+ if (result != 0) {
+ pr_err("CI interface detected, but initialisation failed.\n");
goto error;
}
// Setup CI slot IRQ
if (budget_ci->ci_irq) {
tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt);
- if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+ if (budget_ci->slot_status != SLOTSTATUS_NONE)
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
- } else {
+ else
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
- }
SAA7146_IER_ENABLE(saa, MASK_03);
}
@@ -506,7 +504,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
CICONTROL_RESET, 1, 0);
// success!
- printk("budget_ci: CI interface initialised\n");
+ pr_info("CI interface initialised\n");
budget_ci->budget.ci_present = 1;
// forge a fake CI IRQ so the CAM state is setup correctly
@@ -551,7 +549,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
saa7146_write(saa, MC1, MASK_27);
}
-static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
+static void budget_ci_irq(struct saa7146_dev *dev, u32 *isr)
{
struct budget_ci *budget_ci = dev->ext_priv;
@@ -651,7 +649,7 @@ static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe)
struct budget_ci *budget_ci = fe->dvb->priv;
u32 div;
u8 buf[4];
- struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+ struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
if ((p->frequency < 950000) || (p->frequency > 2150000))
return -EINVAL;
@@ -701,7 +699,7 @@ static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe)
struct budget_ci *budget_ci = fe->dvb->priv;
static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
- struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len =
+ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, .flags = 0, .buf = td1316_init, .len =
sizeof(td1316_init) };
// setup PLL configuration
@@ -731,7 +729,7 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct budget_ci *budget_ci = fe->dvb->priv;
u8 tuner_buf[4];
- struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
+ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, .flags = 0, .buf = tuner_buf, .len = sizeof(tuner_buf) };
int tuner_frequency = 0;
u8 band, cp, filter;
@@ -856,9 +854,9 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
// determine charge pump
tuner_frequency = p->frequency + 36125000;
- if (tuner_frequency < 87000000)
+ if (tuner_frequency < 87000000) {
return -EINVAL;
- else if (tuner_frequency < 130000000) {
+ } else if (tuner_frequency < 130000000) {
cp = 3;
band = 1;
} else if (tuner_frequency < 160000000) {
@@ -885,8 +883,9 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
} else if (tuner_frequency < 895000000) {
cp = 7;
band = 4;
- } else
+ } else {
return -EINVAL;
+ }
// assume PLL filter should always be 8MHz for the moment.
filter = 1;
@@ -1035,222 +1034,222 @@ static struct tda827x_config tda827x_config = {
/* TT S2-3200 DVB-S (STB0899) Inittab */
static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
- { STB0899_DEV_ID , 0x81 },
- { STB0899_DISCNTRL1 , 0x32 },
- { STB0899_DISCNTRL2 , 0x80 },
- { STB0899_DISRX_ST0 , 0x04 },
- { STB0899_DISRX_ST1 , 0x00 },
- { STB0899_DISPARITY , 0x00 },
- { STB0899_DISSTATUS , 0x20 },
- { STB0899_DISF22 , 0x8c },
- { STB0899_DISF22RX , 0x9a },
- { STB0899_SYSREG , 0x0b },
- { STB0899_ACRPRESC , 0x11 },
- { STB0899_ACRDIV1 , 0x0a },
- { STB0899_ACRDIV2 , 0x05 },
- { STB0899_DACR1 , 0x00 },
- { STB0899_DACR2 , 0x00 },
- { STB0899_OUTCFG , 0x00 },
- { STB0899_MODECFG , 0x00 },
- { STB0899_IRQSTATUS_3 , 0x30 },
- { STB0899_IRQSTATUS_2 , 0x00 },
- { STB0899_IRQSTATUS_1 , 0x00 },
- { STB0899_IRQSTATUS_0 , 0x00 },
- { STB0899_IRQMSK_3 , 0xf3 },
- { STB0899_IRQMSK_2 , 0xfc },
- { STB0899_IRQMSK_1 , 0xff },
- { STB0899_IRQMSK_0 , 0xff },
- { STB0899_IRQCFG , 0x00 },
- { STB0899_I2CCFG , 0x88 },
- { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
- { STB0899_IOPVALUE5 , 0x00 },
- { STB0899_IOPVALUE4 , 0x20 },
- { STB0899_IOPVALUE3 , 0xc9 },
- { STB0899_IOPVALUE2 , 0x90 },
- { STB0899_IOPVALUE1 , 0x40 },
- { STB0899_IOPVALUE0 , 0x00 },
- { STB0899_GPIO00CFG , 0x82 },
- { STB0899_GPIO01CFG , 0x82 },
- { STB0899_GPIO02CFG , 0x82 },
- { STB0899_GPIO03CFG , 0x82 },
- { STB0899_GPIO04CFG , 0x82 },
- { STB0899_GPIO05CFG , 0x82 },
- { STB0899_GPIO06CFG , 0x82 },
- { STB0899_GPIO07CFG , 0x82 },
- { STB0899_GPIO08CFG , 0x82 },
- { STB0899_GPIO09CFG , 0x82 },
- { STB0899_GPIO10CFG , 0x82 },
- { STB0899_GPIO11CFG , 0x82 },
- { STB0899_GPIO12CFG , 0x82 },
- { STB0899_GPIO13CFG , 0x82 },
- { STB0899_GPIO14CFG , 0x82 },
- { STB0899_GPIO15CFG , 0x82 },
- { STB0899_GPIO16CFG , 0x82 },
- { STB0899_GPIO17CFG , 0x82 },
- { STB0899_GPIO18CFG , 0x82 },
- { STB0899_GPIO19CFG , 0x82 },
- { STB0899_GPIO20CFG , 0x82 },
- { STB0899_SDATCFG , 0xb8 },
- { STB0899_SCLTCFG , 0xba },
- { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */
- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
- { STB0899_DIRCLKCFG , 0x82 },
- { STB0899_CLKOUT27CFG , 0x7e },
- { STB0899_STDBYCFG , 0x82 },
- { STB0899_CS0CFG , 0x82 },
- { STB0899_CS1CFG , 0x82 },
- { STB0899_DISEQCOCFG , 0x20 },
- { STB0899_GPIO32CFG , 0x82 },
- { STB0899_GPIO33CFG , 0x82 },
- { STB0899_GPIO34CFG , 0x82 },
- { STB0899_GPIO35CFG , 0x82 },
- { STB0899_GPIO36CFG , 0x82 },
- { STB0899_GPIO37CFG , 0x82 },
- { STB0899_GPIO38CFG , 0x82 },
- { STB0899_GPIO39CFG , 0x82 },
- { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
- { STB0899_FILTCTRL , 0x00 },
- { STB0899_SYSCTRL , 0x00 },
- { STB0899_STOPCLK1 , 0x20 },
- { STB0899_STOPCLK2 , 0x00 },
- { STB0899_INTBUFSTATUS , 0x00 },
- { STB0899_INTBUFCTRL , 0x0a },
- { 0xffff , 0xff },
+ { STB0899_DEV_ID, 0x81 },
+ { STB0899_DISCNTRL1, 0x32 },
+ { STB0899_DISCNTRL2, 0x80 },
+ { STB0899_DISRX_ST0, 0x04 },
+ { STB0899_DISRX_ST1, 0x00 },
+ { STB0899_DISPARITY, 0x00 },
+ { STB0899_DISSTATUS, 0x20 },
+ { STB0899_DISF22, 0x8c },
+ { STB0899_DISF22RX, 0x9a },
+ { STB0899_SYSREG, 0x0b },
+ { STB0899_ACRPRESC, 0x11 },
+ { STB0899_ACRDIV1, 0x0a },
+ { STB0899_ACRDIV2, 0x05 },
+ { STB0899_DACR1, 0x00 },
+ { STB0899_DACR2, 0x00 },
+ { STB0899_OUTCFG, 0x00 },
+ { STB0899_MODECFG, 0x00 },
+ { STB0899_IRQSTATUS_3, 0x30 },
+ { STB0899_IRQSTATUS_2, 0x00 },
+ { STB0899_IRQSTATUS_1, 0x00 },
+ { STB0899_IRQSTATUS_0, 0x00 },
+ { STB0899_IRQMSK_3, 0xf3 },
+ { STB0899_IRQMSK_2, 0xfc },
+ { STB0899_IRQMSK_1, 0xff },
+ { STB0899_IRQMSK_0, 0xff },
+ { STB0899_IRQCFG, 0x00 },
+ { STB0899_I2CCFG, 0x88 },
+ { STB0899_I2CRPT, 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
+ { STB0899_IOPVALUE5, 0x00 },
+ { STB0899_IOPVALUE4, 0x20 },
+ { STB0899_IOPVALUE3, 0xc9 },
+ { STB0899_IOPVALUE2, 0x90 },
+ { STB0899_IOPVALUE1, 0x40 },
+ { STB0899_IOPVALUE0, 0x00 },
+ { STB0899_GPIO00CFG, 0x82 },
+ { STB0899_GPIO01CFG, 0x82 },
+ { STB0899_GPIO02CFG, 0x82 },
+ { STB0899_GPIO03CFG, 0x82 },
+ { STB0899_GPIO04CFG, 0x82 },
+ { STB0899_GPIO05CFG, 0x82 },
+ { STB0899_GPIO06CFG, 0x82 },
+ { STB0899_GPIO07CFG, 0x82 },
+ { STB0899_GPIO08CFG, 0x82 },
+ { STB0899_GPIO09CFG, 0x82 },
+ { STB0899_GPIO10CFG, 0x82 },
+ { STB0899_GPIO11CFG, 0x82 },
+ { STB0899_GPIO12CFG, 0x82 },
+ { STB0899_GPIO13CFG, 0x82 },
+ { STB0899_GPIO14CFG, 0x82 },
+ { STB0899_GPIO15CFG, 0x82 },
+ { STB0899_GPIO16CFG, 0x82 },
+ { STB0899_GPIO17CFG, 0x82 },
+ { STB0899_GPIO18CFG, 0x82 },
+ { STB0899_GPIO19CFG, 0x82 },
+ { STB0899_GPIO20CFG, 0x82 },
+ { STB0899_SDATCFG, 0xb8 },
+ { STB0899_SCLTCFG, 0xba },
+ { STB0899_AGCRFCFG, 0x1c }, /* 0x11 */
+ { STB0899_GPIO22, 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21, 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG, 0x82 },
+ { STB0899_CLKOUT27CFG, 0x7e },
+ { STB0899_STDBYCFG, 0x82 },
+ { STB0899_CS0CFG, 0x82 },
+ { STB0899_CS1CFG, 0x82 },
+ { STB0899_DISEQCOCFG, 0x20 },
+ { STB0899_GPIO32CFG, 0x82 },
+ { STB0899_GPIO33CFG, 0x82 },
+ { STB0899_GPIO34CFG, 0x82 },
+ { STB0899_GPIO35CFG, 0x82 },
+ { STB0899_GPIO36CFG, 0x82 },
+ { STB0899_GPIO37CFG, 0x82 },
+ { STB0899_GPIO38CFG, 0x82 },
+ { STB0899_GPIO39CFG, 0x82 },
+ { STB0899_NCOARSE, 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL, 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL, 0x00 },
+ { STB0899_SYSCTRL, 0x00 },
+ { STB0899_STOPCLK1, 0x20 },
+ { STB0899_STOPCLK2, 0x00 },
+ { STB0899_INTBUFSTATUS, 0x00 },
+ { STB0899_INTBUFCTRL, 0x0a },
+ { 0xffff, 0xff },
};
static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
- { STB0899_DEMOD , 0x00 },
- { STB0899_RCOMPC , 0xc9 },
- { STB0899_AGC1CN , 0x41 },
- { STB0899_AGC1REF , 0x10 },
- { STB0899_RTC , 0x7a },
- { STB0899_TMGCFG , 0x4e },
- { STB0899_AGC2REF , 0x34 },
- { STB0899_TLSR , 0x84 },
- { STB0899_CFD , 0xc7 },
- { STB0899_ACLC , 0x87 },
- { STB0899_BCLC , 0x94 },
- { STB0899_EQON , 0x41 },
- { STB0899_LDT , 0xdd },
- { STB0899_LDT2 , 0xc9 },
- { STB0899_EQUALREF , 0xb4 },
- { STB0899_TMGRAMP , 0x10 },
- { STB0899_TMGTHD , 0x30 },
- { STB0899_IDCCOMP , 0xfb },
- { STB0899_QDCCOMP , 0x03 },
- { STB0899_POWERI , 0x3b },
- { STB0899_POWERQ , 0x3d },
- { STB0899_RCOMP , 0x81 },
- { STB0899_AGCIQIN , 0x80 },
- { STB0899_AGC2I1 , 0x04 },
- { STB0899_AGC2I2 , 0xf5 },
- { STB0899_TLIR , 0x25 },
- { STB0899_RTF , 0x80 },
- { STB0899_DSTATUS , 0x00 },
- { STB0899_LDI , 0xca },
- { STB0899_CFRM , 0xf1 },
- { STB0899_CFRL , 0xf3 },
- { STB0899_NIRM , 0x2a },
- { STB0899_NIRL , 0x05 },
- { STB0899_ISYMB , 0x17 },
- { STB0899_QSYMB , 0xfa },
- { STB0899_SFRH , 0x2f },
- { STB0899_SFRM , 0x68 },
- { STB0899_SFRL , 0x40 },
- { STB0899_SFRUPH , 0x2f },
- { STB0899_SFRUPM , 0x68 },
- { STB0899_SFRUPL , 0x40 },
- { STB0899_EQUAI1 , 0xfd },
- { STB0899_EQUAQ1 , 0x04 },
- { STB0899_EQUAI2 , 0x0f },
- { STB0899_EQUAQ2 , 0xff },
- { STB0899_EQUAI3 , 0xdf },
- { STB0899_EQUAQ3 , 0xfa },
- { STB0899_EQUAI4 , 0x37 },
- { STB0899_EQUAQ4 , 0x0d },
- { STB0899_EQUAI5 , 0xbd },
- { STB0899_EQUAQ5 , 0xf7 },
- { STB0899_DSTATUS2 , 0x00 },
- { STB0899_VSTATUS , 0x00 },
- { STB0899_VERROR , 0xff },
- { STB0899_IQSWAP , 0x2a },
- { STB0899_ECNT1M , 0x00 },
- { STB0899_ECNT1L , 0x00 },
- { STB0899_ECNT2M , 0x00 },
- { STB0899_ECNT2L , 0x00 },
- { STB0899_ECNT3M , 0x00 },
- { STB0899_ECNT3L , 0x00 },
- { STB0899_FECAUTO1 , 0x06 },
- { STB0899_FECM , 0x01 },
- { STB0899_VTH12 , 0xf0 },
- { STB0899_VTH23 , 0xa0 },
- { STB0899_VTH34 , 0x78 },
- { STB0899_VTH56 , 0x4e },
- { STB0899_VTH67 , 0x48 },
- { STB0899_VTH78 , 0x38 },
- { STB0899_PRVIT , 0xff },
- { STB0899_VITSYNC , 0x19 },
- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
- { STB0899_TSULC , 0x42 },
- { STB0899_RSLLC , 0x40 },
- { STB0899_TSLPL , 0x12 },
- { STB0899_TSCFGH , 0x0c },
- { STB0899_TSCFGM , 0x00 },
- { STB0899_TSCFGL , 0x0c },
- { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
- { STB0899_RSSYNCDEL , 0x00 },
- { STB0899_TSINHDELH , 0x02 },
- { STB0899_TSINHDELM , 0x00 },
- { STB0899_TSINHDELL , 0x00 },
- { STB0899_TSLLSTKM , 0x00 },
- { STB0899_TSLLSTKL , 0x00 },
- { STB0899_TSULSTKM , 0x00 },
- { STB0899_TSULSTKL , 0xab },
- { STB0899_PCKLENUL , 0x00 },
- { STB0899_PCKLENLL , 0xcc },
- { STB0899_RSPCKLEN , 0xcc },
- { STB0899_TSSTATUS , 0x80 },
- { STB0899_ERRCTRL1 , 0xb6 },
- { STB0899_ERRCTRL2 , 0x96 },
- { STB0899_ERRCTRL3 , 0x89 },
- { STB0899_DMONMSK1 , 0x27 },
- { STB0899_DMONMSK0 , 0x03 },
- { STB0899_DEMAPVIT , 0x5c },
- { STB0899_PLPARM , 0x1f },
- { STB0899_PDELCTRL , 0x48 },
- { STB0899_PDELCTRL2 , 0x00 },
- { STB0899_BBHCTRL1 , 0x00 },
- { STB0899_BBHCTRL2 , 0x00 },
- { STB0899_HYSTTHRESH , 0x77 },
- { STB0899_MATCSTM , 0x00 },
- { STB0899_MATCSTL , 0x00 },
- { STB0899_UPLCSTM , 0x00 },
- { STB0899_UPLCSTL , 0x00 },
- { STB0899_DFLCSTM , 0x00 },
- { STB0899_DFLCSTL , 0x00 },
- { STB0899_SYNCCST , 0x00 },
- { STB0899_SYNCDCSTM , 0x00 },
- { STB0899_SYNCDCSTL , 0x00 },
- { STB0899_ISI_ENTRY , 0x00 },
- { STB0899_ISI_BIT_EN , 0x00 },
- { STB0899_MATSTRM , 0x00 },
- { STB0899_MATSTRL , 0x00 },
- { STB0899_UPLSTRM , 0x00 },
- { STB0899_UPLSTRL , 0x00 },
- { STB0899_DFLSTRM , 0x00 },
- { STB0899_DFLSTRL , 0x00 },
- { STB0899_SYNCSTR , 0x00 },
- { STB0899_SYNCDSTRM , 0x00 },
- { STB0899_SYNCDSTRL , 0x00 },
- { STB0899_CFGPDELSTATUS1 , 0x10 },
- { STB0899_CFGPDELSTATUS2 , 0x00 },
- { STB0899_BBFERRORM , 0x00 },
- { STB0899_BBFERRORL , 0x00 },
- { STB0899_UPKTERRORM , 0x00 },
- { STB0899_UPKTERRORL , 0x00 },
- { 0xffff , 0xff },
+ { STB0899_DEMOD, 0x00 },
+ { STB0899_RCOMPC, 0xc9 },
+ { STB0899_AGC1CN, 0x41 },
+ { STB0899_AGC1REF, 0x10 },
+ { STB0899_RTC, 0x7a },
+ { STB0899_TMGCFG, 0x4e },
+ { STB0899_AGC2REF, 0x34 },
+ { STB0899_TLSR, 0x84 },
+ { STB0899_CFD, 0xc7 },
+ { STB0899_ACLC, 0x87 },
+ { STB0899_BCLC, 0x94 },
+ { STB0899_EQON, 0x41 },
+ { STB0899_LDT, 0xdd },
+ { STB0899_LDT2, 0xc9 },
+ { STB0899_EQUALREF, 0xb4 },
+ { STB0899_TMGRAMP, 0x10 },
+ { STB0899_TMGTHD, 0x30 },
+ { STB0899_IDCCOMP, 0xfb },
+ { STB0899_QDCCOMP, 0x03 },
+ { STB0899_POWERI, 0x3b },
+ { STB0899_POWERQ, 0x3d },
+ { STB0899_RCOMP, 0x81 },
+ { STB0899_AGCIQIN, 0x80 },
+ { STB0899_AGC2I1, 0x04 },
+ { STB0899_AGC2I2, 0xf5 },
+ { STB0899_TLIR, 0x25 },
+ { STB0899_RTF, 0x80 },
+ { STB0899_DSTATUS, 0x00 },
+ { STB0899_LDI, 0xca },
+ { STB0899_CFRM, 0xf1 },
+ { STB0899_CFRL, 0xf3 },
+ { STB0899_NIRM, 0x2a },
+ { STB0899_NIRL, 0x05 },
+ { STB0899_ISYMB, 0x17 },
+ { STB0899_QSYMB, 0xfa },
+ { STB0899_SFRH, 0x2f },
+ { STB0899_SFRM, 0x68 },
+ { STB0899_SFRL, 0x40 },
+ { STB0899_SFRUPH, 0x2f },
+ { STB0899_SFRUPM, 0x68 },
+ { STB0899_SFRUPL, 0x40 },
+ { STB0899_EQUAI1, 0xfd },
+ { STB0899_EQUAQ1, 0x04 },
+ { STB0899_EQUAI2, 0x0f },
+ { STB0899_EQUAQ2, 0xff },
+ { STB0899_EQUAI3, 0xdf },
+ { STB0899_EQUAQ3, 0xfa },
+ { STB0899_EQUAI4, 0x37 },
+ { STB0899_EQUAQ4, 0x0d },
+ { STB0899_EQUAI5, 0xbd },
+ { STB0899_EQUAQ5, 0xf7 },
+ { STB0899_DSTATUS2, 0x00 },
+ { STB0899_VSTATUS, 0x00 },
+ { STB0899_VERROR, 0xff },
+ { STB0899_IQSWAP, 0x2a },
+ { STB0899_ECNT1M, 0x00 },
+ { STB0899_ECNT1L, 0x00 },
+ { STB0899_ECNT2M, 0x00 },
+ { STB0899_ECNT2L, 0x00 },
+ { STB0899_ECNT3M, 0x00 },
+ { STB0899_ECNT3L, 0x00 },
+ { STB0899_FECAUTO1, 0x06 },
+ { STB0899_FECM, 0x01 },
+ { STB0899_VTH12, 0xf0 },
+ { STB0899_VTH23, 0xa0 },
+ { STB0899_VTH34, 0x78 },
+ { STB0899_VTH56, 0x4e },
+ { STB0899_VTH67, 0x48 },
+ { STB0899_VTH78, 0x38 },
+ { STB0899_PRVIT, 0xff },
+ { STB0899_VITSYNC, 0x19 },
+ { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC, 0x42 },
+ { STB0899_RSLLC, 0x40 },
+ { STB0899_TSLPL, 0x12 },
+ { STB0899_TSCFGH, 0x0c },
+ { STB0899_TSCFGM, 0x00 },
+ { STB0899_TSCFGL, 0x0c },
+ { STB0899_TSOUT, 0x4d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL, 0x00 },
+ { STB0899_TSINHDELH, 0x02 },
+ { STB0899_TSINHDELM, 0x00 },
+ { STB0899_TSINHDELL, 0x00 },
+ { STB0899_TSLLSTKM, 0x00 },
+ { STB0899_TSLLSTKL, 0x00 },
+ { STB0899_TSULSTKM, 0x00 },
+ { STB0899_TSULSTKL, 0xab },
+ { STB0899_PCKLENUL, 0x00 },
+ { STB0899_PCKLENLL, 0xcc },
+ { STB0899_RSPCKLEN, 0xcc },
+ { STB0899_TSSTATUS, 0x80 },
+ { STB0899_ERRCTRL1, 0xb6 },
+ { STB0899_ERRCTRL2, 0x96 },
+ { STB0899_ERRCTRL3, 0x89 },
+ { STB0899_DMONMSK1, 0x27 },
+ { STB0899_DMONMSK0, 0x03 },
+ { STB0899_DEMAPVIT, 0x5c },
+ { STB0899_PLPARM, 0x1f },
+ { STB0899_PDELCTRL, 0x48 },
+ { STB0899_PDELCTRL2, 0x00 },
+ { STB0899_BBHCTRL1, 0x00 },
+ { STB0899_BBHCTRL2, 0x00 },
+ { STB0899_HYSTTHRESH, 0x77 },
+ { STB0899_MATCSTM, 0x00 },
+ { STB0899_MATCSTL, 0x00 },
+ { STB0899_UPLCSTM, 0x00 },
+ { STB0899_UPLCSTL, 0x00 },
+ { STB0899_DFLCSTM, 0x00 },
+ { STB0899_DFLCSTL, 0x00 },
+ { STB0899_SYNCCST, 0x00 },
+ { STB0899_SYNCDCSTM, 0x00 },
+ { STB0899_SYNCDCSTL, 0x00 },
+ { STB0899_ISI_ENTRY, 0x00 },
+ { STB0899_ISI_BIT_EN, 0x00 },
+ { STB0899_MATSTRM, 0x00 },
+ { STB0899_MATSTRL, 0x00 },
+ { STB0899_UPLSTRM, 0x00 },
+ { STB0899_UPLSTRL, 0x00 },
+ { STB0899_DFLSTRM, 0x00 },
+ { STB0899_DFLSTRL, 0x00 },
+ { STB0899_SYNCSTR, 0x00 },
+ { STB0899_SYNCDSTRM, 0x00 },
+ { STB0899_SYNCDSTRL, 0x00 },
+ { STB0899_CFGPDELSTATUS1, 0x10 },
+ { STB0899_CFGPDELSTATUS2, 0x00 },
+ { STB0899_BBFERRORM, 0x00 },
+ { STB0899_BBFERRORL, 0x00 },
+ { STB0899_UPKTERRORM, 0x00 },
+ { STB0899_UPKTERRORL, 0x00 },
+ { 0xffff, 0xff },
};
static struct stb0899_config tt3200_config = {
@@ -1359,7 +1358,7 @@ static void frontend_init(struct budget_ci *budget_ci)
budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) {
- printk("%s: No LNBP21 found!\n", __func__);
+ pr_err("%s(): No LNBP21 found!\n", __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
@@ -1370,7 +1369,7 @@ static void frontend_init(struct budget_ci *budget_ci)
budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48);
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) {
- printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+ pr_err("%s(): No tda827x found!\n", __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
@@ -1382,12 +1381,12 @@ static void frontend_init(struct budget_ci *budget_ci)
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) {
if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
- printk(KERN_ERR "%s: No LNBP21 found!\n", __func__);
+ pr_err("%s(): No LNBP21 found!\n", __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
} else {
- printk(KERN_ERR "%s: No STB6000 found!\n", __func__);
+ pr_err("%s(): No STB6000 found!\n", __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
@@ -1422,13 +1421,13 @@ static void frontend_init(struct budget_ci *budget_ci)
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
- printk("%s: No LNBP21 found!\n", __func__);
+ pr_err("%s(): No LNBP21 found!\n", __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
} else {
- dvb_frontend_detach(budget_ci->budget.dvb_frontend);
- budget_ci->budget.dvb_frontend = NULL;
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
}
}
break;
@@ -1436,7 +1435,7 @@ static void frontend_init(struct budget_ci *budget_ci)
}
if (budget_ci->budget.dvb_frontend == NULL) {
- printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
budget_ci->budget.dev->pci->vendor,
budget_ci->budget.dev->pci->device,
budget_ci->budget.dev->pci->subsystem_vendor,
@@ -1444,7 +1443,7 @@ static void frontend_init(struct budget_ci *budget_ci)
} else {
if (dvb_register_frontend
(&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
- printk("budget-ci: Frontend registration failed!\n");
+ pr_err("Frontend registration failed!\n");
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
@@ -1538,7 +1537,7 @@ static const struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b),
{
.vendor = 0,
- }
+ }
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
index 25f44c3eebf3..d33adeca196f 100644
--- a/drivers/media/pci/ttpci/budget-core.c
+++ b/drivers/media/pci/ttpci/budget-core.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ * budget-core.ko: base-driver for the SAA7146 based Budget DVB cards
*
* Compiled from various sources by Michael Hunold <michael@mihu.de>
*
@@ -34,6 +34,7 @@
#define BUFFER_WARNING_WAIT (30*HZ)
int budget_debug;
+EXPORT_SYMBOL_GPL(budget_debug);
static int dma_buffer_size = TS_MIN_BUFSIZE_K;
module_param_named(debug, budget_debug, int, 0644);
module_param_named(bufsize, dma_buffer_size, int, 0444);
@@ -80,7 +81,7 @@ static int start_ts_capture(struct budget *budget)
* Pitch: 188, NumBytes3: 188, NumLines3: 1024
*/
- switch(budget->card->type) {
+ switch (budget->card->type) {
case BUDGET_FS_ACTIVY:
saa7146_write(dev, DD1_INIT, 0x04000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25));
@@ -208,7 +209,7 @@ static void vpeirq(struct tasklet_struct *t)
budget->buffer_warnings++;
if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) {
- printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
+ pr_warn("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
budget->dev->name, __func__, budget->buffer_warnings, count);
budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT;
budget->buffer_warnings = 0;
@@ -259,6 +260,7 @@ int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count
return ttpci_budget_debiread_nolock(budget, config, addr,
count, nobusyloop);
}
+EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config,
int addr, int count, u32 value, int nobusyloop)
@@ -299,6 +301,7 @@ int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
return ttpci_budget_debiwrite_nolock(budget, config, addr,
count, value, nobusyloop);
}
+EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
/****************************************************************************
@@ -423,7 +426,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
budget->card = bi;
budget->dev = (struct saa7146_dev *) dev;
- switch(budget->card->type) {
+ switch (budget->card->type) {
case BUDGET_FS_ACTIVY:
budget->buffer_width = TS_WIDTH_ACTIVY;
max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
@@ -470,7 +473,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
budget->dev->name,
budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
budget->buffer_width, budget->buffer_height);
- printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
+ pr_info("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
owner, &budget->dev->pci->dev, adapter_nums);
@@ -491,8 +494,10 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
spin_lock_init(&budget->feedlock);
spin_lock_init(&budget->debilock);
- /* the Siemens DVB needs this if you want to have the i2c chips
- get recognized before the main driver is loaded */
+ /*
+ * the Siemens DVB needs this if you want to have the i2c chips
+ * get recognized before the main driver is loaded
+ */
if (bi->type != BUDGET_FS_ACTIVY)
saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
@@ -511,7 +516,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt);
- if (NULL == budget->grabbing) {
+ if (budget->grabbing == NULL) {
ret = -ENOMEM;
goto err_del_i2c;
}
@@ -526,7 +531,8 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
if (bi->type != BUDGET_FS_ACTIVY)
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
- if ((ret = budget_register(budget)) == 0)
+ ret = budget_register(budget);
+ if (ret == 0)
return 0; /* Everything OK */
/* An error occurred, cleanup resources */
@@ -540,6 +546,7 @@ err_dvb_unregister:
return ret;
}
+EXPORT_SYMBOL_GPL(ttpci_budget_init);
void ttpci_budget_init_hooks(struct budget *budget)
{
@@ -548,6 +555,7 @@ void ttpci_budget_init_hooks(struct budget *budget)
budget->dvb_frontend->ops.read_status = budget_read_fe_status;
}
}
+EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks);
int ttpci_budget_deinit(struct budget *budget)
{
@@ -567,8 +575,9 @@ int ttpci_budget_deinit(struct budget *budget)
return 0;
}
+EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
-void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
+void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 *isr)
{
struct budget *budget = dev->ext_priv;
@@ -577,6 +586,7 @@ void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
if (*isr & MASK_10)
tasklet_schedule(&budget->vpe_tasklet);
}
+EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
{
@@ -590,14 +600,6 @@ void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
}
spin_unlock(&budget->feedlock);
}
-
-EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
-EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
-EXPORT_SYMBOL_GPL(ttpci_budget_init);
-EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks);
-EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
-EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
-EXPORT_SYMBOL_GPL(budget_debug);
MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
index b76a1b330b50..f623c250909b 100644
--- a/drivers/media/pci/ttpci/budget.c
+++ b/drivers/media/pci/ttpci/budget.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * budget.c: driver for the SAA7146 based Budget DVB cards
+ * budget.ko: driver for the SAA7146 based Budget DVB cards
+ * without analog video input or CI
*
* Compiled from various sources by Michael Hunold <michael@mihu.de>
*
@@ -42,20 +43,24 @@ MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-static void Set22K (struct budget *budget, int state)
+static void Set22K(struct budget *budget, int state)
{
- struct saa7146_dev *dev=budget->dev;
+ struct saa7146_dev *dev = budget->dev;
+
dprintk(2, "budget: %p\n", budget);
saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
}
-/* Diseqc functions only for TT Budget card */
-/* taken from the Skyvision DVB driver by
- Ralph Metzler <rjkm@metzlerbros.de> */
+/*
+ * Diseqc functions only for TT Budget card
+ * taken from the Skyvision DVB driver by
+ * Ralph Metzler <rjkm@metzlerbros.de>
+ */
-static void DiseqcSendBit (struct budget *budget, int data)
+static void DiseqcSendBit(struct budget *budget, int data)
{
- struct saa7146_dev *dev=budget->dev;
+ struct saa7146_dev *dev = budget->dev;
+
dprintk(2, "budget: %p\n", budget);
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
@@ -64,13 +69,13 @@ static void DiseqcSendBit (struct budget *budget, int data)
udelay(data ? 1000 : 500);
}
-static void DiseqcSendByte (struct budget *budget, int data)
+static void DiseqcSendByte(struct budget *budget, int data)
{
- int i, par=1, d;
+ int i, par = 1, d;
dprintk(2, "budget: %p\n", budget);
- for (i=7; i>=0; i--) {
+ for (i = 7; i >= 0; i--) {
d = (data>>i)&1;
par ^= d;
DiseqcSendBit(budget, d);
@@ -79,9 +84,9 @@ static void DiseqcSendByte (struct budget *budget, int data)
DiseqcSendBit(budget, par);
}
-static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+static int SendDiSEqCMsg(struct budget *budget, int len, u8 *msg, unsigned long burst)
{
- struct saa7146_dev *dev=budget->dev;
+ struct saa7146_dev *dev = budget->dev;
int i;
dprintk(2, "budget: %p\n", budget);
@@ -89,15 +94,15 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
mdelay(16);
- for (i=0; i<len; i++)
+ for (i = 0; i < len; i++)
DiseqcSendByte(budget, msg[i]);
mdelay(16);
- if (burst!=-1) {
- if (burst)
+ if (burst != -1) {
+ if (burst) {
DiseqcSendByte(budget, 0xff);
- else {
+ } else {
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
mdelay(12);
udelay(500);
@@ -118,24 +123,24 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long
static int SetVoltage_Activy(struct budget *budget,
enum fe_sec_voltage voltage)
{
- struct saa7146_dev *dev=budget->dev;
+ struct saa7146_dev *dev = budget->dev;
dprintk(2, "budget: %p\n", budget);
switch (voltage) {
- case SEC_VOLTAGE_13:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
- break;
- case SEC_VOLTAGE_18:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
- break;
- case SEC_VOLTAGE_OFF:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
- break;
- default:
- return -EINVAL;
+ case SEC_VOLTAGE_13:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+ break;
+ case SEC_VOLTAGE_18:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+ break;
+ case SEC_VOLTAGE_OFF:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -146,7 +151,7 @@ static int siemens_budget_set_voltage(struct dvb_frontend *fe,
{
struct budget *budget = fe->dvb->priv;
- return SetVoltage_Activy (budget, voltage);
+ return SetVoltage_Activy(budget, voltage);
}
static int budget_set_tone(struct dvb_frontend *fe,
@@ -156,11 +161,11 @@ static int budget_set_tone(struct dvb_frontend *fe,
switch (tone) {
case SEC_TONE_ON:
- Set22K (budget, 1);
+ Set22K(budget, 1);
break;
case SEC_TONE_OFF:
- Set22K (budget, 0);
+ Set22K(budget, 0);
break;
default:
@@ -170,11 +175,11 @@ static int budget_set_tone(struct dvb_frontend *fe,
return 0;
}
-static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+static int budget_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
{
struct budget *budget = fe->dvb->priv;
- SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+ SendDiSEqCMsg(budget, cmd->msg_len, cmd->msg, 0);
return 0;
}
@@ -184,7 +189,7 @@ static int budget_diseqc_send_burst(struct dvb_frontend *fe,
{
struct budget *budget = fe->dvb->priv;
- SendDiSEqCMsg (budget, 0, NULL, minicmd);
+ SendDiSEqCMsg(budget, 0, NULL, minicmd);
return 0;
}
@@ -208,7 +213,8 @@ static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
pwr = 0;
else if (c->frequency >= 1100000)
pwr = 1;
- else pwr = 2;
+ else
+ pwr = 2;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
@@ -220,12 +226,12 @@ static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
return 0;
}
-static struct ves1x93_config alps_bsrv2_config =
-{
+static struct ves1x93_config alps_bsrv2_config = {
.demod_address = 0x08,
.xin = 90100000UL,
.invert_pwm = 0,
@@ -248,7 +254,8 @@ static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
return 0;
}
@@ -303,7 +310,8 @@ static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
return 0;
}
@@ -333,7 +341,8 @@ static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
return 0;
}
@@ -365,7 +374,8 @@ static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
return 0;
}
@@ -422,12 +432,12 @@ static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
}
-static u8 read_pwm(struct budget* budget)
+static u8 read_pwm(struct budget *budget)
{
u8 b = 0xff;
u8 pwm;
- struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
- { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+ struct i2c_msg msg[] = { { .addr = 0x50, .flags = 0, .buf = &b, .len = 1 },
+ { .addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} };
if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
pwm = 0x48;
@@ -478,7 +488,7 @@ static void frontend_init(struct budget *budget)
{
(void)alps_bsbe1_config; /* avoid warning */
- switch(budget->dev->pci->subsystem_device) {
+ switch (budget->dev->pci->subsystem_device) {
case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
case 0x1013:
// try the ALPS BSRV2 first of all
@@ -527,7 +537,7 @@ static void frontend_init(struct budget *budget)
case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n");
+ pr_info("tuner ALPS BSRU6 in Philips Semi. Sylt detected\n");
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
break;
@@ -545,7 +555,7 @@ static void frontend_init(struct budget *budget)
/* assume ALPS BSRU6 */
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
+ pr_info("tuner ALPS BSRU6 detected\n");
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
@@ -561,7 +571,7 @@ static void frontend_init(struct budget *budget)
msleep(250);
budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
+ pr_info("tuner ALPS BSBE1 detected\n");
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
@@ -607,7 +617,7 @@ static void frontend_init(struct budget *budget)
budget->dvb_frontend = fe;
if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap,
0, 0) == NULL) {
- printk("%s: No LNBP21 found!\n", __func__);
+ pr_err("%s(): No LNBP21 found!\n", __func__);
goto error_out;
}
break;
@@ -629,10 +639,10 @@ static void frontend_init(struct budget *budget)
budget->dvb_frontend = fe;
if (dvb_attach(tda826x_attach, fe, 0x60,
&budget->i2c_adap, 0) == NULL)
- printk("%s: No tda826x found!\n", __func__);
+ pr_err("%s(): No tda826x found!\n", __func__);
if (dvb_attach(lnbp21_attach, fe,
&budget->i2c_adap, 0, 0) == NULL) {
- printk("%s: No LNBP21 found!\n", __func__);
+ pr_err("%s(): No LNBP21 found!\n", __func__);
goto error_out;
}
break;
@@ -642,6 +652,7 @@ static void frontend_init(struct budget *budget)
case 0x101c: { /* TT S2-1600 */
const struct stv6110x_devctl *ctl;
+
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
msleep(50);
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
@@ -672,9 +683,11 @@ static void frontend_init(struct budget *budget)
tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
- /* call the init function once to initialize
- tuner's clock output divider and demod's
- master clock */
+ /*
+ * call the init function once to initialize
+ * tuner's clock output divider and demod's
+ * master clock
+ */
if (budget->dvb_frontend->ops.init)
budget->dvb_frontend->ops.init(budget->dvb_frontend);
@@ -682,11 +695,11 @@ static void frontend_init(struct budget *budget)
budget->dvb_frontend,
&budget->i2c_adap,
&tt1600_isl6423_config) == NULL) {
- printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
+ pr_err("%s(): No Intersil ISL6423 found!\n", __func__);
goto error_out;
}
} else {
- printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ pr_err("%s(): No STV6110(A) Silicon Tuner found!\n", __func__);
goto error_out;
}
}
@@ -695,6 +708,7 @@ static void frontend_init(struct budget *budget)
case 0x1020: { /* Omicom S2 */
const struct stv6110x_devctl *ctl;
+
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
msleep(50);
saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
@@ -706,7 +720,7 @@ static void frontend_init(struct budget *budget)
STV090x_DEMODULATOR_0);
if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: Omicom S2 detected\n");
+ pr_info("Omicom S2 detected\n");
ctl = dvb_attach(stv6110x_attach,
budget->dvb_frontend,
@@ -726,9 +740,11 @@ static void frontend_init(struct budget *budget)
tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
- /* call the init function once to initialize
- tuner's clock output divider and demod's
- master clock */
+ /*
+ * call the init function once to initialize
+ * tuner's clock output divider and demod's
+ * master clock
+ */
if (budget->dvb_frontend->ops.init)
budget->dvb_frontend->ops.init(budget->dvb_frontend);
@@ -737,12 +753,11 @@ static void frontend_init(struct budget *budget)
&budget->i2c_adap,
LNBH24_PCL | LNBH24_TTX,
LNBH24_TEN, 0x14>>1) == NULL) {
- printk(KERN_ERR
- "No LNBH24 found!\n");
+ pr_err("No LNBH24 found!\n");
goto error_out;
}
} else {
- printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ pr_err("%s(): No STV6110(A) Silicon Tuner found!\n", __func__);
goto error_out;
}
}
@@ -751,7 +766,7 @@ static void frontend_init(struct budget *budget)
}
if (budget->dvb_frontend == NULL) {
- printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
budget->dev->pci->vendor,
budget->dev->pci->device,
budget->dev->pci->subsystem_vendor,
@@ -763,21 +778,19 @@ static void frontend_init(struct budget *budget)
return;
error_out:
- printk("budget: Frontend registration failed!\n");
+ pr_err("Frontend registration failed!\n");
dvb_frontend_detach(budget->dvb_frontend);
budget->dvb_frontend = NULL;
- return;
}
-static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+static int budget_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
struct budget *budget = NULL;
int err;
budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
- if( NULL == budget ) {
+ if (budget == NULL)
return -ENOMEM;
- }
dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
@@ -785,8 +798,8 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_
err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
if (err) {
- printk("==> failed\n");
- kfree (budget);
+ pr_err("==> failed\n");
+ kfree(budget);
return err;
}
@@ -798,7 +811,7 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_
return 0;
}
-static int budget_detach (struct saa7146_dev* dev)
+static int budget_detach(struct saa7146_dev *dev)
{
struct budget *budget = dev->ext_priv;
int err;
@@ -808,9 +821,9 @@ static int budget_detach (struct saa7146_dev* dev)
dvb_frontend_detach(budget->dvb_frontend);
}
- err = ttpci_budget_deinit (budget);
+ err = ttpci_budget_deinit(budget);
- kfree (budget);
+ kfree(budget);
dev->ext_priv = NULL;
return err;
@@ -839,8 +852,8 @@ static const struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
- MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
- MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+ MAKE_EXTENSION_PCI(fsacs1, 0x1131, 0x4f60),
+ MAKE_EXTENSION_PCI(fsacs0, 0x1131, 0x4f61),
MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020),
diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
index bd87432e6cde..83ead34dc766 100644
--- a/drivers/media/pci/ttpci/budget.h
+++ b/drivers/media/pci/ttpci/budget.h
@@ -3,6 +3,12 @@
#ifndef __BUDGET_DVB__
#define __BUDGET_DVB__
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <media/dvb_frontend.h>
#include <media/dvbdev.h>
#include <media/demux.h>
@@ -22,9 +28,8 @@ extern int budget_debug;
#endif
#define dprintk(level, fmt, arg...) do { \
- if (level & budget_debug) \
- printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \
- __func__, ##arg); \
+ if ((level) & budget_debug) \
+ pr_info("%s(): " fmt, __func__, ##arg); \
} while (0)
#define TS_SIZE 188
@@ -83,13 +88,13 @@ struct budget {
void *priv;
};
-#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
+#define MAKE_BUDGET_INFO(x_var, x_name, x_type) \
static struct budget_info x_var ## _info = { \
- .name=x_name, \
- .type=x_type }; \
+ .name = x_name, \
+ .type = x_type }; \
static struct saa7146_pci_extension_data x_var = { \
.ext_priv = &x_var ## _info, \
- .ext = &budget_extension };
+ .ext = &budget_extension }
#define BUDGET_TT 0
#define BUDGET_TT_HW_DISEQC 1
@@ -119,7 +124,7 @@ extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
struct module *owner, short *adapter_nums);
extern void ttpci_budget_init_hooks(struct budget *budget);
extern int ttpci_budget_deinit(struct budget *budget);
-extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
+extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 *isr);
extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port);
extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
int uselocks, int nobusyloop);
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 91e54215de3a..2d79bfc68c15 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -67,6 +67,7 @@ source "drivers/media/platform/amlogic/Kconfig"
source "drivers/media/platform/amphion/Kconfig"
source "drivers/media/platform/aspeed/Kconfig"
source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/broadcom/Kconfig"
source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/chips-media/Kconfig"
source "drivers/media/platform/intel/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 3296ec1ebe16..da17301f7439 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -10,6 +10,7 @@ obj-y += amlogic/
obj-y += amphion/
obj-y += aspeed/
obj-y += atmel/
+obj-y += broadcom/
obj-y += cadence/
obj-y += chips-media/
obj-y += intel/
diff --git a/drivers/media/platform/broadcom/Kconfig b/drivers/media/platform/broadcom/Kconfig
new file mode 100644
index 000000000000..32b76ebfcd9a
--- /dev/null
+++ b/drivers/media/platform/broadcom/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_BCM2835_UNICAM
+ tristate "Broadcom BCM283x/BCM271x Unicam video capture driver"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on COMMON_CLK && PM
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver.
+ This is a V4L2 driver that controls the CSI-2 receiver directly,
+ independently from the VC4 firmware.
+
+ This driver is mutually exclusive with the use of bcm2835-camera. The
+ firmware will disable all access to the peripheral from within the
+ firmware if it finds a DT node using it, and bcm2835-camera will
+ therefore fail to probe.
+
+ To compile this driver as a module, choose M here. The module will be
+ called bcm2835-unicam.
diff --git a/drivers/media/platform/broadcom/Makefile b/drivers/media/platform/broadcom/Makefile
new file mode 100644
index 000000000000..03d2045aba2e
--- /dev/null
+++ b/drivers/media/platform/broadcom/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam-regs.h b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h
new file mode 100644
index 000000000000..fce6fa92707c
--- /dev/null
+++ b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2017-2020 Raspberry Pi Trading.
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ */
+
+#ifndef VC4_REGS_UNICAM_H
+#define VC4_REGS_UNICAM_H
+
+#include <linux/bits.h>
+
+/*
+ * The following values are taken from files found within the code drop
+ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
+ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
+ * They have been modified to be only the register offset.
+ */
+#define UNICAM_CTRL 0x000
+#define UNICAM_STA 0x004
+#define UNICAM_ANA 0x008
+#define UNICAM_PRI 0x00c
+#define UNICAM_CLK 0x010
+#define UNICAM_CLT 0x014
+#define UNICAM_DAT0 0x018
+#define UNICAM_DAT1 0x01c
+#define UNICAM_DAT2 0x020
+#define UNICAM_DAT3 0x024
+#define UNICAM_DLT 0x028
+#define UNICAM_CMP0 0x02c
+#define UNICAM_CMP1 0x030
+#define UNICAM_CAP0 0x034
+#define UNICAM_CAP1 0x038
+#define UNICAM_ICTL 0x100
+#define UNICAM_ISTA 0x104
+#define UNICAM_IDI0 0x108
+#define UNICAM_IPIPE 0x10c
+#define UNICAM_IBSA0 0x110
+#define UNICAM_IBEA0 0x114
+#define UNICAM_IBLS 0x118
+#define UNICAM_IBWP 0x11c
+#define UNICAM_IHWIN 0x120
+#define UNICAM_IHSTA 0x124
+#define UNICAM_IVWIN 0x128
+#define UNICAM_IVSTA 0x12c
+#define UNICAM_ICC 0x130
+#define UNICAM_ICS 0x134
+#define UNICAM_IDC 0x138
+#define UNICAM_IDPO 0x13c
+#define UNICAM_IDCA 0x140
+#define UNICAM_IDCD 0x144
+#define UNICAM_IDS 0x148
+#define UNICAM_DCS 0x200
+#define UNICAM_DBSA0 0x204
+#define UNICAM_DBEA0 0x208
+#define UNICAM_DBWP 0x20c
+#define UNICAM_DBCTL 0x300
+#define UNICAM_IBSA1 0x304
+#define UNICAM_IBEA1 0x308
+#define UNICAM_IDI1 0x30c
+#define UNICAM_DBSA1 0x310
+#define UNICAM_DBEA1 0x314
+#define UNICAM_MISC 0x400
+
+/*
+ * The following bitmasks are from the kernel released by Broadcom
+ * for Android - https://android.googlesource.com/kernel/bcm/
+ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
+ * Unicam block as BCM2835, as defined in eg
+ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
+ * Values reworked to use the kernel BIT and GENMASK macros.
+ *
+ * Some of the bit mnenomics have been amended to match the datasheet.
+ */
+/* UNICAM_CTRL Register */
+#define UNICAM_CPE BIT(0)
+#define UNICAM_MEM BIT(1)
+#define UNICAM_CPR BIT(2)
+#define UNICAM_CPM_MASK GENMASK(3, 3)
+#define UNICAM_CPM_CSI2 0
+#define UNICAM_CPM_CCP2 1
+#define UNICAM_SOE BIT(4)
+#define UNICAM_DCM_MASK GENMASK(5, 5)
+#define UNICAM_DCM_STROBE 0
+#define UNICAM_DCM_DATA 1
+#define UNICAM_SLS BIT(6)
+#define UNICAM_PFT_MASK GENMASK(11, 8)
+#define UNICAM_OET_MASK GENMASK(20, 12)
+
+/* UNICAM_STA Register */
+#define UNICAM_SYN BIT(0)
+#define UNICAM_CS BIT(1)
+#define UNICAM_SBE BIT(2)
+#define UNICAM_PBE BIT(3)
+#define UNICAM_HOE BIT(4)
+#define UNICAM_PLE BIT(5)
+#define UNICAM_SSC BIT(6)
+#define UNICAM_CRCE BIT(7)
+#define UNICAM_OES BIT(8)
+#define UNICAM_IFO BIT(9)
+#define UNICAM_OFO BIT(10)
+#define UNICAM_BFO BIT(11)
+#define UNICAM_DL BIT(12)
+#define UNICAM_PS BIT(13)
+#define UNICAM_IS BIT(14)
+#define UNICAM_PI0 BIT(15)
+#define UNICAM_PI1 BIT(16)
+#define UNICAM_FSI_S BIT(17)
+#define UNICAM_FEI_S BIT(18)
+#define UNICAM_LCI_S BIT(19)
+#define UNICAM_BUF0_RDY BIT(20)
+#define UNICAM_BUF0_NO BIT(21)
+#define UNICAM_BUF1_RDY BIT(22)
+#define UNICAM_BUF1_NO BIT(23)
+#define UNICAM_DI BIT(24)
+
+#define UNICAM_STA_MASK_ALL \
+ (UNICAM_SBE | UNICAM_PBE | UNICAM_HOE | UNICAM_PLE | UNICAM_SSC | \
+ UNICAM_CRCE | UNICAM_IFO | UNICAM_OFO | UNICAM_DL | UNICAM_PS | \
+ UNICAM_PI0 | UNICAM_PI1)
+
+/* UNICAM_ANA Register */
+#define UNICAM_APD BIT(0)
+#define UNICAM_BPD BIT(1)
+#define UNICAM_AR BIT(2)
+#define UNICAM_DDL BIT(3)
+#define UNICAM_CTATADJ_MASK GENMASK(7, 4)
+#define UNICAM_PTATADJ_MASK GENMASK(11, 8)
+
+/* UNICAM_PRI Register */
+#define UNICAM_PE BIT(0)
+#define UNICAM_PT_MASK GENMASK(2, 1)
+#define UNICAM_NP_MASK GENMASK(7, 4)
+#define UNICAM_PP_MASK GENMASK(11, 8)
+#define UNICAM_BS_MASK GENMASK(15, 12)
+#define UNICAM_BL_MASK GENMASK(17, 16)
+
+/* UNICAM_CLK Register */
+#define UNICAM_CLE BIT(0)
+#define UNICAM_CLPD BIT(1)
+#define UNICAM_CLLPE BIT(2)
+#define UNICAM_CLHSE BIT(3)
+#define UNICAM_CLTRE BIT(4)
+#define UNICAM_CLAC_MASK GENMASK(8, 5)
+#define UNICAM_CLSTE BIT(29)
+
+/* UNICAM_CLT Register */
+#define UNICAM_CLT1_MASK GENMASK(7, 0)
+#define UNICAM_CLT2_MASK GENMASK(15, 8)
+
+/* UNICAM_DATn Registers */
+#define UNICAM_DLE BIT(0)
+#define UNICAM_DLPD BIT(1)
+#define UNICAM_DLLPE BIT(2)
+#define UNICAM_DLHSE BIT(3)
+#define UNICAM_DLTRE BIT(4)
+#define UNICAM_DLSM BIT(5)
+#define UNICAM_DLFO BIT(28)
+#define UNICAM_DLSTE BIT(29)
+
+#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE | UNICAM_DLFO)
+
+/* UNICAM_DLT Register */
+#define UNICAM_DLT1_MASK GENMASK(7, 0)
+#define UNICAM_DLT2_MASK GENMASK(15, 8)
+#define UNICAM_DLT3_MASK GENMASK(23, 16)
+
+/* UNICAM_ICTL Register */
+#define UNICAM_FSIE BIT(0)
+#define UNICAM_FEIE BIT(1)
+#define UNICAM_IBOB BIT(2)
+#define UNICAM_FCM BIT(3)
+#define UNICAM_TFC BIT(4)
+#define UNICAM_LIP_MASK GENMASK(6, 5)
+#define UNICAM_LCIE_MASK GENMASK(28, 16)
+
+/* UNICAM_IDI0/1 Register */
+#define UNICAM_ID0_MASK GENMASK(7, 0)
+#define UNICAM_ID1_MASK GENMASK(15, 8)
+#define UNICAM_ID2_MASK GENMASK(23, 16)
+#define UNICAM_ID3_MASK GENMASK(31, 24)
+
+/* UNICAM_ISTA Register */
+#define UNICAM_FSI BIT(0)
+#define UNICAM_FEI BIT(1)
+#define UNICAM_LCI BIT(2)
+
+#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI | UNICAM_FEI | UNICAM_LCI)
+
+/* UNICAM_IPIPE Register */
+#define UNICAM_PUM_MASK GENMASK(2, 0)
+/* Unpacking modes */
+#define UNICAM_PUM_NONE 0
+#define UNICAM_PUM_UNPACK6 1
+#define UNICAM_PUM_UNPACK7 2
+#define UNICAM_PUM_UNPACK8 3
+#define UNICAM_PUM_UNPACK10 4
+#define UNICAM_PUM_UNPACK12 5
+#define UNICAM_PUM_UNPACK14 6
+#define UNICAM_PUM_UNPACK16 7
+#define UNICAM_DDM_MASK GENMASK(6, 3)
+#define UNICAM_PPM_MASK GENMASK(9, 7)
+/* Packing modes */
+#define UNICAM_PPM_NONE 0
+#define UNICAM_PPM_PACK8 1
+#define UNICAM_PPM_PACK10 2
+#define UNICAM_PPM_PACK12 3
+#define UNICAM_PPM_PACK14 4
+#define UNICAM_PPM_PACK16 5
+#define UNICAM_DEM_MASK GENMASK(11, 10)
+#define UNICAM_DEBL_MASK GENMASK(14, 12)
+#define UNICAM_ICM_MASK GENMASK(16, 15)
+#define UNICAM_IDM_MASK GENMASK(17, 17)
+
+/* UNICAM_ICC Register */
+#define UNICAM_ICFL_MASK GENMASK(4, 0)
+#define UNICAM_ICFH_MASK GENMASK(9, 5)
+#define UNICAM_ICST_MASK GENMASK(12, 10)
+#define UNICAM_ICLT_MASK GENMASK(15, 13)
+#define UNICAM_ICLL_MASK GENMASK(31, 16)
+
+/* UNICAM_DCS Register */
+#define UNICAM_DIE BIT(0)
+#define UNICAM_DIM BIT(1)
+#define UNICAM_DBOB BIT(3)
+#define UNICAM_FDE BIT(4)
+#define UNICAM_LDP BIT(5)
+#define UNICAM_EDL_MASK GENMASK(15, 8)
+
+/* UNICAM_DBCTL Register */
+#define UNICAM_DBEN BIT(0)
+#define UNICAM_BUF0_IE BIT(1)
+#define UNICAM_BUF1_IE BIT(2)
+
+/* UNICAM_CMP[0,1] register */
+#define UNICAM_PCE BIT(31)
+#define UNICAM_GI BIT(9)
+#define UNICAM_CPH BIT(8)
+#define UNICAM_PCVC_MASK GENMASK(7, 6)
+#define UNICAM_PCDT_MASK GENMASK(5, 0)
+
+/* UNICAM_MISC register */
+#define UNICAM_FL0 BIT(6)
+#define UNICAM_FL1 BIT(9)
+
+#endif
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
new file mode 100644
index 000000000000..a1d93c14553d
--- /dev/null
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -0,0 +1,2739 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BCM283x / BCM271x Unicam Capture Driver
+ *
+ * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd.
+ * Copyright (C) 2024 - Ideas on Board
+ *
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ *
+ * Based on TI am437x driver by
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * and TI CAL camera interface driver by
+ * Benoit Parrot <bparrot@ti.com>
+ *
+ *
+ * There are two camera drivers in the kernel for BCM283x - this one and
+ * bcm2835-camera (currently in staging).
+ *
+ * This driver directly controls the Unicam peripheral - there is no
+ * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data
+ * and writes it into SDRAM. The only potential processing options are to
+ * repack Bayer data into an alternate format, and applying windowing. The
+ * repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P to
+ * V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, but
+ * not generically up to V4L2_PIX_FMT_Sxxxx16. Support for windowing may be
+ * added later.
+ *
+ * It should be possible to connect this driver to any sensor with a suitable
+ * output interface and V4L2 subdevice driver.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "bcm2835-unicam-regs.h"
+
+#define UNICAM_MODULE_NAME "unicam"
+
+/*
+ * Unicam must request a minimum of 250Mhz from the VPU clock.
+ * Otherwise the input FIFOs overrun and cause image corruption.
+ */
+#define UNICAM_MIN_VPU_CLOCK_RATE (250 * 1000 * 1000)
+
+/* Unicam has an internal DMA alignment constraint of 16 bytes for each line. */
+#define UNICAM_DMA_BPL_ALIGNMENT 16
+
+/*
+ * The image stride is stored in a 16 bit register, and needs to be aligned to
+ * the DMA constraint. As the ISP in the same SoC has a 32 bytes alignment
+ * constraint on its input, set the image stride alignment to 32 bytes here as
+ * well to avoid incompatible configurations.
+ */
+#define UNICAM_IMAGE_BPL_ALIGNMENT 32
+#define UNICAM_IMAGE_MAX_BPL ((1U << 16) - UNICAM_IMAGE_BPL_ALIGNMENT)
+
+/*
+ * Max width is therefore determined by the max stride divided by the number of
+ * bits per pixel. Take 32bpp as a worst case. No imposed limit on the height,
+ * so adopt a square image for want of anything better.
+ */
+#define UNICAM_IMAGE_MIN_WIDTH 16
+#define UNICAM_IMAGE_MIN_HEIGHT 16
+#define UNICAM_IMAGE_MAX_WIDTH (UNICAM_IMAGE_MAX_BPL / 4)
+#define UNICAM_IMAGE_MAX_HEIGHT UNICAM_IMAGE_MAX_WIDTH
+
+/*
+ * There's no intrinsic limits on the width and height for embedded data. Use
+ * the same maximum values as for the image, to avoid overflows in the image
+ * size computation.
+ */
+#define UNICAM_META_MIN_WIDTH 1
+#define UNICAM_META_MIN_HEIGHT 1
+#define UNICAM_META_MAX_WIDTH UNICAM_IMAGE_MAX_WIDTH
+#define UNICAM_META_MAX_HEIGHT UNICAM_IMAGE_MAX_HEIGHT
+
+/*
+ * Size of the dummy buffer. Can be any size really, but the DMA
+ * allocation works in units of page sizes.
+ */
+#define UNICAM_DUMMY_BUF_SIZE PAGE_SIZE
+
+enum unicam_pad {
+ UNICAM_SD_PAD_SINK,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ UNICAM_SD_NUM_PADS
+};
+
+enum unicam_node_type {
+ UNICAM_IMAGE_NODE,
+ UNICAM_METADATA_NODE,
+ UNICAM_MAX_NODES
+};
+
+/*
+ * struct unicam_format_info - Unicam media bus format information
+ * @fourcc: V4L2 pixel format FCC identifier. 0 if n/a.
+ * @unpacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded
+ * out to 16bpp. 0 if n/a.
+ * @code: V4L2 media bus format code.
+ * @depth: Bits per pixel as delivered from the source.
+ * @csi_dt: CSI data type.
+ * @unpack: PUM value when unpacking to @unpacked_fourcc
+ */
+struct unicam_format_info {
+ u32 fourcc;
+ u32 unpacked_fourcc;
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+ u8 unpack;
+};
+
+struct unicam_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ dma_addr_t dma_addr;
+ unsigned int size;
+};
+
+static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct unicam_buffer, vb.vb2_buf);
+}
+
+struct unicam_node {
+ bool registered;
+ unsigned int id;
+
+ /* Pointer to the current v4l2_buffer */
+ struct unicam_buffer *cur_frm;
+ /* Pointer to the next v4l2_buffer */
+ struct unicam_buffer *next_frm;
+ /* Used to store current pixel format */
+ struct v4l2_format fmt;
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ /* IRQ lock for DMA queue */
+ spinlock_t dma_queue_lock;
+ /* Identifies video device for this channel */
+ struct video_device video_dev;
+ /* Pointer to the parent handle */
+ struct unicam_device *dev;
+ struct media_pad pad;
+ /*
+ * Dummy buffer intended to be used by unicam
+ * if we have no other queued buffers to swap to.
+ */
+ struct unicam_buffer dummy_buf;
+ void *dummy_buf_cpu_addr;
+};
+
+struct unicam_device {
+ struct kref kref;
+
+ /* peripheral base address */
+ void __iomem *base;
+ /* clock gating base address */
+ void __iomem *clk_gate_base;
+ /* lp clock handle */
+ struct clk *clock;
+ /* vpu clock handle */
+ struct clk *vpu_clock;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ struct media_device mdev;
+
+ /* parent device */
+ struct device *dev;
+ /* subdevice async notifier */
+ struct v4l2_async_notifier notifier;
+ unsigned int sequence;
+
+ /* Sensor node */
+ struct {
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ } sensor;
+
+ /* Internal subdev */
+ struct {
+ struct v4l2_subdev sd;
+ struct media_pad pads[UNICAM_SD_NUM_PADS];
+ unsigned int enabled_streams;
+ } subdev;
+
+ enum v4l2_mbus_type bus_type;
+ /*
+ * Stores bus.mipi_csi2.flags for CSI2 sensors, or
+ * bus.mipi_csi1.strobe for CCP2.
+ */
+ unsigned int bus_flags;
+ unsigned int max_data_lanes;
+
+ struct {
+ struct media_pipeline pipe;
+ unsigned int num_data_lanes;
+ unsigned int nodes;
+ } pipe;
+
+ /* Lock used for the video devices of both nodes */
+ struct mutex lock;
+ struct unicam_node node[UNICAM_MAX_NODES];
+};
+
+static inline struct unicam_device *
+notifier_to_unicam_device(struct v4l2_async_notifier *notifier)
+{
+ return container_of(notifier, struct unicam_device, notifier);
+}
+
+static inline struct unicam_device *
+sd_to_unicam_device(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct unicam_device, subdev.sd);
+}
+
+static void unicam_release(struct kref *kref)
+{
+ struct unicam_device *unicam =
+ container_of(kref, struct unicam_device, kref);
+
+ if (unicam->mdev.dev)
+ media_device_cleanup(&unicam->mdev);
+
+ mutex_destroy(&unicam->lock);
+ kfree(unicam);
+}
+
+static struct unicam_device *unicam_get(struct unicam_device *unicam)
+{
+ kref_get(&unicam->kref);
+
+ return unicam;
+}
+
+static void unicam_put(struct unicam_device *unicam)
+{
+ kref_put(&unicam->kref, unicam_release);
+}
+
+/* -----------------------------------------------------------------------------
+ * Misc helper functions
+ */
+
+static inline bool unicam_sd_pad_is_source(u32 pad)
+{
+ /* Camera RX has 1 sink pad, and N source pads */
+ return pad != UNICAM_SD_PAD_SINK;
+}
+
+static inline bool is_metadata_node(struct unicam_node *node)
+{
+ return node->video_dev.device_caps & V4L2_CAP_META_CAPTURE;
+}
+
+static inline bool is_image_node(struct unicam_node *node)
+{
+ return node->video_dev.device_caps & V4L2_CAP_VIDEO_CAPTURE;
+}
+
+/* -----------------------------------------------------------------------------
+ * Format data table and helper functions
+ */
+
+static const struct v4l2_mbus_framefmt unicam_default_image_format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ .flags = 0,
+};
+
+static const struct v4l2_mbus_framefmt unicam_default_meta_format = {
+ .width = 640,
+ .height = 2,
+ .code = MEDIA_BUS_FMT_META_8,
+ .field = V4L2_FIELD_NONE,
+};
+
+static const struct unicam_format_info unicam_image_formats[] = {
+ /* YUV Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ /* RGB Formats */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB565,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ }, {
+ /* Bayer Formats */
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR14,
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG14,
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG14,
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB14,
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ /* 16 bit Bayer formats could be supported. */
+
+ /* Greyscale formats */
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y10,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y12,
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y14,
+ .code = MEDIA_BUS_FMT_Y14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ },
+};
+
+static const struct unicam_format_info unicam_meta_formats[] = {
+ {
+ .fourcc = V4L2_META_FMT_GENERIC_8,
+ .code = MEDIA_BUS_FMT_META_8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_10,
+ .code = MEDIA_BUS_FMT_META_10,
+ .depth = 10,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_12,
+ .code = MEDIA_BUS_FMT_META_12,
+ .depth = 12,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_14,
+ .code = MEDIA_BUS_FMT_META_14,
+ .depth = 14,
+ },
+};
+
+/* Format setup functions */
+static const struct unicam_format_info *
+unicam_find_format_by_code(u32 code, u32 pad)
+{
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+ unsigned int i;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ for (i = 0; i < num_formats; i++) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static const struct unicam_format_info *
+unicam_find_format_by_fourcc(u32 fourcc, u32 pad)
+{
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+ unsigned int i;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ for (i = 0; i < num_formats; ++i) {
+ if (formats[i].fourcc == fourcc)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static void unicam_calc_image_size_bpl(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo,
+ struct v4l2_pix_format *pix)
+{
+ u32 min_bpl;
+
+ v4l_bound_align_image(&pix->width, UNICAM_IMAGE_MIN_WIDTH,
+ UNICAM_IMAGE_MAX_WIDTH, 2,
+ &pix->height, UNICAM_IMAGE_MIN_HEIGHT,
+ UNICAM_IMAGE_MAX_HEIGHT, 0, 0);
+
+ /* Unpacking always goes to 16bpp */
+ if (pix->pixelformat == fmtinfo->unpacked_fourcc)
+ min_bpl = pix->width * 2;
+ else
+ min_bpl = pix->width * fmtinfo->depth / 8;
+ min_bpl = ALIGN(min_bpl, UNICAM_IMAGE_BPL_ALIGNMENT);
+
+ pix->bytesperline = ALIGN(pix->bytesperline, UNICAM_IMAGE_BPL_ALIGNMENT);
+ pix->bytesperline = clamp_t(unsigned int, pix->bytesperline, min_bpl,
+ UNICAM_IMAGE_MAX_BPL);
+
+ pix->sizeimage = pix->height * pix->bytesperline;
+}
+
+static void unicam_calc_meta_size_bpl(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo,
+ struct v4l2_meta_format *meta)
+{
+ v4l_bound_align_image(&meta->width, UNICAM_META_MIN_WIDTH,
+ UNICAM_META_MAX_WIDTH, 0,
+ &meta->height, UNICAM_META_MIN_HEIGHT,
+ UNICAM_META_MAX_HEIGHT, 0, 0);
+
+ meta->bytesperline = ALIGN(meta->width * fmtinfo->depth / 8,
+ UNICAM_DMA_BPL_ALIGNMENT);
+ meta->buffersize = meta->height * meta->bytesperline;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware handling
+ */
+
+static inline void unicam_clk_write(struct unicam_device *unicam, u32 val)
+{
+ /* Pass the CM_PASSWORD along with the value. */
+ writel(val | 0x5a000000, unicam->clk_gate_base);
+}
+
+static inline u32 unicam_reg_read(struct unicam_device *unicam, u32 offset)
+{
+ return readl(unicam->base + offset);
+}
+
+static inline void unicam_reg_write(struct unicam_device *unicam, u32 offset, u32 val)
+{
+ writel(val, unicam->base + offset);
+}
+
+static inline int unicam_get_field(u32 value, u32 mask)
+{
+ return (value & mask) >> __ffs(mask);
+}
+
+static inline void unicam_set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offset,
+ u32 field, u32 mask)
+{
+ u32 val = unicam_reg_read(unicam, offset);
+
+ unicam_set_field(&val, field, mask);
+ unicam_reg_write(unicam, offset, val);
+}
+
+static void unicam_wr_dma_addr(struct unicam_node *node,
+ struct unicam_buffer *buf)
+{
+ dma_addr_t endaddr = buf->dma_addr + buf->size;
+
+ if (node->id == UNICAM_IMAGE_NODE) {
+ unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr);
+ unicam_reg_write(node->dev, UNICAM_IBEA0, endaddr);
+ } else {
+ unicam_reg_write(node->dev, UNICAM_DBSA0, buf->dma_addr);
+ unicam_reg_write(node->dev, UNICAM_DBEA0, endaddr);
+ }
+}
+
+static unsigned int unicam_get_lines_done(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ unsigned int stride = node->fmt.fmt.pix.bytesperline;
+ struct unicam_buffer *frm = node->cur_frm;
+ dma_addr_t cur_addr;
+
+ if (!frm)
+ return 0;
+
+ cur_addr = unicam_reg_read(unicam, UNICAM_IBWP);
+ return (unsigned int)(cur_addr - frm->dma_addr) / stride;
+}
+
+static void unicam_schedule_next_buffer(struct unicam_node *node)
+{
+ struct unicam_buffer *buf;
+
+ buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list);
+ node->next_frm = buf;
+ list_del(&buf->list);
+
+ unicam_wr_dma_addr(node, buf);
+}
+
+static void unicam_schedule_dummy_buffer(struct unicam_node *node)
+{
+ int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE;
+
+ dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id);
+
+ unicam_wr_dma_addr(node, &node->dummy_buf);
+
+ node->next_frm = NULL;
+}
+
+static void unicam_process_buffer_complete(struct unicam_node *node,
+ unsigned int sequence)
+{
+ node->cur_frm->vb.field = node->fmt.fmt.pix.field;
+ node->cur_frm->vb.sequence = sequence;
+
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void unicam_queue_event_sof(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence = unicam->sequence,
+ };
+
+ v4l2_event_queue(&node->video_dev, &event);
+}
+
+static irqreturn_t unicam_isr(int irq, void *dev)
+{
+ struct unicam_device *unicam = dev;
+ unsigned int lines_done = unicam_get_lines_done(dev);
+ unsigned int sequence = unicam->sequence;
+ unsigned int i;
+ u32 ista, sta;
+ bool fe;
+ u64 ts;
+
+ sta = unicam_reg_read(unicam, UNICAM_STA);
+ /* Write value back to clear the interrupts */
+ unicam_reg_write(unicam, UNICAM_STA, sta);
+
+ ista = unicam_reg_read(unicam, UNICAM_ISTA);
+ /* Write value back to clear the interrupts */
+ unicam_reg_write(unicam, UNICAM_ISTA, ista);
+
+ dev_dbg(unicam->dev, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d\n",
+ ista, sta, sequence, lines_done);
+
+ if (!(sta & (UNICAM_IS | UNICAM_PI0)))
+ return IRQ_HANDLED;
+
+ /*
+ * Look for either the Frame End interrupt or the Packet Capture status
+ * to signal a frame end.
+ */
+ fe = ista & UNICAM_FEI || sta & UNICAM_PI0;
+
+ /*
+ * We must run the frame end handler first. If we have a valid next_frm
+ * and we get a simultaneout FE + FS interrupt, running the FS handler
+ * first would null out the next_frm ptr and we would have lost the
+ * buffer forever.
+ */
+ if (fe) {
+ /*
+ * Ensure we have swapped buffers already as we can't
+ * stop the peripheral. If no buffer is available, use a
+ * dummy buffer to dump out frames until we get a new buffer
+ * to use.
+ */
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ /*
+ * If cur_frm == next_frm, it means we have not had
+ * a chance to swap buffers, likely due to having
+ * multiple interrupts occurring simultaneously (like FE
+ * + FS + LS). In this case, we cannot signal the buffer
+ * as complete, as the HW will reuse that buffer.
+ */
+ if (node->cur_frm && node->cur_frm != node->next_frm)
+ unicam_process_buffer_complete(node, sequence);
+ node->cur_frm = node->next_frm;
+ }
+ unicam->sequence++;
+ }
+
+ if (ista & UNICAM_FSI) {
+ /*
+ * Timestamp is to be when the first data byte was captured,
+ * aka frame start.
+ */
+ ts = ktime_get_ns();
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ if (node->cur_frm)
+ node->cur_frm->vb.vb2_buf.timestamp = ts;
+ else
+ dev_dbg(unicam->v4l2_dev.dev,
+ "ISR: [%d] Dropping frame, buffer not available at FS\n",
+ i);
+ /*
+ * Set the next frame output to go to a dummy frame
+ * if we have not managed to obtain another frame
+ * from the queue.
+ */
+ unicam_schedule_dummy_buffer(node);
+ }
+
+ unicam_queue_event_sof(unicam);
+ }
+
+ /*
+ * Cannot swap buffer at frame end, there may be a race condition
+ * where the HW does not actually swap it if the new frame has
+ * already started.
+ */
+ if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) {
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ spin_lock(&node->dma_queue_lock);
+ if (!list_empty(&node->dma_queue) && !node->next_frm)
+ unicam_schedule_next_buffer(node);
+ spin_unlock(&node->dma_queue_lock);
+ }
+ }
+
+ if (unicam_reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) {
+ /* Switch out of trigger mode if selected */
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC);
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM);
+ }
+ return IRQ_HANDLED;
+}
+
+static void unicam_set_packing_config(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ u32 pack, unpack;
+ u32 val;
+
+ if (node->fmt.fmt.pix.pixelformat == fmtinfo->fourcc) {
+ unpack = UNICAM_PUM_NONE;
+ pack = UNICAM_PPM_NONE;
+ } else {
+ unpack = fmtinfo->unpack;
+ /* Repacking is always to 16bpp */
+ pack = UNICAM_PPM_PACK16;
+ }
+
+ val = 0;
+ unicam_set_field(&val, unpack, UNICAM_PUM_MASK);
+ unicam_set_field(&val, pack, UNICAM_PPM_MASK);
+ unicam_reg_write(unicam, UNICAM_IPIPE, val);
+}
+
+static void unicam_cfg_image_id(struct unicam_device *unicam, u8 vc, u8 dt)
+{
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 mode */
+ unicam_reg_write(unicam, UNICAM_IDI0, (vc << 6) | dt);
+ } else {
+ /* CCP2 mode */
+ unicam_reg_write(unicam, UNICAM_IDI0, 0x80 | dt);
+ }
+}
+
+static void unicam_enable_ed(struct unicam_device *unicam)
+{
+ u32 val = unicam_reg_read(unicam, UNICAM_DCS);
+
+ unicam_set_field(&val, 2, UNICAM_EDL_MASK);
+ /* Do not wrap at the end of the embedded data buffer */
+ unicam_set_field(&val, 0, UNICAM_DBOB);
+
+ unicam_reg_write(unicam, UNICAM_DCS, val);
+}
+
+static int unicam_get_image_vc_dt(struct unicam_device *unicam,
+ struct v4l2_subdev_state *state,
+ u8 *vc, u8 *dt)
+{
+ struct v4l2_mbus_frame_desc fd;
+ u32 stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ 0, NULL, &stream);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_frame_desc,
+ unicam->sensor.pad->index, &fd);
+ if (ret)
+ return ret;
+
+ /* Only CSI-2 supports DTs. */
+ if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < fd.num_entries; ++i) {
+ const struct v4l2_mbus_frame_desc_entry *fde = &fd.entry[i];
+
+ if (fde->stream == stream) {
+ *vc = fde->bus.csi2.vc;
+ *dt = fde->bus.csi2.dt;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void unicam_start_rx(struct unicam_device *unicam,
+ struct v4l2_subdev_state *state)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ const struct unicam_format_info *fmtinfo;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int line_int_freq;
+ u8 vc, dt;
+ u32 val;
+ int ret;
+
+ fmt = v4l2_subdev_state_get_format(state, UNICAM_SD_PAD_SOURCE_IMAGE, 0);
+ fmtinfo = unicam_find_format_by_code(fmt->code,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (WARN_ON(!fmtinfo))
+ return;
+
+ /*
+ * Enable lane clocks. The register is structured as follows:
+ *
+ * [9:8] - DAT3
+ * [7:6] - DAT2
+ * [5:4] - DAT1
+ * [3:2] - DAT0
+ * [1:0] - CLK
+ *
+ * Enabled lane must be set to b01, and disabled lanes to b00. The clock
+ * lane is always enabled.
+ */
+ val = 0x155 & GENMASK(unicam->pipe.num_data_lanes * 2 + 1, 0);
+ unicam_clk_write(unicam, val);
+
+ /* Basic init */
+ unicam_reg_write(unicam, UNICAM_CTRL, UNICAM_MEM);
+
+ /* Enable analogue control, and leave in reset. */
+ val = UNICAM_AR;
+ unicam_set_field(&val, 7, UNICAM_CTATADJ_MASK);
+ unicam_set_field(&val, 7, UNICAM_PTATADJ_MASK);
+ unicam_reg_write(unicam, UNICAM_ANA, val);
+ usleep_range(1000, 2000);
+
+ /* Come out of reset */
+ unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_AR);
+
+ /* Peripheral reset */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR);
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Enable Rx control. */
+ val = unicam_reg_read(unicam, UNICAM_CTRL);
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ unicam_set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+ unicam_set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
+ } else {
+ unicam_set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
+ unicam_set_field(&val, unicam->bus_flags, UNICAM_DCM_MASK);
+ }
+ /* Packet framer timeout */
+ unicam_set_field(&val, 0xf, UNICAM_PFT_MASK);
+ unicam_set_field(&val, 128, UNICAM_OET_MASK);
+ unicam_reg_write(unicam, UNICAM_CTRL, val);
+
+ unicam_reg_write(unicam, UNICAM_IHWIN, 0);
+ unicam_reg_write(unicam, UNICAM_IVWIN, 0);
+
+ /* AXI bus access QoS setup */
+ val = unicam_reg_read(unicam, UNICAM_PRI);
+ unicam_set_field(&val, 0, UNICAM_BL_MASK);
+ unicam_set_field(&val, 0, UNICAM_BS_MASK);
+ unicam_set_field(&val, 0xe, UNICAM_PP_MASK);
+ unicam_set_field(&val, 8, UNICAM_NP_MASK);
+ unicam_set_field(&val, 2, UNICAM_PT_MASK);
+ unicam_set_field(&val, 1, UNICAM_PE);
+ unicam_reg_write(unicam, UNICAM_PRI, val);
+
+ unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL);
+
+ /* Always start in trigger frame capture mode (UNICAM_FCM set) */
+ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB;
+ line_int_freq = max(fmt->height >> 2, 128);
+ unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
+ unicam_reg_write(unicam, UNICAM_ICTL, val);
+ unicam_reg_write(unicam, UNICAM_STA, UNICAM_STA_MASK_ALL);
+ unicam_reg_write(unicam, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
+
+ /* tclk_term_en */
+ unicam_reg_write_field(unicam, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
+ /* tclk_settle */
+ unicam_reg_write_field(unicam, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
+ /* td_term_en */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
+ /* ths_settle */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
+ /* trx_enable */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
+
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_SOE);
+
+ /* Packet compare setup - required to avoid missing frame ends */
+ val = 0;
+ unicam_set_field(&val, 1, UNICAM_PCE);
+ unicam_set_field(&val, 1, UNICAM_GI);
+ unicam_set_field(&val, 1, UNICAM_CPH);
+ unicam_set_field(&val, 0, UNICAM_PCVC_MASK);
+ unicam_set_field(&val, 1, UNICAM_PCDT_MASK);
+ unicam_reg_write(unicam, UNICAM_CMP0, val);
+
+ /* Enable clock lane and set up terminations */
+ val = 0;
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 */
+ unicam_set_field(&val, 1, UNICAM_CLE);
+ unicam_set_field(&val, 1, UNICAM_CLLPE);
+ if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) {
+ unicam_set_field(&val, 1, UNICAM_CLTRE);
+ unicam_set_field(&val, 1, UNICAM_CLHSE);
+ }
+ } else {
+ /* CCP2 */
+ unicam_set_field(&val, 1, UNICAM_CLE);
+ unicam_set_field(&val, 1, UNICAM_CLHSE);
+ unicam_set_field(&val, 1, UNICAM_CLTRE);
+ }
+ unicam_reg_write(unicam, UNICAM_CLK, val);
+
+ /*
+ * Enable required data lanes with appropriate terminations.
+ * The same value needs to be written to UNICAM_DATn registers for
+ * the active lanes, and 0 for inactive ones.
+ */
+ val = 0;
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 */
+ unicam_set_field(&val, 1, UNICAM_DLE);
+ unicam_set_field(&val, 1, UNICAM_DLLPE);
+ if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) {
+ unicam_set_field(&val, 1, UNICAM_DLTRE);
+ unicam_set_field(&val, 1, UNICAM_DLHSE);
+ }
+ } else {
+ /* CCP2 */
+ unicam_set_field(&val, 1, UNICAM_DLE);
+ unicam_set_field(&val, 1, UNICAM_DLHSE);
+ unicam_set_field(&val, 1, UNICAM_DLTRE);
+ }
+ unicam_reg_write(unicam, UNICAM_DAT0, val);
+
+ if (unicam->pipe.num_data_lanes == 1)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT1, val);
+
+ if (unicam->max_data_lanes > 2) {
+ /*
+ * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the
+ * instance supports more than 2 data lanes.
+ */
+ if (unicam->pipe.num_data_lanes == 2)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT2, val);
+
+ if (unicam->pipe.num_data_lanes == 3)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT3, val);
+ }
+
+ unicam_reg_write(unicam, UNICAM_IBLS,
+ node->fmt.fmt.pix.bytesperline);
+ unicam_wr_dma_addr(node, node->cur_frm);
+ unicam_set_packing_config(unicam, fmtinfo);
+
+ ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt);
+ if (ret) {
+ /*
+ * If the source doesn't support frame descriptors, default to
+ * VC 0 and use the DT corresponding to the format.
+ */
+ vc = 0;
+ dt = fmtinfo->csi_dt;
+ }
+
+ unicam_cfg_image_id(unicam, vc, dt);
+
+ val = unicam_reg_read(unicam, UNICAM_MISC);
+ unicam_set_field(&val, 1, UNICAM_FL0);
+ unicam_set_field(&val, 1, UNICAM_FL1);
+ unicam_reg_write(unicam, UNICAM_MISC, val);
+
+ /* Enable peripheral */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPE);
+
+ /* Load image pointers */
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
+ /*
+ * Enable trigger only for the first frame to
+ * sync correctly to the FS from the source.
+ */
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC);
+}
+
+static void unicam_start_metadata(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE];
+
+ unicam_enable_ed(unicam);
+ unicam_wr_dma_addr(node, node->cur_frm);
+ unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP);
+}
+
+static void unicam_disable(struct unicam_device *unicam)
+{
+ /* Analogue lane control disable */
+ unicam_reg_write_field(unicam, UNICAM_ANA, 1, UNICAM_DDL);
+
+ /* Stop the output engine */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_SOE);
+
+ /* Disable the data lanes. */
+ unicam_reg_write(unicam, UNICAM_DAT0, 0);
+ unicam_reg_write(unicam, UNICAM_DAT1, 0);
+
+ if (unicam->max_data_lanes > 2) {
+ unicam_reg_write(unicam, UNICAM_DAT2, 0);
+ unicam_reg_write(unicam, UNICAM_DAT3, 0);
+ }
+
+ /* Peripheral reset */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR);
+ usleep_range(50, 100);
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ /* Disable peripheral */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Clear ED setup */
+ unicam_reg_write(unicam, UNICAM_DCS, 0);
+
+ /* Disable all lane clocks */
+ unicam_clk_write(unicam, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int __unicam_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_route *route;
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+ if (ret)
+ return ret;
+
+ for_each_active_route(&state->routing, route) {
+ const struct v4l2_mbus_framefmt *def_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (route->source_pad == UNICAM_SD_PAD_SOURCE_IMAGE)
+ def_fmt = &unicam_default_image_format;
+ else
+ def_fmt = &unicam_default_meta_format;
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+ *fmt = *def_fmt;
+ fmt = v4l2_subdev_state_get_format(state, route->source_pad,
+ route->source_stream);
+ *fmt = *def_fmt;
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = UNICAM_SD_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = UNICAM_SD_PAD_SOURCE_IMAGE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .len_routes = ARRAY_SIZE(routes),
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ /* Initialize routing to single route to the fist source pad. */
+ return __unicam_subdev_set_routing(sd, state, &routing);
+}
+
+static int unicam_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ u32 pad, stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ code->pad, code->stream,
+ &pad, &stream);
+ if (ret)
+ return ret;
+
+ if (unicam_sd_pad_is_source(code->pad)) {
+ /* No transcoding, source and sink codes must match. */
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (!fmt)
+ return -EINVAL;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = fmt->code;
+ } else {
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ if (code->index >= num_formats)
+ return -EINVAL;
+
+ code->code = formats[code->index].code;
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ u32 pad, stream;
+ int ret;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, fse->pad,
+ fse->stream, &pad,
+ &stream);
+ if (ret)
+ return ret;
+
+ if (unicam_sd_pad_is_source(fse->pad)) {
+ /* No transcoding, source and sink formats must match. */
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (!fmt)
+ return -EINVAL;
+
+ if (fse->code != fmt->code)
+ return -EINVAL;
+
+ fse->min_width = fmt->width;
+ fse->max_width = fmt->width;
+ fse->min_height = fmt->height;
+ fse->max_height = fmt->height;
+ } else {
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_code(fse->code, pad);
+ if (!fmtinfo)
+ return -EINVAL;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ fse->min_width = UNICAM_IMAGE_MIN_WIDTH;
+ fse->max_width = UNICAM_IMAGE_MAX_WIDTH;
+ fse->min_height = UNICAM_IMAGE_MIN_HEIGHT;
+ fse->max_height = UNICAM_IMAGE_MAX_HEIGHT;
+ } else {
+ fse->min_width = UNICAM_META_MIN_WIDTH;
+ fse->max_width = UNICAM_META_MAX_WIDTH;
+ fse->min_height = UNICAM_META_MIN_HEIGHT;
+ fse->max_height = UNICAM_META_MAX_HEIGHT;
+ }
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ struct v4l2_mbus_framefmt *sink_format, *source_format;
+ const struct unicam_format_info *fmtinfo;
+ u32 source_pad, source_stream;
+ int ret;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ unicam->subdev.enabled_streams)
+ return -EBUSY;
+
+ /* No transcoding, source and sink formats must match. */
+ if (unicam_sd_pad_is_source(format->pad))
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ /*
+ * Allowed formats for the stream on the sink pad depend on what source
+ * pad the stream is routed to. Find the corresponding source pad and
+ * use it to validate the media bus code.
+ */
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ format->pad, format->stream,
+ &source_pad, &source_stream);
+ if (ret)
+ return ret;
+
+ fmtinfo = unicam_find_format_by_code(format->format.code, source_pad);
+ if (!fmtinfo) {
+ fmtinfo = source_pad == UNICAM_SD_PAD_SOURCE_IMAGE
+ ? &unicam_image_formats[0] : &unicam_meta_formats[0];
+ format->format.code = fmtinfo->code;
+ }
+
+ if (source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ format->format.width = clamp_t(unsigned int,
+ format->format.width,
+ UNICAM_IMAGE_MIN_WIDTH,
+ UNICAM_IMAGE_MAX_WIDTH);
+ format->format.height = clamp_t(unsigned int,
+ format->format.height,
+ UNICAM_IMAGE_MIN_HEIGHT,
+ UNICAM_IMAGE_MAX_HEIGHT);
+ format->format.field = V4L2_FIELD_NONE;
+ } else {
+ format->format.width = clamp_t(unsigned int,
+ format->format.width,
+ UNICAM_META_MIN_WIDTH,
+ UNICAM_META_MAX_WIDTH);
+ format->format.height = clamp_t(unsigned int,
+ format->format.height,
+ UNICAM_META_MIN_HEIGHT,
+ UNICAM_META_MAX_HEIGHT);
+ format->format.field = V4L2_FIELD_NONE;
+
+ /* Colorspace don't apply to metadata. */
+ format->format.colorspace = 0;
+ format->format.ycbcr_enc = 0;
+ format->format.quantization = 0;
+ format->format.xfer_func = 0;
+ }
+
+ sink_format = v4l2_subdev_state_get_format(state, format->pad,
+ format->stream);
+ source_format = v4l2_subdev_state_get_format(state, source_pad,
+ source_stream);
+ *sink_format = format->format;
+ *source_format = format->format;
+
+ return 0;
+}
+
+static int unicam_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE && unicam->subdev.enabled_streams)
+ return -EBUSY;
+
+ return __unicam_subdev_set_routing(sd, state, routing);
+}
+
+static int unicam_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ u32 other_pad, other_stream;
+ int ret;
+
+ if (!unicam->subdev.enabled_streams) {
+ /* Configure and start Unicam. */
+ unicam->sequence = 0;
+
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE))
+ unicam_start_metadata(unicam);
+
+ unicam_start_rx(unicam, state);
+ }
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ &other_pad, &other_stream);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_enable_streams(unicam->sensor.subdev,
+ unicam->sensor.pad->index,
+ BIT(other_stream));
+ if (ret) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ unicam->subdev.enabled_streams |= BIT(other_stream);
+
+ return 0;
+}
+
+static int unicam_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ u32 other_pad, other_stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ &other_pad, &other_stream);
+ if (ret)
+ return ret;
+
+ v4l2_subdev_disable_streams(unicam->sensor.subdev,
+ unicam->sensor.pad->index,
+ BIT(other_stream));
+
+ unicam->subdev.enabled_streams &= ~BIT(other_stream);
+
+ if (!unicam->subdev.enabled_streams)
+ unicam_disable(unicam);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops unicam_subdev_pad_ops = {
+ .enum_mbus_code = unicam_subdev_enum_mbus_code,
+ .enum_frame_size = unicam_subdev_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = unicam_subdev_set_format,
+ .set_routing = unicam_subdev_set_routing,
+ .enable_streams = unicam_sd_enable_streams,
+ .disable_streams = unicam_sd_disable_streams,
+};
+
+static const struct v4l2_subdev_ops unicam_subdev_ops = {
+ .pad = &unicam_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops unicam_subdev_internal_ops = {
+ .init_state = unicam_subdev_init_state,
+};
+
+static const struct media_entity_operations unicam_subdev_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+static int unicam_subdev_init(struct unicam_device *unicam)
+{
+ struct v4l2_subdev *sd = &unicam->subdev.sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &unicam_subdev_ops);
+ sd->internal_ops = &unicam_subdev_internal_ops;
+ v4l2_set_subdevdata(sd, unicam);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &unicam_subdev_media_ops;
+ sd->dev = unicam->dev;
+ sd->owner = THIS_MODULE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+
+ strscpy(sd->name, "unicam", sizeof(sd->name));
+
+ unicam->subdev.pads[UNICAM_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_IMAGE].flags = MEDIA_PAD_FL_SOURCE;
+ unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_METADATA].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(unicam->subdev.pads),
+ unicam->subdev.pads);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to initialize media entity: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to initialize subdev: %d\n", ret);
+ goto err_entity;
+ }
+
+ ret = v4l2_device_register_subdev(&unicam->v4l2_dev, sd);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to register subdev: %d\n", ret);
+ goto err_subdev;
+ }
+
+ return 0;
+
+err_subdev:
+ v4l2_subdev_cleanup(sd);
+err_entity:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+static void unicam_subdev_cleanup(struct unicam_device *unicam)
+{
+ v4l2_subdev_cleanup(&unicam->subdev.sd);
+ media_entity_cleanup(&unicam->subdev.sd.entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Videobuf2 queue operations
+ */
+
+static int unicam_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage
+ : node->fmt.fmt.meta.buffersize;
+
+ if (*nplanes) {
+ if (sizes[0] < size) {
+ dev_dbg(node->dev->dev, "sizes[0] %i < size %u\n",
+ sizes[0], size);
+ return -EINVAL;
+ }
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int unicam_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = to_unicam_buffer(vb);
+ u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage
+ : node->fmt.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_dbg(node->dev->dev,
+ "data will not fit into plane (%lu < %u)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ buf->size = size;
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+
+ return 0;
+}
+
+static void unicam_return_buffers(struct unicam_node *node,
+ enum vb2_buffer_state state)
+{
+ struct unicam_buffer *buf, *tmp;
+
+ list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ if (node->cur_frm)
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
+ state);
+ if (node->next_frm && node->cur_frm != node->next_frm)
+ vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+ state);
+
+ node->cur_frm = NULL;
+ node->next_frm = NULL;
+}
+
+static int unicam_num_data_lanes(struct unicam_device *unicam)
+{
+ struct v4l2_mbus_config mbus_config = { 0 };
+ unsigned int num_data_lanes;
+ int ret;
+
+ if (unicam->bus_type != V4L2_MBUS_CSI2_DPHY)
+ return unicam->max_data_lanes;
+
+ ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_mbus_config,
+ unicam->sensor.pad->index, &mbus_config);
+ if (ret == -ENOIOCTLCMD)
+ return unicam->max_data_lanes;
+
+ if (ret < 0) {
+ dev_err(unicam->dev, "Failed to get mbus config: %d\n", ret);
+ return ret;
+ }
+
+ num_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+
+ if (num_data_lanes != 1 && num_data_lanes != 2 && num_data_lanes != 4) {
+ dev_err(unicam->dev,
+ "Device %s has requested %u data lanes, invalid\n",
+ unicam->sensor.subdev->name, num_data_lanes);
+ return -EINVAL;
+ }
+
+ if (num_data_lanes > unicam->max_data_lanes) {
+ dev_err(unicam->dev,
+ "Device %s has requested %u data lanes, >%u configured in DT\n",
+ unicam->sensor.subdev->name, num_data_lanes,
+ unicam->max_data_lanes);
+ return -EINVAL;
+ }
+
+ return num_data_lanes;
+}
+
+static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *unicam = node->dev;
+ struct unicam_buffer *buf;
+ struct media_pipeline_pad_iter iter;
+ struct media_pad *pad;
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(unicam->dev, "Starting stream on %s device\n",
+ is_metadata_node(node) ? "metadata" : "image");
+
+ /*
+ * Start the pipeline. This validates all links, and populates the
+ * pipeline structure.
+ */
+ ret = video_device_pipeline_start(&node->video_dev, &unicam->pipe.pipe);
+ if (ret < 0) {
+ dev_dbg(unicam->dev, "Failed to start media pipeline: %d\n", ret);
+ goto err_buffers;
+ }
+
+ /*
+ * Determine which video nodes are included in the pipeline, and get the
+ * number of data lanes.
+ */
+ if (unicam->pipe.pipe.start_count == 1) {
+ unicam->pipe.nodes = 0;
+
+ media_pipeline_for_each_pad(&unicam->pipe.pipe, &iter, pad) {
+ if (pad->entity != &unicam->subdev.sd.entity)
+ continue;
+
+ if (pad->index == UNICAM_SD_PAD_SOURCE_IMAGE)
+ unicam->pipe.nodes |= BIT(UNICAM_IMAGE_NODE);
+ else if (pad->index == UNICAM_SD_PAD_SOURCE_METADATA)
+ unicam->pipe.nodes |= BIT(UNICAM_METADATA_NODE);
+ }
+
+ if (!(unicam->pipe.nodes & BIT(UNICAM_IMAGE_NODE))) {
+ dev_dbg(unicam->dev,
+ "Pipeline does not include image node\n");
+ ret = -EPIPE;
+ goto err_pipeline;
+ }
+
+ ret = unicam_num_data_lanes(unicam);
+ if (ret < 0)
+ goto err_pipeline;
+
+ unicam->pipe.num_data_lanes = ret;
+
+ dev_dbg(unicam->dev, "Running with %u data lanes, nodes %u\n",
+ unicam->pipe.num_data_lanes, unicam->pipe.nodes);
+ }
+
+ /* Arm the node with the first buffer from the DMA queue. */
+ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list);
+ node->cur_frm = buf;
+ node->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+
+ /*
+ * Wait for all the video devices in the pipeline to have been started
+ * before starting the hardware. In the general case, this would
+ * prevent capturing multiple streams independently. However, the
+ * Unicam DMA engines are not generic, they have been designed to
+ * capture image data and embedded data from the same camera sensor.
+ * Not only does the main use case not benefit from independent
+ * capture, it requires proper synchronization of the streams at start
+ * time.
+ */
+ if (unicam->pipe.pipe.start_count < hweight32(unicam->pipe.nodes))
+ return 0;
+
+ ret = pm_runtime_resume_and_get(unicam->dev);
+ if (ret < 0) {
+ dev_err(unicam->dev, "PM runtime resume failed: %d\n", ret);
+ goto err_pipeline;
+ }
+
+ /* Enable the streams on the source. */
+ ret = v4l2_subdev_enable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ BIT(0));
+ if (ret < 0) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ goto err_pm_put;
+ }
+
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) {
+ ret = v4l2_subdev_enable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ BIT(0));
+ if (ret < 0) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ goto err_disable_streams;
+ }
+ }
+
+ return 0;
+
+err_disable_streams:
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE, BIT(0));
+err_pm_put:
+ pm_runtime_put_sync(unicam->dev);
+err_pipeline:
+ video_device_pipeline_stop(&node->video_dev);
+err_buffers:
+ unicam_return_buffers(node, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void unicam_stop_streaming(struct vb2_queue *vq)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *unicam = node->dev;
+
+ /* Stop the hardware when the first video device gets stopped. */
+ if (unicam->pipe.pipe.start_count == hweight32(unicam->pipe.nodes)) {
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE))
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ BIT(0));
+
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ BIT(0));
+
+ pm_runtime_put(unicam->dev);
+ }
+
+ video_device_pipeline_stop(&node->video_dev);
+
+ /* Clear all queued buffers for the node */
+ unicam_return_buffers(node, VB2_BUF_STATE_ERROR);
+}
+
+static void unicam_buffer_queue(struct vb2_buffer *vb)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = to_unicam_buffer(vb);
+
+ spin_lock_irq(&node->dma_queue_lock);
+ list_add_tail(&buf->list, &node->dma_queue);
+ spin_unlock_irq(&node->dma_queue_lock);
+}
+
+static const struct vb2_ops unicam_video_qops = {
+ .queue_setup = unicam_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_prepare = unicam_buffer_prepare,
+ .start_streaming = unicam_start_streaming,
+ .stop_streaming = unicam_stop_streaming,
+ .buf_queue = unicam_buffer_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 video device operations
+ */
+
+static int unicam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+
+ cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
+
+ return 0;
+}
+
+static int unicam_enum_fmt_vid(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int index;
+ unsigned int i;
+
+ for (i = 0, index = 0; i < ARRAY_SIZE(unicam_image_formats); i++) {
+ if (f->mbus_code && unicam_image_formats[i].code != f->mbus_code)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_image_formats[i].fourcc;
+ return 0;
+ }
+
+ index++;
+
+ if (!unicam_image_formats[i].unpacked_fourcc)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_image_formats[i].unpacked_fourcc;
+ return 0;
+ }
+
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int unicam_g_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ *f = node->fmt;
+
+ return 0;
+}
+
+static void __unicam_try_fmt_vid(struct unicam_node *node,
+ struct v4l2_pix_format *pix)
+{
+ const struct unicam_format_info *fmtinfo;
+
+ /*
+ * Default to the first format if the requested pixel format code isn't
+ * supported.
+ */
+ fmtinfo = unicam_find_format_by_fourcc(pix->pixelformat,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (!fmtinfo) {
+ fmtinfo = &unicam_image_formats[0];
+ pix->pixelformat = fmtinfo->fourcc;
+ }
+
+ unicam_calc_image_size_bpl(node->dev, fmtinfo, pix);
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+}
+
+static int unicam_try_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ __unicam_try_fmt_vid(node, &f->fmt.pix);
+ return 0;
+}
+
+static int unicam_s_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ __unicam_try_fmt_vid(node, &f->fmt.pix);
+ node->fmt = *f;
+
+ return 0;
+}
+
+static int unicam_enum_fmt_meta(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int i, index;
+
+ for (i = 0, index = 0; i < ARRAY_SIZE(unicam_meta_formats); i++) {
+ if (f->mbus_code && unicam_meta_formats[i].code != f->mbus_code)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_meta_formats[i].fourcc;
+ f->type = V4L2_BUF_TYPE_META_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_META_LINE_BASED;
+ return 0;
+ }
+
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int unicam_g_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ f->fmt.meta = node->fmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct unicam_format_info *
+__unicam_try_fmt_meta(struct unicam_node *node, struct v4l2_meta_format *meta)
+{
+ const struct unicam_format_info *fmtinfo;
+
+ /*
+ * Default to the first format if the requested pixel format code isn't
+ * supported.
+ */
+ fmtinfo = unicam_find_format_by_fourcc(meta->dataformat,
+ UNICAM_SD_PAD_SOURCE_METADATA);
+ if (!fmtinfo) {
+ fmtinfo = &unicam_meta_formats[0];
+ meta->dataformat = fmtinfo->fourcc;
+ }
+
+ unicam_calc_meta_size_bpl(node->dev, fmtinfo, meta);
+
+ return fmtinfo;
+}
+
+static int unicam_try_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ __unicam_try_fmt_meta(node, &f->fmt.meta);
+ return 0;
+}
+
+static int unicam_s_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ __unicam_try_fmt_meta(node, &f->fmt.meta);
+ node->fmt = *f;
+
+ return 0;
+}
+
+static int unicam_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct unicam_node *node = video_drvdata(file);
+ int ret = -EINVAL;
+
+ if (fsize->index > 0)
+ return ret;
+
+ if (is_image_node(node)) {
+ if (!unicam_find_format_by_fourcc(fsize->pixel_format,
+ UNICAM_SD_PAD_SOURCE_IMAGE))
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = UNICAM_IMAGE_MIN_WIDTH;
+ fsize->stepwise.max_width = UNICAM_IMAGE_MAX_WIDTH;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = UNICAM_IMAGE_MIN_HEIGHT;
+ fsize->stepwise.max_height = UNICAM_IMAGE_MAX_HEIGHT;
+ fsize->stepwise.step_height = 1;
+ } else {
+ if (!unicam_find_format_by_fourcc(fsize->pixel_format,
+ UNICAM_SD_PAD_SOURCE_METADATA))
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = UNICAM_META_MIN_WIDTH;
+ fsize->stepwise.max_width = UNICAM_META_MAX_WIDTH;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = UNICAM_META_MIN_HEIGHT;
+ fsize->stepwise.max_height = UNICAM_META_MAX_HEIGHT;
+ fsize->stepwise.step_height = 1;
+ }
+
+ return 0;
+}
+
+static int unicam_log_status(struct file *file, void *fh)
+{
+ struct unicam_node *node = video_drvdata(file);
+ struct unicam_device *unicam = node->dev;
+ u32 reg;
+
+ /* status for sub devices */
+ v4l2_device_call_all(&unicam->v4l2_dev, 0, core, log_status);
+
+ dev_info(unicam->dev, "-----Receiver status-----\n");
+ dev_info(unicam->dev, "V4L2 width/height: %ux%u\n",
+ node->fmt.fmt.pix.width, node->fmt.fmt.pix.height);
+ dev_info(unicam->dev, "V4L2 format: %08x\n",
+ node->fmt.fmt.pix.pixelformat);
+ reg = unicam_reg_read(unicam, UNICAM_IPIPE);
+ dev_info(unicam->dev, "Unpacking/packing: %u / %u\n",
+ unicam_get_field(reg, UNICAM_PUM_MASK),
+ unicam_get_field(reg, UNICAM_PPM_MASK));
+ dev_info(unicam->dev, "----Live data----\n");
+ dev_info(unicam->dev, "Programmed stride: %4u\n",
+ unicam_reg_read(unicam, UNICAM_IBLS));
+ dev_info(unicam->dev, "Detected resolution: %ux%u\n",
+ unicam_reg_read(unicam, UNICAM_IHSTA),
+ unicam_reg_read(unicam, UNICAM_IVSTA));
+ dev_info(unicam->dev, "Write pointer: %08x\n",
+ unicam_reg_read(unicam, UNICAM_IBWP));
+
+ return 0;
+}
+
+static int unicam_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
+ .vidioc_querycap = unicam_querycap,
+
+ .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid,
+ .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid,
+ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid,
+ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid,
+
+ .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta,
+ .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta,
+ .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta,
+ .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta,
+
+ .vidioc_enum_framesizes = unicam_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = unicam_log_status,
+ .vidioc_subscribe_event = unicam_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/* unicam capture driver file operations */
+static const struct v4l2_file_operations unicam_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int unicam_video_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ struct unicam_node *node = video_get_drvdata(vdev);
+ const u32 pad = is_image_node(node) ? UNICAM_SD_PAD_SOURCE_IMAGE
+ : UNICAM_SD_PAD_SOURCE_METADATA;
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ format = v4l2_subdev_state_get_format(state, pad, 0);
+ if (!format) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (is_image_node(node)) {
+ const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix;
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (WARN_ON(!fmtinfo)) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (fmtinfo->code != format->code ||
+ fmt->height != format->height ||
+ fmt->width != format->width ||
+ fmt->field != format->field) {
+ dev_dbg(node->dev->dev,
+ "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n",
+ fmt->width, fmt->height, fmtinfo->code,
+ v4l2_field_names[fmt->field],
+ format->width, format->height, format->code,
+ v4l2_field_names[format->field]);
+ ret = -EPIPE;
+ }
+ } else {
+ const struct v4l2_meta_format *fmt = &node->fmt.fmt.meta;
+
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_fourcc(fmt->dataformat,
+ UNICAM_SD_PAD_SOURCE_METADATA);
+ if (WARN_ON(!fmtinfo)) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (fmtinfo->code != format->code ||
+ fmt->height != format->height ||
+ fmt->width != format->width) {
+ dev_dbg(node->dev->dev,
+ "meta: (%u x %u) 0x%04x != (%u x %u) 0x%04x\n",
+ fmt->width, fmt->height, fmtinfo->code,
+ format->width, format->height, format->code);
+ ret = -EPIPE;
+ }
+ }
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
+}
+
+static const struct media_entity_operations unicam_video_media_ops = {
+ .link_validate = unicam_video_link_validate,
+};
+
+static void unicam_node_release(struct video_device *vdev)
+{
+ struct unicam_node *node = video_get_drvdata(vdev);
+
+ unicam_put(node->dev);
+}
+
+static void unicam_set_default_format(struct unicam_node *node)
+{
+ if (is_image_node(node)) {
+ struct v4l2_pix_format *fmt = &node->fmt.fmt.pix;
+ const struct unicam_format_info *fmtinfo =
+ &unicam_image_formats[0];
+
+ node->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ v4l2_fill_pix_format(fmt, &unicam_default_image_format);
+ fmt->pixelformat = fmtinfo->fourcc;
+ unicam_calc_image_size_bpl(node->dev, fmtinfo, fmt);
+ } else {
+ struct v4l2_meta_format *fmt = &node->fmt.fmt.meta;
+ const struct unicam_format_info *fmtinfo =
+ &unicam_meta_formats[0];
+
+ node->fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
+
+ fmt->dataformat = fmtinfo->fourcc;
+ fmt->width = unicam_default_meta_format.width;
+ fmt->height = unicam_default_meta_format.height;
+ unicam_calc_meta_size_bpl(node->dev, fmtinfo, fmt);
+ }
+}
+
+static int unicam_register_node(struct unicam_device *unicam,
+ enum unicam_node_type type)
+{
+ const u32 pad_index = type == UNICAM_IMAGE_NODE
+ ? UNICAM_SD_PAD_SOURCE_IMAGE
+ : UNICAM_SD_PAD_SOURCE_METADATA;
+ struct unicam_node *node = &unicam->node[type];
+ struct video_device *vdev = &node->video_dev;
+ struct vb2_queue *q = &node->buffer_queue;
+ int ret;
+
+ node->dev = unicam_get(unicam);
+ node->id = type;
+
+ spin_lock_init(&node->dma_queue_lock);
+
+ INIT_LIST_HEAD(&node->dma_queue);
+
+ /* Initialize the videobuf2 queue. */
+ q->type = type == UNICAM_IMAGE_NODE ? V4L2_BUF_TYPE_VIDEO_CAPTURE
+ : V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = node;
+ q->ops = &unicam_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct unicam_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &unicam->lock;
+ q->min_queued_buffers = 1;
+ q->dev = unicam->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(unicam->dev, "vb2_queue_init() failed\n");
+ goto err_unicam_put;
+ }
+
+ /* Initialize the video device. */
+ vdev->release = unicam_node_release;
+ vdev->fops = &unicam_fops;
+ vdev->ioctl_ops = &unicam_ioctl_ops;
+ vdev->v4l2_dev = &unicam->v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &unicam->lock;
+ vdev->device_caps = type == UNICAM_IMAGE_NODE
+ ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE;
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ vdev->entity.ops = &unicam_video_media_ops;
+
+ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME,
+ type == UNICAM_IMAGE_NODE ? "image" : "embedded");
+
+ video_set_drvdata(vdev, node);
+
+ if (type == UNICAM_IMAGE_NODE)
+ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto err_unicam_put;
+
+ node->dummy_buf.size = UNICAM_DUMMY_BUF_SIZE;
+ node->dummy_buf_cpu_addr = dma_alloc_coherent(unicam->dev,
+ node->dummy_buf.size,
+ &node->dummy_buf.dma_addr,
+ GFP_KERNEL);
+ if (!node->dummy_buf_cpu_addr) {
+ dev_err(unicam->dev, "Unable to allocate dummy buffer.\n");
+ ret = -ENOMEM;
+ goto err_entity_cleanup;
+ }
+
+ unicam_set_default_format(node);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register video device %s\n",
+ vdev->name);
+ goto err_dma_free;
+ }
+
+ node->registered = true;
+
+ ret = media_create_pad_link(&unicam->subdev.sd.entity,
+ pad_index,
+ &node->video_dev.entity,
+ 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ /*
+ * No need for cleanup, the caller will unregister the
+ * video device, which will drop the reference on the
+ * device and trigger the cleanup.
+ */
+ dev_err(unicam->dev, "Unable to create pad link for %s\n",
+ unicam->sensor.subdev->name);
+ return ret;
+ }
+
+ return 0;
+
+err_dma_free:
+ dma_free_coherent(unicam->dev, node->dummy_buf.size,
+ node->dummy_buf_cpu_addr,
+ node->dummy_buf.dma_addr);
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_unicam_put:
+ unicam_put(unicam);
+ return ret;
+}
+
+static void unicam_unregister_nodes(struct unicam_device *unicam)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (node->registered) {
+ vb2_video_unregister_device(&node->video_dev);
+ node->registered = false;
+ }
+
+ if (node->dummy_buf_cpu_addr)
+ dma_free_coherent(unicam->dev, node->dummy_buf.size,
+ node->dummy_buf_cpu_addr,
+ node->dummy_buf.dma_addr);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int unicam_runtime_resume(struct device *dev)
+{
+ struct unicam_device *unicam = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_set_min_rate(unicam->vpu_clock, UNICAM_MIN_VPU_CLOCK_RATE);
+ if (ret) {
+ dev_err(unicam->dev, "failed to set up VPU clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(unicam->vpu_clock);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to enable VPU clock: %d\n", ret);
+ goto err_vpu_clock;
+ }
+
+ ret = clk_set_rate(unicam->clock, 100 * 1000 * 1000);
+ if (ret) {
+ dev_err(unicam->dev, "failed to set up CSI clock\n");
+ goto err_vpu_prepare;
+ }
+
+ ret = clk_prepare_enable(unicam->clock);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to enable CSI clock: %d\n", ret);
+ goto err_vpu_prepare;
+ }
+
+ return 0;
+
+err_vpu_prepare:
+ clk_disable_unprepare(unicam->vpu_clock);
+err_vpu_clock:
+ if (clk_set_min_rate(unicam->vpu_clock, 0))
+ dev_err(unicam->dev, "failed to reset the VPU clock\n");
+
+ return ret;
+}
+
+static int unicam_runtime_suspend(struct device *dev)
+{
+ struct unicam_device *unicam = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(unicam->clock);
+
+ if (clk_set_min_rate(unicam->vpu_clock, 0))
+ dev_err(unicam->dev, "failed to reset the VPU clock\n");
+
+ clk_disable_unprepare(unicam->vpu_clock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops unicam_pm_ops = {
+ RUNTIME_PM_OPS(unicam_runtime_suspend, unicam_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 async notifier
+ */
+
+static int unicam_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct unicam_device *unicam = notifier_to_unicam_device(notifier);
+ struct media_pad *sink = &unicam->subdev.pads[UNICAM_SD_PAD_SINK];
+ struct media_pad *source;
+ int ret;
+
+ dev_dbg(unicam->dev, "Using sensor %s for capture\n",
+ subdev->name);
+
+ ret = v4l2_create_fwnode_links_to_pad(subdev, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+
+ source = media_pad_remote_pad_unique(sink);
+ if (IS_ERR(source)) {
+ dev_err(unicam->dev, "No connected sensor pad\n");
+ return PTR_ERR(source);
+ }
+
+ unicam->sensor.subdev = subdev;
+ unicam->sensor.pad = source;
+
+ return 0;
+}
+
+static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct unicam_device *unicam = notifier_to_unicam_device(notifier);
+ int ret;
+
+ ret = unicam_register_node(unicam, UNICAM_IMAGE_NODE);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register image video device.\n");
+ goto unregister;
+ }
+
+ ret = unicam_register_node(unicam, UNICAM_METADATA_NODE);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register metadata video device.\n");
+ goto unregister;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register subdev nodes.\n");
+ goto unregister;
+ }
+
+ return 0;
+
+unregister:
+ unicam_unregister_nodes(unicam);
+ unicam_put(unicam);
+
+ return ret;
+}
+
+static const struct v4l2_async_notifier_operations unicam_async_ops = {
+ .bound = unicam_async_bound,
+ .complete = unicam_async_complete,
+};
+
+static int unicam_async_nf_init(struct unicam_device *unicam)
+{
+ struct v4l2_fwnode_endpoint ep = { };
+ struct fwnode_handle *ep_handle;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ ret = of_property_read_u32(unicam->dev->of_node, "brcm,num-data-lanes",
+ &unicam->max_data_lanes);
+ if (ret < 0) {
+ dev_err(unicam->dev, "Missing %s DT property\n",
+ "brcm,num-data-lanes");
+ return -EINVAL;
+ }
+
+ /* Get and parse the local endpoint. */
+ ep_handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(unicam->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep_handle) {
+ dev_err(unicam->dev, "No endpoint found\n");
+ return -ENODEV;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep_handle, &ep);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to parse endpoint: %d\n", ret);
+ goto error;
+ }
+
+ unicam->bus_type = ep.bus_type;
+
+ switch (ep.bus_type) {
+ case V4L2_MBUS_CSI2_DPHY: {
+ unsigned int num_data_lanes = ep.bus.mipi_csi2.num_data_lanes;
+
+ if (num_data_lanes != 1 && num_data_lanes != 2 &&
+ num_data_lanes != 4) {
+ dev_err(unicam->dev, "%u data lanes not supported\n",
+ num_data_lanes);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (num_data_lanes > unicam->max_data_lanes) {
+ dev_err(unicam->dev,
+ "Endpoint uses %u data lanes when %u are supported\n",
+ num_data_lanes, unicam->max_data_lanes);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ unicam->max_data_lanes = num_data_lanes;
+ unicam->bus_flags = ep.bus.mipi_csi2.flags;
+ break;
+ }
+
+ case V4L2_MBUS_CCP2:
+ unicam->max_data_lanes = 1;
+ unicam->bus_flags = ep.bus.mipi_csi1.strobe;
+ break;
+
+ default:
+ /* Unsupported bus type */
+ dev_err(unicam->dev, "Unsupported bus type %u\n", ep.bus_type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Initialize and register the async notifier. */
+ v4l2_async_nf_init(&unicam->notifier, &unicam->v4l2_dev);
+
+ asc = v4l2_async_nf_add_fwnode_remote(&unicam->notifier, ep_handle,
+ struct v4l2_async_connection);
+ fwnode_handle_put(ep_handle);
+ ep_handle = NULL;
+
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ dev_err(unicam->dev, "Failed to add entry to notifier: %d\n",
+ ret);
+ goto error;
+ }
+
+ unicam->notifier.ops = &unicam_async_ops;
+
+ ret = v4l2_async_nf_register(&unicam->notifier);
+ if (ret) {
+ dev_err(unicam->dev, "Error registering device notifier: %d\n",
+ ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ fwnode_handle_put(ep_handle);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe & remove
+ */
+
+static int unicam_media_init(struct unicam_device *unicam)
+{
+ int ret;
+
+ unicam->mdev.dev = unicam->dev;
+ strscpy(unicam->mdev.model, UNICAM_MODULE_NAME,
+ sizeof(unicam->mdev.model));
+ unicam->mdev.hw_revision = 0;
+
+ media_device_init(&unicam->mdev);
+
+ unicam->v4l2_dev.mdev = &unicam->mdev;
+
+ ret = v4l2_device_register(unicam->dev, &unicam->v4l2_dev);
+ if (ret < 0) {
+ dev_err(unicam->dev, "Unable to register v4l2 device\n");
+ goto err_media_cleanup;
+ }
+
+ ret = media_device_register(&unicam->mdev);
+ if (ret < 0) {
+ dev_err(unicam->dev,
+ "Unable to register media-controller device\n");
+ goto err_v4l2_unregister;
+ }
+
+ return 0;
+
+err_v4l2_unregister:
+ v4l2_device_unregister(&unicam->v4l2_dev);
+err_media_cleanup:
+ media_device_cleanup(&unicam->mdev);
+ return ret;
+}
+
+static int unicam_probe(struct platform_device *pdev)
+{
+ struct unicam_device *unicam;
+ int ret;
+
+ unicam = kzalloc(sizeof(*unicam), GFP_KERNEL);
+ if (!unicam)
+ return -ENOMEM;
+
+ kref_init(&unicam->kref);
+ mutex_init(&unicam->lock);
+
+ unicam->dev = &pdev->dev;
+ platform_set_drvdata(pdev, unicam);
+
+ unicam->base = devm_platform_ioremap_resource_byname(pdev, "unicam");
+ if (IS_ERR(unicam->base)) {
+ ret = PTR_ERR(unicam->base);
+ goto err_unicam_put;
+ }
+
+ unicam->clk_gate_base = devm_platform_ioremap_resource_byname(pdev, "cmi");
+ if (IS_ERR(unicam->clk_gate_base)) {
+ ret = PTR_ERR(unicam->clk_gate_base);
+ goto err_unicam_put;
+ }
+
+ unicam->clock = devm_clk_get(&pdev->dev, "lp");
+ if (IS_ERR(unicam->clock)) {
+ dev_err(unicam->dev, "Failed to get lp clock\n");
+ ret = PTR_ERR(unicam->clock);
+ goto err_unicam_put;
+ }
+
+ unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu");
+ if (IS_ERR(unicam->vpu_clock)) {
+ dev_err(unicam->dev, "Failed to get vpu clock\n");
+ ret = PTR_ERR(unicam->vpu_clock);
+ goto err_unicam_put;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto err_unicam_put;
+
+ ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
+ "unicam_capture0", unicam);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request interrupt\n");
+ goto err_unicam_put;
+ }
+
+ /* Enable the block power domain. */
+ pm_runtime_enable(&pdev->dev);
+
+ ret = unicam_media_init(unicam);
+ if (ret)
+ goto err_pm_runtime;
+
+ ret = unicam_subdev_init(unicam);
+ if (ret)
+ goto err_media_unregister;
+
+ ret = unicam_async_nf_init(unicam);
+ if (ret)
+ goto err_subdev_unregister;
+
+ return 0;
+
+err_subdev_unregister:
+ unicam_subdev_cleanup(unicam);
+err_media_unregister:
+ media_device_unregister(&unicam->mdev);
+err_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+err_unicam_put:
+ unicam_put(unicam);
+
+ return ret;
+}
+
+static void unicam_remove(struct platform_device *pdev)
+{
+ struct unicam_device *unicam = platform_get_drvdata(pdev);
+
+ unicam_unregister_nodes(unicam);
+ v4l2_device_unregister(&unicam->v4l2_dev);
+ media_device_unregister(&unicam->mdev);
+ v4l2_async_nf_unregister(&unicam->notifier);
+
+ unicam_subdev_cleanup(unicam);
+
+ unicam_put(unicam);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id unicam_of_match[] = {
+ { .compatible = "brcm,bcm2835-unicam", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, unicam_of_match);
+
+static struct platform_driver unicam_driver = {
+ .probe = unicam_probe,
+ .remove_new = unicam_remove,
+ .driver = {
+ .name = UNICAM_MODULE_NAME,
+ .pm = pm_ptr(&unicam_pm_ops),
+ .of_match_table = unicam_of_match,
+ },
+};
+
+module_platform_driver(unicam_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
+MODULE_DESCRIPTION("BCM2835 Unicam driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 2d7b0508cc9a..6f7d27a48eff 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -239,10 +239,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
- ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
- if (ret)
- goto err_disable_pclk;
-
/* Enable DPHY clk and data lanes. */
if (csi2rx->dphy) {
reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
@@ -252,6 +248,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
}
writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+
+ ret = csi2rx_configure_ext_dphy(csi2rx);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Failed to configure external DPHY: %d\n", ret);
+ goto err_disable_pclk;
+ }
}
/*
@@ -291,14 +294,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
reset_control_deassert(csi2rx->sys_rst);
- if (csi2rx->dphy) {
- ret = csi2rx_configure_ext_dphy(csi2rx);
- if (ret) {
- dev_err(csi2rx->dev,
- "Failed to configure external DPHY: %d\n", ret);
- goto err_disable_sysclk;
- }
- }
+ ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
+ if (ret)
+ goto err_disable_sysclk;
clk_disable_unprepare(csi2rx->p_clk);
@@ -312,6 +310,10 @@ err_disable_pixclk:
clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
}
+ if (csi2rx->dphy) {
+ writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+ phy_power_off(csi2rx->dphy);
+ }
err_disable_pclk:
clk_disable_unprepare(csi2rx->p_clk);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
index 8433ecab230c..7e0f34bfa5be 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
@@ -52,11 +52,12 @@ int wave5_vpu_release_device(struct file *filp,
char *name)
{
struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
+ struct vpu_device *dev = inst->dev;
+ int ret = 0;
v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
if (inst->state != VPU_INST_STATE_NONE) {
u32 fail_res;
- int ret;
ret = close_func(inst, &fail_res);
if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
@@ -71,8 +72,20 @@ int wave5_vpu_release_device(struct file *filp,
}
wave5_cleanup_instance(inst);
+ if (dev->irq < 0) {
+ ret = mutex_lock_interruptible(&dev->dev_lock);
+ if (ret)
+ return ret;
- return 0;
+ if (list_empty(&dev->instances)) {
+ dev_dbg(dev->dev, "Disabling the hrtimer\n");
+ hrtimer_cancel(&dev->hrtimer);
+ }
+
+ mutex_unlock(&dev->dev_lock);
+ }
+
+ return ret;
}
int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index ef227af72348..c8624c681fa6 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -1810,7 +1810,6 @@ static int wave5_vpu_open_dec(struct file *filp)
v4l2_fh_add(&inst->v4l2_fh);
INIT_LIST_HEAD(&inst->list);
- list_add_tail(&inst->list, &dev->instances);
inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev;
inst->v4l2_fh.m2m_ctx =
@@ -1867,6 +1866,18 @@ static int wave5_vpu_open_dec(struct file *filp)
wave5_vdi_allocate_sram(inst->dev);
+ ret = mutex_lock_interruptible(&dev->dev_lock);
+ if (ret)
+ goto cleanup_inst;
+
+ if (dev->irq < 0 && !hrtimer_active(&dev->hrtimer) && list_empty(&dev->instances))
+ hrtimer_start(&dev->hrtimer, ns_to_ktime(dev->vpu_poll_interval * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL_PINNED);
+
+ list_add_tail(&inst->list, &dev->instances);
+
+ mutex_unlock(&dev->dev_lock);
+
return 0;
cleanup_inst:
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index 8bbf9d10b467..a45a2f699000 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -1554,7 +1554,6 @@ static int wave5_vpu_open_enc(struct file *filp)
v4l2_fh_add(&inst->v4l2_fh);
INIT_LIST_HEAD(&inst->list);
- list_add_tail(&inst->list, &dev->instances);
inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev;
inst->v4l2_fh.m2m_ctx =
@@ -1729,6 +1728,18 @@ static int wave5_vpu_open_enc(struct file *filp)
wave5_vdi_allocate_sram(inst->dev);
+ ret = mutex_lock_interruptible(&dev->dev_lock);
+ if (ret)
+ goto cleanup_inst;
+
+ if (dev->irq < 0 && !hrtimer_active(&dev->hrtimer) && list_empty(&dev->instances))
+ hrtimer_start(&dev->hrtimer, ns_to_ktime(dev->vpu_poll_interval * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL_PINNED);
+
+ list_add_tail(&inst->list, &dev->instances);
+
+ mutex_unlock(&dev->dev_lock);
+
return 0;
cleanup_inst:
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
index 1b3df5b04249..68a519ac412d 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -26,6 +26,9 @@ struct wave5_match_data {
const char *fw_name;
};
+static int vpu_poll_interval = 5;
+module_param(vpu_poll_interval, int, 0644);
+
int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
{
int ret;
@@ -40,7 +43,7 @@ int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
return 0;
}
-static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
+static void wave5_vpu_handle_irq(void *dev_id)
{
u32 seq_done;
u32 cmd_done;
@@ -48,42 +51,67 @@ static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
struct vpu_instance *inst;
struct vpu_device *dev = dev_id;
- if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
- irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
- wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
- wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
-
- list_for_each_entry(inst, &dev->instances, list) {
- seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
- cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
-
- if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
- irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
- if (seq_done & BIT(inst->id)) {
- seq_done &= ~BIT(inst->id);
- wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO,
- seq_done);
- complete(&inst->irq_done);
- }
+ irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
+ wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
+ wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
+
+ list_for_each_entry(inst, &dev->instances, list) {
+ seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
+ cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
+
+ if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
+ irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
+ if (seq_done & BIT(inst->id)) {
+ seq_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO,
+ seq_done);
+ complete(&inst->irq_done);
}
+ }
- if (irq_reason & BIT(INT_WAVE5_DEC_PIC) ||
- irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
- if (cmd_done & BIT(inst->id)) {
- cmd_done &= ~BIT(inst->id);
- wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
- cmd_done);
- inst->ops->finish_process(inst);
- }
+ if (irq_reason & BIT(INT_WAVE5_DEC_PIC) ||
+ irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
+ if (cmd_done & BIT(inst->id)) {
+ cmd_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
+ cmd_done);
+ inst->ops->finish_process(inst);
}
-
- wave5_vpu_clear_interrupt(inst, irq_reason);
}
+
+ wave5_vpu_clear_interrupt(inst, irq_reason);
}
+}
+
+static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
+{
+ struct vpu_device *dev = dev_id;
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS))
+ wave5_vpu_handle_irq(dev);
return IRQ_HANDLED;
}
+static void wave5_vpu_irq_work_fn(struct kthread_work *work)
+{
+ struct vpu_device *dev = container_of(work, struct vpu_device, work);
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS))
+ wave5_vpu_handle_irq(dev);
+}
+
+static enum hrtimer_restart wave5_vpu_timer_callback(struct hrtimer *timer)
+{
+ struct vpu_device *dev =
+ container_of(timer, struct vpu_device, hrtimer);
+
+ kthread_queue_work(dev->worker, &dev->work);
+ hrtimer_forward_now(timer, ns_to_ktime(vpu_poll_interval * NSEC_PER_MSEC));
+
+ return HRTIMER_RESTART;
+}
+
static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
u32 *revision)
{
@@ -185,6 +213,28 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
dev->product = wave5_vpu_get_product_id(dev);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n");
+ hrtimer_init(&dev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
+ dev->hrtimer.function = &wave5_vpu_timer_callback;
+ dev->worker = kthread_create_worker(0, "vpu_irq_thread");
+ if (IS_ERR(dev->worker)) {
+ dev_err(&pdev->dev, "failed to create vpu irq worker\n");
+ ret = PTR_ERR(dev->worker);
+ goto err_vdi_release;
+ }
+ dev->vpu_poll_interval = vpu_poll_interval;
+ kthread_init_work(&dev->work, wave5_vpu_irq_work_fn);
+ } else {
+ ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
+ wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
+ goto err_enc_unreg;
+ }
+ }
+
INIT_LIST_HEAD(&dev->instances);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
@@ -207,20 +257,6 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
}
- dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
- ret = -ENXIO;
- goto err_enc_unreg;
- }
-
- ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
- wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
- if (ret) {
- dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
- goto err_enc_unreg;
- }
-
ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
if (ret) {
dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
@@ -254,6 +290,11 @@ static void wave5_vpu_remove(struct platform_device *pdev)
{
struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
+ if (dev->irq < 0) {
+ kthread_destroy_worker(dev->worker);
+ hrtimer_cancel(&dev->hrtimer);
+ }
+
mutex_destroy(&dev->dev_lock);
mutex_destroy(&dev->hw_lock);
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
index 352f6e904e50..edc50450ddb8 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
@@ -756,6 +756,10 @@ struct vpu_device {
u32 product_code;
struct ida inst_ida;
struct clk_bulk_data *clks;
+ struct hrtimer hrtimer;
+ struct kthread_work work;
+ struct kthread_worker *worker;
+ int vpu_poll_interval;
int num_clks;
};
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
index 1d64bac34b90..ea2ea119dd2a 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
@@ -524,7 +524,7 @@ static void mdp_auto_release_work(struct work_struct *work)
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
- if (atomic_dec_and_test(&mdp->job_count)) {
+ if (refcount_dec_and_test(&mdp->job_count)) {
if (cmd->mdp_ctx)
mdp_m2m_job_finish(cmd->mdp_ctx);
@@ -575,7 +575,7 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg)
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
- if (atomic_dec_and_test(&mdp->job_count))
+ if (refcount_dec_and_test(&mdp->job_count))
wake_up(&mdp->callback_wq);
mdp_cmdq_pkt_destroy(&cmd->pkt);
@@ -724,9 +724,9 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param)
int i, ret;
u8 pp_used = __get_pp_num(param->param->type);
- atomic_set(&mdp->job_count, pp_used);
+ refcount_set(&mdp->job_count, pp_used);
if (atomic_read(&mdp->suspended)) {
- atomic_set(&mdp->job_count, 0);
+ refcount_set(&mdp->job_count, 0);
return -ECANCELED;
}
@@ -764,7 +764,7 @@ err_clock_off:
mdp_comp_clocks_off(&mdp->pdev->dev, cmd[i]->comps,
cmd[i]->num_comps);
err_cancel_job:
- atomic_set(&mdp->job_count, 0);
+ refcount_set(&mdp->job_count, 0);
return ret;
}
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 5209f531ef8d..c1f3bf98120a 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -380,14 +380,14 @@ static int __maybe_unused mdp_suspend(struct device *dev)
atomic_set(&mdp->suspended, 1);
- if (atomic_read(&mdp->job_count)) {
+ if (refcount_read(&mdp->job_count)) {
ret = wait_event_timeout(mdp->callback_wq,
- !atomic_read(&mdp->job_count),
+ !refcount_read(&mdp->job_count),
2 * HZ);
if (ret == 0) {
dev_err(dev,
"%s:flushed cmdq task incomplete, count=%d\n",
- __func__, atomic_read(&mdp->job_count));
+ __func__, refcount_read(&mdp->job_count));
return -EBUSY;
}
}
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
index 8c09e984fd01..430251f63754 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
@@ -134,7 +134,7 @@ struct mdp_dev {
/* synchronization protect for m2m device operation */
struct mutex m2m_lock;
atomic_t suspended;
- atomic_t job_count;
+ refcount_t job_count;
};
struct mdp_pipe_info {
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
index 35a8b059bde5..0e69128a3772 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
@@ -104,14 +104,14 @@ static void mdp_m2m_device_run(void *priv)
task.cb_data = NULL;
task.mdp_ctx = ctx;
- if (atomic_read(&ctx->mdp_dev->job_count)) {
+ if (refcount_read(&ctx->mdp_dev->job_count)) {
ret = wait_event_timeout(ctx->mdp_dev->callback_wq,
- !atomic_read(&ctx->mdp_dev->job_count),
+ !refcount_read(&ctx->mdp_dev->job_count),
2 * HZ);
if (ret == 0) {
dev_err(&ctx->mdp_dev->pdev->dev,
"%d jobs not yet done\n",
- atomic_read(&ctx->mdp_dev->job_count));
+ refcount_read(&ctx->mdp_dev->job_count));
goto worker_end;
}
}
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
index 6bbe55de6ce9..ff23b225db70 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
@@ -79,6 +79,8 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use
}
fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ if (!fw)
+ return ERR_PTR(-ENOMEM);
fw->type = SCP;
fw->ops = &mtk_vcodec_rproc_msg;
fw->scp = scp;
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
index 9ce34a3b5ee6..c60e4c193b25 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
@@ -49,7 +49,6 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem)
{
enum mtk_instance_type inst_type = *((unsigned int *)priv);
struct platform_device *plat_dev;
- unsigned long size = mem->size;
int id;
if (inst_type == MTK_INST_ENCODER) {
@@ -64,15 +63,15 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem)
id = dec_ctx->id;
}
- mem->va = dma_alloc_coherent(&plat_dev->dev, size, &mem->dma_addr, GFP_KERNEL);
+ mem->va = dma_alloc_coherent(&plat_dev->dev, mem->size, &mem->dma_addr, GFP_KERNEL);
if (!mem->va) {
- mtk_v4l2_err(plat_dev, "%s dma_alloc size=%ld failed!",
- dev_name(&plat_dev->dev), size);
+ mtk_v4l2_err(plat_dev, "%s dma_alloc size=0x%zx failed!",
+ __func__, mem->size);
return -ENOMEM;
}
- mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va,
- (unsigned long)mem->dma_addr, size);
+ mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va,
+ (unsigned long)mem->dma_addr, mem->size);
return 0;
}
@@ -82,7 +81,6 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem)
{
enum mtk_instance_type inst_type = *((unsigned int *)priv);
struct platform_device *plat_dev;
- unsigned long size = mem->size;
int id;
if (inst_type == MTK_INST_ENCODER) {
@@ -98,15 +96,16 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem)
}
if (!mem->va) {
- mtk_v4l2_err(plat_dev, "%s dma_free size=%ld failed!",
- dev_name(&plat_dev->dev), size);
+ mtk_v4l2_err(plat_dev, "%s: Tried to free a NULL VA", __func__);
+ if (mem->size)
+ mtk_v4l2_err(plat_dev, "Failed to free %zu bytes", mem->size);
return;
}
- mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va,
- (unsigned long)mem->dma_addr, size);
+ mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va,
+ (unsigned long)mem->dma_addr, mem->size);
- dma_free_coherent(&plat_dev->dev, size, mem->va, mem->dma_addr);
+ dma_free_coherent(&plat_dev->dev, mem->size, mem->va, mem->dma_addr);
mem->va = NULL;
mem->dma_addr = 0;
mem->size = 0;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
index ba742f0e391d..9107707de6c4 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
@@ -262,7 +262,7 @@ static int vidioc_try_fmt(struct mtk_vcodec_dec_ctx *ctx, struct v4l2_format *f,
int tmp_w, tmp_h;
/*
- * Find next closer width align 64, heign align 64, size align
+ * Find next closer width align 64, height align 64, size align
* 64 rectangle
* Note: This only get default value, the real HW needed value
* only available when ctx in MTK_STATE_HEADER state
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index 85b2c0d3d8bc..ac568ed14fa2 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -67,11 +67,11 @@ enum mtk_vdec_hw_arch {
* @pic_w: picture width
* @pic_h: picture height
* @buf_w: picture buffer width (64 aligned up from pic_w)
- * @buf_h: picture buffer heiht (64 aligned up from pic_h)
+ * @buf_h: picture buffer height (64 aligned up from pic_h)
* @fb_sz: bitstream size of each plane
* E.g. suppose picture size is 176x144,
* buffer size will be aligned to 176x160.
- * @cap_fourcc: fourcc number(may changed when resolution change)
+ * @cap_fourcc: fourcc number(may change on a resolution change)
* @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os.
*/
struct vdec_pic_info {
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 2b6a5adbc419..bf21f2467a0f 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -326,7 +326,7 @@ struct vdec_av1_slice_quantization {
* @use_lr: whether to use loop restoration
* @use_chroma_lr: whether to use chroma loop restoration
* @frame_restoration_type: specifies the type of restoration used for each plane
- * @loop_restoration_size: pecifies the size of loop restoration units in units
+ * @loop_restoration_size: specifies the size of loop restoration units in units
* of samples in the current plane
*/
struct vdec_av1_slice_lr {
@@ -347,7 +347,7 @@ struct vdec_av1_slice_lr {
* and loop_filter_sharpness together determine when
* a block edge is filtered, and by how much the
* filtering can change the sample values
- * @loop_filter_delta_enabled: filetr level depends on the mode and reference
+ * @loop_filter_delta_enabled: filter level depends on the mode and reference
* frame used to predict a block
*/
struct vdec_av1_slice_loop_filter {
@@ -392,7 +392,7 @@ struct vdec_av1_slice_mfmv {
/**
* struct vdec_av1_slice_tile - AV1 Tile info
* @tile_cols: specifies the number of tiles across the frame
- * @tile_rows: pecifies the number of tiles down the frame
+ * @tile_rows: specifies the number of tiles down the frame
* @mi_col_starts: an array specifying the start column
* @mi_row_starts: an array specifying the start row
* @context_update_tile_id: specifies which tile to use for the CDF update
@@ -423,15 +423,15 @@ struct vdec_av1_slice_tile {
* or the tile sizes are coded
* @interpolation_filter: specifies the filter selection used for performing inter prediction
* @allow_warped_motion: motion_mode may be present or not
- * @is_motion_mode_switchable : euqlt to 0 specifies that only the SIMPLE motion mode will be used
+ * @is_motion_mode_switchable : equal to 0 specifies that only the SIMPLE motion mode will be used
* @reference_mode : frame reference mode selected
* @allow_high_precision_mv: specifies that motion vectors are specified to
* quarter pel precision or to eighth pel precision
- * @allow_intra_bc: ubducates that intra block copy may be used in this frame
+ * @allow_intra_bc: allows that intra block copy may be used in this frame
* @force_integer_mv: specifies motion vectors will always be integers or
* can contain fractional bits
* @allow_screen_content_tools: intra blocks may use palette encoding
- * @error_resilient_mode: error resislent mode is enable/disable
+ * @error_resilient_mode: error resilient mode is enable/disable
* @frame_type: specifies the AV1 frame type
* @primary_ref_frame: specifies which reference frame contains the CDF values
* and other state that should be loaded at the start of the frame
@@ -440,8 +440,8 @@ struct vdec_av1_slice_tile {
* @disable_cdf_update: specified whether the CDF update in the symbol
* decoding process should be disables
* @skip_mode: av1 skip mode parameters
- * @seg: av1 segmentaon parameters
- * @delta_q_lf: av1 delta loop fileter
+ * @seg: av1 segmentation parameters
+ * @delta_q_lf: av1 delta loop filter
* @quant: av1 Quantization params
* @lr: av1 Loop Restauration parameters
* @superres_denom: the denominator for the upscaling ratio
@@ -450,8 +450,8 @@ struct vdec_av1_slice_tile {
* @mfmv: av1 mfmv parameters
* @tile: av1 Tile info
* @frame_is_intra: intra frame
- * @loss_less_array: loss less array
- * @coded_loss_less: coded lsss less
+ * @loss_less_array: lossless array
+ * @coded_loss_less: coded lossless
* @mi_rows: size of mi unit in rows
* @mi_cols: size of mi unit in cols
*/
@@ -1023,18 +1023,26 @@ static void vdec_av1_slice_free_working_buffer(struct vdec_av1_slice_instance *i
int i;
for (i = 0; i < ARRAY_SIZE(instance->mv); i++)
- mtk_vcodec_mem_free(ctx, &instance->mv[i]);
+ if (instance->mv[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->mv[i]);
for (i = 0; i < ARRAY_SIZE(instance->seg); i++)
- mtk_vcodec_mem_free(ctx, &instance->seg[i]);
+ if (instance->seg[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->seg[i]);
for (i = 0; i < ARRAY_SIZE(instance->cdf); i++)
- mtk_vcodec_mem_free(ctx, &instance->cdf[i]);
+ if (instance->cdf[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf[i]);
+
- mtk_vcodec_mem_free(ctx, &instance->tile);
- mtk_vcodec_mem_free(ctx, &instance->cdf_temp);
- mtk_vcodec_mem_free(ctx, &instance->cdf_table);
- mtk_vcodec_mem_free(ctx, &instance->iq_table);
+ if (instance->tile.va)
+ mtk_vcodec_mem_free(ctx, &instance->tile);
+ if (instance->cdf_temp.va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf_temp);
+ if (instance->cdf_table.va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf_table);
+ if (instance->iq_table.va)
+ mtk_vcodec_mem_free(ctx, &instance->iq_table);
instance->level = AV1_RES_NONE;
}
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
index bf7dffe60d07..795cb19b075d 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
@@ -94,7 +94,7 @@ struct vdec_h264_dec_info {
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
* @hdr_buf : Header parsing buffer (AP-W, VPU-R)
- * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R)
+ * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R)
* @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R)
* @list_free : free frame buffer ring list (AP-W/R, VPU-W)
* @list_disp : display frame buffer ring list (AP-R, VPU-W)
@@ -117,7 +117,7 @@ struct vdec_h264_vsi {
* struct vdec_h264_inst - h264 decoder instance
* @num_nalu : how many nalus be decoded
* @ctx : point to mtk_vcodec_dec_ctx
- * @pred_buf : HW working predication buffer
+ * @pred_buf : HW working prediction buffer
* @mv_buf : HW working motion vector buffer
* @vpu : VPU instance
* @vsi : VPU shared information
@@ -136,7 +136,7 @@ static unsigned int get_mv_buf_size(unsigned int width, unsigned int height)
return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN);
}
-static int allocate_predication_buf(struct vdec_h264_inst *inst)
+static int allocate_prediction_buf(struct vdec_h264_inst *inst)
{
int err = 0;
@@ -151,7 +151,7 @@ static int allocate_predication_buf(struct vdec_h264_inst *inst)
return 0;
}
-static void free_predication_buf(struct vdec_h264_inst *inst)
+static void free_prediction_buf(struct vdec_h264_inst *inst)
{
struct mtk_vcodec_mem *mem = NULL;
@@ -286,7 +286,7 @@ static int vdec_h264_init(struct mtk_vcodec_dec_ctx *ctx)
}
inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi;
- err = allocate_predication_buf(inst);
+ err = allocate_prediction_buf(inst);
if (err)
goto error_deinit;
@@ -308,7 +308,7 @@ static void vdec_h264_deinit(void *h_vdec)
struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec;
vpu_dec_deinit(&inst->vpu);
- free_predication_buf(inst);
+ free_prediction_buf(inst);
free_mv_buf(inst);
kfree(inst);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
index ac82be336055..31ffa13160a3 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
@@ -175,7 +175,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list,
int num_valid);
/**
- * mtk_vdec_h264_get_ctrl_ptr - get each CID contrl address.
+ * mtk_vdec_h264_get_ctrl_ptr - get each CID control address.
*
* @ctx: v4l2 ctx
* @id: CID control ID
@@ -185,7 +185,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list,
void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id);
/**
- * mtk_vdec_h264_fill_dpb_info - get each CID contrl address.
+ * mtk_vdec_h264_fill_dpb_info - Fill the decoded picture buffer info
*
* @ctx: v4l2 ctx
* @decode_params: slice decode params
@@ -225,10 +225,13 @@ void mtk_vdec_h264_copy_slice_hd_params(struct mtk_h264_slice_hd_param *dst_para
const struct v4l2_ctrl_h264_decode_params *dec_param);
/**
- * mtk_vdec_h264_copy_scaling_matrix - get each CID contrl address.
+ * mtk_vdec_h264_copy_scaling_matrix - Copy scaling matrix from a control to the driver
*
- * @dst_matrix: scaling list params for hw decoder
- * @src_matrix: scaling list params from user driver
+ * @dst_matrix: scaling list params for the HW decoder
+ * @src_matrix: scaling list params from a V4L2 control
+ *
+ * This function is used to copy the scaling matrix from a
+ * v4l2 control into the slice parameters for a decode.
*/
void mtk_vdec_h264_copy_scaling_matrix(struct slice_api_h264_scaling_matrix *dst_matrix,
const struct v4l2_ctrl_h264_scaling_matrix *src_matrix);
@@ -246,7 +249,7 @@ mtk_vdec_h264_copy_decode_params(struct slice_api_h264_decode_param *dst_params,
const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]);
/**
- * mtk_vdec_h264_update_dpb - updata dpb list.
+ * mtk_vdec_h264_update_dpb - update dpb list.
*
* @dec_param: v4l2 control decode params
* @dpb: dpb entry informaton
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
index 5600f1df653d..73d5cef33b2a 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
@@ -27,7 +27,7 @@ struct mtk_h264_dec_slice_param {
/**
* struct vdec_h264_dec_info - decode information
* @dpb_sz : decoding picture buffer size
- * @resolution_changed : resoltion change happen
+ * @resolution_changed : flag to notify that a resolution change happened
* @realloc_mv_buf : flag to notify driver to re-allocate mv buffer
* @cap_num_planes : number planes of capture buffer
* @bs_dma : Input bit-stream buffer dma address
@@ -54,7 +54,7 @@ struct vdec_h264_dec_info {
* by VPU.
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
- * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R)
+ * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R)
* @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R)
* @dec : decode information (AP-R, VPU-W)
* @pic : picture information (AP-R, VPU-W)
@@ -74,7 +74,7 @@ struct vdec_h264_vsi {
* struct vdec_h264_slice_inst - h264 decoder instance
* @num_nalu : how many nalus be decoded
* @ctx : point to mtk_vcodec_dec_ctx
- * @pred_buf : HW working predication buffer
+ * @pred_buf : HW working prediction buffer
* @mv_buf : HW working motion vector buffer
* @vpu : VPU instance
* @vsi_ctx : Local VSI data for this decoding context
@@ -154,7 +154,7 @@ static int get_vdec_decode_parameters(struct vdec_h264_slice_inst *inst)
return 0;
}
-static int allocate_predication_buf(struct vdec_h264_slice_inst *inst)
+static int allocate_prediction_buf(struct vdec_h264_slice_inst *inst)
{
int err;
@@ -169,7 +169,7 @@ static int allocate_predication_buf(struct vdec_h264_slice_inst *inst)
return 0;
}
-static void free_predication_buf(struct vdec_h264_slice_inst *inst)
+static void free_prediction_buf(struct vdec_h264_slice_inst *inst)
{
struct mtk_vcodec_mem *mem = &inst->pred_buf;
@@ -292,7 +292,7 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
inst->vsi_ctx.dec.resolution_changed = true;
inst->vsi_ctx.dec.realloc_mv_buf = true;
- err = allocate_predication_buf(inst);
+ err = allocate_prediction_buf(inst);
if (err)
goto error_deinit;
@@ -320,7 +320,7 @@ static void vdec_h264_slice_deinit(void *h_vdec)
struct vdec_h264_slice_inst *inst = h_vdec;
vpu_dec_deinit(&inst->vpu);
- free_predication_buf(inst);
+ free_prediction_buf(inst);
free_mv_buf(inst);
kfree(inst);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 0e741e0dc8ba..2d4611e7fa0b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -51,7 +51,7 @@ struct vdec_h264_slice_lat_dec_param {
* struct vdec_h264_slice_info - decode information
*
* @nal_info: nal info of current picture
- * @timeout: Decode timeout: 1 timeout, 0 no timeount
+ * @timeout: Decode timeout: 1 timeout, 0 no timeout
* @bs_buf_size: bitstream size
* @bs_buf_addr: bitstream buffer dma address
* @y_fb_dma: Y frame buffer dma address
@@ -131,9 +131,9 @@ struct vdec_h264_slice_share_info {
/**
* struct vdec_h264_slice_inst - h264 decoder instance
*
- * @slice_dec_num: how many picture be decoded
+ * @slice_dec_num: Number of frames to be decoded
* @ctx: point to mtk_vcodec_dec_ctx
- * @pred_buf: HW working predication buffer
+ * @pred_buf: HW working prediction buffer
* @mv_buf: HW working motion vector buffer
* @vpu: VPU instance
* @vsi: vsi used for lat
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index 21836dd6ef85..aa721cc43647 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -254,7 +254,7 @@ struct vdec_hevc_slice_lat_dec_param {
* struct vdec_hevc_slice_info - decode information
*
* @wdma_end_addr_offset: wdma end address offset
- * @timeout: Decode timeout: 1 timeout, 0 no timeount
+ * @timeout: Decode timeout: 1 timeout, 0 no timeout
* @vdec_fb_va: VDEC frame buffer struct virtual address
* @crc: Used to check whether hardware's status is right
*/
@@ -342,7 +342,7 @@ struct vdec_hevc_slice_share_info {
/**
* struct vdec_hevc_slice_inst - hevc decoder instance
*
- * @slice_dec_num: how many picture be decoded
+ * @slice_dec_num: Number of frames to be decoded
* @ctx: point to mtk_vcodec_dec_ctx
* @mv_buf: HW working motion vector buffer
* @vpu: VPU instance
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
index 987b3d71b662..4bc89c8644fe 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
@@ -56,7 +56,7 @@
* @cur_c_fb_dma : current plane C frame buffer dma address
* @bs_dma : bitstream dma address
* @bs_sz : bitstream size
- * @resolution_changed: resolution change flag 1 - changed, 0 - not change
+ * @resolution_changed: resolution change flag 1 - changed, 0 - not changed
* @show_frame : display this frame or not
* @wait_key_frame : wait key frame coming
*/
@@ -109,7 +109,7 @@ struct vdec_vp8_hw_reg_base {
/**
* struct vdec_vp8_vpu_inst - VPU instance for VP8 decode
* @wq_hd : Wait queue to wait VPU message ack
- * @signaled : 1 - Host has received ack message from VPU, 0 - not receive
+ * @signaled : 1 - Host has received ack message from VPU, 0 - not received
* @failure : VPU execution result status 0 - success, others - fail
* @inst_addr : VPU decoder instance address
*/
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
index f677e499fefa..e27e728f392e 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
@@ -35,7 +35,7 @@
* @cur_c_fb_dma: current plane C frame buffer dma address
* @bs_dma: bitstream dma address
* @bs_sz: bitstream size
- * @resolution_changed:resolution change flag 1 - changed, 0 - not change
+ * @resolution_changed:resolution change flag 1 - changed, 0 - not changed
* @frame_header_type: current frame header type
* @crc: used to check whether hardware's status is right
* @reserved: reserved, currently unused
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
index 039082f600c8..eb3354192853 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
@@ -42,7 +42,7 @@ struct vp9_dram_buf {
/**
* struct vp9_fb_info - contains frame buffer info
- * @fb : frmae buffer
+ * @fb : frame buffer
* @reserved : reserved field used by vpu
*/
struct vp9_fb_info {
@@ -90,7 +90,7 @@ struct vp9_sf_ref_fb {
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
* @sf_bs_buf : super frame backup buffer (AP-W, VPU-R)
- * @sf_ref_fb : record supoer frame reference buffer information
+ * @sf_ref_fb : record super frame reference buffer information
* (AP-R/W, VPU-R/W)
* @sf_next_ref_fb_idx : next available super frame (AP-W, VPU-R)
* @sf_frm_cnt : super frame count, filled by vpu (AP-R, VPU-W)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
index 1d9beb9e4a14..b0f576867f4b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
@@ -158,14 +158,14 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf
struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx);
/**
- * vdec_msg_queue_update_ube_rptr - used to updata the ube read point.
+ * vdec_msg_queue_update_ube_rptr - used to update the ube read point.
* @msg_queue: used to store the lat buffer information
* @ube_rptr: current ube read point
*/
void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr);
/**
- * vdec_msg_queue_update_ube_wptr - used to updata the ube write point.
+ * vdec_msg_queue_update_ube_wptr - used to update the ube write point.
* @msg_queue: used to store the lat buffer information
* @ube_wptr: current ube write point
*/
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
index aa7d08afc2f4..57ed9b1f5eaa 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
@@ -81,7 +81,7 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu);
/**
* vpu_dec_reset - reset decoder, use for flush decoder when end of stream or
- * seek. Remainig non displayed frame will be pushed to display.
+ * seek. Remaining non displayed frame will be pushed to display.
*
* @vpu: instance for vdec_vpu_inst
*/
@@ -98,7 +98,7 @@ int vpu_dec_core(struct vdec_vpu_inst *vpu);
/**
* vpu_dec_core_end - core end decoding, basically the function will be invoked once
* when core HW decoding done and receive interrupt successfully. The
- * decoder in VPU will updata hardware information and deinit hardware
+ * decoder in VPU will update hardware information and deinit hardware
* and check if there is a new decoded frame available to display.
*
* @vpu : instance for vdec_vpu_inst
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index 181884e798fd..7eaf0e24c9fc 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -311,7 +311,7 @@ static int vidioc_try_fmt_out(struct mtk_vcodec_enc_ctx *ctx, struct v4l2_format
pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VENC_MIN_H, max_height);
pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VENC_MIN_W, max_width);
- /* find next closer width align 16, heign align 32, size align
+ /* find next closer width align 16, height align 32, size align
* 64 rectangle
*/
tmp_w = pix_fmt_mp->width;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
index a22b7dfc656e..1a2b14a3e219 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
@@ -58,13 +58,15 @@ int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *mtkdev)
return 0;
}
-void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm)
+int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm)
{
int ret;
ret = pm_runtime_resume_and_get(pm->dev);
if (ret)
dev_err(pm->dev, "pm_runtime_resume_and_get fail: %d", ret);
+
+ return ret;
}
void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm)
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
index 157ea08ba9e3..2e28f25e36cc 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
@@ -10,7 +10,7 @@
#include "mtk_vcodec_enc_drv.h"
int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *dev);
-void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm);
+int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm);
void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm);
void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm);
void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
index a68dac72c4e4..f8145998fcaf 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
@@ -301,11 +301,12 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst)
* other buffers need to be freed by AP.
*/
for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) {
- if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME)
+ if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME && inst->work_bufs[i].va)
mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]);
}
- mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
+ if (inst->pps_buf.va)
+ mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
}
static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit)
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
index c402a686f3cb..e83747b8d69a 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
@@ -64,7 +64,9 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx,
ctx->dev->curr_ctx = ctx;
spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
- mtk_vcodec_enc_pw_on(&ctx->dev->pm);
+ ret = mtk_vcodec_enc_pw_on(&ctx->dev->pm);
+ if (ret)
+ goto venc_if_encode_pw_on_err;
mtk_vcodec_enc_clock_on(&ctx->dev->pm);
ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf,
bs_buf, result);
@@ -75,6 +77,7 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx,
ctx->dev->curr_ctx = NULL;
spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+venc_if_encode_pw_on_err:
mtk_venc_unlock(ctx);
return ret;
}
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
index d00fb68b8235..889440a436b6 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
@@ -156,7 +156,7 @@ int venc_if_set_param(struct mtk_vcodec_enc_ctx *ctx,
* @ctx: device context
* @opt: encode frame option
* @frm_buf: input frame buffer information
- * @bs_buf: output bitstream buffer infomraiton
+ * @bs_buf: output bitstream buffer information
* @result: encode result
* Return: 0 if encoding frame successfully, otherwise it is failed.
*/
diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c
index 204e474d57f7..cfea5572a1b8 100644
--- a/drivers/media/platform/nvidia/tegra-vde/h264.c
+++ b/drivers/media/platform/nvidia/tegra-vde/h264.c
@@ -633,7 +633,9 @@ static int tegra_vde_decode_end(struct tegra_vde *vde)
timeout = wait_for_completion_interruptible_timeout(
&vde->decode_completion, msecs_to_jiffies(1000));
- if (timeout == 0) {
+ if (timeout < 0) {
+ ret = timeout;
+ } else if (timeout == 0) {
bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10);
macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF;
read_bytes = bsev_ptr ? bsev_ptr - vde->bitstream_data_addr : 0;
@@ -642,8 +644,6 @@ static int tegra_vde_decode_end(struct tegra_vde *vde)
read_bytes, macroblocks_nb);
ret = -EIO;
- } else if (timeout < 0) {
- ret = timeout;
} else {
ret = 0;
}
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index db8ff5f5c4d3..f49b06978f14 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -30,6 +30,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
@@ -742,6 +743,18 @@ static void mipi_csis_stop_stream(struct mipi_csis_device *csis)
mipi_csis_system_enable(csis, false);
}
+static void mipi_csis_queue_event_sof(struct mipi_csis_device *csis)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+ u32 frame;
+
+ frame = mipi_csis_read(csis, MIPI_CSIS_FRAME_COUNTER_CH(0));
+ event.u.frame_sync.frame_sequence = frame;
+ v4l2_event_queue(csis->sd.devnode, &event);
+}
+
static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
{
struct mipi_csis_device *csis = dev_id;
@@ -765,6 +778,10 @@ static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
event->counter++;
}
}
+
+ if (status & MIPI_CSIS_INT_SRC_FRAME_START)
+ mipi_csis_queue_event_sof(csis);
+
spin_unlock_irqrestore(&csis->slock, flags);
mipi_csis_write(csis, MIPI_CSIS_INT_SRC, status);
@@ -1154,8 +1171,23 @@ static int mipi_csis_log_status(struct v4l2_subdev *sd)
return 0;
}
+static int mipi_csis_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
static const struct v4l2_subdev_core_ops mipi_csis_core_ops = {
.log_status = mipi_csis_log_status,
+ .subscribe_event = mipi_csis_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
@@ -1358,7 +1390,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis)
snprintf(sd->name, sizeof(sd->name), "csis-%s",
dev_name(csis->dev));
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->ctrl_handler = NULL;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 4e2222358973..0d4389ab312d 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -14,7 +14,7 @@ qcom-camss-objs += \
camss-vfe-4-1.o \
camss-vfe-4-7.o \
camss-vfe-4-8.o \
- camss-vfe-170.o \
+ camss-vfe-17x.o \
camss-vfe-480.o \
camss-vfe-gen1.o \
camss-vfe.o \
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index f50e2235c37f..df7e93a5a4f6 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -148,6 +148,91 @@ csiphy_reg_t lane_regs_sdm845[5][14] = {
},
};
+/* GEN2 1.1 2PH */
+static const struct
+csiphy_reg_t lane_regs_sc8280xp[5][14] = {
+ {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
/* GEN2 1.2.1 2PH */
static const struct
csiphy_reg_t lane_regs_sm8250[5][20] = {
@@ -428,6 +513,10 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
r = &lane_regs_sm8250[0][0];
array_size = ARRAY_SIZE(lane_regs_sm8250[0]);
break;
+ case CAMSS_8280XP:
+ r = &lane_regs_sc8280xp[0][0];
+ array_size = ARRAY_SIZE(lane_regs_sc8280xp[0]);
+ break;
default:
WARN(1, "unknown cspi version\n");
return;
@@ -463,13 +552,26 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
return lane_mask;
}
+static bool csiphy_is_gen2(u32 version)
+{
+ bool ret = false;
+
+ switch (version) {
+ case CAMSS_845:
+ case CAMSS_8250:
+ case CAMSS_8280XP:
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
static void csiphy_lanes_enable(struct csiphy_device *csiphy,
struct csiphy_config *cfg,
s64 link_freq, u8 lane_mask)
{
struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
- bool is_gen2 = (csiphy->camss->res->version == CAMSS_845 ||
- csiphy->camss->res->version == CAMSS_8250);
u8 settle_cnt;
u8 val;
int i;
@@ -491,7 +593,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
val = 0x00;
writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
- if (is_gen2)
+ if (csiphy_is_gen2(csiphy->camss->res->version))
csiphy_gen2_config_lanes(csiphy, settle_cnt);
else
csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 264c99efeae8..45b3a8e5dea4 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -578,6 +578,7 @@ int msm_csiphy_subdev_init(struct camss *camss,
break;
case CAMSS_845:
case CAMSS_8250:
+ case CAMSS_8280XP:
csiphy->formats = csiphy_formats_sdm845;
csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845);
break;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index 795ac3815339..795ac3815339 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-170.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 2062be668f49..d875237cf244 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -225,6 +225,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
case CAMSS_660:
case CAMSS_845:
case CAMSS_8250:
+ case CAMSS_8280XP:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
{
@@ -1518,6 +1519,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
break;
case CAMSS_845:
case CAMSS_8250:
+ case CAMSS_8280XP:
l->formats = formats_rdi_845;
l->nformats = ARRAY_SIZE(formats_rdi_845);
break;
@@ -1595,6 +1597,23 @@ static const struct media_entity_operations vfe_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
+static int vfe_bpl_align(struct vfe_device *vfe)
+{
+ int ret = 8;
+
+ switch (vfe->camss->res->version) {
+ case CAMSS_845:
+ case CAMSS_8250:
+ case CAMSS_8280XP:
+ ret = 16;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
@@ -1661,11 +1680,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
}
video_out->ops = &vfe->video_ops;
- if (vfe->camss->res->version == CAMSS_845 ||
- vfe->camss->res->version == CAMSS_8250)
- video_out->bpl_alignment = 16;
- else
- video_out->bpl_alignment = 8;
+ video_out->bpl_alignment = vfe_bpl_align(vfe);
video_out->line_based = 0;
if (i == VFE_LINE_PIX) {
video_out->bpl_alignment = 16;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index a89da5ef4710..54cd82f74115 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -1028,6 +1028,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
break;
case CAMSS_845:
case CAMSS_8250:
+ case CAMSS_8280XP:
video->formats = formats_rdi_845;
video->nformats = ARRAY_SIZE(formats_rdi_845);
break;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 58f4be660290..1923615f0eea 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -941,6 +941,298 @@ static const struct resources_icc icc_res_sm8250[] = {
},
};
+static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = {},
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .ops = &csiphy_ops_3ph_1_0
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = {},
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .ops = &csiphy_ops_3ph_1_0
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = {},
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .ops = &csiphy_ops_3ph_1_0
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = {},
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .ops = &csiphy_ops_3ph_1_0
+ },
+};
+
+static const struct camss_subdev_resources csid_res_sc8280xp[] = {
+ /* CSID0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .ops = &csid_ops_gen2
+ },
+ /* CSID1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .ops = &csid_ops_gen2
+ },
+ /* CSID2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2", "vfe2_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .ops = &csid_ops_gen2
+ },
+ /* CSID3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe3_csid", "vfe3_cphy_rx", "vfe3", "vfe3_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid3" },
+ .interrupt = { "csid3" },
+ .ops = &csid_ops_gen2
+ },
+ /* CSID_LITE0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid0_lite" },
+ .interrupt = { "csid0_lite" },
+ .is_lite = true,
+ .ops = &csid_ops_gen2
+ },
+ /* CSID_LITE1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid1_lite" },
+ .interrupt = { "csid1_lite" },
+ .is_lite = true,
+ .ops = &csid_ops_gen2
+ },
+ /* CSID_LITE2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite2_csid", "vfe_lite2_cphy_rx", "vfe_lite2" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid2_lite" },
+ .interrupt = { "csid2_lite" },
+ .is_lite = true,
+ .ops = &csid_ops_gen2
+ },
+ /* CSID_LITE3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite3_csid", "vfe_lite3_cphy_rx", "vfe_lite3" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid3_lite" },
+ .interrupt = { "csid3_lite" },
+ .is_lite = true,
+ .ops = &csid_ops_gen2
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_sc8280xp[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe0", "vfe0_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .pd_name = "ife0",
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe1", "vfe1_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .pd_name = "ife1",
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe2", "vfe2_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .pd_name = "ife2",
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE3 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe3", "vfe3_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe3" },
+ .interrupt = { "vfe3" },
+ .pd_name = "ife3",
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE_LITE_0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .is_lite = true,
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE_LITE_1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .is_lite = true,
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE_LITE_2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite2" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000, }, },
+ .reg = { "vfe_lite2" },
+ .interrupt = { "vfe_lite2" },
+ .is_lite = true,
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+ /* VFE_LITE_3 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite3" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite3" },
+ .interrupt = { "vfe_lite3" },
+ .is_lite = true,
+ .line_num = 4,
+ .ops = &vfe_ops_170
+ },
+};
+
+static const struct resources_icc icc_res_sc8280xp[] = {
+ {
+ .name = "cam_ahb",
+ .icc_bw_tbl.avg = 150000,
+ .icc_bw_tbl.peak = 300000,
+ },
+ {
+ .name = "cam_hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
/*
* camss_add_clock_margin - Add margin to clock frequency rate
* @rate: Clock frequency rate
@@ -1826,12 +2118,27 @@ static const struct camss_resources sm8250_resources = {
.vfe_num = ARRAY_SIZE(vfe_res_8250),
};
+static const struct camss_resources sc8280xp_resources = {
+ .version = CAMSS_8280XP,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_sc8280xp,
+ .csid_res = csid_res_sc8280xp,
+ .ispif_res = NULL,
+ .vfe_res = vfe_res_sc8280xp,
+ .icc_res = icc_res_sc8280xp,
+ .icc_path_num = ARRAY_SIZE(icc_res_sc8280xp),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_sc8280xp),
+ .csid_num = ARRAY_SIZE(csid_res_sc8280xp),
+ .vfe_num = ARRAY_SIZE(vfe_res_sc8280xp),
+};
+
static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss", .data = &msm8916_resources },
{ .compatible = "qcom,msm8996-camss", .data = &msm8996_resources },
{ .compatible = "qcom,sdm660-camss", .data = &sdm660_resources },
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
+ { .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources },
{ }
};
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index a0c2dcc779f0..ac15fe23a702 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -77,6 +77,7 @@ enum camss_version {
CAMSS_660,
CAMSS_845,
CAMSS_8250,
+ CAMSS_8280XP,
};
enum icc_count {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index 073f70c6ac68..bb4b07bed28d 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -730,7 +730,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh,
struct v4l2_subdev *sd = vin_to_source(vin);
int ret;
- ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(sd, pad, s_dv_timings,
+ vin->parallel.sink_pad, timings);
if (ret)
return ret;
@@ -744,7 +745,8 @@ static int rvin_g_dv_timings(struct file *file, void *priv_fh,
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd, video, g_dv_timings, timings);
+ return v4l2_subdev_call(sd, pad, g_dv_timings,
+ vin->parallel.sink_pad, timings);
}
static int rvin_query_dv_timings(struct file *file, void *priv_fh,
@@ -753,7 +755,8 @@ static int rvin_query_dv_timings(struct file *file, void *priv_fh,
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd, video, query_dv_timings, timings);
+ return v4l2_subdev_call(sd, pad, query_dv_timings,
+ vin->parallel.sink_pad, timings);
}
static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index 792336dada44..997a66318a29 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -59,7 +59,7 @@ enum rvin_isp_id {
#define RVIN_REMOTES_MAX \
(((unsigned int)RVIN_CSI_MAX) > ((unsigned int)RVIN_ISP_MAX) ? \
- RVIN_CSI_MAX : RVIN_ISP_MAX)
+ (unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX)
/**
* enum rvin_dma_state - DMA states
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
index 702b910509c9..01c33d9c9ec3 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig
+++ b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
@@ -5,7 +5,6 @@ config DVB_C8SECTPFE
depends on PINCTRL && DVB_CORE && I2C
depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
select FW_LOADER
- select DEBUG_FS
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile
index aedfc725cc19..99425137ee0a 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/Makefile
+++ b/drivers/media/platform/st/sti/c8sectpfe/Makefile
@@ -1,6 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \
- c8sectpfe-debugfs.o
+c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
+
+ifneq ($(CONFIG_DEBUG_FS),)
+c8sectpfe-y += c8sectpfe-debugfs.o
+endif
obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index e4cf27b5a072..2f58a0d0df85 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
@@ -24,7 +24,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
@@ -1097,7 +1096,6 @@ static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei)
}
}
- release_firmware(fw);
return err;
}
@@ -1121,6 +1119,7 @@ static int load_c8sectpfe_fw(struct c8sectpfei *fei)
}
err = load_slim_core_fw(fw, fei);
+ release_firmware(fw);
if (err) {
dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err);
return err;
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
index d2c35fb32d7e..8e1bfd860524 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
@@ -12,7 +12,12 @@
#include "c8sectpfe-core.h"
+#if defined(CONFIG_DEBUG_FS)
void c8sectpfe_debugfs_init(struct c8sectpfei *);
void c8sectpfe_debugfs_exit(struct c8sectpfei *);
+#else
+static inline void c8sectpfe_debugfs_init(struct c8sectpfei *) {};
+static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *) {};
+#endif
#endif /* __C8SECTPFE_DEBUG_H */
diff --git a/drivers/media/platform/st/sti/hva/hva-hw.c b/drivers/media/platform/st/sti/hva/hva-hw.c
index fe4ea2e7f37e..fcb18fb52fdd 100644
--- a/drivers/media/platform/st/sti/hva/hva-hw.c
+++ b/drivers/media/platform/st/sti/hva/hva-hw.c
@@ -406,8 +406,7 @@ err_pm:
err_disable:
pm_runtime_disable(dev);
err_clk:
- if (hva->clk)
- clk_unprepare(hva->clk);
+ clk_unprepare(hva->clk);
return ret;
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index bce821eb71ce..4acc3b90d03a 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -439,11 +439,8 @@ static int dcmipp_probe(struct platform_device *pdev)
"Could not get reset control\n");
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get irq\n");
- return irq ? irq : -ENXIO;
- }
+ if (irq < 0)
+ return irq;
dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(dcmipp->regs)) {
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
index 47a8c0fb7eb9..99c401e653bc 100644
--- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
@@ -8,6 +8,7 @@ config VIDEO_SUN8I_A83T_MIPI_CSI2
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
select REGMAP_MMIO
+ select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY.
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index c31a5566fc5a..c28794b6677b 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1132,7 +1132,7 @@ vpif_query_dv_timings(struct file *file, void *priv,
if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
return -ENODATA;
- ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, query_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -ENODATA;
@@ -1177,7 +1177,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EBUSY;
/* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
ret = 0;
if (ret < 0) {
diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 02ede1fe12cb..76d8fa8ad088 100644
--- a/drivers/media/platform/ti/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -934,7 +934,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
}
/* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
ret = 0;
if (ret < 0) {
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 6da83d0cffaa..22442fce7607 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -786,15 +786,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
dev_warn(csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");
+ spin_lock_irqsave(&dma->lock, flags);
ret = ti_csi2rx_start_dma(csi, buf);
if (ret) {
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
- spin_lock_irqsave(&dma->lock, flags);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
dma->state = TI_CSI2RX_DMA_IDLE;
spin_unlock_irqrestore(&dma->lock, flags);
+ dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
} else {
- spin_lock_irqsave(&dma->lock, flags);
list_add_tail(&buf->list, &dma->submitted);
spin_unlock_irqrestore(&dma->lock, flags);
}
diff --git a/drivers/media/platform/verisilicon/hantro_h1_regs.h b/drivers/media/platform/verisilicon/hantro_h1_regs.h
index 30e7e7b920b5..8650cc489392 100644
--- a/drivers/media/platform/verisilicon/hantro_h1_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_h1_regs.h
@@ -62,7 +62,7 @@
#define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16)
#define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x))
#define H1_REG_ENC_CTRL2 0x048
-#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30)
+#define H1_REG_ENC_CTRL2_DEBLOCKING_FILTER_MODE(x) ((x) << 30)
#define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23)
#define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22)
#define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21)
@@ -89,7 +89,7 @@
#define H1_REG_STR_BUF_LIMIT 0x060
#define H1_REG_MAD_CTRL 0x064
#define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28)
-#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22)
+#define H1_REG_MAD_CTRL_MAD_THRESHOLD(x) ((x) << 22)
#define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x))
#define H1_REG_ADDR_VP8_PROB_CNT 0x068
#define H1_REG_QP_VAL 0x06c
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index 941fa23c211a..df6f2536263b 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -756,6 +756,7 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = {
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
index f1c5c0a6a335..e3e6aa87fe08 100644
--- a/drivers/media/radio/radio-shark2.c
+++ b/drivers/media/radio/radio-shark2.c
@@ -62,7 +62,7 @@ struct shark_device {
#ifdef SHARK_USE_LEDS
struct work_struct led_work;
struct led_classdev leds[NO_LEDS];
- char led_names[NO_LEDS][32];
+ char led_names[NO_LEDS][64];
atomic_t brightness[NO_LEDS];
unsigned long brightness_new;
#endif
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 41eeec648803..b29a1a9f381d 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -9,7 +9,6 @@
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 5719dda6e0f0..0b55314a8082 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -845,13 +845,13 @@ static ssize_t imon_clock_show(struct device *d,
mutex_lock(&ictx->lock);
if (!ictx->display_supported) {
- len = snprintf(buf, PAGE_SIZE, "Not supported.");
+ len = sysfs_emit(buf, "Not supported.");
} else {
- len = snprintf(buf, PAGE_SIZE,
- "To set the clock on your iMON display:\n"
- "# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
- "%s", ictx->display_isopen ?
- "\nNOTE: imon device must be closed\n" : "");
+ len = sysfs_emit(buf,
+ "To set the clock on your iMON display:\n"
+ "# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
+ "%s", ictx->display_isopen ?
+ "\nNOTE: imon device must be closed\n" : "");
}
mutex_unlock(&ictx->lock);
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index bbc81bed4f90..8fc8e496e6aa 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -4,13 +4,18 @@
// Copyright (c) 2016 Samsung Electronics Co., Ltd.
// Copyright (c) Andi Shyti <andi@etezian.org>
-#include <linux/delay.h>
-#include <linux/fs.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/math.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of_gpio.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
#include <media/rc-core.h>
#define IR_SPI_DRIVER_NAME "ir-spi"
@@ -31,8 +36,7 @@ struct ir_spi_data {
struct regulator *regulator;
};
-static int ir_spi_tx(struct rc_dev *dev,
- unsigned int *buffer, unsigned int count)
+static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int count)
{
int i;
int ret;
@@ -52,7 +56,7 @@ static int ir_spi_tx(struct rc_dev *dev,
return -EINVAL;
/*
- * the first value in buffer is a pulse, so that 0, 2, 4, ...
+ * The first value in buffer is a pulse, so that 0, 2, 4, ...
* contain a pulse duration. On the contrary, 1, 3, 5, ...
* contain a space duration.
*/
@@ -111,15 +115,16 @@ static int ir_spi_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
static int ir_spi_probe(struct spi_device *spi)
{
+ struct device *dev = &spi->dev;
int ret;
u8 dc;
struct ir_spi_data *idata;
- idata = devm_kzalloc(&spi->dev, sizeof(*idata), GFP_KERNEL);
+ idata = devm_kzalloc(dev, sizeof(*idata), GFP_KERNEL);
if (!idata)
return -ENOMEM;
- idata->regulator = devm_regulator_get(&spi->dev, "irda_regulator");
+ idata->regulator = devm_regulator_get(dev, "irda_regulator");
if (IS_ERR(idata->regulator))
return PTR_ERR(idata->regulator);
@@ -135,32 +140,31 @@ static int ir_spi_probe(struct spi_device *spi)
idata->rc->priv = idata;
idata->spi = spi;
- idata->negated = of_property_read_bool(spi->dev.of_node,
- "led-active-low");
- ret = of_property_read_u8(spi->dev.of_node, "duty-cycle", &dc);
+ idata->negated = device_property_read_bool(dev, "led-active-low");
+ ret = device_property_read_u8(dev, "duty-cycle", &dc);
if (ret)
dc = 50;
- /* ir_spi_set_duty_cycle cannot fail,
- * it returns int to be compatible with the
- * rc->s_tx_duty_cycle function
+ /*
+ * ir_spi_set_duty_cycle() cannot fail, it returns int
+ * to be compatible with the rc->s_tx_duty_cycle function.
*/
ir_spi_set_duty_cycle(idata->rc, dc);
idata->freq = IR_SPI_DEFAULT_FREQUENCY;
- return devm_rc_register_device(&spi->dev, idata->rc);
+ return devm_rc_register_device(dev, idata->rc);
}
static const struct of_device_id ir_spi_of_match[] = {
{ .compatible = "ir-spi-led" },
- {},
+ {}
};
MODULE_DEVICE_TABLE(of, ir_spi_of_match);
static const struct spi_device_id ir_spi_ids[] = {
{ "ir-spi-led" },
- {},
+ {}
};
MODULE_DEVICE_TABLE(spi, ir_spi_ids);
@@ -172,7 +176,6 @@ static struct spi_driver ir_spi_driver = {
.of_match_table = ir_spi_of_match,
},
};
-
module_spi_driver(ir_spi_driver);
MODULE_AUTHOR("Andi Shyti <andi@etezian.org>");
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index caad59f76793..52aea4167718 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -27,7 +27,9 @@ static dev_t lirc_base_dev;
static DEFINE_IDA(lirc_ida);
/* Only used for sysfs but defined to void otherwise */
-static struct class *lirc_class;
+static const struct class lirc_class = {
+ .name = "lirc",
+};
/**
* lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace
@@ -724,7 +726,7 @@ int lirc_register(struct rc_dev *dev)
return minor;
device_initialize(&dev->lirc_dev);
- dev->lirc_dev.class = lirc_class;
+ dev->lirc_dev.class = &lirc_class;
dev->lirc_dev.parent = &dev->dev;
dev->lirc_dev.release = lirc_release_device;
dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor);
@@ -789,15 +791,13 @@ int __init lirc_dev_init(void)
{
int retval;
- lirc_class = class_create("lirc");
- if (IS_ERR(lirc_class)) {
- pr_err("class_create failed\n");
- return PTR_ERR(lirc_class);
- }
+ retval = class_register(&lirc_class);
+ if (retval)
+ return retval;
retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, "lirc");
if (retval) {
- class_destroy(lirc_class);
+ class_unregister(&lirc_class);
pr_err("alloc_chrdev_region failed\n");
return retval;
}
@@ -810,7 +810,7 @@ int __init lirc_dev_init(void)
void __exit lirc_dev_exit(void)
{
- class_destroy(lirc_class);
+ class_unregister(&lirc_class);
unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
}
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
index 6be4e5528879..65fa7f857fca 100644
--- a/drivers/media/spi/cxd2880-spi.c
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -388,7 +388,7 @@ static int cxd2880_start_feed(struct dvb_demux_feed *feed)
if (dvb_spi->feed_count == 0) {
dvb_spi->ts_buf =
- kmalloc(MAX_TRANS_PKT * 188,
+ kzalloc(MAX_TRANS_PKT * 188,
GFP_KERNEL | GFP_DMA);
if (!dvb_spi->ts_buf) {
pr_err("ts buffer allocate failed\n");
diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c
index 75c21a93e6d0..dc5c4c055d29 100644
--- a/drivers/media/spi/gs1662.c
+++ b/drivers/media/spi/gs1662.c
@@ -259,12 +259,15 @@ static inline struct gs *to_gs(struct v4l2_subdev *sd)
return container_of(sd, struct gs, sd);
}
-static int gs_s_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int gs_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct gs *gs = to_gs(sd);
int reg_value;
+ if (pad != 0)
+ return -EINVAL;
+
reg_value = get_register_timings(timings);
if (reg_value == 0x0)
return -EINVAL;
@@ -273,23 +276,29 @@ static int gs_s_dv_timings(struct v4l2_subdev *sd,
return 0;
}
-static int gs_g_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int gs_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct gs *gs = to_gs(sd);
+ if (pad != 0)
+ return -EINVAL;
+
*timings = gs->current_timings;
return 0;
}
-static int gs_query_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings)
+static int gs_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
{
struct gs *gs = to_gs(sd);
struct v4l2_dv_timings fmt;
u16 reg_value, i;
int ret;
+ if (pad != 0)
+ return -EINVAL;
+
if (gs->enabled)
return -EBUSY;
@@ -410,14 +419,14 @@ static const struct v4l2_subdev_core_ops gs_core_ops = {
};
static const struct v4l2_subdev_video_ops gs_video_ops = {
- .s_dv_timings = gs_s_dv_timings,
- .g_dv_timings = gs_g_dv_timings,
.s_stream = gs_s_stream,
.g_input_status = gs_g_input_status,
- .query_dv_timings = gs_query_dv_timings,
};
static const struct v4l2_subdev_pad_ops gs_pad_ops = {
+ .s_dv_timings = gs_s_dv_timings,
+ .g_dv_timings = gs_g_dv_timings,
+ .query_dv_timings = gs_query_dv_timings,
.enum_dv_timings = gs_enum_dv_timings,
.dv_timings_cap = gs_dv_timings_cap,
};
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index e13f5452b927..3e011fe62ae1 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -1345,6 +1345,7 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = {
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index 2a2d19d23bab..ba7550b8ba7e 100644
--- a/drivers/media/test-drivers/vimc/vimc-capture.c
+++ b/drivers/media/test-drivers/vimc/vimc-capture.c
@@ -221,6 +221,7 @@ static const struct v4l2_ioctl_ops vimc_capture_ioctl_ops = {
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_remove_bufs = vb2_ioctl_remove_bufs,
};
static void vimc_capture_return_all_buffers(struct vimc_capture_device *vcapture,
@@ -432,7 +433,7 @@ static struct vimc_ent_device *vimc_capture_add(struct vimc_device *vimc,
q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG
? &vb2_dma_contig_memops : &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_queued_buffers = 2;
+ q->min_reqbufs_allocation = 2;
q->lock = &vcapture->lock;
q->dev = v4l2_dev->dev;
diff --git a/drivers/media/test-drivers/visl/visl-video.c b/drivers/media/test-drivers/visl/visl-video.c
index b9a4b44bd0ed..f8d970319764 100644
--- a/drivers/media/test-drivers/visl/visl-video.c
+++ b/drivers/media/test-drivers/visl/visl-video.c
@@ -539,6 +539,7 @@ const struct v4l2_ioctl_ops visl_ioctl_ops = {
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index 159c72cbb5bf..0273bc9863b0 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -769,6 +769,7 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_remove_bufs = vb2_ioctl_remove_bufs,
.vidioc_enum_input = vivid_enum_input,
.vidioc_g_input = vivid_g_input,
@@ -861,7 +862,7 @@ static const struct media_device_ops vivid_media_ops = {
static int vivid_create_queue(struct vivid_dev *dev,
struct vb2_queue *q,
u32 buf_type,
- unsigned int min_queued_buffers,
+ unsigned int min_reqbufs_allocation,
const struct vb2_ops *ops)
{
if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar)
@@ -898,7 +899,7 @@ static int vivid_create_queue(struct vivid_dev *dev,
q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops :
&vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_queued_buffers = supports_requests[dev->inst] ? 0 : min_queued_buffers;
+ q->min_reqbufs_allocation = min_reqbufs_allocation;
q->lock = &dev->mutex;
q->dev = dev->v4l2_dev.dev;
q->supports_requests = supports_requests[dev->inst];
@@ -1364,7 +1365,7 @@ static int vivid_create_queues(struct vivid_dev *dev)
if (dev->has_meta_out) {
/* initialize meta_out queue */
ret = vivid_create_queue(dev, &dev->vb_meta_out_q,
- V4L2_BUF_TYPE_META_OUTPUT, 1,
+ V4L2_BUF_TYPE_META_OUTPUT, 2,
&vivid_meta_out_qops);
if (ret)
return ret;
@@ -1373,7 +1374,7 @@ static int vivid_create_queues(struct vivid_dev *dev)
if (dev->has_touch_cap) {
/* initialize touch_cap queue */
ret = vivid_create_queue(dev, &dev->vb_touch_cap_q,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, 1,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, 2,
&vivid_touch_cap_qops);
if (ret)
return ret;
diff --git a/drivers/media/test-drivers/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c
index 4a569a6e58be..82ab3b26914e 100644
--- a/drivers/media/test-drivers/vivid/vivid-meta-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c
@@ -18,7 +18,6 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
struct device *alloc_devs[])
{
struct vivid_dev *dev = vb2_get_drv_priv(vq);
- unsigned int q_num_bufs = vb2_get_num_buffers(vq);
unsigned int size = sizeof(struct vivid_meta_out_buf);
if (!vivid_is_webcam(dev))
@@ -31,9 +30,6 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
sizes[0] = size;
}
- if (q_num_bufs + *nbuffers < 2)
- *nbuffers = 2 - q_num_bufs;
-
*nplanes = 1;
return 0;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c
index 4b3c6ea0afde..3888c21b4d0c 100644
--- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c
@@ -13,7 +13,6 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
struct device *alloc_devs[])
{
struct vivid_dev *dev = vb2_get_drv_priv(vq);
- unsigned int q_num_bufs = vb2_get_num_buffers(vq);
struct v4l2_pix_format *f = &dev->tch_format;
unsigned int size = f->sizeimage;
@@ -24,9 +23,6 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
sizes[0] = size;
}
- if (q_num_bufs + *nbuffers < 2)
- *nbuffers = 2 - q_num_bufs;
-
*nplanes = 1;
return 0;
}
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
index 2182e5b7b606..30aa4ee958bd 100644
--- a/drivers/media/tuners/xc5000.c
+++ b/drivers/media/tuners/xc5000.c
@@ -58,7 +58,7 @@ struct xc5000_priv {
struct dvb_frontend *fe;
struct delayed_work timer_sleep;
- const struct firmware *firmware;
+ bool inited;
};
/* Misc Defines */
@@ -1110,23 +1110,19 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
if (!force && xc5000_is_firmware_loaded(fe) == 0)
return 0;
- if (!priv->firmware) {
- ret = request_firmware(&fw, desired_fw->name,
- priv->i2c_props.adap->dev.parent);
- if (ret) {
- pr_err("xc5000: Upload failed. rc %d\n", ret);
- return ret;
- }
- dprintk(1, "firmware read %zu bytes.\n", fw->size);
+ ret = request_firmware(&fw, desired_fw->name,
+ priv->i2c_props.adap->dev.parent);
+ if (ret) {
+ pr_err("xc5000: Upload failed. rc %d\n", ret);
+ return ret;
+ }
+ dprintk(1, "firmware read %zu bytes.\n", fw->size);
- if (fw->size != desired_fw->size) {
- pr_err("xc5000: Firmware file with incorrect size\n");
- release_firmware(fw);
- return -EINVAL;
- }
- priv->firmware = fw;
- } else
- fw = priv->firmware;
+ if (fw->size != desired_fw->size) {
+ pr_err("xc5000: Firmware file with incorrect size\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
/* Try up to 5 times to load firmware */
for (i = 0; i < 5; i++) {
@@ -1204,6 +1200,7 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
}
err:
+ release_firmware(fw);
if (!ret)
printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n",
desired_fw->name);
@@ -1274,7 +1271,7 @@ static int xc5000_resume(struct dvb_frontend *fe)
/* suspended before firmware is loaded.
Avoid firmware load in resume path. */
- if (!priv->firmware)
+ if (!priv->inited)
return 0;
return xc5000_set_params(fe);
@@ -1293,6 +1290,8 @@ static int xc5000_init(struct dvb_frontend *fe)
if (debug)
xc_debug_dump(priv);
+ priv->inited = true;
+
return 0;
}
@@ -1306,10 +1305,6 @@ static void xc5000_release(struct dvb_frontend *fe)
if (priv) {
cancel_delayed_work(&priv->timer_sleep);
- if (priv->firmware) {
- release_firmware(priv->firmware);
- priv->firmware = NULL;
- }
hybrid_tuner_release_state(priv);
}
diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c
index 6b380144d6c2..e0ef66a522e2 100644
--- a/drivers/media/usb/as102/as102_usb_drv.c
+++ b/drivers/media/usb/as102/as102_usb_drv.c
@@ -259,7 +259,7 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
for (i = 0; i < MAX_STREAM_URB; i++) {
struct urb *urb;
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
if (urb == NULL) {
as102_free_usb_stream_buffer(dev);
return -ENOMEM;
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index fd9fc43d47e0..2ec49ea479d5 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -602,10 +602,7 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
if (dev->vbi_read < vbi_field_size) {
remain = vbi_field_size - dev->vbi_read;
- if (len < remain)
- lencopy = len;
- else
- lencopy = remain;
+ lencopy = umin(len, remain);
if (vbi_buf != NULL)
au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c
index 790787f0eba8..90f1aea99dac 100644
--- a/drivers/media/usb/b2c2/flexcop-usb.c
+++ b/drivers/media/usb/b2c2/flexcop-usb.c
@@ -197,10 +197,7 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
return -EINVAL;
}
for (i = 0; i < len;) {
- pagechunk =
- wMax < bytes_left_to_read_on_page(addr, len) ?
- wMax :
- bytes_left_to_read_on_page(addr, len);
+ pagechunk = min(wMax, bytes_left_to_read_on_page(addr, len));
deb_info("%x\n",
(addr & V8_MEMORY_PAGE_MASK) |
(V8_MEMORY_EXTENDED*extended));
@@ -448,7 +445,7 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
/* creating iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
- GFP_ATOMIC);
+ GFP_KERNEL);
if (fc_usb->iso_urb[i] == NULL) {
ret = -ENOMEM;
goto urb_error;
@@ -481,7 +478,7 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
frame_offset += frame_size;
}
- if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
+ if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_KERNEL))) {
err("submitting urb %d failed with %d.", i, ret);
goto urb_error;
}
@@ -515,7 +512,7 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb)
alt = fc_usb->uintf->cur_altsetting;
- if (alt->desc.bNumEndpoints < 1)
+ if (alt->desc.bNumEndpoints < 2)
return -ENODEV;
if (!usb_endpoint_is_isoc_in(&alt->endpoint[0].desc))
return -ENODEV;
@@ -531,6 +528,12 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb)
case USB_SPEED_HIGH:
info("running at HIGH speed.");
break;
+ case USB_SPEED_SUPER:
+ info("running at SUPER speed.");
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ info("running at SUPER+ speed.");
+ break;
case USB_SPEED_UNKNOWN:
default:
err("cannot handle USB speed because it is unknown.");
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index 4eb7dd4599b7..0d2c42819d39 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -868,6 +868,9 @@ static int af9035_read_config(struct dvb_usb_device *d)
if ((le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) &&
(le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_TD310)) {
state->it930x_addresses = 1;
+ /* TD310 RC works with NEC defaults */
+ state->ir_mode = 0x05;
+ state->ir_type = 0x00;
}
return 0;
}
@@ -2066,6 +2069,11 @@ static const struct dvb_usb_device_properties it930x_props = {
.tuner_attach = it930x_tuner_attach,
.tuner_detach = it930x_tuner_detach,
.init = it930x_init,
+ /*
+ * dvb_usbv2_remote_init() calls rc_config() only for those devices
+ * which have non-empty rc_map, so it's safe to enable it for every IT930x
+ */
+ .get_rc_config = af9035_get_rc_config,
.get_stream_config = af9035_get_stream_config,
.get_adapter_count = af9035_get_adapter_count,
@@ -2157,7 +2165,7 @@ static const struct usb_device_id af9035_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9303,
&it930x_props, "ITE 9303 Generic", NULL) },
{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TD310,
- &it930x_props, "AVerMedia TD310 DVB-T2", NULL) },
+ &it930x_props, "AVerMedia TD310 DVB-T2", RC_MAP_AVERMEDIA_RM_KS) },
{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x0100,
&it930x_props, "Logilink VG0022A", NULL) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_TC2_STICK,
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c
index a1235d0cce92..8699846eb416 100644
--- a/drivers/media/usb/dvb-usb-v2/anysee.c
+++ b/drivers/media/usb/dvb-usb-v2/anysee.c
@@ -202,14 +202,14 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
while (i < num) {
if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].len != 2 || msg[i + 1].len > 60) {
+ if (msg[i].len < 1 || msg[i].len > 2 || msg[i + 1].len > 60) {
ret = -EOPNOTSUPP;
break;
}
buf[0] = CMD_I2C_READ;
buf[1] = (msg[i].addr << 1) | 0x01;
buf[2] = msg[i].buf[0];
- buf[3] = msg[i].buf[1];
+ buf[3] = (msg[i].len < 2) ? 0 : msg[i].buf[1];
buf[4] = msg[i].len-1;
buf[5] = msg[i+1].len;
ret = anysee_ctrl_msg(d, buf, 6, msg[i+1].buf,
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 3af594134a6d..6ddc20513393 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -2412,7 +2412,12 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap)
adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
+ if (!adap->fe_adap[0].fe) {
+ release_firmware(state->frontend_firmware);
+ return -ENODEV;
+ }
+
+ return 0;
}
static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
@@ -2485,8 +2490,10 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap)
dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80);
adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
- if (adap->fe_adap[0].fe == NULL)
+ if (!adap->fe_adap[0].fe) {
+ release_firmware(state->frontend_firmware);
return -ENODEV;
+ }
i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82);
@@ -2494,7 +2501,12 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap)
fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]);
dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave);
- return fe_slave == NULL ? -ENODEV : 0;
+ if (!fe_slave) {
+ release_firmware(state->frontend_firmware);
+ return -ENODEV;
+ }
+
+ return 0;
}
static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index b3bb1805829a..79e2ccf974c9 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
@@ -36,7 +36,6 @@
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 64
-
#define DW210X_READ_MSG 0
#define DW210X_WRITE_MSG 1
@@ -53,10 +52,10 @@
#define DW2102_FIRMWARE "dvb-usb-dw2102.fw"
#define DW2104_FIRMWARE "dvb-usb-dw2104.fw"
#define DW3101_FIRMWARE "dvb-usb-dw3101.fw"
-#define S630_FIRMWARE "dvb-usb-s630.fw"
-#define S660_FIRMWARE "dvb-usb-s660.fw"
-#define P1100_FIRMWARE "dvb-usb-p1100.fw"
-#define P7500_FIRMWARE "dvb-usb-p7500.fw"
+#define S630_FIRMWARE "dvb-usb-s630.fw"
+#define S660_FIRMWARE "dvb-usb-s660.fw"
+#define P1100_FIRMWARE "dvb-usb-p1100.fw"
+#define P7500_FIRMWARE "dvb-usb-p7500.fw"
#define err_str "did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware"
@@ -87,7 +86,7 @@ MODULE_PARM_DESC(demod, "demod to probe (1=cx24116 2=stv0903+stv6110 4=stv0903+s
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
- u16 index, u8 * data, u16 len, int flags)
+ u16 index, u8 *data, u16 len, int flags)
{
int ret;
u8 *u8buf;
@@ -99,11 +98,10 @@ static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
if (!u8buf)
return -ENOMEM;
-
if (flags == DW210X_WRITE_MSG)
memcpy(u8buf, data, len);
ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
- value, index , u8buf, len, 2000);
+ value, index, u8buf, len, 2000);
if (flags == DW210X_READ_MSG)
memcpy(data, u8buf, len);
@@ -114,7 +112,7 @@ static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
/* I2C */
static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0;
@@ -136,7 +134,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
value = msg[0].buf[0];/* register */
for (i = 0; i < msg[1].len; i++) {
dw210x_op_rw(d->udev, 0xb5, value + i, 0,
- buf6, 2, DW210X_READ_MSG);
+ buf6, 2, DW210X_READ_MSG);
msg[1].buf[i] = buf6[0];
}
break;
@@ -152,7 +150,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
buf6[1] = msg[0].buf[0];
buf6[2] = msg[0].buf[1];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- buf6, 3, DW210X_WRITE_MSG);
+ buf6, 3, DW210X_WRITE_MSG);
break;
case 0x60:
if (msg[0].flags == 0) {
@@ -169,7 +167,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
buf6[5] = msg[0].buf[2];
buf6[6] = msg[0].buf[3];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- buf6, 7, DW210X_WRITE_MSG);
+ buf6, 7, DW210X_WRITE_MSG);
} else {
if (msg[0].len < 1) {
num = -EOPNOTSUPP;
@@ -177,7 +175,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
/* read from tuner */
dw210x_op_rw(d->udev, 0xb5, 0, 0,
- buf6, 1, DW210X_READ_MSG);
+ buf6, 1, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
}
break;
@@ -187,7 +185,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
break;
}
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- buf6, 2, DW210X_READ_MSG);
+ buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
break;
@@ -199,7 +197,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- buf6, 2, DW210X_WRITE_MSG);
+ buf6, 2, DW210X_WRITE_MSG);
break;
}
@@ -211,7 +209,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
- struct i2c_msg msg[], int num)
+ struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
u8 buf6[] = {0, 0, 0, 0, 0, 0, 0};
@@ -242,10 +240,10 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
buf6[1] = msg[0].len;
buf6[2] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- buf6, msg[0].len + 2, DW210X_WRITE_MSG);
+ buf6, msg[0].len + 2, DW210X_WRITE_MSG);
/* read si2109 register */
dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
- buf6, msg[1].len + 2, DW210X_READ_MSG);
+ buf6, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, buf6 + 2, msg[1].len);
break;
@@ -264,11 +262,11 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
buf6[1] = msg[0].len;
memcpy(buf6 + 2, msg[0].buf, msg[0].len);
dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
- msg[0].len + 2, DW210X_WRITE_MSG);
+ msg[0].len + 2, DW210X_WRITE_MSG);
break;
case(DW2102_RC_QUERY):
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- buf6, 2, DW210X_READ_MSG);
+ buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
break;
@@ -276,7 +274,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- buf6, 2, DW210X_WRITE_MSG);
+ buf6, 2, DW210X_WRITE_MSG);
break;
}
break;
@@ -320,10 +318,10 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[1] = msg[0].len;
obuf[2] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
/* second read registers */
- dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0,
- ibuf, msg[1].len + 2, DW210X_READ_MSG);
+ dw210x_op_rw(d->udev, 0xc3, 0xd1, 0,
+ ibuf, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, ibuf + 2, msg[1].len);
break;
@@ -345,7 +343,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
case 0x61: {
@@ -363,22 +361,24 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
case(DW2102_RC_QUERY): {
u8 ibuf[2];
+
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- ibuf, 2, DW210X_READ_MSG);
- memcpy(msg[0].buf, ibuf , 2);
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[0].buf, ibuf, 2);
break;
}
case(DW2102_VOLTAGE_CTRL): {
u8 obuf[2];
+
obuf[0] = 0x30;
obuf[1] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- obuf, 2, DW210X_WRITE_MSG);
+ obuf, 2, DW210X_WRITE_MSG);
break;
}
}
@@ -406,23 +406,26 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
switch (msg[j].addr) {
case(DW2102_RC_QUERY): {
u8 ibuf[2];
+
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- ibuf, 2, DW210X_READ_MSG);
- memcpy(msg[j].buf, ibuf , 2);
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[j].buf, ibuf, 2);
break;
}
case(DW2102_VOLTAGE_CTRL): {
u8 obuf[2];
+
obuf[0] = 0x30;
obuf[1] = msg[j].buf[0];
dw210x_op_rw(d->udev, 0xb2, 0, 0,
- obuf, 2, DW210X_WRITE_MSG);
+ obuf, 2, DW210X_WRITE_MSG);
break;
}
- /*case 0x55: cx24116
- case 0x6a: stv0903
- case 0x68: ds3000, stv0903
- case 0x60: ts2020, stv6110, stb6100 */
+ /* case 0x55: cx24116
+ * case 0x6a: stv0903
+ * case 0x68: ds3000, stv0903
+ * case 0x60: ts2020, stv6110, stb6100
+ */
default: {
if (msg[j].flags == I2C_M_RD) {
/* read registers */
@@ -436,17 +439,16 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
}
dw210x_op_rw(d->udev, 0xc3,
- (msg[j].addr << 1) + 1, 0,
- ibuf, msg[j].len + 2,
- DW210X_READ_MSG);
+ (msg[j].addr << 1) + 1, 0,
+ ibuf, msg[j].len + 2,
+ DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf + 2, msg[j].len);
mdelay(10);
- } else if (((msg[j].buf[0] == 0xb0) &&
- (msg[j].addr == 0x68)) ||
- ((msg[j].buf[0] == 0xf7) &&
- (msg[j].addr == 0x55))) {
+ } else if (((msg[j].buf[0] == 0xb0) && (msg[j].addr == 0x68)) ||
+ ((msg[j].buf[0] == 0xf7) && (msg[j].addr == 0x55))) {
/* write firmware */
u8 obuf[19];
+
obuf[0] = msg[j].addr << 1;
obuf[1] = (msg[j].len > 15 ? 17 : msg[j].len);
obuf[2] = msg[j].buf[0];
@@ -454,10 +456,10 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
i = 1;
do {
memcpy(obuf + 3, msg[j].buf + i,
- (len > 16 ? 16 : len));
+ (len > 16 ? 16 : len));
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, (len > 16 ? 16 : len) + 3,
- DW210X_WRITE_MSG);
+ obuf, (len > 16 ? 16 : len) + 3,
+ DW210X_WRITE_MSG);
i += 16;
len -= 16;
} while (len > 0);
@@ -476,13 +478,12 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
obuf[1] = msg[j].len;
memcpy(obuf + 2, msg[j].buf, msg[j].len);
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[j].len + 2,
- DW210X_WRITE_MSG);
+ obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
}
break;
}
}
-
}
ret = num;
@@ -492,7 +493,7 @@ unlock:
}
static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int ret;
@@ -525,10 +526,10 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[1] = msg[0].len;
obuf[2] = msg[0].buf[0];
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
/* second read registers */
- dw210x_op_rw(d->udev, 0xc3, 0x19 , 0,
- ibuf, msg[1].len + 2, DW210X_READ_MSG);
+ dw210x_op_rw(d->udev, 0xc3, 0x19, 0,
+ ibuf, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, ibuf + 2, msg[1].len);
break;
@@ -550,14 +551,15 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
case(DW2102_RC_QUERY): {
u8 ibuf[2];
+
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- ibuf, 2, DW210X_READ_MSG);
- memcpy(msg[0].buf, ibuf , 2);
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[0].buf, ibuf, 2);
break;
}
}
@@ -567,7 +569,7 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
for (i = 0; i < num; i++) {
deb_xfer("%02x:%02x: %s ", i, msg[i].addr,
- msg[i].flags == 0 ? ">>>" : "<<<");
+ msg[i].flags == 0 ? ">>>" : "<<<");
debug_dump(msg[i].buf, msg[i].len, deb_xfer);
}
ret = num;
@@ -578,7 +580,7 @@ unlock:
}
static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct usb_device *udev;
@@ -594,8 +596,9 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
switch (msg[j].addr) {
case (DW2102_RC_QUERY): {
u8 ibuf[5];
+
dw210x_op_rw(d->udev, 0xb8, 0, 0,
- ibuf, 5, DW210X_READ_MSG);
+ ibuf, 5, DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf + 3, 2);
break;
}
@@ -605,11 +608,11 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = 1;
obuf[1] = msg[j].buf[1];/* off-on */
dw210x_op_rw(d->udev, 0x8a, 0, 0,
- obuf, 2, DW210X_WRITE_MSG);
+ obuf, 2, DW210X_WRITE_MSG);
obuf[0] = 3;
obuf[1] = msg[j].buf[0];/* 13v-18v */
dw210x_op_rw(d->udev, 0x8a, 0, 0,
- obuf, 2, DW210X_WRITE_MSG);
+ obuf, 2, DW210X_WRITE_MSG);
break;
}
case (DW2102_LED_CTRL): {
@@ -618,14 +621,15 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = 5;
obuf[1] = msg[j].buf[0];
dw210x_op_rw(d->udev, 0x8a, 0, 0,
- obuf, 2, DW210X_WRITE_MSG);
+ obuf, 2, DW210X_WRITE_MSG);
break;
}
- /*case 0x55: cx24116
- case 0x6a: stv0903
- case 0x68: ds3000, stv0903, rs2000
- case 0x60: ts2020, stv6110, stb6100
- case 0xa0: eeprom */
+ /* case 0x55: cx24116
+ * case 0x6a: stv0903
+ * case 0x68: ds3000, stv0903, rs2000
+ * case 0x60: ts2020, stv6110, stb6100
+ * case 0xa0: eeprom
+ */
default: {
if (msg[j].flags == I2C_M_RD) {
/* read registers */
@@ -639,14 +643,14 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
dw210x_op_rw(d->udev, 0x91, 0, 0,
- ibuf, msg[j].len,
+ ibuf, msg[j].len,
DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf, msg[j].len);
break;
- } else if ((msg[j].buf[0] == 0xb0) &&
- (msg[j].addr == 0x68)) {
+ } else if ((msg[j].buf[0] == 0xb0) && (msg[j].addr == 0x68)) {
/* write firmware */
u8 obuf[19];
+
obuf[0] = (msg[j].len > 16 ?
18 : msg[j].len + 1);
obuf[1] = msg[j].addr << 1;
@@ -655,10 +659,10 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
i = 1;
do {
memcpy(obuf + 3, msg[j].buf + i,
- (len > 16 ? 16 : len));
+ (len > 16 ? 16 : len));
dw210x_op_rw(d->udev, 0x80, 0, 0,
- obuf, (len > 16 ? 16 : len) + 3,
- DW210X_WRITE_MSG);
+ obuf, (len > 16 ? 16 : len) + 3,
+ DW210X_WRITE_MSG);
i += 16;
len -= 16;
} while (len > 0);
@@ -677,10 +681,9 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
dw210x_op_rw(d->udev,
- le16_to_cpu(udev->descriptor.idProduct) ==
- 0x7500 ? 0x92 : 0x90, 0, 0,
- obuf, msg[j].len + 2,
- DW210X_WRITE_MSG);
+ le16_to_cpu(udev->descriptor.idProduct) == 0x7500 ? 0x92 : 0x90,
+ 0, 0, obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
break;
} else {
/* write registers */
@@ -696,8 +699,8 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
dw210x_op_rw(d->udev, 0x80, 0, 0,
- obuf, msg[j].len + 2,
- DW210X_WRITE_MSG);
+ obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
break;
}
break;
@@ -712,10 +715,11 @@ unlock:
}
static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct dw2102_state *state;
+ int j;
if (!d)
return -ENODEV;
@@ -729,77 +733,102 @@ static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
return -EAGAIN;
}
- switch (num) {
- case 1:
- switch (msg[0].addr) {
+ j = 0;
+ while (j < num) {
+ switch (msg[j].addr) {
case SU3000_STREAM_CTRL:
- state->data[0] = msg[0].buf[0] + 0x36;
+ state->data[0] = msg[j].buf[0] + 0x36;
state->data[1] = 3;
state->data[2] = 0;
if (dvb_usb_generic_rw(d, state->data, 3,
- state->data, 0, 0) < 0)
+ state->data, 0, 0) < 0)
err("i2c transfer failed.");
break;
case DW2102_RC_QUERY:
state->data[0] = 0x10;
if (dvb_usb_generic_rw(d, state->data, 1,
- state->data, 2, 0) < 0)
+ state->data, 2, 0) < 0)
err("i2c transfer failed.");
- msg[0].buf[1] = state->data[0];
- msg[0].buf[0] = state->data[1];
+ msg[j].buf[1] = state->data[0];
+ msg[j].buf[0] = state->data[1];
break;
default:
- if (3 + msg[0].len > sizeof(state->data)) {
- warn("i2c wr: len=%d is too big!\n",
- msg[0].len);
- num = -EOPNOTSUPP;
+ /* if the current write msg is followed by a another
+ * read msg to/from the same address
+ */
+ if ((j + 1 < num) && (msg[j + 1].flags & I2C_M_RD) &&
+ (msg[j].addr == msg[j + 1].addr)) {
+ /* join both i2c msgs to one usb read command */
+ if (4 + msg[j].len > sizeof(state->data)) {
+ warn("i2c combined wr/rd: write len=%d is too big!\n",
+ msg[j].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+ if (1 + msg[j + 1].len > sizeof(state->data)) {
+ warn("i2c combined wr/rd: read len=%d is too big!\n",
+ msg[j + 1].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
+ state->data[0] = 0x09;
+ state->data[1] = msg[j].len;
+ state->data[2] = msg[j + 1].len;
+ state->data[3] = msg[j].addr;
+ memcpy(&state->data[4], msg[j].buf, msg[j].len);
+
+ if (dvb_usb_generic_rw(d, state->data, msg[j].len + 4,
+ state->data, msg[j + 1].len + 1, 0) < 0)
+ err("i2c transfer failed.");
+
+ memcpy(msg[j + 1].buf, &state->data[1], msg[j + 1].len);
+ j++;
break;
}
- /* always i2c write*/
- state->data[0] = 0x08;
- state->data[1] = msg[0].addr;
- state->data[2] = msg[0].len;
+ if (msg[j].flags & I2C_M_RD) {
+ /* single read */
+ if (4 + msg[j].len > sizeof(state->data)) {
+ warn("i2c rd: len=%d is too big!\n", msg[j].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
- memcpy(&state->data[3], msg[0].buf, msg[0].len);
+ state->data[0] = 0x09;
+ state->data[1] = 0;
+ state->data[2] = msg[j].len;
+ state->data[3] = msg[j].addr;
+ memcpy(&state->data[4], msg[j].buf, msg[j].len);
- if (dvb_usb_generic_rw(d, state->data, msg[0].len + 3,
- state->data, 1, 0) < 0)
- err("i2c transfer failed.");
+ if (dvb_usb_generic_rw(d, state->data, 4,
+ state->data, msg[j].len + 1, 0) < 0)
+ err("i2c transfer failed.");
- }
- break;
- case 2:
- /* always i2c read */
- if (4 + msg[0].len > sizeof(state->data)) {
- warn("i2c rd: len=%d is too big!\n",
- msg[0].len);
- num = -EOPNOTSUPP;
- break;
- }
- if (1 + msg[1].len > sizeof(state->data)) {
- warn("i2c rd: len=%d is too big!\n",
- msg[1].len);
- num = -EOPNOTSUPP;
- break;
- }
+ memcpy(msg[j].buf, &state->data[1], msg[j].len);
+ break;
+ }
- state->data[0] = 0x09;
- state->data[1] = msg[0].len;
- state->data[2] = msg[1].len;
- state->data[3] = msg[0].addr;
- memcpy(&state->data[4], msg[0].buf, msg[0].len);
+ /* single write */
+ if (3 + msg[j].len > sizeof(state->data)) {
+ warn("i2c wr: len=%d is too big!\n", msg[j].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
- if (dvb_usb_generic_rw(d, state->data, msg[0].len + 4,
- state->data, msg[1].len + 1, 0) < 0)
- err("i2c transfer failed.");
+ state->data[0] = 0x08;
+ state->data[1] = msg[j].addr;
+ state->data[2] = msg[j].len;
- memcpy(msg[1].buf, &state->data[1], msg[1].len);
- break;
- default:
- warn("more than 2 i2c messages at a time is not handled yet.");
- break;
- }
+ memcpy(&state->data[3], msg[j].buf, msg[j].len);
+
+ if (dvb_usb_generic_rw(d, state->data, msg[j].len + 3,
+ state->data, 1, 0) < 0)
+ err("i2c transfer failed.");
+ } // switch
+ j++;
+
+ } // while
mutex_unlock(&d->data_mutex);
mutex_unlock(&d->i2c_mutex);
return num;
@@ -852,11 +881,11 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
u8 eeprom[256], eepromline[16];
for (i = 0; i < 256; i++) {
- if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) {
+ if (dw210x_op_rw(d->udev, 0xb6, 0xa0, i, ibuf, 2, DW210X_READ_MSG) < 0) {
err("read eeprom failed.");
return -EIO;
} else {
- eepromline[i%16] = ibuf[0];
+ eepromline[i % 16] = ibuf[0];
eeprom[i] = ibuf[0];
}
if ((i % 16) == 15) {
@@ -963,7 +992,6 @@ static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
.flags = I2C_M_RD,
.buf = ibuf,
.len = 1,
-
}
};
@@ -983,8 +1011,6 @@ static int su3000_identify_state(struct usb_device *udev,
const struct dvb_usb_device_description **desc,
int *cold)
{
- info("%s", __func__);
-
*cold = 0;
return 0;
}
@@ -1003,6 +1029,7 @@ static int dw210x_set_voltage(struct dvb_frontend *fe,
};
struct dvb_usb_adapter *udev_adap = fe->dvb->priv;
+
if (voltage == SEC_VOLTAGE_18)
msg.buf = command_18v;
else if (voltage == SEC_VOLTAGE_13)
@@ -1206,11 +1233,11 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
if (demod_probe & 4) {
d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
- &d->dev->i2c_adap, 0);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap, 0);
+ if (d->fe_adap[0].fe) {
if (dvb_attach(stb6100_attach, d->fe_adap[0].fe,
- &dw2104a_stb6100_config,
- &d->dev->i2c_adap)) {
+ &dw2104a_stb6100_config,
+ &d->dev->i2c_adap)) {
tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops;
tuner_ops->set_frequency = stb6100_set_freq;
tuner_ops->get_frequency = stb6100_get_freq;
@@ -1225,11 +1252,11 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
if (demod_probe & 2) {
d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
- &d->dev->i2c_adap, 0);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap, 0);
+ if (d->fe_adap[0].fe) {
if (dvb_attach(stv6110_attach, d->fe_adap[0].fe,
- &dw2104_stv6110_config,
- &d->dev->i2c_adap)) {
+ &dw2104_stv6110_config,
+ &d->dev->i2c_adap)) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached STV0900+STV6110A!");
return 0;
@@ -1239,8 +1266,8 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
if (demod_probe & 1) {
d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached cx24116!");
return 0;
@@ -1248,10 +1275,10 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
}
d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
dvb_attach(ts2020_attach, d->fe_adap[0].fe,
- &dw2104_ts2020_config, &d->dev->i2c_adap);
+ &dw2104_ts2020_config, &d->dev->i2c_adap);
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached DS3000!");
return 0;
@@ -1269,8 +1296,8 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = NULL;*/
d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached si21xx!");
return 0;
@@ -1279,10 +1306,10 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61,
- &d->dev->i2c_adap)) {
+ &d->dev->i2c_adap)) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0288!");
return 0;
@@ -1293,8 +1320,8 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
/*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached stv0299!");
return 0;
@@ -1306,8 +1333,8 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
{
d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
- &d->dev->i2c_adap, 0x48);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap, 0x48);
+ if (d->fe_adap[0].fe) {
info("Attached tda10023!");
return 0;
}
@@ -1317,10 +1344,10 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
static int zl100313_frontend_attach(struct dvb_usb_adapter *d)
{
d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config,
- &d->dev->i2c_adap);
- if (d->fe_adap[0].fe != NULL) {
+ &d->dev->i2c_adap);
+ if (d->fe_adap[0].fe) {
if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60,
- &d->dev->i2c_adap)) {
+ &d->dev->i2c_adap)) {
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
info("Attached zl100313+zl10039!");
return 0;
@@ -1335,12 +1362,12 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
u8 obuf[] = {7, 1};
d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config,
- &d->dev->i2c_adap);
+ &d->dev->i2c_adap);
- if (d->fe_adap[0].fe == NULL)
+ if (!d->fe_adap[0].fe)
return -EIO;
- if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap))
+ if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap) == NULL)
return -EIO;
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
@@ -1350,7 +1377,6 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
info("Attached stv0288+stb6000!");
return 0;
-
}
static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
@@ -1359,13 +1385,13 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
u8 obuf[] = {7, 1};
d->fe_adap[0].fe = dvb_attach(ds3000_attach, &s660_ds3000_config,
- &d->dev->i2c_adap);
+ &d->dev->i2c_adap);
- if (d->fe_adap[0].fe == NULL)
+ if (!d->fe_adap[0].fe)
return -EIO;
dvb_attach(ts2020_attach, d->fe_adap[0].fe, &s660_ts2020_config,
- &d->dev->i2c_adap);
+ &d->dev->i2c_adap);
st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage;
d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage;
@@ -1382,8 +1408,8 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
u8 obuf[] = {7, 1};
d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
- &d->dev->i2c_adap, 0);
- if (d->fe_adap[0].fe == NULL)
+ &d->dev->i2c_adap, 0);
+ if (!d->fe_adap[0].fe)
return -EIO;
d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
@@ -1439,12 +1465,12 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *adap)
mutex_unlock(&d->data_mutex);
adap->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
- &d->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
+ &d->i2c_adap);
+ if (!adap->fe_adap[0].fe)
return -EIO;
if (dvb_attach(ts2020_attach, adap->fe_adap[0].fe,
- &dw2104_ts2020_config,
+ &dw2104_ts2020_config,
&d->i2c_adap)) {
info("Attached DS3000/TS2020!");
return 0;
@@ -1499,10 +1525,10 @@ static int t220_frontend_attach(struct dvb_usb_adapter *adap)
mutex_unlock(&d->data_mutex);
adap->fe_adap[0].fe = dvb_attach(cxd2820r_attach, &cxd2820r_config,
- &d->i2c_adap, NULL);
- if (adap->fe_adap[0].fe != NULL) {
+ &d->i2c_adap, NULL);
+ if (adap->fe_adap[0].fe) {
if (dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0x60,
- &d->i2c_adap, &tda18271_config)) {
+ &d->i2c_adap, &tda18271_config)) {
info("Attached TDA18271HD/CXD2820R!");
return 0;
}
@@ -1527,14 +1553,14 @@ static int m88rs2000_frontend_attach(struct dvb_usb_adapter *adap)
mutex_unlock(&d->data_mutex);
adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
- &s421_m88rs2000_config,
- &d->i2c_adap);
+ &s421_m88rs2000_config,
+ &d->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
+ if (!adap->fe_adap[0].fe)
return -EIO;
if (dvb_attach(ts2020_attach, adap->fe_adap[0].fe,
- &dw2104_ts2020_config,
+ &dw2104_ts2020_config,
&d->i2c_adap)) {
info("Attached RS2000/TS2020!");
return 0;
@@ -1701,14 +1727,14 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap)
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
- &adap->dev->i2c_adap, DVB_PLL_OPERA1);
+ &adap->dev->i2c_adap, DVB_PLL_OPERA1);
return 0;
}
static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
- &adap->dev->i2c_adap, DVB_PLL_TUA6034);
+ &adap->dev->i2c_adap, DVB_PLL_TUA6034);
return 0;
}
@@ -1726,7 +1752,7 @@ static int dw2102_rc_query(struct dvb_usb_device *d)
if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
if (msg.buf[0] != 0xff) {
deb_rc("%s: rc code: %x, %x\n",
- __func__, key[0], key[1]);
+ __func__, key[0], key[1]);
rc_keydown(d->rc_dev, RC_PROTO_UNKNOWN, key[0], 0);
}
}
@@ -1747,7 +1773,7 @@ static int prof_rc_query(struct dvb_usb_device *d)
if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
if (msg.buf[0] != 0xff) {
deb_rc("%s: rc code: %x, %x\n",
- __func__, key[0], key[1]);
+ __func__, key[0], key[1]);
rc_keydown(d->rc_dev, RC_PROTO_UNKNOWN, key[0] ^ 0xff,
0);
}
@@ -1769,7 +1795,7 @@ static int su3000_rc_query(struct dvb_usb_device *d)
if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
if (msg.buf[0] != 0xff) {
deb_rc("%s: rc code: %x, %x\n",
- __func__, key[0], key[1]);
+ __func__, key[0], key[1]);
rc_keydown(d->rc_dev, RC_PROTO_RC5,
RC_SCANCODE_RC5(key[1], key[0]), 0);
}
@@ -1807,7 +1833,6 @@ enum dw2102_table_entry {
TECHNOTREND_CONNECT_S2_4600,
TEVII_S482_1,
TEVII_S482_2,
- TERRATEC_CINERGY_S2_BOX,
TEVII_S662
};
@@ -1840,7 +1865,6 @@ static struct usb_device_id dw2102_table[] = {
DVB_USB_DEV(TECHNOTREND, TECHNOTREND_CONNECT_S2_4600),
DVB_USB_DEV(TEVII, TEVII_S482_1),
DVB_USB_DEV(TEVII, TEVII_S482_2),
- DVB_USB_DEV(TERRATEC, TERRATEC_CINERGY_S2_BOX),
DVB_USB_DEV(TEVII, TEVII_S662),
{ }
};
@@ -1848,7 +1872,7 @@ static struct usb_device_id dw2102_table[] = {
MODULE_DEVICE_TABLE(usb, dw2102_table);
static int dw2102_load_firmware(struct usb_device *dev,
- const struct firmware *frmwr)
+ const struct firmware *frmwr)
{
u8 *b, *p;
int ret = 0, i;
@@ -1875,12 +1899,12 @@ static int dw2102_load_firmware(struct usb_device *dev,
dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG);
dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG);
- if (p != NULL) {
+ if (p) {
memcpy(p, fw->data, fw->size);
for (i = 0; i < fw->size; i += 0x40) {
- b = (u8 *) p + i;
- if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40,
- DW210X_WRITE_MSG) != 0x40) {
+ b = (u8 *)p + i;
+ if (dw210x_op_rw(dev, 0xa0, i, 0, b, 0x40,
+ DW210X_WRITE_MSG) != 0x40) {
err("error while transferring firmware");
ret = -EINVAL;
break;
@@ -1906,50 +1930,49 @@ static int dw2102_load_firmware(struct usb_device *dev,
case USB_PID_CYPRESS_DW2104:
reset = 1;
dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1,
- DW210X_WRITE_MSG);
+ DW210X_WRITE_MSG);
fallthrough;
case USB_PID_CYPRESS_DW3101:
reset = 0;
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
- DW210X_WRITE_MSG);
+ DW210X_WRITE_MSG);
break;
case USB_PID_TERRATEC_CINERGY_S:
case USB_PID_CYPRESS_DW2102:
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
- DW210X_WRITE_MSG);
+ DW210X_WRITE_MSG);
dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
/* check STV0299 frontend */
dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) {
dw2102_properties.i2c_algo = &dw2102_i2c_algo;
dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach;
break;
- } else {
- /* check STV0288 frontend */
- reset16[0] = 0xd0;
- reset16[1] = 1;
- reset16[2] = 0;
- dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3,
- DW210X_WRITE_MSG);
- dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3,
- DW210X_READ_MSG);
- if (reset16[2] == 0x11) {
- dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo;
- break;
- }
+ }
+ /* check STV0288 frontend */
+ reset16[0] = 0xd0;
+ reset16[1] = 1;
+ reset16[2] = 0;
+ dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3,
+ DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3,
+ DW210X_READ_MSG);
+ if (reset16[2] == 0x11) {
+ dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo;
+ break;
}
fallthrough;
case 0x2101:
dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
- DW210X_READ_MSG);
+ DW210X_READ_MSG);
break;
}
@@ -2551,7 +2574,7 @@ static struct dvb_usb_device_properties tt_s2_4600_properties = {
{ NULL },
},
{ "Terratec Cinergy S2 USB BOX",
- { &dw2102_table[TERRATEC_CINERGY_S2_BOX], NULL },
+ { &dw2102_table[TERRATEC_CINERGY_S2_R4], NULL },
{ NULL },
},
{ "TeVii S662",
@@ -2562,18 +2585,18 @@ static struct dvb_usb_device_properties tt_s2_4600_properties = {
};
static int dw2102_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+ const struct usb_device_id *id)
{
if (!(dvb_usb_device_init(intf, &dw2102_properties,
- THIS_MODULE, NULL, adapter_nr) &&
+ THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &dw2104_properties,
THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &dw3101_properties,
- THIS_MODULE, NULL, adapter_nr) &&
+ THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &s6x0_properties,
- THIS_MODULE, NULL, adapter_nr) &&
+ THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &p1100_properties,
- THIS_MODULE, NULL, adapter_nr) &&
+ THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &s660_properties,
THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &p7500_properties,
@@ -2586,7 +2609,6 @@ static int dw2102_probe(struct usb_interface *intf,
THIS_MODULE, NULL, adapter_nr) &&
dvb_usb_device_init(intf, &tt_s2_4600_properties,
THIS_MODULE, NULL, adapter_nr))) {
-
return 0;
}
diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c
index 018019ba47d4..86ce593e0c54 100644
--- a/drivers/media/usb/go7007/go7007-fw.c
+++ b/drivers/media/usb/go7007/go7007-fw.c
@@ -1289,8 +1289,8 @@ static int avsync_to_package(struct go7007 *go, __le16 *code, int space)
0xbf99, (u16)((-adjratio) >> 16),
0xbf92, 0,
0xbf93, 0,
- 0xbff4, f1 > f2 ? f1 : f2,
- 0xbff5, f1 < f2 ? f1 : f2,
+ 0xbff4, max(f1, f2),
+ 0xbff5, min(f1, f2),
0xbff6, f1 < f2 ? ratio : ratio + 1,
0xbff7, f1 > f2 ? ratio : ratio + 1,
0xbff8, 0,
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
index 5f5fa851ca64..14aaf36cde6e 100644
--- a/drivers/media/usb/gspca/cpia1.c
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -604,10 +604,8 @@ static int find_over_exposure(int brightness)
MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
FLICKER_BRIGHTNESS_CONSTANT;
- if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
- OverExposure = MaxAllowableOverExposure;
- else
- OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+ OverExposure = min(MaxAllowableOverExposure,
+ FLICKER_ALLOWABLE_OVER_EXPOSURE);
return OverExposure;
}
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 8e1de1e8bd12..a6e450181fd0 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -247,7 +247,7 @@ struct s2255_vc {
struct s2255_dev {
struct s2255_vc vc[MAX_CHANNELS];
struct v4l2_device v4l2_dev;
- atomic_t num_channels;
+ refcount_t num_channels;
int frames;
struct mutex lock; /* channels[].vdev.lock */
struct mutex cmdlock; /* protects cmdbuf */
@@ -1550,11 +1550,11 @@ static void s2255_video_device_release(struct video_device *vdev)
container_of(vdev, struct s2255_vc, vdev);
dprintk(dev, 4, "%s, chnls: %d\n", __func__,
- atomic_read(&dev->num_channels));
+ refcount_read(&dev->num_channels));
v4l2_ctrl_handler_free(&vc->hdl);
- if (atomic_dec_and_test(&dev->num_channels))
+ if (refcount_dec_and_test(&dev->num_channels))
s2255_destroy(dev);
return;
}
@@ -1659,7 +1659,7 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
"failed to register video device!\n");
break;
}
- atomic_inc(&dev->num_channels);
+ refcount_inc(&dev->num_channels);
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(&vc->vdev));
@@ -1667,11 +1667,11 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
pr_info("Sensoray 2255 V4L driver Revision: %s\n",
S2255_VERSION);
/* if no channels registered, return error and probe will fail*/
- if (atomic_read(&dev->num_channels) == 0) {
+ if (refcount_read(&dev->num_channels) == 0) {
v4l2_device_unregister(&dev->v4l2_dev);
return ret;
}
- if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
+ if (refcount_read(&dev->num_channels) != MAX_CHANNELS)
pr_warn("s2255: Not all channels available.\n");
return 0;
}
@@ -2221,7 +2221,7 @@ static int s2255_probe(struct usb_interface *interface,
goto errorFWDATA1;
}
- atomic_set(&dev->num_channels, 0);
+ refcount_set(&dev->num_channels, 0);
dev->pid = id->idProduct;
dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
if (!dev->fw_data)
@@ -2341,12 +2341,12 @@ static void s2255_disconnect(struct usb_interface *interface)
{
struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
int i;
- int channels = atomic_read(&dev->num_channels);
+ int channels = refcount_read(&dev->num_channels);
mutex_lock(&dev->lock);
v4l2_device_disconnect(&dev->v4l2_dev);
mutex_unlock(&dev->lock);
/*see comments in the uvc_driver.c usb disconnect function */
- atomic_inc(&dev->num_channels);
+ refcount_inc(&dev->num_channels);
/* unregister each video device. */
for (i = 0; i < channels; i++)
video_unregister_device(&dev->vc[i].vdev);
@@ -2359,7 +2359,7 @@ static void s2255_disconnect(struct usb_interface *interface)
dev->vc[i].vidstatus_ready = 1;
wake_up(&dev->vc[i].wait_vidstatus);
}
- if (atomic_dec_and_test(&dev->num_channels))
+ if (refcount_dec_and_test(&dev->num_channels))
s2255_destroy(dev);
dev_info(&interface->dev, "%s\n", __func__);
}
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index 723510520d09..2c8179a84991 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -40,7 +40,7 @@ struct smsusb_urb_t {
struct smscore_buffer_t *cb;
struct smsusb_device_t *dev;
- struct urb urb;
+ struct urb *urb;
/* For the bottom half */
struct work_struct wq;
@@ -160,7 +160,7 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev,
}
usb_fill_bulk_urb(
- &surb->urb,
+ surb->urb,
dev->udev,
usb_rcvbulkpipe(dev->udev, dev->in_ep),
surb->cb->p,
@@ -168,9 +168,9 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev,
smsusb_onresponse,
surb
);
- surb->urb.transfer_flags |= URB_FREE_BUFFER;
+ surb->urb->transfer_flags |= URB_FREE_BUFFER;
- return usb_submit_urb(&surb->urb, GFP_ATOMIC);
+ return usb_submit_urb(surb->urb, GFP_ATOMIC);
}
static void smsusb_stop_streaming(struct smsusb_device_t *dev)
@@ -178,7 +178,7 @@ static void smsusb_stop_streaming(struct smsusb_device_t *dev)
int i;
for (i = 0; i < MAX_URBS; i++) {
- usb_kill_urb(&dev->surbs[i].urb);
+ usb_kill_urb(dev->surbs[i].urb);
if (dev->surbs[i].wq.func)
cancel_work_sync(&dev->surbs[i].wq);
@@ -338,6 +338,8 @@ static void smsusb_term_device(struct usb_interface *intf)
struct smsusb_device_t *dev = usb_get_intfdata(intf);
if (dev) {
+ int i;
+
dev->state = SMSUSB_DISCONNECTED;
smsusb_stop_streaming(dev);
@@ -346,6 +348,9 @@ static void smsusb_term_device(struct usb_interface *intf)
if (dev->coredev)
smscore_unregister_device(dev->coredev);
+ for (i = 0; i < MAX_URBS; i++)
+ usb_free_urb(dev->surbs[i].urb);
+
pr_debug("device 0x%p destroyed\n", dev);
kfree(dev);
}
@@ -463,7 +468,9 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id)
/* initialize urbs */
for (i = 0; i < MAX_URBS; i++) {
dev->surbs[i].dev = dev;
- usb_init_urb(&dev->surbs[i].urb);
+ dev->surbs[i].urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->surbs[i].urb)
+ goto err_unregister_device;
}
pr_debug("smsusb_start_streaming(...).\n");
@@ -486,6 +493,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id)
return rc;
err_unregister_device:
+ /* smsusb_term_device() frees any allocated urb. */
smsusb_term_device(intf);
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
media_device_unregister(mdev);
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 366f0e4a5dc0..9cbd957ecc90 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -99,7 +99,7 @@ void stk1160_buffer_done(struct stk1160 *dev)
static inline
void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len)
{
- int linesdone, lineoff, lencopy;
+ int linesdone, lineoff, lencopy, offset;
int bytesperline = dev->width * 2;
struct stk1160_buffer *buf = dev->isoc_ctl.buf;
u8 *dst = buf->mem;
@@ -130,17 +130,19 @@ void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len)
dst += linesdone * bytesperline * 2 + lineoff;
/* Copy the remaining of current line */
- if (remain < (bytesperline - lineoff))
- lencopy = remain;
- else
- lencopy = bytesperline - lineoff;
+ lencopy = min(remain, bytesperline - lineoff);
/*
* Check if we have enough space left in the buffer.
* In that case, we force loop exit after copy.
*/
- if (lencopy > buf->bytesused - buf->length) {
- lencopy = buf->bytesused - buf->length;
+ offset = dst - (u8 *)buf->mem;
+ if (offset > buf->length) {
+ dev_warn_ratelimited(dev->dev, "out of bounds offset\n");
+ return;
+ }
+ if (lencopy > buf->length - offset) {
+ lencopy = buf->length - offset;
remain = lencopy;
}
@@ -173,17 +175,19 @@ void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len)
src += lencopy;
/* Copy one line at a time */
- if (remain < bytesperline)
- lencopy = remain;
- else
- lencopy = bytesperline;
+ lencopy = min(remain, bytesperline);
/*
* Check if we have enough space left in the buffer.
* In that case, we force loop exit after copy.
*/
- if (lencopy > buf->bytesused - buf->length) {
- lencopy = buf->bytesused - buf->length;
+ offset = dst - (u8 *)buf->mem;
+ if (offset > buf->length) {
+ dev_warn_ratelimited(dev->dev, "offset out of bounds\n");
+ return;
+ }
+ if (lencopy > buf->length - offset) {
+ lencopy = buf->length - offset;
remain = lencopy;
}
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index e59a463c2761..4b685f883e4d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -836,7 +836,7 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
while (1) {
u8 byte = *data & mask;
value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
- bits -= 8 - (offset > 0 ? offset : 0);
+ bits -= 8 - max(offset, 0);
if (bits <= 0)
break;
@@ -1850,16 +1850,18 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
list_for_each_entry(entity, &chain->entities, chain) {
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback,
&err_ctrl);
- if (ret < 0)
+ if (ret < 0) {
+ if (ctrls)
+ ctrls->error_idx =
+ uvc_ctrl_find_ctrl_idx(entity, ctrls,
+ err_ctrl);
goto done;
+ }
}
if (!rollback)
uvc_ctrl_send_events(handle, ctrls->controls, ctrls->count);
done:
- if (ret < 0 && ctrls)
- ctrls->error_idx = uvc_ctrl_find_ctrl_idx(entity, ctrls,
- err_ctrl);
mutex_unlock(&chain->ctrl_mutex);
return ret;
}
@@ -2165,7 +2167,7 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry)
{
- struct uvc_entity *entity;
+ struct uvc_entity *entity, *iter;
struct uvc_control *ctrl;
unsigned int i;
bool found;
@@ -2175,16 +2177,16 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
int ret;
/* Find the extension unit. */
- found = false;
- list_for_each_entry(entity, &chain->entities, chain) {
- if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
- entity->id == xqry->unit) {
- found = true;
+ entity = NULL;
+ list_for_each_entry(iter, &chain->entities, chain) {
+ if (UVC_ENTITY_TYPE(iter) == UVC_VC_EXTENSION_UNIT &&
+ iter->id == xqry->unit) {
+ entity = iter;
break;
}
}
- if (!found) {
+ if (!entity) {
uvc_dbg(chain->dev, CONTROL, "Extension unit %u not found\n",
xqry->unit);
return -ENOENT;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index bbd90123a4e7..8fe24c98087e 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/usb/quirks.h>
#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
@@ -2232,8 +2233,14 @@ static int uvc_probe(struct usb_interface *intf,
goto error;
}
+ if (dev->quirks & UVC_QUIRK_NO_RESET_RESUME)
+ udev->quirks &= ~USB_QUIRK_RESET_RESUME;
+
+ if (!(dev->quirks & UVC_QUIRK_DISABLE_AUTOSUSPEND))
+ usb_enable_autosuspend(udev);
+
uvc_dbg(dev, PROBE, "UVC device initialized\n");
- usb_enable_autosuspend(udev);
+
return 0;
error:
@@ -2574,6 +2581,33 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) },
+ /* Logitech Rally Bar Huddle */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x087c,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
+ /* Logitech Rally Bar */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x089b,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
+ /* Logitech Rally Bar Mini */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08d3,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
/* Chicony CNF7129 (Asus EEE 100HE) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -3012,6 +3046,15 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
+ /* Insta360 Link */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x2e1a,
+ .idProduct = 0x4c01,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) },
/* Lenovo Integrated Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -3030,6 +3073,15 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
+ /* Shine-Optics Integrated Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x3277,
+ .idProduct = 0x009e,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
+ .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Acer EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 6fb0a78b1b00..3653b2c8a86c 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -73,6 +73,8 @@
#define UVC_QUIRK_FORCE_Y8 0x00000800
#define UVC_QUIRK_FORCE_BPP 0x00001000
#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000
+#define UVC_QUIRK_NO_RESET_RESUME 0x00004000
+#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 3ec323bd528b..222f01665f7c 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -316,9 +316,8 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier);
static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n,
struct v4l2_subdev *sd)
{
- struct media_link *link = NULL;
-
#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
+ struct media_link *link;
if (sd->entity.function != MEDIA_ENT_F_LENS &&
sd->entity.function != MEDIA_ENT_F_FLASH)
@@ -326,9 +325,10 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n,
link = media_create_ancillary_link(&n->sd->entity, &sd->entity);
-#endif
-
return IS_ERR(link) ? PTR_ERR(link) : 0;
+#else
+ return 0;
+#endif
}
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
@@ -341,7 +341,7 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
int ret;
if (list_empty(&sd->asc_list)) {
- ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ ret = __v4l2_device_register_subdev(v4l2_dev, sd, sd->owner);
if (ret < 0)
return ret;
registered = true;
@@ -563,6 +563,7 @@ void v4l2_async_nf_init(struct v4l2_async_notifier *notifier,
{
INIT_LIST_HEAD(&notifier->waiting_list);
INIT_LIST_HEAD(&notifier->done_list);
+ INIT_LIST_HEAD(&notifier->notifier_entry);
notifier->v4l2_dev = v4l2_dev;
}
EXPORT_SYMBOL(v4l2_async_nf_init);
@@ -572,6 +573,7 @@ void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier,
{
INIT_LIST_HEAD(&notifier->waiting_list);
INIT_LIST_HEAD(&notifier->done_list);
+ INIT_LIST_HEAD(&notifier->notifier_entry);
notifier->sd = sd;
}
EXPORT_SYMBOL_GPL(v4l2_async_subdev_nf_init);
@@ -618,16 +620,10 @@ err_unlock:
int v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
{
- int ret;
-
if (WARN_ON(!notifier->v4l2_dev == !notifier->sd))
return -EINVAL;
- ret = __v4l2_async_nf_register(notifier);
- if (ret)
- notifier->v4l2_dev = NULL;
-
- return ret;
+ return __v4l2_async_nf_register(notifier);
}
EXPORT_SYMBOL(v4l2_async_nf_register);
@@ -639,7 +635,7 @@ __v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
v4l2_async_nf_unbind_all_subdevs(notifier);
- list_del(&notifier->notifier_entry);
+ list_del_init(&notifier->notifier_entry);
}
void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
@@ -787,7 +783,7 @@ v4l2_async_connection_unique(struct v4l2_subdev *sd)
}
EXPORT_SYMBOL_GPL(v4l2_async_connection_unique);
-int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
{
struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier;
@@ -811,6 +807,8 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
return -EINVAL;
}
+ sd->owner = module;
+
mutex_lock(&list_lock);
list_for_each_entry(notifier, &notifier_list, notifier_entry) {
@@ -853,9 +851,11 @@ err_unbind:
mutex_unlock(&list_lock);
+ sd->owner = NULL;
+
return ret;
}
-EXPORT_SYMBOL(v4l2_async_register_subdev);
+EXPORT_SYMBOL(__v4l2_async_register_subdev);
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
{
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index d34d210908d9..4165c815faef 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -263,7 +263,9 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_Y210, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
.block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index d9a422017bd9..e5a364efd5e6 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -1052,35 +1052,40 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr
if (id >= node2id(hdl->ctrl_refs.prev)) {
ref = NULL; /* Yes, so there is no next control */
} else if (ref) {
+ struct v4l2_ctrl_ref *pos = ref;
+
/*
* We found a control with the given ID, so just get
* the next valid one in the list.
*/
- list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) {
- is_compound = ref->ctrl->is_array ||
- ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
- if (id < ref->ctrl->id &&
- (is_compound & mask) == match)
+ ref = NULL;
+ list_for_each_entry_continue(pos, &hdl->ctrl_refs, node) {
+ is_compound = pos->ctrl->is_array ||
+ pos->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+ if (id < pos->ctrl->id &&
+ (is_compound & mask) == match) {
+ ref = pos;
break;
+ }
}
- if (&ref->node == &hdl->ctrl_refs)
- ref = NULL;
} else {
+ struct v4l2_ctrl_ref *pos;
+
/*
* No control with the given ID exists, so start
* searching for the next largest ID. We know there
* is one, otherwise the first 'if' above would have
* been true.
*/
- list_for_each_entry(ref, &hdl->ctrl_refs, node) {
- is_compound = ref->ctrl->is_array ||
- ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
- if (id < ref->ctrl->id &&
- (is_compound & mask) == match)
+ list_for_each_entry(pos, &hdl->ctrl_refs, node) {
+ is_compound = pos->ctrl->is_array ||
+ pos->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+ if (id < pos->ctrl->id &&
+ (is_compound & mask) == match) {
+ ref = pos;
break;
+ }
}
- if (&ref->node == &hdl->ctrl_refs)
- ref = NULL;
}
}
mutex_unlock(hdl->lock);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index c4d995f32191..eeab6a5eb7ba 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -295,6 +295,9 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_U32:
pr_cont("%u", (unsigned)*ptr.p_u32);
break;
+ case V4L2_CTRL_TYPE_AREA:
+ pr_cont("%ux%u", ptr.p_area->width, ptr.p_area->height);
+ break;
case V4L2_CTRL_TYPE_H264_SPS:
pr_cont("H264_SPS");
break;
@@ -2504,8 +2507,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
/* Log the control name and value */
-static void log_ctrl(const struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl *ctrl,
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
const char *prefix, const char *colon)
{
if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
@@ -2515,11 +2517,7 @@ static void log_ctrl(const struct v4l2_ctrl_handler *hdl,
pr_info("%s%s%s: ", prefix, colon, ctrl->name);
- if (ctrl->handler != hdl)
- v4l2_ctrl_lock(ctrl);
ctrl->type_ops->log(ctrl);
- if (ctrl->handler != hdl)
- v4l2_ctrl_unlock(ctrl);
if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
V4L2_CTRL_FLAG_GRABBED |
@@ -2538,7 +2536,7 @@ static void log_ctrl(const struct v4l2_ctrl_handler *hdl,
void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
const char *prefix)
{
- struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
const char *colon = "";
int len;
@@ -2550,12 +2548,9 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
if (len && prefix[len - 1] != ' ')
colon = ": ";
mutex_lock(hdl->lock);
- list_for_each_entry(ref, &hdl->ctrl_refs, node) {
- if (ref->from_other_dev ||
- (ref->ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
- continue;
- log_ctrl(hdl, ref->ctrl, prefix, colon);
- }
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
mutex_unlock(hdl->lock);
}
EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
@@ -2564,6 +2559,9 @@ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ctrl_ops,
const struct v4l2_fwnode_device_properties *p)
{
+ if (hdl->error)
+ return hdl->error;
+
if (p->orientation != V4L2_FWNODE_PROPERTY_UNSET) {
u32 orientation_ctrl;
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index d13954bd31fd..be2ba7ca5de2 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -722,6 +722,9 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ /* VIDIOC_CREATE_BUFS support is mandatory to enable VIDIOC_REMOVE_BUFS */
+ if (ops->vidioc_create_bufs)
+ SET_VALID_IOCTL(ops, VIDIOC_REMOVE_BUFS, vidioc_remove_bufs);
}
if (is_vid || is_vbi || is_meta) {
@@ -1036,8 +1039,10 @@ int __video_register_device(struct video_device *vdev,
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ mutex_lock(&videodev_lock);
ret = device_register(&vdev->dev);
if (ret < 0) {
+ mutex_unlock(&videodev_lock);
pr_err("%s: device_register failed\n", __func__);
goto cleanup;
}
@@ -1057,6 +1062,7 @@ int __video_register_device(struct video_device *vdev,
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
return 0;
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index d2e58ae91f9b..5e537454f5cd 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -108,8 +108,8 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
-int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
- struct v4l2_subdev *sd)
+int __v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd, struct module *module)
{
int err;
@@ -125,9 +125,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
* try_module_get() such sub-device owners.
*/
sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
- sd->owner == v4l2_dev->dev->driver->owner;
+ module == v4l2_dev->dev->driver->owner;
- if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
+ if (!sd->owner_v4l2_dev && !try_module_get(module))
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
@@ -152,6 +152,8 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
goto error_unregister;
}
+ sd->owner = module;
+
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
@@ -168,7 +170,7 @@ error_module:
sd->v4l2_dev = NULL;
return err;
}
-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev);
static void v4l2_subdev_release(struct v4l2_subdev *sd)
{
diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c
index b4acca75644b..586c46544255 100644
--- a/drivers/media/v4l2-core/v4l2-i2c.c
+++ b/drivers/media/v4l2-core/v4l2-i2c.c
@@ -100,7 +100,7 @@ struct v4l2_subdev
* Register with the v4l2_device which increases the module's
* use count as well.
*/
- if (v4l2_device_register_subdev(v4l2_dev, sd))
+ if (__v4l2_device_register_subdev(v4l2_dev, sd, sd->owner))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->dev.driver->owner);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 6e7b8b682d13..4c76d17b4629 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only)
case V4L2_BUF_TYPE_META_OUTPUT:
meta = &p->fmt.meta;
pixelformat = meta->dataformat;
- pr_cont(", dataformat=%p4cc, buffersize=%u\n",
- &pixelformat, meta->buffersize);
+ pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n",
+ &pixelformat, meta->buffersize, meta->width,
+ meta->height, meta->bytesperline);
break;
}
}
@@ -489,6 +490,14 @@ static void v4l_print_create_buffers(const void *arg, bool write_only)
v4l_print_format(&p->format, write_only);
}
+static void v4l_print_remove_buffers(const void *arg, bool write_only)
+{
+ const struct v4l2_remove_buffers *p = arg;
+
+ pr_cont("type=%s, index=%u, count=%u\n",
+ prt_names(p->type, v4l2_type_names), p->index, p->count);
+}
+
static void v4l_print_streamparm(const void *arg, bool write_only)
{
const struct v4l2_streamparm *p = arg;
@@ -1312,6 +1321,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_Y10BPACK: descr = "10-bit Greyscale (Packed)"; break;
case V4L2_PIX_FMT_Y10P: descr = "10-bit Greyscale (MIPI Packed)"; break;
case V4L2_PIX_FMT_IPU3_Y10: descr = "10-bit greyscale (IPU3 Packed)"; break;
+ case V4L2_PIX_FMT_Y12P: descr = "12-bit Greyscale (MIPI Packed)"; break;
+ case V4L2_PIX_FMT_Y14P: descr = "14-bit Greyscale (MIPI Packed)"; break;
case V4L2_PIX_FMT_Y8I: descr = "Interleaved 8-bit Greyscale"; break;
case V4L2_PIX_FMT_Y12I: descr = "Interleaved 12-bit Greyscale"; break;
case V4L2_PIX_FMT_Z16: descr = "16-bit Depth"; break;
@@ -1452,6 +1463,13 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_Y210: descr = "10-bit YUYV Packed"; break;
case V4L2_PIX_FMT_Y212: descr = "12-bit YUYV Packed"; break;
case V4L2_PIX_FMT_Y216: descr = "16-bit YUYV Packed"; break;
+ case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_14: descr = "8-bit Generic Meta, 14b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_16: descr = "8-bit Generic Meta, 16b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_20: descr = "8-bit Generic Meta, 20b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_24: descr = "8-bit Generic Meta, 24b CSI-2"; break;
default:
/* Compressed formats */
@@ -1522,6 +1540,22 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
}
}
+ if (fmt->type == V4L2_BUF_TYPE_META_CAPTURE) {
+ switch (fmt->pixelformat) {
+ case V4L2_META_FMT_GENERIC_8:
+ case V4L2_META_FMT_GENERIC_CSI2_10:
+ case V4L2_META_FMT_GENERIC_CSI2_12:
+ case V4L2_META_FMT_GENERIC_CSI2_14:
+ case V4L2_META_FMT_GENERIC_CSI2_16:
+ case V4L2_META_FMT_GENERIC_CSI2_20:
+ case V4L2_META_FMT_GENERIC_CSI2_24:
+ fmt->flags |= V4L2_FMT_FLAG_META_LINE_BASED;
+ break;
+ default:
+ fmt->flags &= ~V4L2_FMT_FLAG_META_LINE_BASED;
+ }
+ }
+
if (descr)
WARN_ON(strscpy(fmt->description, descr, sz) < 0);
fmt->flags |= flags;
@@ -2092,6 +2126,7 @@ static int v4l_overlay(const struct v4l2_ioctl_ops *ops,
static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_requestbuffers *p = arg;
int ret = check_fmt(file, p->type);
@@ -2100,6 +2135,10 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
memset_after(p, 0, flags);
+ p->capabilities = 0;
+ if (is_valid_ioctl(vfd, VIDIOC_REMOVE_BUFS))
+ p->capabilities = V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS;
+
return ops->vidioc_reqbufs(file, fh, p);
}
@@ -2133,6 +2172,7 @@ static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_create_buffers *create = arg;
int ret = check_fmt(file, create->format.type);
@@ -2143,6 +2183,10 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
v4l_sanitize_format(&create->format);
+ create->capabilities = 0;
+ if (is_valid_ioctl(vfd, VIDIOC_REMOVE_BUFS))
+ create->capabilities = V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS;
+
ret = ops->vidioc_create_bufs(file, fh, create);
if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
@@ -2161,6 +2205,17 @@ static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
}
+static int v4l_remove_bufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_remove_buffers *remove = arg;
+
+ if (ops->vidioc_remove_bufs)
+ return ops->vidioc_remove_bufs(file, fh, remove);
+
+ return -ENOTTY;
+}
+
static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
@@ -2910,6 +2965,7 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
+ IOCTL_INFO(VIDIOC_REMOVE_BUFS, v4l_remove_bufs, v4l_print_remove_buffers, INFO_FL_PRIO | INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_remove_buffers, type)),
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -3147,13 +3203,13 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
case VIDIOC_SUBDEV_S_ROUTING: {
struct v4l2_subdev_routing *routing = parg;
- if (routing->num_routes > 256)
+ if (routing->len_routes > 256)
return -E2BIG;
*user_ptr = u64_to_user_ptr(routing->routes);
*kernel_ptr = (void **)&routing->routes;
*array_size = sizeof(struct v4l2_subdev_route)
- * routing->num_routes;
+ * routing->len_routes;
ret = 1;
break;
}
@@ -3407,11 +3463,14 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
* FIXME: subdev IOCTLS are partially handled here and partially in
* v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS
* defined here as part of the 'v4l2_ioctls' array. As
- * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even
- * in case of failure, but it is not defined here as part of the
+ * VIDIOC_SUBDEV_[GS]_ROUTING needs to return results to applications
+ * even in case of failure, but it is not defined here as part of the
* 'v4l2_ioctls' array, insert an ad-hoc check to address that.
*/
- if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING)
+ if (cmd == VIDIOC_SUBDEV_G_ROUTING || cmd == VIDIOC_SUBDEV_S_ROUTING)
+ always_copy = true;
+
+ if (err < 0 && !always_copy)
goto out;
if (has_array_args) {
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 75517134a5e9..eb22d6172462 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -1386,6 +1386,21 @@ int v4l2_m2m_ioctl_create_bufs(struct file *file, void *priv,
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_create_bufs);
+int v4l2_m2m_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *remove)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct vb2_queue *q = v4l2_m2m_get_vq(fh->m2m_ctx, remove->type);
+
+ if (!q)
+ return -EINVAL;
+ if (q->type != remove->type)
+ return -EINVAL;
+
+ return vb2_core_remove_bufs(q, remove->index, remove->count);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_remove_bufs);
+
int v4l2_m2m_ioctl_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
diff --git a/drivers/media/v4l2-core/v4l2-spi.c b/drivers/media/v4l2-core/v4l2-spi.c
index a7092c3930d6..1baf8e63f19e 100644
--- a/drivers/media/v4l2-core/v4l2-spi.c
+++ b/drivers/media/v4l2-core/v4l2-spi.c
@@ -59,7 +59,7 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
* Register with the v4l2_device which increases the module's
* use count as well.
*/
- if (v4l2_device_register_subdev(v4l2_dev, sd))
+ if (__v4l2_device_register_subdev(v4l2_dev, sd, sd->owner))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 4c6198c48dd6..8470d6eda9a3 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -369,6 +369,36 @@ 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);
}
+static int call_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->s_dv_timings(sd, pad, timings);
+}
+
+static int call_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->g_dv_timings(sd, pad, timings);
+}
+
+static int call_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->query_dv_timings(sd, pad, timings);
+}
+
static int call_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
@@ -412,15 +442,6 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
if (WARN_ON(!!sd->enabled_streams == !!enable))
return 0;
-#if IS_REACHABLE(CONFIG_LEDS_CLASS)
- if (!IS_ERR_OR_NULL(sd->privacy_led)) {
- if (enable)
- led_set_brightness(sd->privacy_led,
- sd->privacy_led->max_brightness);
- else
- led_set_brightness(sd->privacy_led, 0);
- }
-#endif
ret = sd->ops->video->s_stream(sd, enable);
if (!enable && ret < 0) {
@@ -428,9 +449,20 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
ret = 0;
}
- if (!ret)
+ if (!ret) {
sd->enabled_streams = enable ? BIT(0) : 0;
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ if (enable)
+ led_set_brightness(sd->privacy_led,
+ sd->privacy_led->max_brightness);
+ else
+ led_set_brightness(sd->privacy_led, 0);
+ }
+#endif
+ }
+
return ret;
}
@@ -487,6 +519,9 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = {
.set_frame_interval = call_set_frame_interval,
.get_edid = call_get_edid,
.set_edid = call_set_edid,
+ .s_dv_timings = call_s_dv_timings,
+ .g_dv_timings = call_g_dv_timings,
+ .query_dv_timings = call_query_dv_timings,
.dv_timings_cap = call_dv_timings_cap,
.enum_dv_timings = call_enum_dv_timings,
.get_frame_desc = call_get_frame_desc,
@@ -732,6 +767,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
+ sel.stream = crop->stream;
sel.target = V4L2_SEL_TGT_CROP;
rval = v4l2_subdev_call(
@@ -756,6 +792,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
+ sel.stream = crop->stream;
sel.target = V4L2_SEL_TGT_CROP;
sel.r = crop->rect;
@@ -873,16 +910,16 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
}
case VIDIOC_SUBDEV_QUERY_DV_TIMINGS:
- return v4l2_subdev_call(sd, video, query_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, query_dv_timings, 0, arg);
case VIDIOC_SUBDEV_G_DV_TIMINGS:
- return v4l2_subdev_call(sd, video, g_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, g_dv_timings, 0, arg);
case VIDIOC_SUBDEV_S_DV_TIMINGS:
if (ro_subdev)
return -EPERM;
- return v4l2_subdev_call(sd, video, s_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, s_dv_timings, 0, arg);
case VIDIOC_SUBDEV_G_STD:
return v4l2_subdev_call(sd, video, g_std, arg);
@@ -923,14 +960,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
krouting = &state->routing;
- if (routing->num_routes < krouting->num_routes) {
- routing->num_routes = krouting->num_routes;
- return -ENOSPC;
- }
-
memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
krouting->routes,
- krouting->num_routes * sizeof(*krouting->routes));
+ min(krouting->num_routes, routing->len_routes) *
+ sizeof(*krouting->routes));
routing->num_routes = krouting->num_routes;
return 0;
@@ -952,6 +985,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
+ if (routing->num_routes > routing->len_routes)
+ return -EINVAL;
+
memset(routing->reserved, 0, sizeof(routing->reserved));
for (i = 0; i < routing->num_routes; ++i) {
@@ -977,11 +1013,36 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
return -EINVAL;
}
+ /*
+ * If the driver doesn't support setting routing, just return
+ * the routing table.
+ */
+ if (!v4l2_subdev_has_op(sd, pad, set_routing)) {
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ state->routing.routes,
+ min(state->routing.num_routes, routing->len_routes) *
+ sizeof(*state->routing.routes));
+ routing->num_routes = state->routing.num_routes;
+
+ return 0;
+ }
+
krouting.num_routes = routing->num_routes;
+ krouting.len_routes = routing->len_routes;
krouting.routes = routes;
- return v4l2_subdev_call(sd, pad, set_routing, state,
+ rval = v4l2_subdev_call(sd, pad, set_routing, state,
routing->which, &krouting);
+ if (rval < 0)
+ return rval;
+
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ state->routing.routes,
+ min(state->routing.num_routes, routing->len_routes) *
+ sizeof(*state->routing.routes));
+ routing->num_routes = state->routing.num_routes;
+
+ return 0;
}
case VIDIOC_SUBDEV_G_CLIENT_CAP: {
@@ -1400,17 +1461,13 @@ int v4l2_subdev_link_validate(struct media_link *link)
states_locked = sink_state && source_state;
- if (states_locked) {
- v4l2_subdev_lock_state(sink_state);
- v4l2_subdev_lock_state(source_state);
- }
+ if (states_locked)
+ v4l2_subdev_lock_states(sink_state, source_state);
ret = v4l2_subdev_link_validate_locked(link, states_locked);
- if (states_locked) {
- v4l2_subdev_unlock_state(sink_state);
- v4l2_subdev_unlock_state(source_state);
- }
+ if (states_locked)
+ v4l2_subdev_unlock_states(sink_state, source_state);
return ret;
}
diff --git a/drivers/staging/media/atomisp/Makefile b/drivers/staging/media/atomisp/Makefile
index fcd3e51ae9ce..43116c74781d 100644
--- a/drivers/staging/media/atomisp/Makefile
+++ b/drivers/staging/media/atomisp/Makefile
@@ -21,7 +21,6 @@ atomisp-objs += \
pci/atomisp_fops.o \
pci/atomisp_ioctl.o \
pci/atomisp_subdev.o \
- pci/atomisp_tpg.o \
pci/atomisp_v4l2.o \
pci/sh_css_firmware.o \
pci/sh_css_host_data.o \
diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig
index f62d183b787f..8710c483015c 100644
--- a/drivers/staging/media/atomisp/i2c/Kconfig
+++ b/drivers/staging/media/atomisp/i2c/Kconfig
@@ -56,18 +56,3 @@ config VIDEO_ATOMISP_GC0310
help
This is a Video4Linux2 sensor-level driver for the Galaxycore
GC0310 0.3MP sensor.
-
-#
-# Kconfig for flash drivers
-#
-
-config VIDEO_ATOMISP_LM3554
- tristate "LM3554 flash light driver"
- depends on ACPI
- depends on VIDEO_DEV && I2C
- help
- This is a Video4Linux2 sub-dev driver for the LM3554
- flash light driver.
-
- To compile this driver as a module, choose M here: the
- module will be called lm3554
diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile
index e946cc91e5ff..3073cfa75ecf 100644
--- a/drivers/staging/media/atomisp/i2c/Makefile
+++ b/drivers/staging/media/atomisp/i2c/Makefile
@@ -9,8 +9,3 @@ obj-$(CONFIG_VIDEO_ATOMISP_OV2722) += atomisp-ov2722.o
obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o
obj-$(CONFIG_VIDEO_ATOMISP_MSRLIST_HELPER) += atomisp-libmsrlisthelper.o
-
-# Makefile for flash drivers
-#
-
-obj-$(CONFIG_VIDEO_ATOMISP_LM3554) += atomisp-lm3554.o
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index bec4c5615864..994b8bceb4f5 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -852,7 +852,7 @@ static int gc2235_probe(struct i2c_client *client)
if (ret)
gc2235_remove(client);
- return atomisp_register_i2c_module(&dev->sd, gcpdev, RAW_CAMERA);
+ return atomisp_register_i2c_module(&dev->sd, gcpdev);
out_free:
v4l2_device_unregister_subdev(&dev->sd);
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c b/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c
deleted file mode 100644
index cf5d9317b11a..000000000000
--- a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c
+++ /dev/null
@@ -1,955 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * LED flash driver for LM3554
- *
- * Copyright (c) 2010-2012 Intel Corporation. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/slab.h>
-
-#include "../include/media/lm3554.h"
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <linux/acpi.h>
-#include "../include/linux/atomisp_gmin_platform.h"
-#include "../include/linux/atomisp.h"
-
-/* Registers */
-
-#define LM3554_TORCH_BRIGHTNESS_REG 0xA0
-#define LM3554_TORCH_MODE_SHIFT 0
-#define LM3554_TORCH_CURRENT_SHIFT 3
-#define LM3554_INDICATOR_CURRENT_SHIFT 6
-
-#define LM3554_FLASH_BRIGHTNESS_REG 0xB0
-#define LM3554_FLASH_MODE_SHIFT 0
-#define LM3554_FLASH_CURRENT_SHIFT 3
-#define LM3554_STROBE_SENSITIVITY_SHIFT 7
-
-#define LM3554_FLASH_DURATION_REG 0xC0
-#define LM3554_FLASH_TIMEOUT_SHIFT 0
-#define LM3554_CURRENT_LIMIT_SHIFT 5
-
-#define LM3554_FLAGS_REG 0xD0
-#define LM3554_FLAG_TIMEOUT BIT(0)
-#define LM3554_FLAG_THERMAL_SHUTDOWN BIT(1)
-#define LM3554_FLAG_LED_FAULT BIT(2)
-#define LM3554_FLAG_TX1_INTERRUPT BIT(3)
-#define LM3554_FLAG_TX2_INTERRUPT BIT(4)
-#define LM3554_FLAG_LED_THERMAL_FAULT BIT(5)
-#define LM3554_FLAG_UNUSED BIT(6)
-#define LM3554_FLAG_INPUT_VOLTAGE_LOW BIT(7)
-
-#define LM3554_CONFIG_REG_1 0xE0
-#define LM3554_ENVM_TX2_SHIFT 5
-#define LM3554_TX2_POLARITY_SHIFT 6
-
-struct lm3554 {
- struct v4l2_subdev sd;
-
- struct mutex power_lock;
- struct v4l2_ctrl_handler ctrl_handler;
- int power_count;
-
- unsigned int mode;
- int timeout;
- u8 torch_current;
- u8 indicator_current;
- u8 flash_current;
-
- struct timer_list flash_off_delay;
- struct lm3554_platform_data *pdata;
-};
-
-#define to_lm3554(p_sd) container_of(p_sd, struct lm3554, sd)
-
-/* Return negative errno else zero on success */
-static int lm3554_write(struct lm3554 *flash, u8 addr, u8 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, addr, val);
-
- dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
- ret < 0 ? "fail" : "ok");
-
- return ret;
-}
-
-/* Return negative errno else a data byte received from the device. */
-static int lm3554_read(struct lm3554 *flash, u8 addr)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, addr);
-
- dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, ret,
- ret < 0 ? "fail" : "ok");
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware configuration
- */
-
-static int lm3554_set_mode(struct lm3554 *flash, unsigned int mode)
-{
- u8 val;
- int ret;
-
- val = (mode << LM3554_FLASH_MODE_SHIFT) |
- (flash->flash_current << LM3554_FLASH_CURRENT_SHIFT);
-
- ret = lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, val);
- if (ret == 0)
- flash->mode = mode;
- return ret;
-}
-
-static int lm3554_set_torch(struct lm3554 *flash)
-{
- u8 val;
-
- val = (flash->mode << LM3554_TORCH_MODE_SHIFT) |
- (flash->torch_current << LM3554_TORCH_CURRENT_SHIFT) |
- (flash->indicator_current << LM3554_INDICATOR_CURRENT_SHIFT);
-
- return lm3554_write(flash, LM3554_TORCH_BRIGHTNESS_REG, val);
-}
-
-static int lm3554_set_flash(struct lm3554 *flash)
-{
- u8 val;
-
- val = (flash->mode << LM3554_FLASH_MODE_SHIFT) |
- (flash->flash_current << LM3554_FLASH_CURRENT_SHIFT);
-
- return lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, val);
-}
-
-static int lm3554_set_duration(struct lm3554 *flash)
-{
- u8 val;
-
- val = (flash->timeout << LM3554_FLASH_TIMEOUT_SHIFT) |
- (flash->pdata->current_limit << LM3554_CURRENT_LIMIT_SHIFT);
-
- return lm3554_write(flash, LM3554_FLASH_DURATION_REG, val);
-}
-
-static int lm3554_set_config1(struct lm3554 *flash)
-{
- u8 val;
-
- val = (flash->pdata->envm_tx2 << LM3554_ENVM_TX2_SHIFT) |
- (flash->pdata->tx2_polarity << LM3554_TX2_POLARITY_SHIFT);
- return lm3554_write(flash, LM3554_CONFIG_REG_1, val);
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware trigger
- */
-static void lm3554_flash_off_delay(struct timer_list *t)
-{
- struct lm3554 *flash = from_timer(flash, t, flash_off_delay);
- struct lm3554_platform_data *pdata = flash->pdata;
-
- gpiod_set_value(pdata->gpio_strobe, 0);
-}
-
-static int lm3554_hw_strobe(struct i2c_client *client, bool strobe)
-{
- int ret, timer_pending;
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(sd);
- struct lm3554_platform_data *pdata = flash->pdata;
-
- /*
- * An abnormal high flash current is observed when strobe off the
- * flash. Workaround here is firstly set flash current to lower level,
- * wait a short moment, and then strobe off the flash.
- */
-
- timer_pending = del_timer_sync(&flash->flash_off_delay);
-
- /* Flash off */
- if (!strobe) {
- /* set current to 70mA and wait a while */
- ret = lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, 0);
- if (ret < 0)
- goto err;
- mod_timer(&flash->flash_off_delay,
- jiffies + msecs_to_jiffies(LM3554_TIMER_DELAY));
- return 0;
- }
-
- /* Flash on */
-
- /*
- * If timer is killed before run, flash is not strobe off,
- * so must strobe off here
- */
- if (timer_pending)
- gpiod_set_value(pdata->gpio_strobe, 0);
-
- /* Restore flash current settings */
- ret = lm3554_set_flash(flash);
- if (ret < 0)
- goto err;
-
- /* Strobe on Flash */
- gpiod_set_value(pdata->gpio_strobe, 1);
-
- return 0;
-err:
- dev_err(&client->dev, "failed to %s flash strobe (%d)\n",
- strobe ? "on" : "off", ret);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 controls
- */
-
-static int lm3554_read_status(struct lm3554 *flash)
-{
- int ret;
- struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
-
- /* NOTE: reading register clear fault status */
- ret = lm3554_read(flash, LM3554_FLAGS_REG);
- if (ret < 0)
- return ret;
-
- /*
- * Accordingly to datasheet we read back '1' in bit 6.
- * Clear it first.
- */
- ret &= ~LM3554_FLAG_UNUSED;
-
- /*
- * Do not take TX1/TX2 signal as an error
- * because MSIC will not turn off flash, but turn to
- * torch mode according to gsm modem signal by hardware.
- */
- ret &= ~(LM3554_FLAG_TX1_INTERRUPT | LM3554_FLAG_TX2_INTERRUPT);
-
- if (ret > 0)
- dev_dbg(&client->dev, "LM3554 flag status: %02x\n", ret);
-
- return ret;
-}
-
-static int lm3554_s_flash_timeout(struct v4l2_subdev *sd, u32 val)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- val = clamp(val, LM3554_MIN_TIMEOUT, LM3554_MAX_TIMEOUT);
- val = val / LM3554_TIMEOUT_STEPSIZE - 1;
-
- flash->timeout = val;
-
- return lm3554_set_duration(flash);
-}
-
-static int lm3554_g_flash_timeout(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- *val = (u32)(flash->timeout + 1) * LM3554_TIMEOUT_STEPSIZE;
-
- return 0;
-}
-
-static int lm3554_s_flash_intensity(struct v4l2_subdev *sd, u32 intensity)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- intensity = LM3554_CLAMP_PERCENTAGE(intensity);
- intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_FLASH_STEP);
-
- flash->flash_current = intensity;
-
- return lm3554_set_flash(flash);
-}
-
-static int lm3554_g_flash_intensity(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- *val = LM3554_VALUE_TO_PERCENT((u32)flash->flash_current,
- LM3554_FLASH_STEP);
-
- return 0;
-}
-
-static int lm3554_s_torch_intensity(struct v4l2_subdev *sd, u32 intensity)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- intensity = LM3554_CLAMP_PERCENTAGE(intensity);
- intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_TORCH_STEP);
-
- flash->torch_current = intensity;
-
- return lm3554_set_torch(flash);
-}
-
-static int lm3554_g_torch_intensity(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- *val = LM3554_VALUE_TO_PERCENT((u32)flash->torch_current,
- LM3554_TORCH_STEP);
-
- return 0;
-}
-
-static int lm3554_s_indicator_intensity(struct v4l2_subdev *sd, u32 intensity)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- intensity = LM3554_CLAMP_PERCENTAGE(intensity);
- intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_INDICATOR_STEP);
-
- flash->indicator_current = intensity;
-
- return lm3554_set_torch(flash);
-}
-
-static int lm3554_g_indicator_intensity(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
-
- *val = LM3554_VALUE_TO_PERCENT((u32)flash->indicator_current,
- LM3554_INDICATOR_STEP);
-
- return 0;
-}
-
-static int lm3554_s_flash_strobe(struct v4l2_subdev *sd, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return lm3554_hw_strobe(client, val);
-}
-
-static int lm3554_s_flash_mode(struct v4l2_subdev *sd, u32 new_mode)
-{
- struct lm3554 *flash = to_lm3554(sd);
- unsigned int mode;
-
- switch (new_mode) {
- case ATOMISP_FLASH_MODE_OFF:
- mode = LM3554_MODE_SHUTDOWN;
- break;
- case ATOMISP_FLASH_MODE_FLASH:
- mode = LM3554_MODE_FLASH;
- break;
- case ATOMISP_FLASH_MODE_INDICATOR:
- mode = LM3554_MODE_INDICATOR;
- break;
- case ATOMISP_FLASH_MODE_TORCH:
- mode = LM3554_MODE_TORCH;
- break;
- default:
- return -EINVAL;
- }
-
- return lm3554_set_mode(flash, mode);
-}
-
-static int lm3554_g_flash_mode(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
- *val = flash->mode;
- return 0;
-}
-
-static int lm3554_g_flash_status(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
- int value;
-
- value = lm3554_read_status(flash);
- if (value < 0)
- return value;
-
- if (value & LM3554_FLAG_TIMEOUT)
- *val = ATOMISP_FLASH_STATUS_TIMEOUT;
- else if (value > 0)
- *val = ATOMISP_FLASH_STATUS_HW_ERROR;
- else
- *val = ATOMISP_FLASH_STATUS_OK;
-
- return 0;
-}
-
-static int lm3554_g_flash_status_register(struct v4l2_subdev *sd, s32 *val)
-{
- struct lm3554 *flash = to_lm3554(sd);
- int ret;
-
- ret = lm3554_read(flash, LM3554_FLAGS_REG);
-
- if (ret < 0)
- return ret;
-
- *val = ret;
- return 0;
-}
-
-static int lm3554_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct lm3554 *dev =
- container_of(ctrl->handler, struct lm3554, ctrl_handler);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_FLASH_TIMEOUT:
- ret = lm3554_s_flash_timeout(&dev->sd, ctrl->val);
- break;
- case V4L2_CID_FLASH_INTENSITY:
- ret = lm3554_s_flash_intensity(&dev->sd, ctrl->val);
- break;
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- ret = lm3554_s_torch_intensity(&dev->sd, ctrl->val);
- break;
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- ret = lm3554_s_indicator_intensity(&dev->sd, ctrl->val);
- break;
- case V4L2_CID_FLASH_STROBE:
- ret = lm3554_s_flash_strobe(&dev->sd, ctrl->val);
- break;
- case V4L2_CID_FLASH_MODE:
- ret = lm3554_s_flash_mode(&dev->sd, ctrl->val);
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int lm3554_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct lm3554 *dev =
- container_of(ctrl->handler, struct lm3554, ctrl_handler);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_FLASH_TIMEOUT:
- ret = lm3554_g_flash_timeout(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_INTENSITY:
- ret = lm3554_g_flash_intensity(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- ret = lm3554_g_torch_intensity(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- ret = lm3554_g_indicator_intensity(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_MODE:
- ret = lm3554_g_flash_mode(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_STATUS:
- ret = lm3554_g_flash_status(&dev->sd, &ctrl->val);
- break;
- case V4L2_CID_FLASH_STATUS_REGISTER:
- ret = lm3554_g_flash_status_register(&dev->sd, &ctrl->val);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static const struct v4l2_ctrl_ops ctrl_ops = {
- .s_ctrl = lm3554_s_ctrl,
- .g_volatile_ctrl = lm3554_g_volatile_ctrl
-};
-
-static const struct v4l2_ctrl_config lm3554_controls[] = {
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_TIMEOUT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Flash Timeout",
- .min = 0x0,
- .max = LM3554_MAX_TIMEOUT,
- .step = 0x01,
- .def = LM3554_DEFAULT_TIMEOUT,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_INTENSITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Flash Intensity",
- .min = LM3554_MIN_PERCENT,
- .max = LM3554_MAX_PERCENT,
- .step = 0x01,
- .def = LM3554_FLASH_DEFAULT_BRIGHTNESS,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_TORCH_INTENSITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Torch Intensity",
- .min = LM3554_MIN_PERCENT,
- .max = LM3554_MAX_PERCENT,
- .step = 0x01,
- .def = LM3554_TORCH_DEFAULT_BRIGHTNESS,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_INDICATOR_INTENSITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Indicator Intensity",
- .min = LM3554_MIN_PERCENT,
- .max = LM3554_MAX_PERCENT,
- .step = 0x01,
- .def = LM3554_INDICATOR_DEFAULT_BRIGHTNESS,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_STROBE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flash Strobe",
- .min = 0,
- .max = 1,
- .step = 1,
- .def = 0,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_MODE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Flash Mode",
- .min = 0,
- .max = 100,
- .step = 1,
- .def = ATOMISP_FLASH_MODE_OFF,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_STATUS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Flash Status",
- .min = ATOMISP_FLASH_STATUS_OK,
- .max = ATOMISP_FLASH_STATUS_TIMEOUT,
- .step = 1,
- .def = ATOMISP_FLASH_STATUS_OK,
- .flags = 0,
- },
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FLASH_STATUS_REGISTER,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Flash Status Register",
- .min = 0,
- .max = 255,
- .step = 1,
- .def = 0,
- .flags = 0,
- },
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-/* Put device into known state. */
-static int lm3554_setup(struct lm3554 *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
- int ret;
-
- /* clear the flags register */
- ret = lm3554_read(flash, LM3554_FLAGS_REG);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Fault info: %02x\n", ret);
-
- ret = lm3554_set_config1(flash);
- if (ret < 0)
- return ret;
-
- ret = lm3554_set_duration(flash);
- if (ret < 0)
- return ret;
-
- ret = lm3554_set_torch(flash);
- if (ret < 0)
- return ret;
-
- ret = lm3554_set_flash(flash);
- if (ret < 0)
- return ret;
-
- /* read status */
- ret = lm3554_read_status(flash);
- if (ret < 0)
- return ret;
-
- return ret ? -EIO : 0;
-}
-
-static int __lm3554_s_power(struct lm3554 *flash, int power)
-{
- struct lm3554_platform_data *pdata = flash->pdata;
- int ret;
-
- /*initialize flash driver*/
- gpiod_set_value(pdata->gpio_reset, power);
- usleep_range(100, 100 + 1);
-
- if (power) {
- /* Setup default values. This makes sure that the chip
- * is in a known state.
- */
- ret = lm3554_setup(flash);
- if (ret < 0) {
- __lm3554_s_power(flash, 0);
- return ret;
- }
- }
-
- return 0;
-}
-
-static int lm3554_s_power(struct v4l2_subdev *sd, int power)
-{
- struct lm3554 *flash = to_lm3554(sd);
- int ret = 0;
-
- mutex_lock(&flash->power_lock);
-
- if (flash->power_count == !power) {
- ret = __lm3554_s_power(flash, !!power);
- if (ret < 0)
- goto done;
- }
-
- flash->power_count += power ? 1 : -1;
- WARN_ON(flash->power_count < 0);
-
-done:
- mutex_unlock(&flash->power_lock);
- return ret;
-}
-
-static const struct v4l2_subdev_core_ops lm3554_core_ops = {
- .s_power = lm3554_s_power,
-};
-
-static const struct v4l2_subdev_ops lm3554_ops = {
- .core = &lm3554_core_ops,
-};
-
-static int lm3554_detect(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct i2c_adapter *adapter = client->adapter;
- struct lm3554 *flash = to_lm3554(sd);
- int ret;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev, "lm3554_detect i2c error\n");
- return -ENODEV;
- }
-
- /* Power up the flash driver and reset it */
- ret = lm3554_s_power(&flash->sd, 1);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to power on lm3554 LED flash\n");
- } else {
- dev_dbg(&client->dev, "Successfully detected lm3554 LED flash\n");
- lm3554_s_power(&flash->sd, 0);
- }
-
- return ret;
-}
-
-static int lm3554_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return lm3554_s_power(sd, 1);
-}
-
-static int lm3554_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return lm3554_s_power(sd, 0);
-}
-
-static const struct v4l2_subdev_internal_ops lm3554_internal_ops = {
- .registered = lm3554_detect,
- .open = lm3554_open,
- .close = lm3554_close,
-};
-
-/* -----------------------------------------------------------------------------
- * I2C driver
- */
-#ifdef CONFIG_PM
-
-static int lm3554_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(subdev);
- int rval;
-
- if (flash->power_count == 0)
- return 0;
-
- rval = __lm3554_s_power(flash, 0);
-
- dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
-
- return rval;
-}
-
-static int lm3554_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(subdev);
- int rval;
-
- if (flash->power_count == 0)
- return 0;
-
- rval = __lm3554_s_power(flash, 1);
-
- dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
-
- return rval;
-}
-
-#else
-
-#define lm3554_suspend NULL
-#define lm3554_resume NULL
-
-#endif /* CONFIG_PM */
-
-static int lm3554_gpio_init(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(sd);
- struct lm3554_platform_data *pdata = flash->pdata;
- int ret;
-
- if (!pdata->gpio_reset)
- return -EINVAL;
-
- ret = gpiod_direction_output(pdata->gpio_reset, 0);
- if (ret < 0)
- return ret;
-
- if (!pdata->gpio_strobe)
- return -EINVAL;
-
- ret = gpiod_direction_output(pdata->gpio_strobe, 0);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static void lm3554_gpio_uninit(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(sd);
- struct lm3554_platform_data *pdata = flash->pdata;
- int ret;
-
- ret = gpiod_direction_output(pdata->gpio_strobe, 0);
- if (ret < 0)
- dev_err(&client->dev,
- "gpio request/direction_output fail for gpio_strobe");
-
- ret = gpiod_direction_output(pdata->gpio_reset, 0);
- if (ret < 0)
- dev_err(&client->dev,
- "gpio request/direction_output fail for gpio_reset");
-}
-
-static void *lm3554_platform_data_func(struct i2c_client *client)
-{
- static struct lm3554_platform_data platform_data;
-
- platform_data.gpio_reset = gpiod_get_index(&client->dev,
- NULL, 2, GPIOD_OUT_LOW);
- if (IS_ERR(platform_data.gpio_reset))
- return ERR_CAST(platform_data.gpio_reset);
- platform_data.gpio_strobe = gpiod_get_index(&client->dev,
- NULL, 0, GPIOD_OUT_LOW);
- if (IS_ERR(platform_data.gpio_strobe))
- return ERR_CAST(platform_data.gpio_strobe);
- platform_data.gpio_torch = gpiod_get_index(&client->dev,
- NULL, 1, GPIOD_OUT_LOW);
- if (IS_ERR(platform_data.gpio_torch))
- return ERR_CAST(platform_data.gpio_torch);
-
- /* Set to TX2 mode, then ENVM/TX2 pin is a power amplifier sync input:
- * ENVM/TX pin asserted, flash forced into torch;
- * ENVM/TX pin desserted, flash set back;
- */
- platform_data.envm_tx2 = 1;
- platform_data.tx2_polarity = 0;
-
- /* set peak current limit to be 1000mA */
- platform_data.current_limit = 0;
-
- return &platform_data;
-}
-
-static int lm3554_probe(struct i2c_client *client)
-{
- int err = 0;
- struct lm3554 *flash;
- unsigned int i;
-
- flash = kzalloc(sizeof(*flash), GFP_KERNEL);
- if (!flash)
- return -ENOMEM;
-
- flash->pdata = lm3554_platform_data_func(client);
- if (IS_ERR(flash->pdata)) {
- err = PTR_ERR(flash->pdata);
- goto free_flash;
- }
-
- v4l2_i2c_subdev_init(&flash->sd, client, &lm3554_ops);
- flash->sd.internal_ops = &lm3554_internal_ops;
- flash->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- flash->mode = ATOMISP_FLASH_MODE_OFF;
- flash->timeout = LM3554_MAX_TIMEOUT / LM3554_TIMEOUT_STEPSIZE - 1;
- err =
- v4l2_ctrl_handler_init(&flash->ctrl_handler,
- ARRAY_SIZE(lm3554_controls));
- if (err) {
- dev_err(&client->dev, "error initialize a ctrl_handler.\n");
- goto unregister_subdev;
- }
-
- for (i = 0; i < ARRAY_SIZE(lm3554_controls); i++)
- v4l2_ctrl_new_custom(&flash->ctrl_handler, &lm3554_controls[i],
- NULL);
-
- if (flash->ctrl_handler.error) {
- dev_err(&client->dev, "ctrl_handler error.\n");
- err = flash->ctrl_handler.error;
- goto free_handler;
- }
-
- flash->sd.ctrl_handler = &flash->ctrl_handler;
- err = media_entity_pads_init(&flash->sd.entity, 0, NULL);
- if (err) {
- dev_err(&client->dev, "error initialize a media entity.\n");
- goto free_handler;
- }
-
- flash->sd.entity.function = MEDIA_ENT_F_FLASH;
-
- mutex_init(&flash->power_lock);
-
- timer_setup(&flash->flash_off_delay, lm3554_flash_off_delay, 0);
-
- err = lm3554_gpio_init(client);
- if (err) {
- dev_err(&client->dev, "gpio request/direction_output fail.\n");
- goto cleanup_media;
- }
-
- err = atomisp_register_i2c_module(&flash->sd, NULL, LED_FLASH);
- if (err) {
- dev_err(&client->dev, "fail to register atomisp i2c module.\n");
- goto uninit_gpio;
- }
-
- return 0;
-
-uninit_gpio:
- lm3554_gpio_uninit(client);
-cleanup_media:
- media_entity_cleanup(&flash->sd.entity);
-free_handler:
- v4l2_ctrl_handler_free(&flash->ctrl_handler);
-unregister_subdev:
- v4l2_device_unregister_subdev(&flash->sd);
-free_flash:
- kfree(flash);
-
- return err;
-}
-
-static void lm3554_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct lm3554 *flash = to_lm3554(sd);
-
- media_entity_cleanup(&flash->sd.entity);
- v4l2_ctrl_handler_free(&flash->ctrl_handler);
- v4l2_device_unregister_subdev(sd);
-
- atomisp_gmin_remove_subdev(sd);
-
- timer_shutdown_sync(&flash->flash_off_delay);
-
- lm3554_gpio_uninit(client);
-
- kfree(flash);
-}
-
-static const struct dev_pm_ops lm3554_pm_ops = {
- .suspend = lm3554_suspend,
- .resume = lm3554_resume,
-};
-
-static const struct acpi_device_id lm3554_acpi_match[] = {
- { "INTCF1C" },
- {},
-};
-MODULE_DEVICE_TABLE(acpi, lm3554_acpi_match);
-
-static struct i2c_driver lm3554_driver = {
- .driver = {
- .name = "lm3554",
- .pm = &lm3554_pm_ops,
- .acpi_match_table = lm3554_acpi_match,
- },
- .probe = lm3554_probe,
- .remove = lm3554_remove,
-};
-module_i2c_driver(lm3554_driver);
-
-MODULE_AUTHOR("Jing Tao <jing.tao@intel.com>");
-MODULE_DESCRIPTION("LED flash driver for LM3554");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
index 20f02d18a8de..23b1001c2a55 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
@@ -35,7 +35,7 @@
#include "mt9m114.h"
-#define to_mt9m114_sensor(sd) container_of(sd, struct mt9m114_device, sd)
+#define to_mt9m114_sensor(s) container_of(s, struct mt9m114_device, sd)
/*
* TODO: use debug parameter to actually define when debug messages should
@@ -1552,7 +1552,7 @@ static int mt9m114_probe(struct i2c_client *client)
return ret;
}
- ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA);
+ ret = atomisp_register_i2c_module(&dev->sd, pdata);
if (ret) {
v4l2_device_unregister_subdev(&dev->sd);
kfree(dev);
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
index 133e346ae51b..c31a81d64950 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
@@ -528,9 +528,6 @@ static int power_up(struct v4l2_subdev *sd)
return -ENODEV;
}
- if (dev->power_on == 1)
- return 0; /* Already on */
-
/* power control */
ret = power_ctrl(sd, 1);
if (ret)
@@ -555,7 +552,6 @@ static int power_up(struct v4l2_subdev *sd)
/* according to DS, 20ms is needed between PWDN and i2c access */
msleep(20);
- dev->power_on = 1;
return 0;
fail_clk:
@@ -579,9 +575,6 @@ static int power_down(struct v4l2_subdev *sd)
return -ENODEV;
}
- if (dev->power_on == 0)
- return 0; /* Already off */
-
ret = dev->platform_data->flisclk_ctrl(sd, 0);
if (ret)
dev_err(&client->dev, "flisclk failed\n");
@@ -599,7 +592,6 @@ static int power_down(struct v4l2_subdev *sd)
if (ret)
dev_err(&client->dev, "vprog failed.\n");
- dev->power_on = 0;
return ret;
}
@@ -677,9 +669,6 @@ static int ov2722_set_fmt(struct v4l2_subdev *sd,
mutex_lock(&dev->input_lock);
- /* s_power has not been called yet for std v4l2 clients (camorama) */
- power_up(sd);
-
dev->pixels_per_line = dev->res->pixels_per_line;
dev->lines_per_frame = dev->res->lines_per_frame;
@@ -978,7 +967,6 @@ static int ov2722_probe(struct i2c_client *client)
return -ENOMEM;
mutex_init(&dev->input_lock);
- dev->power_on = -1;
dev->res = &ov2722_res_preview[0];
v4l2_i2c_subdev_init(&dev->sd, client, &ov2722_ops);
@@ -1004,7 +992,7 @@ static int ov2722_probe(struct i2c_client *client)
if (ret)
ov2722_remove(client);
- return atomisp_register_i2c_module(&dev->sd, ovpdev, RAW_CAMERA);
+ return atomisp_register_i2c_module(&dev->sd, ovpdev);
out_ctrl_handler_free:
v4l2_ctrl_handler_free(&dev->ctrl_handler);
diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h
index 640d3ffcaa5c..5920a4d45d06 100644
--- a/drivers/staging/media/atomisp/i2c/ov2722.h
+++ b/drivers/staging/media/atomisp/i2c/ov2722.h
@@ -198,7 +198,6 @@ struct ov2722_device {
struct ov2722_resolution *res;
struct camera_sensor_platform_data *platform_data;
- int power_on;
u16 pixels_per_line;
u16 lines_per_frame;
u8 type;
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h
index bbbd904b696a..16c9da172c03 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp.h
@@ -614,41 +614,6 @@ enum atomisp_camera_port {
ATOMISP_CAMERA_NR_PORTS
};
-/* Flash modes. Default is off.
- * Setting a flash to TORCH or INDICATOR mode will automatically
- * turn it on. Setting it to FLASH mode will not turn on the flash
- * until the FLASH_STROBE command is sent. */
-enum atomisp_flash_mode {
- ATOMISP_FLASH_MODE_OFF,
- ATOMISP_FLASH_MODE_FLASH,
- ATOMISP_FLASH_MODE_TORCH,
- ATOMISP_FLASH_MODE_INDICATOR,
-};
-
-/* Flash statuses, used by atomisp driver to check before starting
- * flash and after having started flash. */
-enum atomisp_flash_status {
- ATOMISP_FLASH_STATUS_OK,
- ATOMISP_FLASH_STATUS_HW_ERROR,
- ATOMISP_FLASH_STATUS_INTERRUPTED,
- ATOMISP_FLASH_STATUS_TIMEOUT,
-};
-
-/* Frame status. This is used to detect corrupted frames and flash
- * exposed frames. Usually, the first 2 frames coming out of the sensor
- * are corrupted. When using flash, the frame before and the frame after
- * the flash exposed frame may be partially exposed by flash. The ISP
- * statistics for these frames should not be used by the 3A library.
- * The frame status value can be found in the "reserved" field in the
- * v4l2_buffer struct. */
-enum atomisp_frame_status {
- ATOMISP_FRAME_STATUS_OK,
- ATOMISP_FRAME_STATUS_CORRUPTED,
- ATOMISP_FRAME_STATUS_FLASH_EXPOSED,
- ATOMISP_FRAME_STATUS_FLASH_PARTIAL,
- ATOMISP_FRAME_STATUS_FLASH_FAILED,
-};
-
enum atomisp_ext_isp_id {
EXT_ISP_CID_ISO = 0,
EXT_ISP_CID_CAPTURE_HDR,
@@ -661,7 +626,6 @@ enum atomisp_ext_isp_id {
EXT_ISP_CID_AF_STATUS,
EXT_ISP_CID_GET_AF_MODE,
EXT_ISP_CID_CAPTURE_BURST,
- EXT_ISP_CID_FLASH_MODE,
EXT_ISP_CID_ZOOM,
EXT_ISP_CID_SHOT_MODE
};
@@ -694,12 +658,6 @@ enum atomisp_burst_capture_options {
EXT_ISP_BURST_CAPTURE_CTRL_STOP
};
-#define EXT_ISP_FLASH_MODE_OFF 0
-#define EXT_ISP_FLASH_MODE_ON 1
-#define EXT_ISP_FLASH_MODE_AUTO 2
-#define EXT_ISP_LED_TORCH_OFF 3
-#define EXT_ISP_LED_TORCH_ON 4
-
#define EXT_ISP_SHOT_MODE_AUTO 0
#define EXT_ISP_SHOT_MODE_BEAUTY_FACE 1
#define EXT_ISP_SHOT_MODE_BEST_PHOTO 2
@@ -837,9 +795,6 @@ enum atomisp_burst_capture_options {
#define ATOMISP_IOC_S_PARAMETERS \
_IOW('v', BASE_VIDIOC_PRIVATE + 32, struct atomisp_parameters)
-#define ATOMISP_IOC_EXT_ISP_CTRL \
- _IOWR('v', BASE_VIDIOC_PRIVATE + 35, struct atomisp_ext_isp_ctrl)
-
#define ATOMISP_IOC_EXP_ID_UNLOCK \
_IOW('v', BASE_VIDIOC_PRIVATE + 36, int)
@@ -897,34 +852,6 @@ enum atomisp_burst_capture_options {
* Exposure, Flash and privacy (indicator) light controls, to be upstreamed */
#define V4L2_CID_CAMERA_LASTP1 (V4L2_CID_CAMERA_CLASS_BASE + 1024)
-/* Flash related CIDs, see also:
- * http://linuxtv.org/downloads/v4l-dvb-apis/extended-controls.html\
- * #flash-controls */
-
-/* Request a number of flash-exposed frames. The frame status can be
- * found in the reserved field in the v4l2_buffer struct. */
-#define V4L2_CID_REQUEST_FLASH (V4L2_CID_CAMERA_LASTP1 + 3)
-/* Query flash driver status. See enum atomisp_flash_status above. */
-#define V4L2_CID_FLASH_STATUS (V4L2_CID_CAMERA_LASTP1 + 5)
-/* Set the flash mode (see enum atomisp_flash_mode) */
-#define V4L2_CID_FLASH_MODE (V4L2_CID_CAMERA_LASTP1 + 10)
-
-/* VCM slew control */
-#define V4L2_CID_VCM_SLEW (V4L2_CID_CAMERA_LASTP1 + 11)
-/* VCM step time */
-#define V4L2_CID_VCM_TIMING (V4L2_CID_CAMERA_LASTP1 + 12)
-
-/* Query Focus Status */
-#define V4L2_CID_FOCUS_STATUS (V4L2_CID_CAMERA_LASTP1 + 14)
-
-/* number of frames to skip at stream start */
-#define V4L2_CID_G_SKIP_FRAMES (V4L2_CID_CAMERA_LASTP1 + 17)
-
-/* Query sensor's 2A status */
-#define V4L2_CID_2A_STATUS (V4L2_CID_CAMERA_LASTP1 + 18)
-#define V4L2_2A_STATUS_AE_READY BIT(0)
-#define V4L2_2A_STATUS_AWB_READY BIT(1)
-
#define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20)
#define ATOMISP_RUN_MODE_VIDEO 1
#define ATOMISP_RUN_MODE_STILL_CAPTURE 2
@@ -944,9 +871,6 @@ enum atomisp_burst_capture_options {
#define ATOMISP_VFPP_DISABLE_SCALER 1
#define ATOMISP_VFPP_DISABLE_LOWLAT 2
-/* Query real flash status register value */
-#define V4L2_CID_FLASH_STATUS_REGISTER (V4L2_CID_CAMERA_LASTP1 + 26)
-
#define V4L2_CID_START_ZSL_CAPTURE (V4L2_CID_CAMERA_LASTP1 + 28)
/* Lock and unlock raw buffer */
#define V4L2_CID_ENABLE_RAW_BUFFER_LOCK (V4L2_CID_CAMERA_LASTP1 + 29)
@@ -955,11 +879,6 @@ enum atomisp_burst_capture_options {
/* Disable digital zoom */
#define V4L2_CID_DISABLE_DZ (V4L2_CID_CAMERA_LASTP1 + 32)
-#define V4L2_CID_TEST_PATTERN_COLOR_R (V4L2_CID_CAMERA_LASTP1 + 33)
-#define V4L2_CID_TEST_PATTERN_COLOR_GR (V4L2_CID_CAMERA_LASTP1 + 34)
-#define V4L2_CID_TEST_PATTERN_COLOR_GB (V4L2_CID_CAMERA_LASTP1 + 35)
-#define V4L2_CID_TEST_PATTERN_COLOR_B (V4L2_CID_CAMERA_LASTP1 + 36)
-
#define V4L2_CID_ATOMISP_SELECT_ISP_VERSION (V4L2_CID_CAMERA_LASTP1 + 38)
#define V4L2_BUF_FLAG_BUFFER_INVALID 0x0400
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
index 64bd54835c32..ecd82220f04a 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
@@ -19,8 +19,7 @@
#include "atomisp_platform.h"
int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
- struct camera_sensor_platform_data *plat_data,
- enum intel_v4l2_subdev_type type);
+ struct camera_sensor_platform_data *plat_data);
int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd);
int gmin_get_var_int(struct device *dev, bool is_gmin,
const char *var, int def);
@@ -29,7 +28,4 @@ gmin_camera_platform_data(
struct v4l2_subdev *subdev,
enum atomisp_input_format csi_format,
enum atomisp_bayer_order csi_bayer);
-
-int atomisp_gmin_register_vcm_control(struct camera_vcm_control *);
-
#endif
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
index 487ef5846c24..0e3f6fb78483 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
@@ -109,30 +109,12 @@ enum atomisp_input_format {
#define N_ATOMISP_INPUT_FORMAT (ATOMISP_INPUT_FORMAT_USER_DEF8 + 1)
-enum intel_v4l2_subdev_type {
- RAW_CAMERA = 1,
- CAMERA_MOTOR = 2,
- LED_FLASH = 3,
- TEST_PATTERN = 4,
-};
-
-struct intel_v4l2_subdev_id {
- char name[17];
- enum intel_v4l2_subdev_type type;
- enum atomisp_camera_port port;
-};
-
struct intel_v4l2_subdev_table {
- enum intel_v4l2_subdev_type type;
enum atomisp_camera_port port;
unsigned int lanes;
struct v4l2_subdev *subdev;
};
-struct atomisp_platform_data {
- struct intel_v4l2_subdev_table *subdevs;
-};
-
/*
* Sensor of external ISP can send multiple steams with different mipi data
* type in the same virtual channel. This information needs to come from the
@@ -161,25 +143,6 @@ struct atomisp_input_stream_info {
struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL];
};
-struct camera_vcm_control;
-struct camera_vcm_ops {
- int (*power_up)(struct v4l2_subdev *sd, struct camera_vcm_control *vcm);
- int (*power_down)(struct v4l2_subdev *sd,
- struct camera_vcm_control *vcm);
- int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc,
- struct camera_vcm_control *vcm);
- int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl,
- struct camera_vcm_control *vcm);
- int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl,
- struct camera_vcm_control *vcm);
-};
-
-struct camera_vcm_control {
- char camera_module[CAMERA_MODULE_ID_LEN];
- struct camera_vcm_ops *ops;
- struct list_head list;
-};
-
struct camera_sensor_platform_data {
int (*flisclk_ctrl)(struct v4l2_subdev *subdev, int flag);
int (*csi_cfg)(struct v4l2_subdev *subdev, int flag);
@@ -193,8 +156,6 @@ struct camera_sensor_platform_data {
int (*v1p8_ctrl)(struct v4l2_subdev *subdev, int on);
int (*v2p8_ctrl)(struct v4l2_subdev *subdev, int on);
int (*v1p2_ctrl)(struct v4l2_subdev *subdev, int on);
- struct camera_vcm_control *(*get_vcm_ctrl)(struct v4l2_subdev *subdev,
- char *module_id);
};
struct camera_mipi_info {
@@ -208,7 +169,7 @@ struct camera_mipi_info {
const u32 *metadata_effective_width;
};
-const struct atomisp_platform_data *atomisp_get_platform_data(void);
+const struct intel_v4l2_subdev_table *atomisp_platform_get_subdevs(void);
int atomisp_register_sensor_no_gmin(struct v4l2_subdev *subdev, u32 lanes,
enum atomisp_input_format format,
enum atomisp_bayer_order bayer_order);
diff --git a/drivers/staging/media/atomisp/include/media/lm3554.h b/drivers/staging/media/atomisp/include/media/lm3554.h
deleted file mode 100644
index 711b7d7c9950..000000000000
--- a/drivers/staging/media/atomisp/include/media/lm3554.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * include/media/lm3554.h
- *
- * Copyright (c) 2010-2012 Intel Corporation. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
-#ifndef _LM3554_H_
-#define _LM3554_H_
-
-#include <linux/gpio/consumer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-
-#define LM3554_ID 3554
-
-#define v4l2_queryctrl_entry_integer(_id, _name,\
- _minimum, _maximum, _step, \
- _default_value, _flags) \
- {\
- .id = (_id), \
- .type = V4L2_CTRL_TYPE_INTEGER, \
- .name = _name, \
- .minimum = (_minimum), \
- .maximum = (_maximum), \
- .step = (_step), \
- .default_value = (_default_value),\
- .flags = (_flags),\
- }
-#define v4l2_queryctrl_entry_boolean(_id, _name,\
- _default_value, _flags) \
- {\
- .id = (_id), \
- .type = V4L2_CTRL_TYPE_BOOLEAN, \
- .name = _name, \
- .minimum = 0, \
- .maximum = 1, \
- .step = 1, \
- .default_value = (_default_value),\
- .flags = (_flags),\
- }
-
-#define s_ctrl_id_entry_integer(_id, _name, \
- _minimum, _maximum, _step, \
- _default_value, _flags, \
- _s_ctrl, _g_ctrl) \
- {\
- .qc = v4l2_queryctrl_entry_integer(_id, _name,\
- _minimum, _maximum, _step,\
- _default_value, _flags), \
- .s_ctrl = _s_ctrl, \
- .g_ctrl = _g_ctrl, \
- }
-
-#define s_ctrl_id_entry_boolean(_id, _name, \
- _default_value, _flags, \
- _s_ctrl, _g_ctrl) \
- {\
- .qc = v4l2_queryctrl_entry_boolean(_id, _name,\
- _default_value, _flags), \
- .s_ctrl = _s_ctrl, \
- .g_ctrl = _g_ctrl, \
- }
-
-/* Value settings for Flash Time-out Duration*/
-#define LM3554_DEFAULT_TIMEOUT 512U
-#define LM3554_MIN_TIMEOUT 32U
-#define LM3554_MAX_TIMEOUT 1024U
-#define LM3554_TIMEOUT_STEPSIZE 32U
-
-/* Flash modes */
-#define LM3554_MODE_SHUTDOWN 0
-#define LM3554_MODE_INDICATOR 1
-#define LM3554_MODE_TORCH 2
-#define LM3554_MODE_FLASH 3
-
-/* timer delay time */
-#define LM3554_TIMER_DELAY 5
-
-/* Percentage <-> value macros */
-#define LM3554_MIN_PERCENT 0U
-#define LM3554_MAX_PERCENT 100U
-#define LM3554_CLAMP_PERCENTAGE(val) \
- clamp(val, LM3554_MIN_PERCENT, LM3554_MAX_PERCENT)
-
-#define LM3554_VALUE_TO_PERCENT(v, step) (((((unsigned long)(v)) * (step)) + 50) / 100)
-#define LM3554_PERCENT_TO_VALUE(p, step) (((((unsigned long)(p)) * 100) + (step >> 1)) / (step))
-
-/* Product specific limits
- * TODO: get these from platform data */
-#define LM3554_FLASH_MAX_LVL 0x0F /* 1191mA */
-
-/* Flash brightness, input is percentage, output is [0..15] */
-#define LM3554_FLASH_STEP \
- ((100ul * (LM3554_MAX_PERCENT) + ((LM3554_FLASH_MAX_LVL) >> 1)) / ((LM3554_FLASH_MAX_LVL)))
-#define LM3554_FLASH_DEFAULT_BRIGHTNESS \
- LM3554_VALUE_TO_PERCENT(13, LM3554_FLASH_STEP)
-
-/* Torch brightness, input is percentage, output is [0..7] */
-#define LM3554_TORCH_STEP 1250
-#define LM3554_TORCH_DEFAULT_BRIGHTNESS \
- LM3554_VALUE_TO_PERCENT(2, LM3554_TORCH_STEP)
-
-/* Indicator brightness, input is percentage, output is [0..3] */
-#define LM3554_INDICATOR_STEP 2500
-#define LM3554_INDICATOR_DEFAULT_BRIGHTNESS \
- LM3554_VALUE_TO_PERCENT(1, LM3554_INDICATOR_STEP)
-
-/*
- * lm3554_platform_data - Flash controller platform data
- */
-struct lm3554_platform_data {
- struct gpio_desc *gpio_torch;
- struct gpio_desc *gpio_strobe;
- struct gpio_desc *gpio_reset;
-
- unsigned int current_limit;
- unsigned int envm_tx2;
- unsigned int tx2_polarity;
-};
-
-#endif /* _LM3554_H_ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 8593ba90605f..102d39a45c9c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -61,13 +61,6 @@
#include "ia_css_debug.h"
#include "bits.h"
-/* We should never need to run the flash for more than 2 frames.
- * At 15fps this means 133ms. We set the timeout a bit longer.
- * Each flash driver is supposed to set its own timeout, but
- * just in case someone else changed the timeout, we set it
- * here to make sure we don't damage the flash hardware. */
-#define FLASH_TIMEOUT 800 /* ms */
-
union host {
struct {
void *kernel_ptr;
@@ -676,7 +669,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
struct atomisp_metadata_buf *md_buf = NULL, *_md_buf_tmp, *md_iter;
enum atomisp_metadata_type md_type;
struct atomisp_device *isp = asd->isp;
- struct v4l2_control ctrl;
int i, err;
lockdep_assert_held(&isp->mutex);
@@ -791,19 +783,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
dev_dbg(isp->dev, "%s: vf frame with exp_id %d is ready\n",
__func__, frame->exp_id);
- if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) {
- if (frame->flash_state
- == IA_CSS_FRAME_FLASH_STATE_PARTIAL)
- dev_dbg(isp->dev, "%s thumb partially flashed\n",
- __func__);
- else if (frame->flash_state
- == IA_CSS_FRAME_FLASH_STATE_FULL)
- dev_dbg(isp->dev, "%s thumb completely flashed\n",
- __func__);
- else
- dev_dbg(isp->dev, "%s thumb no flash in this frame\n",
- __func__);
- }
pipe->frame_config_id[frame->vb.vb2_buf.index] = frame->isp_config_id;
break;
case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
@@ -834,40 +813,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
}
pipe->frame_config_id[i] = frame->isp_config_id;
- ctrl.id = V4L2_CID_FLASH_MODE;
- if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) {
- if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_PARTIAL) {
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_PARTIAL;
- dev_dbg(isp->dev, "%s partially flashed\n", __func__);
- } else if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_FULL) {
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
- asd->params.num_flash_frames--;
- dev_dbg(isp->dev, "%s completely flashed\n", __func__);
- } else {
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
- dev_dbg(isp->dev, "%s no flash in this frame\n", __func__);
- }
-
- /* Check if flashing sequence is done */
- if (asd->frame_status[i] == ATOMISP_FRAME_STATUS_FLASH_EXPOSED)
- asd->params.flash_state = ATOMISP_FLASH_DONE;
- } else if (isp->flash) {
- if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 &&
- ctrl.value == ATOMISP_FLASH_MODE_TORCH) {
- ctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY;
- if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 &&
- ctrl.value > 0)
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
- else
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
- } else {
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
- }
- } else {
- asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
- }
-
- asd->params.last_frame_status = asd->frame_status[i];
if (asd->params.css_update_params_needed) {
atomisp_apply_css_parameters(asd,
@@ -1010,51 +955,18 @@ out_unlock:
mutex_unlock(&isp->mutex);
}
-void atomisp_setup_flash(struct atomisp_sub_device *asd)
-{
- struct atomisp_device *isp = asd->isp;
- struct v4l2_control ctrl;
-
- if (!isp->flash)
- return;
-
- if (asd->params.flash_state != ATOMISP_FLASH_REQUESTED &&
- asd->params.flash_state != ATOMISP_FLASH_DONE)
- return;
-
- if (asd->params.num_flash_frames) {
- /* make sure the timeout is set before setting flash mode */
- ctrl.id = V4L2_CID_FLASH_TIMEOUT;
- ctrl.value = FLASH_TIMEOUT;
-
- if (v4l2_s_ctrl(NULL, isp->flash->ctrl_handler, &ctrl)) {
- dev_err(isp->dev, "flash timeout configure failed\n");
- return;
- }
-
- ia_css_stream_request_flash(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream);
-
- asd->params.flash_state = ATOMISP_FLASH_ONGOING;
- } else {
- asd->params.flash_state = ATOMISP_FLASH_IDLE;
- }
-}
-
irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr)
{
struct atomisp_device *isp = isp_ptr;
unsigned long flags;
-
- dev_dbg(isp->dev, ">%s\n", __func__);
+ bool streaming;
spin_lock_irqsave(&isp->lock, flags);
+ streaming = isp->asd.streaming;
+ spin_unlock_irqrestore(&isp->lock, flags);
- if (!isp->asd.streaming) {
- spin_unlock_irqrestore(&isp->lock, flags);
+ if (!streaming)
return IRQ_HANDLED;
- }
-
- spin_unlock_irqrestore(&isp->lock, flags);
/*
* The standard CSS2.0 API tells the following calling sequence of
@@ -1082,14 +994,8 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr)
* time, instead, dequue one and process one, then another
*/
mutex_lock(&isp->mutex);
- if (atomisp_css_isr_thread(isp))
- goto out;
-
- if (isp->asd.streaming)
- atomisp_setup_flash(&isp->asd);
-out:
+ atomisp_css_isr_thread(isp);
mutex_unlock(&isp->mutex);
- dev_dbg(isp->dev, "<%s\n", __func__);
return IRQ_HANDLED;
}
@@ -1123,7 +1029,7 @@ v4l2_fmt_to_sh_fmt(u32 fmt)
return IA_CSS_FRAME_FORMAT_YUYV;
case V4L2_PIX_FMT_RGB24:
return IA_CSS_FRAME_FORMAT_PLANAR_RGB888;
- case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_RGBX32:
return IA_CSS_FRAME_FORMAT_RGBA888;
case V4L2_PIX_FMT_RGB565:
return IA_CSS_FRAME_FORMAT_RGB565;
@@ -1210,7 +1116,7 @@ u32 atomisp_get_pixel_depth(u32 pixelformat)
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_YUV444:
return 24;
- case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_RGBX32:
return 32;
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_CUSTOM_M10MO_RAW:
@@ -1261,11 +1167,11 @@ int atomisp_gdc_cac(struct atomisp_sub_device *asd, int flag,
}
asd->params.gdc_cac_en = !!*value;
- if (asd->params.gdc_cac_en) {
+ if (asd->params.gdc_cac_en)
asd->params.config.morph_table = asd->params.css_param.morph_table;
- } else {
+ else
asd->params.config.morph_table = NULL;
- }
+
asd->params.css_update_params_needed = true;
atomisp_update_capture_mode(asd);
return 0;
@@ -3035,8 +2941,8 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe)
}
/*
-* Function to configure ISP parameters
-*/
+ * Function to configure ISP parameters
+ */
int atomisp_set_parameters(struct video_device *vdev,
struct atomisp_parameters *arg)
{
@@ -3367,7 +3273,7 @@ int atomisp_fixed_pattern(struct atomisp_sub_device *asd, int flag,
return 0;
}
- /* Add function to get black from from sensor with shutter off */
+ /* Add function to get black from sensor with shutter off */
return 0;
}
@@ -3721,9 +3627,79 @@ apply_min_padding:
*padding_h = max_t(u32, *padding_h, min_pad_h);
}
-static int atomisp_set_crop_and_fmt(struct atomisp_device *isp,
- struct v4l2_mbus_framefmt *ffmt,
- int which)
+int atomisp_s_sensor_power(struct atomisp_device *isp, unsigned int input, bool on)
+{
+ int ret;
+
+ if (isp->inputs[input].camera_on == on)
+ return 0;
+
+ ret = v4l2_subdev_call(isp->inputs[input].camera, core, s_power, on);
+ if (ret && ret != -ENOIOCTLCMD) {
+ dev_err(isp->dev, "Error setting sensor power %d: %d\n", on, ret);
+ return ret;
+ }
+
+ isp->inputs[input].camera_on = on;
+ return 0;
+}
+
+int atomisp_select_input(struct atomisp_device *isp, unsigned int input)
+{
+ unsigned int input_orig = isp->asd.input_curr;
+ int ret;
+
+ /* Power on new sensor */
+ ret = atomisp_s_sensor_power(isp, input, 1);
+ if (ret)
+ return ret;
+
+ isp->asd.input_curr = input;
+
+ /* Power off previous sensor */
+ if (input != input_orig)
+ atomisp_s_sensor_power(isp, input_orig, 0);
+
+ atomisp_setup_input_links(isp);
+ return 0;
+}
+
+/*
+ * Ensure the CSI-receiver -> ISP link for input_curr is marked as enabled and
+ * the other CSI-receiver -> ISP links are disabled.
+ */
+void atomisp_setup_input_links(struct atomisp_device *isp)
+{
+ struct media_link *link;
+
+ lockdep_assert_held(&isp->media_dev.graph_mutex);
+
+ for (int i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) {
+ link = media_entity_find_link(
+ &isp->csi2_port[i].subdev.entity.pads[CSI2_PAD_SOURCE],
+ &isp->asd.subdev.entity.pads[ATOMISP_SUBDEV_PAD_SINK]);
+ if (!link) {
+ dev_err(isp->dev, "Error cannot find CSI2-port[%d] -> ISP link\n", i);
+ continue; /* Should never happen */
+ }
+
+ /*
+ * Modify the flags directly, calling media_entity_setup_link()
+ * will end up calling atomisp_link_setup() which calls this
+ * function again leading to endless recursion.
+ */
+ if (isp->sensor_subdevs[i] == isp->inputs[isp->asd.input_curr].camera)
+ link->flags |= MEDIA_LNK_FL_ENABLED;
+ else
+ link->flags &= ~MEDIA_LNK_FL_ENABLED;
+
+ link->reverse->flags = link->flags;
+ }
+}
+
+static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp,
+ struct v4l2_mbus_framefmt *ffmt,
+ int which)
{
struct atomisp_input_subdev *input = &isp->inputs[isp->asd.input_curr];
struct v4l2_subdev_selection sel = {
@@ -3742,6 +3718,17 @@ static int atomisp_set_crop_and_fmt(struct atomisp_device *isp,
if (!input->camera)
return -EINVAL;
+ /*
+ * Some old sensor drivers already write the registers on set_fmt
+ * instead of on stream on, power on the sensor now (on newer
+ * sensor drivers the s_power op is a no-op).
+ */
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = atomisp_s_sensor_power(isp, isp->asd.input_curr, 1);
+ if (ret)
+ return ret;
+ }
+
sd_state = (which == V4L2_SUBDEV_FORMAT_TRY) ? input->try_sd_state :
input->camera->active_state;
if (sd_state)
@@ -3776,6 +3763,13 @@ set_fmt:
if (sd_state)
v4l2_subdev_unlock_state(sd_state);
+ /* Propagate new fmt to CSI port */
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = v4l2_subdev_call(input->csi_port, pad, set_fmt, NULL, &format);
+ if (ret)
+ return ret;
+ }
+
*ffmt = format.format;
return ret;
}
@@ -3817,7 +3811,7 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
dev_dbg(isp->dev, "try_mbus_fmt: try %ux%u\n", ffmt.width, ffmt.height);
- ret = atomisp_set_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_TRY);
+ ret = atomisp_set_sensor_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_TRY);
if (ret)
return ret;
@@ -4044,6 +4038,7 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev,
unsigned int width, unsigned int height) =
configure_pp_input_nop;
const struct atomisp_in_fmt_conv *fc = NULL;
+ struct v4l2_mbus_framefmt *ffmt;
int ret, i;
isp_sink_crop = atomisp_subdev_get_rect(
@@ -4054,28 +4049,26 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev,
if (!format)
return -EINVAL;
- if (input->type != TEST_PATTERN) {
- mipi_info = atomisp_to_sensor_mipi_info(input->camera);
+ mipi_info = atomisp_to_sensor_mipi_info(input->camera);
- if (atomisp_set_sensor_mipi_to_isp(asd, ATOMISP_INPUT_STREAM_GENERAL,
- mipi_info))
- return -EINVAL;
-
- if (mipi_info)
- fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt(mipi_info->input_format);
+ if (atomisp_set_sensor_mipi_to_isp(asd, ATOMISP_INPUT_STREAM_GENERAL,
+ mipi_info))
+ return -EINVAL;
- if (!fc)
- fc = atomisp_find_in_fmt_conv(
- atomisp_subdev_get_ffmt(&asd->subdev,
- NULL, V4L2_SUBDEV_FORMAT_ACTIVE,
- ATOMISP_SUBDEV_PAD_SINK)->code);
- if (!fc)
- return -EINVAL;
- if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
- raw_output_format_match_input(fc->atomisp_in_fmt,
- pix->pixelformat))
- return -EINVAL;
+ if (mipi_info)
+ fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt(mipi_info->input_format);
+ if (!fc) {
+ ffmt = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
+ V4L2_SUBDEV_FORMAT_ACTIVE,
+ ATOMISP_SUBDEV_PAD_SINK);
+ fc = atomisp_find_in_fmt_conv(ffmt->code);
}
+ if (!fc)
+ return -EINVAL;
+
+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ raw_output_format_match_input(fc->atomisp_in_fmt, pix->pixelformat))
+ return -EINVAL;
/*
* Configure viewfinder also when vfpp is disabled: the
@@ -4263,7 +4256,7 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p
/* Disable dvs if resolution can't be supported by sensor */
if (asd->params.video_dis_en && asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
- ret = atomisp_set_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_TRY);
+ ret = atomisp_set_sensor_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_TRY);
if (ret)
return ret;
@@ -4281,7 +4274,7 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p
}
}
- ret = atomisp_set_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_ACTIVE);
+ ret = atomisp_set_sensor_crop_and_fmt(isp, &ffmt, V4L2_SUBDEV_FORMAT_ACTIVE);
if (ret)
return ret;
@@ -4420,8 +4413,6 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f)
atomisp_fill_pix_format(&pipe->pix, f->fmt.pix.width, f->fmt.pix.height, format_bridge);
f->fmt.pix = pipe->pix;
- f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width *
- pipe->pix.height * 2);
dev_dbg(isp->dev, "%s: %dx%d, image size: %d, %d bytes per line\n",
__func__,
@@ -4493,28 +4484,6 @@ out:
return ret;
}
-int atomisp_flash_enable(struct atomisp_sub_device *asd, int num_frames)
-{
- struct atomisp_device *isp = asd->isp;
-
- if (num_frames < 0) {
- dev_dbg(isp->dev, "%s ERROR: num_frames: %d\n", __func__,
- num_frames);
- return -EINVAL;
- }
- /* a requested flash is still in progress. */
- if (num_frames && asd->params.flash_state != ATOMISP_FLASH_IDLE) {
- dev_dbg(isp->dev, "%s flash busy: %d frames left: %d\n",
- __func__, asd->params.flash_state,
- asd->params.num_flash_frames);
- return -EBUSY;
- }
-
- asd->params.num_flash_frames = num_frames;
- asd->params.flash_state = ATOMISP_FLASH_REQUESTED;
- return 0;
-}
-
static int __checking_exp_id(struct atomisp_sub_device *asd, int exp_id)
{
struct atomisp_device *isp = asd->isp;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
index b8cd957eebdc..e69ca14645b9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
@@ -58,7 +58,6 @@ void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd);
void atomisp_msi_irq_init(struct atomisp_device *isp);
void atomisp_msi_irq_uninit(struct atomisp_device *isp);
void atomisp_assert_recovery_work(struct work_struct *work);
-void atomisp_setup_flash(struct atomisp_sub_device *asd);
irqreturn_t atomisp_isr(int irq, void *dev);
irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr);
const struct atomisp_format_bridge *get_atomisp_format_bridge_from_mbus(
@@ -241,6 +240,15 @@ int atomisp_compare_grid(struct atomisp_sub_device *asd,
void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
u32 *padding_w, u32 *padding_h);
+/* Set sensor power (no-op if already on/off) */
+int atomisp_s_sensor_power(struct atomisp_device *isp, unsigned int input, bool on);
+
+/* Select which sensor to use, must be called with a valid input */
+int atomisp_select_input(struct atomisp_device *isp, unsigned int input);
+
+/* Setup media-controller links to reflect input_curr setting */
+void atomisp_setup_input_links(struct atomisp_device *isp);
+
/* This function looks up the closest available resolution. */
int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
const struct atomisp_format_bridge **fmt_ret,
@@ -253,9 +261,6 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd,
void atomisp_free_internal_buffers(struct atomisp_sub_device *asd);
-int atomisp_flash_enable(struct atomisp_sub_device *asd,
- int num_frames);
-
int atomisp_freq_scaling(struct atomisp_device *vdev,
enum atomisp_dfs_mode mode,
bool force);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
index 6fe8b0b7467a..a62a5c0b3c00 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
@@ -302,21 +302,6 @@ static void __dump_stream_config(struct atomisp_sub_device *asd,
"stream_config.source.port.compression.uncompressed_bits_per_pixel=%d.\n",
s_config->source.port.compression.
uncompressed_bits_per_pixel);
- } else if (s_config->mode == IA_CSS_INPUT_MODE_TPG) {
- dev_dbg(isp->dev, "stream_config.source.tpg.id=%d.\n",
- s_config->source.tpg.id);
- dev_dbg(isp->dev, "stream_config.source.tpg.mode=%d.\n",
- s_config->source.tpg.mode);
- dev_dbg(isp->dev, "stream_config.source.tpg.x_mask=%d.\n",
- s_config->source.tpg.x_mask);
- dev_dbg(isp->dev, "stream_config.source.tpg.x_delta=%d.\n",
- s_config->source.tpg.x_delta);
- dev_dbg(isp->dev, "stream_config.source.tpg.y_mask=%d.\n",
- s_config->source.tpg.y_mask);
- dev_dbg(isp->dev, "stream_config.source.tpg.y_delta=%d.\n",
- s_config->source.tpg.y_delta);
- dev_dbg(isp->dev, "stream_config.source.tpg.xy_mask=%d.\n",
- s_config->source.tpg.xy_mask);
} else if (s_config->mode == IA_CSS_INPUT_MODE_PRBS) {
dev_dbg(isp->dev, "stream_config.source.prbs.id=%d.\n",
s_config->source.prbs.id);
@@ -1672,26 +1657,12 @@ void atomisp_css_capture_set_mode(struct atomisp_sub_device *asd,
void atomisp_css_input_set_mode(struct atomisp_sub_device *asd,
enum ia_css_input_mode mode)
{
- int i;
- struct atomisp_device *isp = asd->isp;
unsigned int size_mem_words;
+ int i;
for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++)
asd->stream_env[i].stream_config.mode = mode;
- if (isp->inputs[asd->input_curr].type == TEST_PATTERN) {
- struct ia_css_stream_config *s_config =
- &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_config;
- s_config->mode = IA_CSS_INPUT_MODE_TPG;
- s_config->source.tpg.mode = IA_CSS_TPG_MODE_CHECKERBOARD;
- s_config->source.tpg.x_mask = (1 << 4) - 1;
- s_config->source.tpg.x_delta = -2;
- s_config->source.tpg.y_mask = (1 << 4) - 1;
- s_config->source.tpg.y_delta = 3;
- s_config->source.tpg.xy_mask = (1 << 8) - 1;
- return;
- }
-
if (mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
return;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index 89118438a3b6..9288910eeb6c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -107,9 +107,12 @@ int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd,
actual_ffmt->height = clamp_t(u32, ffmt->height,
ATOM_ISP_MIN_HEIGHT,
ATOM_ISP_MAX_HEIGHT);
+ actual_ffmt->field = ffmt->field;
tmp_ffmt = *ffmt = *actual_ffmt;
+ /* Always use V4L2_FIELD_ANY to match the ISP sink pad */
+ tmp_ffmt.field = V4L2_FIELD_ANY;
return atomisp_csi2_set_ffmt(sd, sd_state, which,
CSI2_PAD_SOURCE,
&tmp_ffmt);
@@ -138,27 +141,6 @@ static int csi2_set_format(struct v4l2_subdev *sd,
&fmt->format);
}
-/*
- * csi2_set_stream - Enable/Disable streaming on the CSI2 module
- * @sd: ISP CSI2 V4L2 subdevice
- * @enable: Enable/disable stream (1/0)
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
-/* subdev core operations */
-static const struct v4l2_subdev_core_ops csi2_core_ops = {
-};
-
-/* subdev video operations */
-static const struct v4l2_subdev_video_ops csi2_video_ops = {
- .s_stream = csi2_set_stream,
-};
-
/* subdev pad operations */
static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
.enum_mbus_code = csi2_enum_mbus_code,
@@ -169,8 +151,6 @@ static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
/* subdev operations */
static const struct v4l2_subdev_ops csi2_ops = {
- .core = &csi2_core_ops,
- .video = &csi2_video_ops,
.pad = &csi2_pad_ops,
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c
index 4dba6120af39..50c4123ba006 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_fops.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c
@@ -445,12 +445,8 @@ const struct vb2_ops atomisp_vb2_ops = {
static void atomisp_dev_init_struct(struct atomisp_device *isp)
{
- unsigned int i;
-
isp->isp_fatal_error = false;
- for (i = 0; i < isp->input_cnt; i++)
- isp->inputs[i].asd = NULL;
/*
* For Merrifield, frequency is scalable.
* After boot-up, the default frequency is 200MHz.
@@ -524,21 +520,12 @@ static int atomisp_open(struct file *file)
}
atomisp_dev_init_struct(isp);
-
- ret = v4l2_subdev_call(isp->flash, core, s_power, 1);
- if (ret < 0 && ret != -ENODEV && ret != -ENOIOCTLCMD) {
- dev_err(isp->dev, "Failed to power-on flash\n");
- goto css_error;
- }
-
atomisp_subdev_init_struct(asd);
pipe->users++;
mutex_unlock(&isp->mutex);
return 0;
-css_error:
- pm_runtime_put(vdev->v4l2_dev->dev);
error:
mutex_unlock(&isp->mutex);
v4l2_fh_release(file);
@@ -552,8 +539,6 @@ static int atomisp_release(struct file *file)
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
struct atomisp_sub_device *asd = pipe->asd;
struct v4l2_subdev_fh fh;
- struct v4l2_rect clear_compose = {0};
- int ret;
v4l2_fh_init(&fh.vfh, vdev);
@@ -566,48 +551,16 @@ static int atomisp_release(struct file *file)
pipe->users--;
- /*
- * A little trick here:
- * file injection input resolution is recorded in the sink pad,
- * therefore can not be cleared when releaseing one device node.
- * The sink pad setting can only be cleared when all device nodes
- * get released.
- */
- {
- struct v4l2_mbus_framefmt isp_sink_fmt = { 0 };
-
- atomisp_subdev_set_ffmt(&asd->subdev, fh.state,
- V4L2_SUBDEV_FORMAT_ACTIVE,
- ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt);
- }
-
atomisp_css_free_stat_buffers(asd);
atomisp_free_internal_buffers(asd);
- if (isp->inputs[asd->input_curr].asd == asd) {
- ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- core, s_power, 0);
- if (ret && ret != -ENOIOCTLCMD)
- dev_warn(isp->dev, "Failed to power-off sensor\n");
-
- /* clear the asd field to show this camera is not used */
- isp->inputs[asd->input_curr].asd = NULL;
- }
+ atomisp_s_sensor_power(isp, asd->input_curr, 0);
atomisp_destroy_pipes_stream(asd);
- ret = v4l2_subdev_call(isp->flash, core, s_power, 0);
- if (ret < 0 && ret != -ENODEV && ret != -ENOIOCTLCMD)
- dev_warn(isp->dev, "Failed to power-off flash\n");
-
if (pm_runtime_put_sync(vdev->v4l2_dev->dev) < 0)
dev_err(isp->dev, "Failed to power off device\n");
- atomisp_subdev_set_selection(&asd->subdev, fh.state,
- V4L2_SUBDEV_FORMAT_ACTIVE,
- ATOMISP_SUBDEV_PAD_SOURCE,
- V4L2_SEL_TGT_COMPOSE, 0,
- &clear_compose);
mutex_unlock(&isp->mutex);
return 0;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
index 139ad7ad1dcf..e176483df301 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
@@ -75,7 +75,6 @@ struct gmin_subdev {
struct regulator *v1p8_reg;
struct regulator *v2p8_reg;
struct regulator *v1p2_reg;
- struct regulator *v2p8_vcm_reg;
enum atomisp_camera_port csi_port;
unsigned int csi_lanes;
enum atomisp_input_format csi_fmt;
@@ -85,7 +84,6 @@ struct gmin_subdev {
bool v1p8_on;
bool v2p8_on;
bool v1p2_on;
- bool v2p8_vcm_on;
int v1p8_gpio;
int v2p8_gpio;
@@ -126,35 +124,25 @@ static DEFINE_MUTEX(gmin_regulator_mutex);
static int gmin_v1p8_enable_count;
static int gmin_v2p8_enable_count;
-/* The atomisp uses type==0 for the end-of-list marker, so leave space. */
+/* The atomisp uses subdev==NULL for the end-of-list marker, so leave space. */
static struct intel_v4l2_subdev_table pdata_subdevs[MAX_SUBDEVS + 1];
-static const struct atomisp_platform_data pdata = {
- .subdevs = pdata_subdevs,
-};
-
-static LIST_HEAD(vcm_devices);
-static DEFINE_MUTEX(vcm_lock);
-
static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev);
-const struct atomisp_platform_data *atomisp_get_platform_data(void)
+const struct intel_v4l2_subdev_table *atomisp_platform_get_subdevs(void)
{
- return &pdata;
+ return pdata_subdevs;
}
-EXPORT_SYMBOL_GPL(atomisp_get_platform_data);
+EXPORT_SYMBOL_GPL(atomisp_platform_get_subdevs);
int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
- struct camera_sensor_platform_data *plat_data,
- enum intel_v4l2_subdev_type type)
+ struct camera_sensor_platform_data *plat_data)
{
int i;
struct gmin_subdev *gs;
struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct acpi_device *adev = ACPI_COMPANION(&client->dev);
- dev_info(&client->dev, "register atomisp i2c module type %d\n", type);
-
/* The windows driver model (and thus most BIOSes by default)
* uses ACPI runtime power management for camera devices, but
* we don't. Disable it, or else the rails will be needlessly
@@ -172,10 +160,10 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
adev->power.flags.power_resources = 0;
for (i = 0; i < MAX_SUBDEVS; i++)
- if (!pdata.subdevs[i].type)
+ if (!pdata_subdevs[i].subdev)
break;
- if (pdata.subdevs[i].type)
+ if (i == MAX_SUBDEVS)
return -ENOMEM;
/* Note subtlety of initialization order: at the point where
@@ -187,10 +175,9 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
if (!gs)
return -ENODEV;
- pdata.subdevs[i].type = type;
- pdata.subdevs[i].port = gs->csi_port;
- pdata.subdevs[i].lanes = gs->csi_lanes;
- pdata.subdevs[i].subdev = subdev;
+ pdata_subdevs[i].port = gs->csi_port;
+ pdata_subdevs[i].lanes = gs->csi_lanes;
+ pdata_subdevs[i].subdev = subdev;
return 0;
}
EXPORT_SYMBOL_GPL(atomisp_register_i2c_module);
@@ -203,9 +190,9 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd)
return 0;
for (i = 0; i < MAX_SUBDEVS; i++) {
- if (pdata.subdevs[i].subdev == sd) {
+ if (pdata_subdevs[i].subdev == sd) {
for (j = i + 1; j <= MAX_SUBDEVS; j++)
- pdata.subdevs[j - 1] = pdata.subdevs[j];
+ pdata_subdevs[j - 1] = pdata_subdevs[j];
}
if (gmin_subdevs[i].subdev == sd) {
if (gmin_subdevs[i].gpio0)
@@ -218,7 +205,6 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd)
regulator_put(gmin_subdevs[i].v1p8_reg);
regulator_put(gmin_subdevs[i].v2p8_reg);
regulator_put(gmin_subdevs[i].v1p2_reg);
- regulator_put(gmin_subdevs[i].v2p8_vcm_reg);
}
gmin_subdevs[i].subdev = NULL;
}
@@ -388,20 +374,16 @@ static struct i2c_client *gmin_i2c_dev_exists(struct device *dev, char *name,
struct i2c_client **client)
{
struct acpi_device *adev;
- struct device *d;
adev = acpi_dev_get_first_match_dev(name, NULL, -1);
if (!adev)
return NULL;
- d = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
+ *client = i2c_find_device_by_fwnode(acpi_fwnode_handle(adev));
acpi_dev_put(adev);
- if (!d)
+ if (!*client)
return NULL;
- *client = i2c_verify_client(d);
- put_device(d);
-
dev_dbg(dev, "found '%s' at address 0x%02x, adapter %d\n",
(*client)->name, (*client)->addr, (*client)->adapter->nr);
return *client;
@@ -497,16 +479,19 @@ static u8 gmin_get_pmic_id_and_addr(struct device *dev)
if (pmic_id)
return pmic_i2c_addr;
- if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI, &power))
+ if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI, &power)) {
pmic_id = PMIC_TI;
- else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_AXP, &power))
+ } else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_AXP, &power)) {
pmic_id = PMIC_AXP;
- else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_CRYSTALCOVE, &power))
+ } else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_CRYSTALCOVE, &power)) {
pmic_id = PMIC_CRYSTALCOVE;
- else
+ } else {
pmic_id = PMIC_REGULATOR;
+ return 0;
+ }
- pmic_i2c_addr = power ? power->addr : 0;
+ pmic_i2c_addr = power->addr;
+ put_device(&power->dev);
return pmic_i2c_addr;
}
@@ -669,7 +654,6 @@ static int gmin_subdev_add(struct gmin_subdev *gs)
gs->v2p8_reg = regulator_get(dev, "V2P8SX");
gs->v1p2_reg = regulator_get(dev, "V1P2A");
- gs->v2p8_vcm_reg = regulator_get(dev, "VPROG4B");
/* Note: ideally we would initialize v[12]p8_on to the
* output of regulator_is_enabled(), but sadly that
@@ -1137,7 +1121,7 @@ int atomisp_register_sensor_no_gmin(struct v4l2_subdev *subdev, u32 lanes,
}
for (i = 0; i < MAX_SUBDEVS; i++)
- if (!pdata.subdevs[i].type)
+ if (!pdata_subdevs[i].subdev)
break;
if (i >= MAX_SUBDEVS) {
@@ -1149,10 +1133,9 @@ int atomisp_register_sensor_no_gmin(struct v4l2_subdev *subdev, u32 lanes,
if (ret)
return ret;
- pdata.subdevs[i].type = RAW_CAMERA;
- pdata.subdevs[i].port = port;
- pdata.subdevs[i].lanes = lanes;
- pdata.subdevs[i].subdev = subdev;
+ pdata_subdevs[i].port = port;
+ pdata_subdevs[i].lanes = lanes;
+ pdata_subdevs[i].subdev = subdev;
return 0;
}
EXPORT_SYMBOL_GPL(atomisp_register_sensor_no_gmin);
@@ -1162,43 +1145,17 @@ void atomisp_unregister_subdev(struct v4l2_subdev *subdev)
int i;
for (i = 0; i < MAX_SUBDEVS; i++) {
- if (pdata.subdevs[i].subdev != subdev)
+ if (pdata_subdevs[i].subdev != subdev)
continue;
camera_sensor_csi_free(subdev);
- pdata.subdevs[i].subdev = NULL;
- pdata.subdevs[i].type = 0;
- pdata.subdevs[i].port = 0;
+ pdata_subdevs[i].subdev = NULL;
+ pdata_subdevs[i].port = 0;
break;
}
}
EXPORT_SYMBOL_GPL(atomisp_unregister_subdev);
-static struct camera_vcm_control *gmin_get_vcm_ctrl(struct v4l2_subdev *subdev,
- char *camera_module)
-{
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct gmin_subdev *gs = find_gmin_subdev(subdev);
- struct camera_vcm_control *vcm;
-
- if (!client || !gs)
- return NULL;
-
- if (!camera_module)
- return NULL;
-
- mutex_lock(&vcm_lock);
- list_for_each_entry(vcm, &vcm_devices, list) {
- if (!strcmp(camera_module, vcm->camera_module)) {
- mutex_unlock(&vcm_lock);
- return vcm;
- }
- }
-
- mutex_unlock(&vcm_lock);
- return NULL;
-}
-
static struct camera_sensor_platform_data pmic_gmin_plat = {
.gpio0_ctrl = gmin_gpio0_ctrl,
.gpio1_ctrl = gmin_gpio1_ctrl,
@@ -1207,7 +1164,6 @@ static struct camera_sensor_platform_data pmic_gmin_plat = {
.v1p2_ctrl = gmin_v1p2_ctrl,
.flisclk_ctrl = gmin_flisclk_ctrl,
.csi_cfg = gmin_csi_cfg,
- .get_vcm_ctrl = gmin_get_vcm_ctrl,
};
static struct camera_sensor_platform_data acpi_gmin_plat = {
@@ -1218,7 +1174,6 @@ static struct camera_sensor_platform_data acpi_gmin_plat = {
.v1p2_ctrl = gmin_acpi_pm_ctrl,
.flisclk_ctrl = gmin_acpi_pm_ctrl,
.csi_cfg = gmin_csi_cfg,
- .get_vcm_ctrl = gmin_get_vcm_ctrl,
};
struct camera_sensor_platform_data *
@@ -1243,19 +1198,6 @@ gmin_camera_platform_data(struct v4l2_subdev *subdev,
}
EXPORT_SYMBOL_GPL(gmin_camera_platform_data);
-int atomisp_gmin_register_vcm_control(struct camera_vcm_control *vcmCtrl)
-{
- if (!vcmCtrl)
- return -EINVAL;
-
- mutex_lock(&vcm_lock);
- list_add_tail(&vcmCtrl->list, &vcm_devices);
- mutex_unlock(&vcm_lock);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atomisp_gmin_register_vcm_control);
-
static int gmin_get_hardcoded_var(struct device *dev,
struct gmin_cfg_var *varlist,
const char *var8, char *out, size_t *out_len)
@@ -1416,13 +1358,12 @@ static int gmin_get_config_var(struct device *maindev,
if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
status = efi.get_variable(var16, &GMIN_CFG_VAR_EFI_GUID, NULL,
(unsigned long *)out_len, out);
- if (status == EFI_SUCCESS) {
+ if (status == EFI_SUCCESS)
dev_info(maindev, "found EFI entry for '%s'\n", var8);
- } else if (is_gmin) {
+ else if (is_gmin)
dev_info(maindev, "Failed to find EFI gmin variable %s\n", var8);
- } else {
+ else
dev_info(maindev, "Failed to find EFI variable %s\n", var8);
- }
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h
index bba9bc64d447..9c2bc5332fa4 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
@@ -36,7 +36,6 @@
#include "atomisp_csi2.h"
#include "atomisp_subdev.h"
-#include "atomisp_tpg.h"
#include "atomisp_compat.h"
#include "gp_device.h"
@@ -49,14 +48,13 @@
(((isp)->media_dev.hw_revision & ATOMISP_HW_REVISION_MASK) == \
((rev) << ATOMISP_HW_REVISION_SHIFT))
-#define ATOMISP_PCI_DEVICE_SOC_MASK 0xfff8
+#define ATOMISP_PCI_DEVICE_SOC_BYT 0x0f38
/* MRFLD with 0x1178: ISP freq can burst to 457MHz */
#define ATOMISP_PCI_DEVICE_SOC_MRFLD 0x1178
/* MRFLD with 0x1179: max ISP freq limited to 400MHz */
#define ATOMISP_PCI_DEVICE_SOC_MRFLD_1179 0x1179
/* MRFLD with 0x117a: max ISP freq is 400MHz and max freq at Vmin is 200MHz */
#define ATOMISP_PCI_DEVICE_SOC_MRFLD_117A 0x117a
-#define ATOMISP_PCI_DEVICE_SOC_BYT 0x0f38
#define ATOMISP_PCI_DEVICE_SOC_ANN 0x1478
#define ATOMISP_PCI_DEVICE_SOC_CHT 0x22b8
@@ -123,25 +121,18 @@
round_down((2 * (n) + (d) * (step)) / (2 * (d)), (step))
struct atomisp_input_subdev {
- unsigned int type;
enum atomisp_camera_port port;
u32 code; /* MEDIA_BUS_FMT_* */
bool binning_support;
bool crop_support;
+ bool camera_on;
struct v4l2_subdev *camera;
+ struct v4l2_subdev *csi_port;
/* Sensor rects for sensors which support crop */
struct v4l2_rect native_rect;
struct v4l2_rect active_rect;
/* Sensor state for which == V4L2_SUBDEV_FORMAT_TRY calls */
struct v4l2_subdev_state *try_sd_state;
-
- struct v4l2_subdev *motor;
-
- /*
- * To show this resource is used by
- * which stream, in ISP multiple stream mode
- */
- struct atomisp_sub_device *asd;
};
enum atomisp_dfs_mode {
@@ -184,7 +175,6 @@ struct atomisp_device {
struct media_device media_dev;
struct atomisp_sub_device asd;
struct v4l2_async_notifier notifier;
- struct atomisp_platform_data *pdata;
void *mmu_l1_base;
void __iomem *base;
const struct firmware *firmware;
@@ -195,7 +185,6 @@ struct atomisp_device {
bool pm_only;
struct atomisp_mipi_csi2_device csi2_port[ATOMISP_CAMERA_NR_PORTS];
- struct atomisp_tpg_device tpg;
/* Purpose of mutex is to protect and serialize use of isp data
* structures and css API calls. */
@@ -209,8 +198,6 @@ struct atomisp_device {
struct v4l2_subdev *sensor_subdevs[ATOMISP_CAMERA_NR_PORTS];
unsigned int input_cnt;
struct atomisp_input_subdev inputs[ATOM_ISP_MAX_INPUTS];
- struct v4l2_subdev *flash;
- struct v4l2_subdev *motor;
struct atomisp_regs saved_regs;
struct atomisp_css_env css_env;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index bb8e5e883b50..effc71b5a439 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -83,15 +83,6 @@ static struct v4l2_queryctrl ci_v4l2_controls[] = {
.default_value = 0x00,
},
{
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 1,
- .maximum = 2,
- .step = 1,
- .default_value = 1,
- },
- {
.id = V4L2_CID_COLORFX,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Image Color Effect",
@@ -101,15 +92,6 @@ static struct v4l2_queryctrl ci_v4l2_controls[] = {
.default_value = 0,
},
{
- .id = V4L2_CID_COLORFX_CBCR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Image Color Effect CbCr",
- .minimum = 0,
- .maximum = 0xffff,
- .step = 1,
- .default_value = 0,
- },
- {
.id = V4L2_CID_ATOMISP_BAD_PIXEL_DETECTION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Bad Pixel Correction",
@@ -155,15 +137,6 @@ static struct v4l2_queryctrl ci_v4l2_controls[] = {
.default_value = 0,
},
{
- .id = V4L2_CID_REQUEST_FLASH,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Request flash frames",
- .minimum = 0,
- .maximum = 10,
- .step = 1,
- .default_value = 1,
- },
- {
.id = V4L2_CID_ATOMISP_LOW_LIGHT,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Low light mode",
@@ -172,142 +145,6 @@ static struct v4l2_queryctrl ci_v4l2_controls[] = {
.step = 1,
.default_value = 1,
},
- {
- .id = V4L2_CID_2A_STATUS,
- .type = V4L2_CTRL_TYPE_BITMASK,
- .name = "AE and AWB status",
- .minimum = 0,
- .maximum = V4L2_2A_STATUS_AE_READY | V4L2_2A_STATUS_AWB_READY,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "exposure",
- .minimum = -4,
- .maximum = 4,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE_ZONE_NUM,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "one-time exposure zone number",
- .minimum = 0x0,
- .maximum = 0xffff,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure auto priority",
- .minimum = V4L2_EXPOSURE_AUTO,
- .maximum = V4L2_EXPOSURE_APERTURE_PRIORITY,
- .step = 1,
- .default_value = V4L2_EXPOSURE_AUTO,
- },
- {
- .id = V4L2_CID_SCENE_MODE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "scene mode",
- .minimum = 0,
- .maximum = 13,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_ISO_SENSITIVITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "iso",
- .minimum = -4,
- .maximum = 4,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_ISO_SENSITIVITY_AUTO,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "iso mode",
- .minimum = V4L2_ISO_SENSITIVITY_MANUAL,
- .maximum = V4L2_ISO_SENSITIVITY_AUTO,
- .step = 1,
- .default_value = V4L2_ISO_SENSITIVITY_AUTO,
- },
- {
- .id = V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "white balance",
- .minimum = 0,
- .maximum = 9,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE_METERING,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "metering",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_3A_LOCK,
- .type = V4L2_CTRL_TYPE_BITMASK,
- .name = "3a lock",
- .minimum = 0,
- .maximum = V4L2_LOCK_EXPOSURE | V4L2_LOCK_WHITE_BALANCE
- | V4L2_LOCK_FOCUS,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_TEST_PATTERN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test Pattern",
- .minimum = 0,
- .maximum = 0xffff,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_TEST_PATTERN_COLOR_R,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test Pattern Solid Color R",
- .minimum = INT_MIN,
- .maximum = INT_MAX,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_TEST_PATTERN_COLOR_GR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test Pattern Solid Color GR",
- .minimum = INT_MIN,
- .maximum = INT_MAX,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_TEST_PATTERN_COLOR_GB,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test Pattern Solid Color GB",
- .minimum = INT_MIN,
- .maximum = INT_MAX,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_TEST_PATTERN_COLOR_B,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test Pattern Solid Color B",
- .minimum = INT_MIN,
- .maximum = INT_MAX,
- .step = 1,
- .default_value = 0,
- },
};
static const u32 ctrls_num = ARRAY_SIZE(ci_v4l2_controls);
@@ -376,12 +213,6 @@ const struct atomisp_format_bridge atomisp_output_fmts[] = {
.mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
.sh_fmt = IA_CSS_FRAME_FORMAT_UYVY,
.description = "UYVY, interleaved"
- }, { /* This one is for parallel sensors! DO NOT USE! */
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
- .sh_fmt = IA_CSS_FRAME_FORMAT_UYVY,
- .description = "UYVY, interleaved"
}, {
.pixelformat = V4L2_PIX_FMT_SBGGR16,
.depth = 16,
@@ -461,12 +292,6 @@ const struct atomisp_format_bridge atomisp_output_fmts[] = {
.sh_fmt = IA_CSS_FRAME_FORMAT_RAW,
.description = "Bayer 12"
}, {
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- .mbus_code = V4L2_MBUS_FMT_CUSTOM_RGB32,
- .sh_fmt = IA_CSS_FRAME_FORMAT_RGBA888,
- .description = "32 RGB 8-8-8-8"
- }, {
.pixelformat = V4L2_PIX_FMT_RGB565,
.depth = 16,
.mbus_code = MEDIA_BUS_FMT_BGR565_2X8_LE,
@@ -474,6 +299,17 @@ const struct atomisp_format_bridge atomisp_output_fmts[] = {
.description = "16 RGB 5-6-5"
#if 0
}, {
+ /*
+ * Broken, showing vertical columns with random data.
+ * For each 128 pixels in a row the last 28 (32?) or so pixels
+ * contain random data.
+ */
+ .pixelformat = V4L2_PIX_FMT_RGBX32,
+ .depth = 32,
+ .mbus_code = V4L2_MBUS_FMT_CUSTOM_RGB32,
+ .sh_fmt = IA_CSS_FRAME_FORMAT_RGBA888,
+ .description = "32 RGB 8-8-8-8"
+ }, {
.pixelformat = V4L2_PIX_FMT_JPEG,
.depth = 8,
.mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
@@ -557,7 +393,6 @@ static int atomisp_enum_input(struct file *file, void *fh,
struct video_device *vdev = video_devdata(file);
struct atomisp_device *isp = video_get_drvdata(vdev);
int index = input->index;
- struct v4l2_subdev *motor;
if (index >= isp->input_cnt)
return -EINVAL;
@@ -569,31 +404,8 @@ static int atomisp_enum_input(struct file *file, void *fh,
strscpy(input->name, isp->inputs[index].camera->name,
sizeof(input->name));
- /*
- * HACK: append actuator's name to sensor's
- * As currently userspace can't talk directly to subdev nodes, this
- * ioctl is the only way to enum inputs + possible external actuators
- * for 3A tuning purpose.
- */
- if (!IS_ISP2401)
- motor = isp->inputs[index].motor;
- else
- motor = isp->motor;
-
- if (motor && strlen(motor->name) > 0) {
- const int cur_len = strlen(input->name);
- const int max_size = sizeof(input->name) - cur_len - 1;
-
- if (max_size > 1) {
- input->name[cur_len] = '+';
- strscpy(&input->name[cur_len + 1],
- motor->name, max_size);
- }
- }
-
input->type = V4L2_INPUT_TYPE_CAMERA;
input->index = index;
- input->reserved[0] = isp->inputs[index].type;
input->reserved[1] = isp->inputs[index].port;
return 0;
@@ -627,61 +439,23 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input)
struct video_device *vdev = video_devdata(file);
struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- struct atomisp_sub_device *asd = pipe->asd;
- struct v4l2_subdev *camera = NULL;
- struct v4l2_subdev *motor;
int ret;
- ret = atomisp_pipe_check(pipe, true);
- if (ret)
- return ret;
-
- if (input >= ATOM_ISP_MAX_INPUTS || input >= isp->input_cnt) {
- dev_dbg(isp->dev, "input_cnt: %d\n", isp->input_cnt);
+ if (input >= isp->input_cnt)
return -EINVAL;
- }
- camera = isp->inputs[input].camera;
- if (!camera) {
- dev_err(isp->dev, "%s, no camera\n", __func__);
+ if (!isp->inputs[input].camera)
return -EINVAL;
- }
-
- /* power off the current owned sensor, as it is not used this time */
- if (isp->inputs[asd->input_curr].asd == asd &&
- asd->input_curr != input) {
- ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- core, s_power, 0);
- if (ret && ret != -ENOIOCTLCMD)
- dev_warn(isp->dev,
- "Failed to power-off sensor\n");
- /* clear the asd field to show this camera is not used */
- isp->inputs[asd->input_curr].asd = NULL;
- }
- /* powe on the new sensor */
- ret = v4l2_subdev_call(isp->inputs[input].camera, core, s_power, 1);
- if (ret && ret != -ENOIOCTLCMD) {
- dev_err(isp->dev, "Failed to power-on sensor\n");
+ ret = atomisp_pipe_check(pipe, true);
+ if (ret)
return ret;
- }
- if (!IS_ISP2401) {
- motor = isp->inputs[input].motor;
- } else {
- motor = isp->motor;
- if (motor)
- ret = v4l2_subdev_call(motor, core, s_power, 1);
- }
-
- if (motor)
- ret = v4l2_subdev_call(motor, core, init, 1);
+ mutex_lock(&isp->media_dev.graph_mutex);
+ ret = atomisp_select_input(isp, input);
+ mutex_unlock(&isp->media_dev.graph_mutex);
- asd->input_curr = input;
- /* mark this camera is used by the current stream */
- isp->inputs[input].asd = asd;
-
- return 0;
+ return ret;
}
/*
@@ -1051,7 +825,6 @@ static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer
{
struct video_device *vdev = video_devdata(file);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- struct atomisp_sub_device *asd = pipe->asd;
struct atomisp_device *isp = video_get_drvdata(vdev);
struct ia_css_frame *frame;
struct vb2_buffer *vb;
@@ -1064,15 +837,8 @@ static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer
vb = vb2_get_buffer(&pipe->vb_queue, buf->index);
frame = vb_to_frame(vb);
- buf->reserved = asd->frame_status[buf->index];
-
- /*
- * Hack:
- * Currently frame_status in the enum type which takes no more lower
- * 8 bit.
- * use bit[31:16] for exp_id as it is only in the range of 1~255
- */
- buf->reserved &= 0x0000ffff;
+ /* reserved bit[31:16] is used for exp_id */
+ buf->reserved = 0;
if (!(buf->flags & V4L2_BUF_FLAG_ERROR))
buf->reserved |= frame->exp_id;
buf->reserved2 = pipe->frame_config_id[buf->index];
@@ -1118,6 +884,20 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto out_unlock;
+ /*
+ * When running a classic v4l2 app after a media-controller aware
+ * app, the CSI-receiver -> ISP link for the current sensor may be
+ * disabled. Fix this up before marking the pipeline as started.
+ */
+ mutex_lock(&isp->media_dev.graph_mutex);
+ atomisp_setup_input_links(isp);
+ ret = __media_pipeline_start(&asd->video_out.vdev.entity.pads[0], &asd->video_out.pipe);
+ mutex_unlock(&isp->media_dev.graph_mutex);
+ if (ret) {
+ dev_err(isp->dev, "Error starting mc pipline: %d\n", ret);
+ goto out_unlock;
+ }
+
/* Input system HW workaround */
atomisp_dma_burst_len_cfg(asd);
@@ -1144,9 +924,9 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
spin_lock_irqsave(&isp->lock, irqflags);
asd->streaming = true;
spin_unlock_irqrestore(&isp->lock, irqflags);
- atomic_set(&asd->sof_count, -1);
- atomic_set(&asd->sequence, -1);
- atomic_set(&asd->sequence_temp, -1);
+ atomic_set(&asd->sof_count, 0);
+ atomic_set(&asd->sequence, 0);
+ atomic_set(&asd->sequence_temp, 0);
asd->params.dis_proj_data_valid = false;
asd->latest_preview_exp_id = 0;
@@ -1158,12 +938,6 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
atomisp_qbuffers_to_css(asd);
- if (isp->flash) {
- asd->params.num_flash_frames = 0;
- asd->params.flash_state = ATOMISP_FLASH_IDLE;
- atomisp_setup_flash(asd);
- }
-
atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF,
atomisp_css_valid_sof(isp));
atomisp_csi2_configure(asd);
@@ -1241,11 +1015,6 @@ void atomisp_stop_streaming(struct vb2_queue *vq)
if (ret)
dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret);
- if (isp->flash) {
- asd->params.num_flash_frames = 0;
- asd->params.flash_state = ATOMISP_FLASH_IDLE;
- }
-
/* Disable the CSI interface on ANN B0/K0 */
if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 <<
ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) {
@@ -1270,6 +1039,7 @@ void atomisp_stop_streaming(struct vb2_queue *vq)
if (ret)
dev_warn(isp->dev, "Recreating streams failed: %d\n", ret);
+ media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]);
mutex_unlock(&isp->mutex);
}
@@ -1283,7 +1053,6 @@ static int atomisp_g_ctrl(struct file *file, void *fh,
{
struct video_device *vdev = video_devdata(file);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
int i, ret = -EINVAL;
for (i = 0; i < ctrls_num; i++) {
@@ -1297,27 +1066,6 @@ static int atomisp_g_ctrl(struct file *file, void *fh,
return ret;
switch (control->id) {
- case V4L2_CID_IRIS_ABSOLUTE:
- case V4L2_CID_EXPOSURE_ABSOLUTE:
- case V4L2_CID_2A_STATUS:
- case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
- case V4L2_CID_EXPOSURE:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_SCENE_MODE:
- case V4L2_CID_ISO_SENSITIVITY:
- case V4L2_CID_ISO_SENSITIVITY_AUTO:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_3A_LOCK:
- case V4L2_CID_EXPOSURE_ZONE_NUM:
- case V4L2_CID_TEST_PATTERN:
- case V4L2_CID_TEST_PATTERN_COLOR_R:
- case V4L2_CID_TEST_PATTERN_COLOR_GR:
- case V4L2_CID_TEST_PATTERN_COLOR_GB:
- case V4L2_CID_TEST_PATTERN_COLOR_B:
- return v4l2_g_ctrl(isp->inputs[asd->input_curr].camera->
- ctrl_handler, control);
case V4L2_CID_COLORFX:
ret = atomisp_color_effect(asd, 0, &control->value);
break;
@@ -1357,7 +1105,6 @@ static int atomisp_s_ctrl(struct file *file, void *fh,
{
struct video_device *vdev = video_devdata(file);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
int i, ret = -EINVAL;
for (i = 0; i < ctrls_num; i++) {
@@ -1371,28 +1118,6 @@ static int atomisp_s_ctrl(struct file *file, void *fh,
return ret;
switch (control->id) {
- case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
- case V4L2_CID_EXPOSURE:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- case V4L2_CID_SCENE_MODE:
- case V4L2_CID_ISO_SENSITIVITY:
- case V4L2_CID_ISO_SENSITIVITY_AUTO:
- case V4L2_CID_POWER_LINE_FREQUENCY:
- case V4L2_CID_EXPOSURE_METERING:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_3A_LOCK:
- case V4L2_CID_COLORFX_CBCR:
- case V4L2_CID_TEST_PATTERN:
- case V4L2_CID_TEST_PATTERN_COLOR_R:
- case V4L2_CID_TEST_PATTERN_COLOR_GR:
- case V4L2_CID_TEST_PATTERN_COLOR_GB:
- case V4L2_CID_TEST_PATTERN_COLOR_B:
- return v4l2_s_ctrl(NULL,
- isp->inputs[asd->input_curr].camera->
- ctrl_handler, control);
case V4L2_CID_COLORFX:
ret = atomisp_color_effect(asd, 1, &control->value);
break;
@@ -1411,9 +1136,6 @@ static int atomisp_s_ctrl(struct file *file, void *fh,
case V4L2_CID_ATOMISP_FALSE_COLOR_CORRECTION:
ret = atomisp_false_color(asd, 1, &control->value);
break;
- case V4L2_CID_REQUEST_FLASH:
- ret = atomisp_flash_enable(asd, control->value);
- break;
case V4L2_CID_ATOMISP_LOW_LIGHT:
ret = atomisp_low_light(asd, 1, &control->value);
break;
@@ -1434,25 +1156,6 @@ static int atomisp_queryctl(struct file *file, void *fh,
struct v4l2_queryctrl *qc)
{
int i, ret = -EINVAL;
- struct video_device *vdev = video_devdata(file);
- struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
-
- switch (qc->id) {
- case V4L2_CID_FOCUS_ABSOLUTE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_FOCUS_STATUS:
- if (!IS_ISP2401) {
- return v4l2_queryctrl(isp->inputs[asd->input_curr].camera->
- ctrl_handler, qc);
- }
- /* ISP2401 */
- if (isp->motor)
- return v4l2_queryctrl(isp->motor->ctrl_handler, qc);
- else
- return v4l2_queryctrl(isp->inputs[asd->input_curr].
- camera->ctrl_handler, qc);
- }
if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
return ret;
@@ -1477,66 +1180,17 @@ static int atomisp_camera_g_ext_ctrls(struct file *file, void *fh,
{
struct video_device *vdev = video_devdata(file);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
- struct v4l2_subdev *motor;
struct v4l2_control ctrl;
int i;
int ret = 0;
- if (!IS_ISP2401)
- motor = isp->inputs[asd->input_curr].motor;
- else
- motor = isp->motor;
-
for (i = 0; i < c->count; i++) {
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
switch (ctrl.id) {
- case V4L2_CID_EXPOSURE_ABSOLUTE:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_IRIS_ABSOLUTE:
- case V4L2_CID_3A_LOCK:
- case V4L2_CID_TEST_PATTERN:
- case V4L2_CID_TEST_PATTERN_COLOR_R:
- case V4L2_CID_TEST_PATTERN_COLOR_GR:
- case V4L2_CID_TEST_PATTERN_COLOR_GB:
- case V4L2_CID_TEST_PATTERN_COLOR_B:
- /*
- * Exposure related control will be handled by sensor
- * driver
- */
- ret =
- v4l2_g_ctrl(isp->inputs[asd->input_curr].camera->
- ctrl_handler, &ctrl);
- break;
- case V4L2_CID_FOCUS_ABSOLUTE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_FOCUS_STATUS:
- case V4L2_CID_FOCUS_AUTO:
- if (motor)
- ret = v4l2_g_ctrl(motor->ctrl_handler, &ctrl);
- break;
- case V4L2_CID_FLASH_STATUS:
- case V4L2_CID_FLASH_INTENSITY:
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- case V4L2_CID_FLASH_TIMEOUT:
- case V4L2_CID_FLASH_STROBE:
- case V4L2_CID_FLASH_MODE:
- case V4L2_CID_FLASH_STATUS_REGISTER:
- if (isp->flash)
- ret =
- v4l2_g_ctrl(isp->flash->ctrl_handler,
- &ctrl);
- break;
case V4L2_CID_ZOOM_ABSOLUTE:
ret = atomisp_digital_zoom(asd, 0, &ctrl.value);
break;
- case V4L2_CID_G_SKIP_FRAMES:
- ret = v4l2_subdev_call(
- isp->inputs[asd->input_curr].camera,
- sensor, g_skip_frames, (u32 *)&ctrl.value);
- break;
default:
ret = -EINVAL;
}
@@ -1583,74 +1237,16 @@ static int atomisp_camera_s_ext_ctrls(struct file *file, void *fh,
{
struct video_device *vdev = video_devdata(file);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
- struct v4l2_subdev *motor;
struct v4l2_control ctrl;
int i;
int ret = 0;
- if (!IS_ISP2401)
- motor = isp->inputs[asd->input_curr].motor;
- else
- motor = isp->motor;
-
for (i = 0; i < c->count; i++) {
struct v4l2_ctrl *ctr;
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
switch (ctrl.id) {
- case V4L2_CID_EXPOSURE_ABSOLUTE:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_EXPOSURE_METERING:
- case V4L2_CID_IRIS_ABSOLUTE:
- case V4L2_CID_VCM_TIMING:
- case V4L2_CID_VCM_SLEW:
- case V4L2_CID_3A_LOCK:
- case V4L2_CID_TEST_PATTERN:
- case V4L2_CID_TEST_PATTERN_COLOR_R:
- case V4L2_CID_TEST_PATTERN_COLOR_GR:
- case V4L2_CID_TEST_PATTERN_COLOR_GB:
- case V4L2_CID_TEST_PATTERN_COLOR_B:
- ret = v4l2_s_ctrl(NULL,
- isp->inputs[asd->input_curr].camera->
- ctrl_handler, &ctrl);
- break;
- case V4L2_CID_FOCUS_ABSOLUTE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_FOCUS_STATUS:
- case V4L2_CID_FOCUS_AUTO:
- if (motor)
- ret = v4l2_s_ctrl(NULL, motor->ctrl_handler,
- &ctrl);
- else
- ret = v4l2_s_ctrl(NULL,
- isp->inputs[asd->input_curr].
- camera->ctrl_handler, &ctrl);
- break;
- case V4L2_CID_FLASH_STATUS:
- case V4L2_CID_FLASH_INTENSITY:
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- case V4L2_CID_FLASH_TIMEOUT:
- case V4L2_CID_FLASH_STROBE:
- case V4L2_CID_FLASH_MODE:
- case V4L2_CID_FLASH_STATUS_REGISTER:
- if (isp->flash) {
- ret =
- v4l2_s_ctrl(NULL, isp->flash->ctrl_handler,
- &ctrl);
- /*
- * When flash mode is changed we need to reset
- * flash state
- */
- if (ctrl.id == V4L2_CID_FLASH_MODE) {
- asd->params.flash_state =
- ATOMISP_FLASH_IDLE;
- asd->params.num_flash_frames = 0;
- }
- }
- break;
case V4L2_CID_ZOOM_ABSOLUTE:
ret = atomisp_digital_zoom(asd, 1, &ctrl.value);
break;
@@ -1776,7 +1372,6 @@ static long atomisp_vidioc_default(struct file *file, void *fh,
bool valid_prio, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
- struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
int err;
@@ -1923,11 +1518,6 @@ static long atomisp_vidioc_default(struct file *file, void *fh,
err = atomisp_fixed_pattern_table(asd, arg);
break;
- case ATOMISP_IOC_S_EXPOSURE:
- err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- core, ioctl, cmd, arg);
- break;
-
case ATOMISP_IOC_S_ISP_SHD_TAB:
err = atomisp_set_shading_table(asd, arg);
break;
@@ -1944,10 +1534,6 @@ static long atomisp_vidioc_default(struct file *file, void *fh,
err = atomisp_set_parameters(vdev, arg);
break;
- case ATOMISP_IOC_EXT_ISP_CTRL:
- err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- core, ioctl, cmd, arg);
- break;
case ATOMISP_IOC_EXP_ID_UNLOCK:
err = atomisp_exp_id_unlock(asd, arg);
break;
@@ -1998,6 +1584,7 @@ const struct v4l2_ioctl_ops atomisp_ioctl_ops = {
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = atomisp_qbuf_wrapper,
.vidioc_dqbuf = atomisp_dqbuf_wrapper,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_default = atomisp_vidioc_default,
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index a87fc74159e2..3a3e84a035e2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -31,6 +31,7 @@
#include "atomisp_compat.h"
#include "atomisp_fops.h"
#include "atomisp_internal.h"
+#include "atomisp_ioctl.h"
const struct atomisp_in_fmt_conv atomisp_in_fmt_conv[] = {
{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, ATOMISP_INPUT_FORMAT_RAW_8, IA_CSS_BAYER_ORDER_BGGR },
@@ -148,18 +149,6 @@ static long isp_subdev_ioctl(struct v4l2_subdev *sd,
return 0;
}
-/*
- * isp_subdev_set_power - Power on/off the CCDC module
- * @sd: ISP V4L2 subdevice
- * @on: power on/off
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int isp_subdev_set_power(struct v4l2_subdev *sd, int on)
-{
- return 0;
-}
-
static int isp_subdev_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
@@ -287,35 +276,6 @@ static void isp_get_fmt_rect(struct v4l2_subdev *sd,
}
}
-static void isp_subdev_propagate(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- u32 which, uint32_t pad, uint32_t target,
- uint32_t flags)
-{
- struct v4l2_mbus_framefmt *ffmt[ATOMISP_SUBDEV_PADS_NUM];
- struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM],
- *comp[ATOMISP_SUBDEV_PADS_NUM];
-
- if (flags & V4L2_SEL_FLAG_KEEP_CONFIG)
- return;
-
- isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp);
-
- switch (pad) {
- case ATOMISP_SUBDEV_PAD_SINK: {
- struct v4l2_rect r = {0};
-
- /* Only crop target supported on sink pad. */
- r.width = ffmt[pad]->width;
- r.height = ffmt[pad]->height;
-
- atomisp_subdev_set_selection(sd, sd_state, which, pad,
- target, flags, &r);
- break;
- }
- }
-}
-
static int isp_subdev_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
@@ -390,11 +350,12 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd,
if (isp_sd->params.video_dis_en &&
isp_sd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
- /* This resolution contains 20 % of DVS slack
+ /*
+ * This resolution contains 20 % of DVS slack
* (of the desired captured image before
* scaling, or 1 / 6 of what we get from the
- * sensor) in both width and height. Remove
- * it. */
+ * sensor) in both width and height. Remove it.
+ */
crop[pad]->width = roundup(crop[pad]->width * 5 / 6,
ATOM_ISP_STEP_WIDTH);
crop[pad]->height = roundup(crop[pad]->height * 5 / 6,
@@ -541,6 +502,7 @@ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd,
case ATOMISP_SUBDEV_PAD_SINK: {
const struct atomisp_in_fmt_conv *fc =
atomisp_find_in_fmt_conv(ffmt->code);
+ struct v4l2_rect r = {};
if (!fc) {
fc = atomisp_in_fmt_conv;
@@ -551,8 +513,12 @@ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd,
*__ffmt = *ffmt;
- isp_subdev_propagate(sd, sd_state, which, pad,
- V4L2_SEL_TGT_CROP, 0);
+ /* Propagate new ffmt to selection */
+ r.width = ffmt->width;
+ r.height = ffmt->height;
+ /* Only crop target supported on sink pad. */
+ atomisp_subdev_set_selection(sd, sd_state, which, pad,
+ V4L2_SEL_TGT_CROP, 0, &r);
if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
atomisp_css_input_set_resolution(isp_sd,
@@ -618,7 +584,7 @@ static int isp_subdev_set_format(struct v4l2_subdev *sd,
/* V4L2 subdev core operations */
static const struct v4l2_subdev_core_ops isp_subdev_v4l2_core_ops = {
- .ioctl = isp_subdev_ioctl, .s_power = isp_subdev_set_power,
+ .ioctl = isp_subdev_ioctl,
.subscribe_event = isp_subdev_subscribe_event,
.unsubscribe_event = isp_subdev_unsubscribe_event,
};
@@ -658,8 +624,45 @@ static void isp_subdev_init_params(struct atomisp_sub_device *asd)
}
/* media operations */
+static int atomisp_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = container_of(entity, struct v4l2_subdev,
+ entity);
+ struct atomisp_sub_device *asd = v4l2_get_subdevdata(sd);
+ struct atomisp_device *isp = asd->isp;
+ int i;
+
+ /* ISP's source is immutable */
+ if (local != &asd->pads[ATOMISP_SUBDEV_PAD_SINK]) {
+ v4l2_err(sd, "Error pad %d does not support changing flags\n",
+ local->index);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < isp->input_cnt; i++) {
+ if (&isp->inputs[i].csi_port->entity.pads[CSI2_PAD_SOURCE] == remote)
+ break;
+ }
+
+ if (i == isp->input_cnt) {
+ v4l2_err(sd, "Error no sensor for selected CSI receiver\n");
+ return -EINVAL;
+ }
+
+ /* Turn off the sensor on link disable */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ atomisp_s_sensor_power(isp, i, 0);
+ return 0;
+ }
+
+ return atomisp_select_input(isp, i);
+}
+
static const struct media_entity_operations isp_subdev_media_ops = {
.link_validate = v4l2_subdev_link_validate,
+ .link_setup = atomisp_link_setup,
/* .set_power = v4l2_subdev_set_power, */
};
@@ -789,7 +792,7 @@ static int atomisp_init_subdev_pipe(struct atomisp_sub_device *asd,
/* Init videobuf2 queue structure */
pipe->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pipe->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ pipe->vb_queue.io_modes = VB2_MMAP | VB2_DMABUF;
pipe->vb_queue.buf_struct_size = sizeof(struct ia_css_frame);
pipe->vb_queue.ops = &atomisp_vb2_ops;
pipe->vb_queue.mem_ops = &vb2_vmalloc_memops;
@@ -823,10 +826,9 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd)
int ret;
v4l2_subdev_init(sd, &isp_subdev_v4l2_ops);
- sprintf(sd->name, "ATOMISP_SUBDEV");
+ sprintf(sd->name, "Atom ISP");
v4l2_set_subdevdata(sd, asd);
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- sd->devnode = &asd->video_out.vdev;
pads[ATOMISP_SUBDEV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[ATOMISP_SUBDEV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
index 9c1703bf439c..b6c66a5d523c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
@@ -49,6 +49,7 @@ struct atomisp_video_pipe {
struct video_device vdev;
enum v4l2_buf_type type;
struct media_pad pad;
+ struct media_pipeline pipe;
struct vb2_queue vb_queue;
/* Lock for vb_queue, when also taking isp->mutex this must be taken first! */
struct mutex vb_queue_mutex;
@@ -107,14 +108,6 @@ struct atomisp_pad_format {
struct v4l2_rect compose;
};
-/* Internal states for flash process */
-enum atomisp_flash_state {
- ATOMISP_FLASH_IDLE,
- ATOMISP_FLASH_REQUESTED,
- ATOMISP_FLASH_ONGOING,
- ATOMISP_FLASH_DONE
-};
-
/*
* This structure is used to cache the CSS parameters, it aligns to
* struct ia_css_isp_config but without un-supported and deprecated parts.
@@ -220,11 +213,6 @@ struct atomisp_subdev_params {
int dvs_ver_proj_bytes;
int dvs_hor_proj_bytes;
- /* Flash */
- int num_flash_frames;
- enum atomisp_flash_state flash_state;
- enum atomisp_frame_status last_frame_status;
-
/* Flag to check if driver needs to update params to css */
bool css_update_params_needed;
};
@@ -286,9 +274,6 @@ struct atomisp_sub_device {
struct list_head dis_stats_in_css;
spinlock_t dis_stats_lock;
- struct ia_css_frame *vf_frame; /* TODO: needed? */
- enum atomisp_frame_status frame_status[VIDEO_MAX_FRAME];
-
/* This field specifies which camera (v4l2 input) is selected. */
int input_curr;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c
deleted file mode 100644
index 92e61ee90993..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Support for Medifield PNW Camera Imaging ISP subsystem.
- *
- * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
- *
- * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
-
-#include <media/v4l2-event.h>
-#include <media/v4l2-mediabus.h>
-#include "atomisp_internal.h"
-#include "atomisp_tpg.h"
-
-static int tpg_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
-static int tpg_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
-{
- /*to fake*/
- return 0;
-}
-
-static int tpg_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *fmt = &format->format;
-
- if (format->pad)
- return -EINVAL;
- /* only raw8 grbg is supported by TPG */
- fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
- if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- *v4l2_subdev_state_get_format(sd_state, 0) = *fmt;
- return 0;
- }
- return 0;
-}
-
-static int tpg_log_status(struct v4l2_subdev *sd)
-{
- /*to fake*/
- return 0;
-}
-
-static int tpg_s_power(struct v4l2_subdev *sd, int on)
-{
- return 0;
-}
-
-static int tpg_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- /*to fake*/
- return 0;
-}
-
-static int tpg_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- /*to fake*/
- return 0;
-}
-
-static int tpg_enum_frame_ival(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- /*to fake*/
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops tpg_video_ops = {
- .s_stream = tpg_s_stream,
-};
-
-static const struct v4l2_subdev_core_ops tpg_core_ops = {
- .log_status = tpg_log_status,
- .s_power = tpg_s_power,
-};
-
-static const struct v4l2_subdev_pad_ops tpg_pad_ops = {
- .enum_mbus_code = tpg_enum_mbus_code,
- .enum_frame_size = tpg_enum_frame_size,
- .enum_frame_interval = tpg_enum_frame_ival,
- .get_fmt = tpg_get_fmt,
- .set_fmt = tpg_set_fmt,
-};
-
-static const struct v4l2_subdev_ops tpg_ops = {
- .core = &tpg_core_ops,
- .video = &tpg_video_ops,
- .pad = &tpg_pad_ops,
-};
-
-void atomisp_tpg_unregister_entities(struct atomisp_tpg_device *tpg)
-{
- media_entity_cleanup(&tpg->sd.entity);
- v4l2_device_unregister_subdev(&tpg->sd);
-}
-
-int atomisp_tpg_register_entities(struct atomisp_tpg_device *tpg,
- struct v4l2_device *vdev)
-{
- int ret;
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &tpg->sd);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- atomisp_tpg_unregister_entities(tpg);
- return ret;
-}
-
-void atomisp_tpg_cleanup(struct atomisp_device *isp)
-{
-}
-
-int atomisp_tpg_init(struct atomisp_device *isp)
-{
- struct atomisp_tpg_device *tpg = &isp->tpg;
- struct v4l2_subdev *sd = &tpg->sd;
- struct media_pad *pads = tpg->pads;
- struct media_entity *me = &sd->entity;
- int ret;
-
- tpg->isp = isp;
- v4l2_subdev_init(sd, &tpg_ops);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- strscpy(sd->name, "tpg_subdev", sizeof(sd->name));
- v4l2_set_subdevdata(sd, tpg);
-
- pads[0].flags = MEDIA_PAD_FL_SINK;
- me->function = MEDIA_ENT_F_PROC_VIDEO_ISP;
-
- ret = media_entity_pads_init(me, 1, pads);
- if (ret < 0)
- goto fail;
- return 0;
-fail:
- atomisp_tpg_cleanup(isp);
- return ret;
-}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.h b/drivers/staging/media/atomisp/pci/atomisp_tpg.h
deleted file mode 100644
index 4176e076f63d..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp_tpg.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Support for Medifield PNW Camera Imaging ISP subsystem.
- *
- * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
- *
- * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
-
-#ifndef __ATOMISP_TPG_H__
-#define __ATOMISP_TPG_H__
-
-#include <media/media-entity.h>
-#include <media/v4l2-subdev.h>
-
-struct atomisp_tpg_device {
- struct v4l2_subdev sd;
- struct atomisp_device *isp;
- struct media_pad pads[1];
-};
-
-void atomisp_tpg_cleanup(struct atomisp_device *isp);
-int atomisp_tpg_init(struct atomisp_device *isp);
-void atomisp_tpg_unregister_entities(struct atomisp_tpg_device *tpg);
-int atomisp_tpg_register_entities(struct atomisp_tpg_device *tpg,
- struct v4l2_device *vdev);
-
-#endif /* __ATOMISP_TPG_H__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index f736e54c7df3..9df0eb7044b7 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -74,13 +74,15 @@ static char firmware_name[256];
module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the default firmware name.");
-/*set to 16x16 since this is the amount of lines and pixels the sensor
-exports extra. If these are kept at the 10x8 that they were on, in yuv
-downscaling modes incorrect resolutions where requested to the sensor
-driver with strange outcomes as a result. The proper way tot do this
-would be to have a list of tables the specify the sensor res, mipi rec,
-output res, and isp output res. however since we do not have this yet,
-the chosen solution is the next best thing. */
+/*
+ * Set to 16x16 since this is the amount of lines and pixels the sensor
+ * exports extra. If these are kept at the 10x8 that they were on, in yuv
+ * downscaling modes incorrect resolutions where requested to the sensor
+ * driver with strange outcomes as a result. The proper way tot do this
+ * would be to have a list of tables the specify the sensor res, mipi rec,
+ * output res, and isp output res. however since we do not have this yet,
+ * the chosen solution is the next best thing.
+ */
int pad_w = 16;
module_param(pad_w, int, 0644);
MODULE_PARM_DESC(pad_w, "extra data for ISP processing");
@@ -503,12 +505,12 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp)
}
done:
/*
- * MRFLD WORKAROUND:
- * before powering off IUNIT, clear the pending interrupts
- * and disable the interrupt. driver should avoid writing 0
- * to IIR. It could block subsequent interrupt messages.
- * HW sighting:4568410.
- */
+ * MRFLD WORKAROUND:
+ * before powering off IUNIT, clear the pending interrupts
+ * and disable the interrupt. driver should avoid writing 0
+ * to IIR. It could block subsequent interrupt messages.
+ * HW sighting:4568410.
+ */
pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq);
irq &= ~BIT(INTR_IER);
pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq);
@@ -521,9 +523,9 @@ done:
}
/*
-* WA for DDR DVFS enable/disable
-* By default, ISP will force DDR DVFS 1600MHz before disable DVFS
-*/
+ * WA for DDR DVFS enable/disable
+ * By default, ISP will force DDR DVFS 1600MHz before disable DVFS
+ */
static void punit_ddr_dvfs_enable(bool enable)
{
int reg;
@@ -781,66 +783,38 @@ int atomisp_csi_lane_config(struct atomisp_device *isp)
static int atomisp_subdev_probe(struct atomisp_device *isp)
{
- const struct atomisp_platform_data *pdata;
- struct intel_v4l2_subdev_table *subdevs;
+ const struct intel_v4l2_subdev_table *subdevs;
int ret, mipi_port;
ret = atomisp_csi2_bridge_parse_firmware(isp);
if (ret)
return ret;
- pdata = atomisp_get_platform_data();
- if (!pdata) {
- dev_err(isp->dev, "no platform data available\n");
- return 0;
- }
-
/*
* TODO: this is left here for now to allow testing atomisp-sensor
* drivers which are still using the atomisp_gmin_platform infra before
* converting them to standard v4l2 sensor drivers using runtime-pm +
* ACPI for pm and v4l2_async_register_subdev_sensor() registration.
*/
- for (subdevs = pdata->subdevs; subdevs->type; ++subdevs) {
+ for (subdevs = atomisp_platform_get_subdevs(); subdevs->subdev; subdevs++) {
ret = v4l2_device_register_subdev(&isp->v4l2_dev, subdevs->subdev);
if (ret)
continue;
- switch (subdevs->type) {
- case RAW_CAMERA:
- if (subdevs->port >= ATOMISP_CAMERA_NR_PORTS) {
- dev_err(isp->dev, "port %d not supported\n", subdevs->port);
- break;
- }
-
- if (isp->sensor_subdevs[subdevs->port]) {
- dev_err(isp->dev, "port %d already has a sensor attached\n",
- subdevs->port);
- break;
- }
+ if (subdevs->port >= ATOMISP_CAMERA_NR_PORTS) {
+ dev_err(isp->dev, "port %d not supported\n", subdevs->port);
+ continue;
+ }
- mipi_port = atomisp_port_to_mipi_port(isp, subdevs->port);
- isp->sensor_lanes[mipi_port] = subdevs->lanes;
- isp->sensor_subdevs[subdevs->port] = subdevs->subdev;
- break;
- case CAMERA_MOTOR:
- if (isp->motor) {
- dev_warn(isp->dev, "too many atomisp motors\n");
- continue;
- }
- isp->motor = subdevs->subdev;
- break;
- case LED_FLASH:
- if (isp->flash) {
- dev_warn(isp->dev, "too many atomisp flash devices\n");
- continue;
- }
- isp->flash = subdevs->subdev;
- break;
- default:
- dev_dbg(isp->dev, "unknown subdev probed\n");
- break;
+ if (isp->sensor_subdevs[subdevs->port]) {
+ dev_err(isp->dev, "port %d already has a sensor attached\n",
+ subdevs->port);
+ continue;
}
+
+ mipi_port = atomisp_port_to_mipi_port(isp, subdevs->port);
+ isp->sensor_lanes[mipi_port] = subdevs->lanes;
+ isp->sensor_subdevs[subdevs->port] = subdevs->subdev;
}
return atomisp_csi_lane_config(isp);
@@ -852,7 +826,6 @@ static void atomisp_unregister_entities(struct atomisp_device *isp)
struct v4l2_subdev *sd, *next;
atomisp_subdev_unregister_entities(&isp->asd);
- atomisp_tpg_unregister_entities(&isp->tpg);
for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++)
atomisp_mipi_csi2_unregister_entities(&isp->csi2_port[i]);
@@ -907,12 +880,6 @@ static int atomisp_register_entities(struct atomisp_device *isp)
goto csi_and_subdev_probe_failed;
}
- ret = atomisp_tpg_register_entities(&isp->tpg, &isp->v4l2_dev);
- if (ret < 0) {
- dev_err(isp->dev, "atomisp_tpg_register_entities\n");
- goto tpg_register_failed;
- }
-
ret = atomisp_subdev_register_subdev(&isp->asd, &isp->v4l2_dev);
if (ret < 0) {
dev_err(isp->dev, "atomisp_subdev_register_subdev fail\n");
@@ -922,8 +889,6 @@ static int atomisp_register_entities(struct atomisp_device *isp)
return 0;
subdev_register_failed:
- atomisp_tpg_unregister_entities(&isp->tpg);
-tpg_register_failed:
for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++)
atomisp_mipi_csi2_unregister_entities(&isp->csi2_port[i]);
csi_and_subdev_probe_failed:
@@ -1060,20 +1025,12 @@ int atomisp_register_device_nodes(struct atomisp_device *isp)
input = &isp->inputs[isp->input_cnt];
- input->type = RAW_CAMERA;
input->port = i;
input->camera = isp->sensor_subdevs[i];
+ input->csi_port = &isp->csi2_port[i].subdev;
atomisp_init_sensor(input);
- /*
- * HACK: Currently VCM belongs to primary sensor only, but correct
- * approach must be to acquire from platform code which sensor
- * owns it.
- */
- if (i == ATOMISP_CAMERA_PORT_PRIMARY)
- input->motor = isp->motor;
-
err = media_create_pad_link(&input->camera->entity, 0,
&isp->csi2_port[i].subdev.entity,
CSI2_PAD_SINK,
@@ -1089,14 +1046,9 @@ int atomisp_register_device_nodes(struct atomisp_device *isp)
else
dev_info(isp->dev, "detected %d camera sensors\n", isp->input_cnt);
- if (isp->input_cnt < ATOM_ISP_MAX_INPUTS) {
- dev_dbg(isp->dev, "TPG detected, camera_cnt: %d\n", isp->input_cnt);
- isp->inputs[isp->input_cnt].type = TEST_PATTERN;
- isp->inputs[isp->input_cnt].port = -1;
- isp->inputs[isp->input_cnt++].camera = &isp->tpg.sd;
- } else {
- dev_warn(isp->dev, "too many atomisp inputs, TPG ignored.\n");
- }
+ mutex_lock(&isp->media_dev.graph_mutex);
+ atomisp_setup_input_links(isp);
+ mutex_unlock(&isp->media_dev.graph_mutex);
isp->asd.video_out.vdev.v4l2_dev = &isp->v4l2_dev;
isp->asd.video_out.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
@@ -1105,7 +1057,8 @@ int atomisp_register_device_nodes(struct atomisp_device *isp)
return err;
err = media_create_pad_link(&isp->asd.subdev.entity, ATOMISP_SUBDEV_PAD_SOURCE,
- &isp->asd.video_out.vdev.entity, 0, 0);
+ &isp->asd.video_out.vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
if (err)
return err;
@@ -1126,12 +1079,6 @@ static int atomisp_initialize_modules(struct atomisp_device *isp)
goto error_mipi_csi2;
}
- ret = atomisp_tpg_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "tpg initialization failed\n");
- goto error_tpg;
- }
-
ret = atomisp_subdev_init(isp);
if (ret < 0) {
dev_err(isp->dev, "ISP subdev initialization failed\n");
@@ -1141,8 +1088,6 @@ static int atomisp_initialize_modules(struct atomisp_device *isp)
return 0;
error_isp_subdev:
-error_tpg:
- atomisp_tpg_cleanup(isp);
error_mipi_csi2:
atomisp_mipi_csi2_cleanup(isp);
return ret;
@@ -1150,7 +1095,6 @@ error_mipi_csi2:
static void atomisp_uninitialize_modules(struct atomisp_device *isp)
{
- atomisp_tpg_cleanup(isp);
atomisp_mipi_csi2_cleanup(isp);
}
@@ -1235,18 +1179,14 @@ static void atomisp_pm_uninit(struct atomisp_device *isp)
static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
- const struct atomisp_platform_data *pdata;
struct atomisp_device *isp;
unsigned int start;
- int err, val;
+ u32 val;
+ int err;
/* Pointer to struct device. */
atomisp_dev = &pdev->dev;
- pdata = atomisp_get_platform_data();
- if (!pdata)
- dev_warn(&pdev->dev, "no platform data available\n");
-
start = pci_resource_start(pdev, ATOM_ISP_PCI_BAR);
dev_dbg(&pdev->dev, "start: 0x%x\n", start);
@@ -1266,8 +1206,10 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
pci_set_drvdata(pdev, isp);
- switch (id->device & ATOMISP_PCI_DEVICE_SOC_MASK) {
+ switch (id->device) {
case ATOMISP_PCI_DEVICE_SOC_MRFLD:
+ case ATOMISP_PCI_DEVICE_SOC_MRFLD_1179:
+ case ATOMISP_PCI_DEVICE_SOC_MRFLD_117A:
isp->media_dev.hw_revision =
(ATOMISP_HW_REVISION_ISP2400
<< ATOMISP_HW_REVISION_SHIFT) |
@@ -1325,7 +1267,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
pdev->d3cold_delay = 0;
break;
case ATOMISP_PCI_DEVICE_SOC_ANN:
- isp->media_dev.hw_revision = ( ATOMISP_HW_REVISION_ISP2401
+ isp->media_dev.hw_revision = (ATOMISP_HW_REVISION_ISP2401
<< ATOMISP_HW_REVISION_SHIFT);
isp->media_dev.hw_revision |= pdev->revision < 2 ?
ATOMISP_HW_STEPPING_A0 : ATOMISP_HW_STEPPING_B0;
@@ -1333,7 +1275,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
isp->hpll_freq = HPLL_FREQ_1600MHZ;
break;
case ATOMISP_PCI_DEVICE_SOC_CHT:
- isp->media_dev.hw_revision = ( ATOMISP_HW_REVISION_ISP2401
+ isp->media_dev.hw_revision = (ATOMISP_HW_REVISION_ISP2401
<< ATOMISP_HW_REVISION_SHIFT);
isp->media_dev.hw_revision |= pdev->revision < 2 ?
ATOMISP_HW_STEPPING_A0 : ATOMISP_HW_STEPPING_B0;
@@ -1363,7 +1305,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
}
if (pdev->revision <= ATOMISP_PCI_REV_BYT_A0_MAX) {
- dev_err(&pdev->dev, "revision %d is not unsupported\n", pdev->revision);
+ dev_err(&pdev->dev, "revision %d is not supported\n", pdev->revision);
return -ENODEV;
}
@@ -1420,28 +1362,25 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
*/
atomisp_css2_hw_store_32(MRFLD_CSI_RECEIVER_SELECTION_REG, 1);
- if ((id->device & ATOMISP_PCI_DEVICE_SOC_MASK) ==
- ATOMISP_PCI_DEVICE_SOC_MRFLD) {
- u32 csi_afe_trim;
-
+ switch (id->device) {
+ case ATOMISP_PCI_DEVICE_SOC_MRFLD:
+ case ATOMISP_PCI_DEVICE_SOC_MRFLD_1179:
+ case ATOMISP_PCI_DEVICE_SOC_MRFLD_117A:
/*
* Workaround for imbalance data eye issue which is observed
* on TNG B0.
*/
- pci_read_config_dword(pdev, MRFLD_PCI_CSI_AFE_TRIM_CONTROL, &csi_afe_trim);
- csi_afe_trim &= ~((MRFLD_PCI_CSI_HSRXCLKTRIM_MASK <<
- MRFLD_PCI_CSI1_HSRXCLKTRIM_SHIFT) |
- (MRFLD_PCI_CSI_HSRXCLKTRIM_MASK <<
- MRFLD_PCI_CSI2_HSRXCLKTRIM_SHIFT) |
- (MRFLD_PCI_CSI_HSRXCLKTRIM_MASK <<
- MRFLD_PCI_CSI3_HSRXCLKTRIM_SHIFT));
- csi_afe_trim |= (MRFLD_PCI_CSI1_HSRXCLKTRIM <<
- MRFLD_PCI_CSI1_HSRXCLKTRIM_SHIFT) |
- (MRFLD_PCI_CSI2_HSRXCLKTRIM <<
- MRFLD_PCI_CSI2_HSRXCLKTRIM_SHIFT) |
- (MRFLD_PCI_CSI3_HSRXCLKTRIM <<
- MRFLD_PCI_CSI3_HSRXCLKTRIM_SHIFT);
- pci_write_config_dword(pdev, MRFLD_PCI_CSI_AFE_TRIM_CONTROL, csi_afe_trim);
+ pci_read_config_dword(pdev, MRFLD_PCI_CSI_AFE_TRIM_CONTROL, &val);
+ val &= ~((MRFLD_PCI_CSI_HSRXCLKTRIM_MASK << MRFLD_PCI_CSI1_HSRXCLKTRIM_SHIFT) |
+ (MRFLD_PCI_CSI_HSRXCLKTRIM_MASK << MRFLD_PCI_CSI2_HSRXCLKTRIM_SHIFT) |
+ (MRFLD_PCI_CSI_HSRXCLKTRIM_MASK << MRFLD_PCI_CSI3_HSRXCLKTRIM_SHIFT));
+ val |= (MRFLD_PCI_CSI1_HSRXCLKTRIM << MRFLD_PCI_CSI1_HSRXCLKTRIM_SHIFT) |
+ (MRFLD_PCI_CSI2_HSRXCLKTRIM << MRFLD_PCI_CSI2_HSRXCLKTRIM_SHIFT) |
+ (MRFLD_PCI_CSI3_HSRXCLKTRIM << MRFLD_PCI_CSI3_HSRXCLKTRIM_SHIFT);
+ pci_write_config_dword(pdev, MRFLD_PCI_CSI_AFE_TRIM_CONTROL, val);
+ break;
+ default:
+ break;
}
err = atomisp_initialize_modules(isp);
diff --git a/drivers/staging/media/atomisp/pci/bits.h b/drivers/staging/media/atomisp/pci/bits.h
index 9fab02ebddc5..f7a66287d763 100644
--- a/drivers/staging/media/atomisp/pci/bits.h
+++ b/drivers/staging/media/atomisp/pci/bits.h
@@ -16,9 +16,9 @@
#ifndef _HRT_BITS_H
#define _HRT_BITS_H
-#include "defs.h"
+#include <linux/args.h>
-#define _hrt_ones(n) HRTCAT(_hrt_ones_, n)
+#define _hrt_ones(n) CONCATENATE(_hrt_ones_, n)
#define _hrt_ones_0x0 0x00000000U
#define _hrt_ones_0x1 0x00000001U
#define _hrt_ones_0x2 0x00000003U
diff --git a/drivers/staging/media/atomisp/pci/defs.h b/drivers/staging/media/atomisp/pci/defs.h
deleted file mode 100644
index 785e7a670a00..000000000000
--- a/drivers/staging/media/atomisp/pci/defs.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#ifndef _HRT_DEFS_H_
-#define _HRT_DEFS_H_
-
-#ifndef HRTCAT
-#define _HRTCAT(m, n) m##n
-#define HRTCAT(m, n) _HRTCAT(m, n)
-#endif
-
-#ifndef HRTSTR
-#define _HRTSTR(x) #x
-#define HRTSTR(x) _HRTSTR(x)
-#endif
-
-#ifndef HRTMIN
-#define HRTMIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifndef HRTMAX
-#define HRTMAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
-#endif /* _HRT_DEFS_H_ */
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h
index 48a1ace79897..1a71dbebbbe2 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h
@@ -19,7 +19,6 @@
#include <type_support.h>
#include "dma_global.h"
-#include <defs.h> /* HRTCAT() */
#include <bits.h> /* _hrt_get_bits() */
#include <hive_isp_css_defs.h> /* HIVE_DMA_NUM_CHANNELS */
#include <dma_v2_defs.h>
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
index ca1ce6689034..8f2f4e8eddd9 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
@@ -426,7 +426,6 @@ static input_system_err_t input_system_configure_channel(
case INPUT_SYSTEM_SOURCE_SENSOR:
error = input_system_configure_channel_sensor(channel);
break;
- case INPUT_SYSTEM_SOURCE_TPG:
case INPUT_SYSTEM_SOURCE_PRBS:
case INPUT_SYSTEM_SOURCE_FIFO:
default:
@@ -814,7 +813,6 @@ static input_system_err_t configuration_to_registers(void)
//...
break;
- case INPUT_SYSTEM_SOURCE_TPG:
case INPUT_SYSTEM_SOURCE_PRBS:
case INPUT_SYSTEM_SOURCE_FIFO:
break;
@@ -1065,42 +1063,6 @@ input_system_err_t input_system_prbs_channel_cfg(
return input_system_configure_channel(channel);
}
-input_system_err_t input_system_tpg_channel_cfg(
- u32 ch_id,
- u32 nof_frames,//not used yet
- u32 x_mask,
- u32 y_mask,
- u32 x_delta,
- u32 y_delta,
- u32 xy_mask,
- u32 sync_gen_width,
- u32 sync_gen_height,
- u32 sync_gen_hblank_cycles,
- u32 sync_gen_vblank_cycles,
- target_cfg2400_t target
-)
-{
- channel_cfg_t channel;
-
- (void)nof_frames;
-
- channel.ch_id = ch_id;
- channel.source_type = INPUT_SYSTEM_SOURCE_TPG;
-
- channel.source_cfg.tpg_cfg.x_mask = x_mask;
- channel.source_cfg.tpg_cfg.y_mask = y_mask;
- channel.source_cfg.tpg_cfg.x_delta = x_delta;
- channel.source_cfg.tpg_cfg.y_delta = y_delta;
- channel.source_cfg.tpg_cfg.xy_mask = xy_mask;
- channel.source_cfg.tpg_cfg.sync_gen_cfg.width = sync_gen_width;
- channel.source_cfg.tpg_cfg.sync_gen_cfg.height = sync_gen_height;
- channel.source_cfg.tpg_cfg.sync_gen_cfg.hblank_cycles = sync_gen_hblank_cycles;
- channel.source_cfg.tpg_cfg.sync_gen_cfg.vblank_cycles = sync_gen_vblank_cycles;
-
- channel.target_cfg = target;
- return input_system_configure_channel(channel);
-}
-
// MW: Don't use system specific names, (even in system specific files) "cfg2400" -> cfg
input_system_err_t input_system_gpfifo_channel_cfg(
u32 ch_id,
diff --git a/drivers/staging/media/atomisp/pci/hive_types.h b/drivers/staging/media/atomisp/pci/hive_types.h
index 55d36931f079..34f462c0c9f9 100644
--- a/drivers/staging/media/atomisp/pci/hive_types.h
+++ b/drivers/staging/media/atomisp/pci/hive_types.h
@@ -17,25 +17,6 @@
#define _HRT_HIVE_TYPES_H
#include "version.h"
-#include "defs.h"
-
-#ifndef HRTCAT3
-#define _HRTCAT3(m, n, o) m##n##o
-#define HRTCAT3(m, n, o) _HRTCAT3(m, n, o)
-#endif
-
-#ifndef HRTCAT4
-#define _HRTCAT4(m, n, o, p) m##n##o##p
-#define HRTCAT4(m, n, o, p) _HRTCAT4(m, n, o, p)
-#endif
-
-#ifndef HRTMIN
-#define HRTMIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifndef HRTMAX
-#define HRTMAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
/* boolean data type */
typedef unsigned int hive_bool;
diff --git a/drivers/staging/media/atomisp/pci/ia_css.h b/drivers/staging/media/atomisp/pci/ia_css.h
index d83e1ae5b0b3..421056287853 100644
--- a/drivers/staging/media/atomisp/pci/ia_css.h
+++ b/drivers/staging/media/atomisp/pci/ia_css.h
@@ -42,7 +42,6 @@
#include "ia_css_properties.h"
#include "ia_css_stream_format.h"
#include "ia_css_stream_public.h"
-#include "ia_css_tpg.h"
#include "ia_css_version.h"
#include "ia_css_mmu.h"
#include "ia_css_morph.h"
diff --git a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
index 7ba464abf447..a26d9598e400 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
@@ -137,12 +137,6 @@ enum ia_css_frame_delay {
IA_CSS_FRAME_DELAY_2 /** Frame delay = 2 */
};
-enum ia_css_frame_flash_state {
- IA_CSS_FRAME_FLASH_STATE_NONE,
- IA_CSS_FRAME_FLASH_STATE_PARTIAL,
- IA_CSS_FRAME_FLASH_STATE_FULL
-};
-
/* Frame structure. This structure describes an image buffer or frame.
* This is the main structure used for all input and output images.
*/
@@ -176,7 +170,6 @@ struct ia_css_frame {
* binary, we use output port, but we expect VF_OUTPUT_DONE event
*/
enum ia_css_buffer_type buf_type;
- enum ia_css_frame_flash_state flash_state;
unsigned int exp_id;
/** exposure id, see ia_css_event_public.h for more detail */
u32 isp_config_id; /** Unique ID to track which config was actually applied to a particular frame */
@@ -202,7 +195,6 @@ struct ia_css_frame {
.frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
.dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID, \
.buf_type = IA_CSS_BUFFER_TYPE_INVALID, \
- .flash_state = IA_CSS_FRAME_FLASH_STATE_NONE, \
}
/* @brief Allocate a CSS frame structure
diff --git a/drivers/staging/media/atomisp/pci/ia_css_stream_public.h b/drivers/staging/media/atomisp/pci/ia_css_stream_public.h
index 47846ece8d64..961c61288083 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_stream_public.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_stream_public.h
@@ -24,7 +24,6 @@
#include "ia_css_types.h"
#include "ia_css_pipe_public.h"
#include "ia_css_metadata.h"
-#include "ia_css_tpg.h"
#include "ia_css_prbs.h"
#include "ia_css_input_port.h"
@@ -34,7 +33,6 @@
enum ia_css_input_mode {
IA_CSS_INPUT_MODE_SENSOR, /** data from sensor */
IA_CSS_INPUT_MODE_FIFO, /** data from input-fifo */
- IA_CSS_INPUT_MODE_TPG, /** data from test-pattern generator */
IA_CSS_INPUT_MODE_PRBS, /** data from pseudo-random bit stream */
IA_CSS_INPUT_MODE_MEMORY, /** data from a frame in memory */
IA_CSS_INPUT_MODE_BUFFERED_SENSOR /** data is sent through mipi buffer */
@@ -91,7 +89,6 @@ struct ia_css_stream_config {
enum ia_css_input_mode mode; /** Input mode */
union {
struct ia_css_input_port port; /** Port, for sensor only. */
- struct ia_css_tpg_config tpg; /** TPG configuration */
struct ia_css_prbs_config prbs; /** PRBS configuration */
} source; /** Source of input data */
unsigned int channel_id; /** Channel on which input data
@@ -460,20 +457,6 @@ ia_css_stream_send_input_embedded_line(const struct ia_css_stream *stream,
void
ia_css_stream_end_input_frame(const struct ia_css_stream *stream);
-/* @brief send a request flash command to SP
- *
- * @param[in] stream The stream.
- * @return None
- *
- * Driver needs to call this function to send a flash request command
- * to SP, SP will be responsible for switching on/off the flash at proper
- * time. Due to the SP multi-threading environment, this request may have
- * one-frame delay, the driver needs to check the flashed flag in frame info
- * to determine which frame is being flashed.
- */
-void
-ia_css_stream_request_flash(struct ia_css_stream *stream);
-
/* @brief Configure a stream with filter coefficients.
* @deprecated {Replaced by
* ia_css_pipe_set_isp_config_on_pipe()}
diff --git a/drivers/staging/media/atomisp/pci/ia_css_tpg.h b/drivers/staging/media/atomisp/pci/ia_css_tpg.h
deleted file mode 100644
index 8c744bedb0a6..000000000000
--- a/drivers/staging/media/atomisp/pci/ia_css_tpg.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#ifndef __IA_CSS_TPG_H
-#define __IA_CSS_TPG_H
-
-/* @file
- * This file contains support for the test pattern generator (TPG)
- */
-
-/* Enumerate the TPG IDs.
- */
-enum ia_css_tpg_id {
- IA_CSS_TPG_ID0,
- IA_CSS_TPG_ID1,
- IA_CSS_TPG_ID2
-};
-
-/**
- * Maximum number of TPG IDs.
- *
- * Make sure the value of this define gets changed to reflect the correct
- * number of ia_css_tpg_id enum if you add/delete an item in the enum.
- */
-#define N_CSS_TPG_IDS (IA_CSS_TPG_ID2 + 1)
-
-/* Enumerate the TPG modes.
- */
-enum ia_css_tpg_mode {
- IA_CSS_TPG_MODE_RAMP,
- IA_CSS_TPG_MODE_CHECKERBOARD,
- IA_CSS_TPG_MODE_FRAME_BASED_COLOR,
- IA_CSS_TPG_MODE_MONO
-};
-
-/* @brief Configure the test pattern generator.
- *
- * Configure the Test Pattern Generator, the way these values are used to
- * generate the pattern can be seen in the HRT extension for the test pattern
- * generator:
- * devices/test_pat_gen/hrt/include/test_pat_gen.h: hrt_calc_tpg_data().
- *
- * This interface is deprecated, it is not portable -> move to input system API
- *
-@code
-unsigned int test_pattern_value(unsigned int x, unsigned int y)
-{
- unsigned int x_val, y_val;
- if (x_delta > 0) (x_val = (x << x_delta) & x_mask;
- else (x_val = (x >> -x_delta) & x_mask;
- if (y_delta > 0) (y_val = (y << y_delta) & y_mask;
- else (y_val = (y >> -y_delta) & x_mask;
- return (x_val + y_val) & xy_mask;
-}
-@endcode
- */
-struct ia_css_tpg_config {
- enum ia_css_tpg_id id;
- enum ia_css_tpg_mode mode;
- unsigned int x_mask;
- int x_delta;
- unsigned int y_mask;
- int y_delta;
- unsigned int xy_mask;
-};
-
-#endif /* __IA_CSS_TPG_H */
diff --git a/drivers/staging/media/atomisp/pci/isp2400_input_system_global.h b/drivers/staging/media/atomisp/pci/isp2400_input_system_global.h
index 3ff61faf0621..c00acf764b93 100644
--- a/drivers/staging/media/atomisp/pci/isp2400_input_system_global.h
+++ b/drivers/staging/media/atomisp/pci/isp2400_input_system_global.h
@@ -35,7 +35,6 @@ typedef enum {
typedef enum {
INPUT_SYSTEM_SOURCE_SENSOR = 0,
INPUT_SYSTEM_SOURCE_FIFO,
- INPUT_SYSTEM_SOURCE_TPG,
INPUT_SYSTEM_SOURCE_PRBS,
INPUT_SYSTEM_SOURCE_MEMORY,
N_INPUT_SYSTEM_SOURCE
diff --git a/drivers/staging/media/atomisp/pci/isp2400_input_system_public.h b/drivers/staging/media/atomisp/pci/isp2400_input_system_public.h
index 523c948923f3..03b7ab7a70d9 100644
--- a/drivers/staging/media/atomisp/pci/isp2400_input_system_public.h
+++ b/drivers/staging/media/atomisp/pci/isp2400_input_system_public.h
@@ -308,21 +308,6 @@ input_system_err_t input_system_prbs_channel_cfg(
target_cfg2400_t target
);
-input_system_err_t input_system_tpg_channel_cfg(
- u32 ch_id,
- u32 nof_frames,//not used yet
- u32 x_mask,
- u32 y_mask,
- u32 x_delta,
- u32 y_delta,
- u32 xy_mask,
- u32 sync_gen_width,
- u32 sync_gen_height,
- u32 sync_gen_hblank_cycles,
- u32 sync_gen_vblank_cycles,
- target_cfg2400_t target
-);
-
input_system_err_t input_system_gpfifo_channel_cfg(
u32 ch_id,
u32 nof_frames,
diff --git a/drivers/staging/media/atomisp/pci/isp2401_input_system_global.h b/drivers/staging/media/atomisp/pci/isp2401_input_system_global.h
index e3c86069b390..b116be415074 100644
--- a/drivers/staging/media/atomisp/pci/isp2401_input_system_global.h
+++ b/drivers/staging/media/atomisp/pci/isp2401_input_system_global.h
@@ -39,7 +39,6 @@
typedef enum {
INPUT_SYSTEM_SOURCE_TYPE_UNDEFINED = 0,
INPUT_SYSTEM_SOURCE_TYPE_SENSOR,
- INPUT_SYSTEM_SOURCE_TYPE_TPG,
INPUT_SYSTEM_SOURCE_TYPE_PRBS,
N_INPUT_SYSTEM_SOURCE_TYPE
} input_system_source_type_t;
diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
index 3e92794555ec..9982e77716a7 100644
--- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
+++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
@@ -31,6 +31,7 @@
#define __INLINE_STREAM2MMIO__
#endif
+#include <linux/args.h>
#include <linux/string.h> /* for strscpy() */
#include "ia_css_debug.h"
@@ -861,7 +862,7 @@ void ia_css_debug_wake_up_sp(void)
}
#define FIND_DMEM_PARAMS_TYPE(stream, kernel, type) \
- (struct HRTCAT(HRTCAT(sh_css_isp_, type), _params) *) \
+ (struct CONCATENATE(CONCATENATE(sh_css_isp_, type), _params) *) \
findf_dmem_params(stream, offsetof(struct ia_css_memory_offsets, dmem.kernel))
#define FIND_DMEM_PARAMS(stream, kernel) FIND_DMEM_PARAMS_TYPE(stream, kernel, kernel)
@@ -1548,23 +1549,6 @@ ia_css_debug_dump_stream_config_source(
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "compression: %d\n",
config->source.port.compression.type);
break;
- case IA_CSS_INPUT_MODE_TPG:
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "source.tpg\n");
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "id: %d\n",
- config->source.tpg.id);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "mode: %d\n",
- config->source.tpg.mode);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "x_mask: 0x%x\n",
- config->source.tpg.x_mask);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "x_delta: %d\n",
- config->source.tpg.x_delta);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "y_mask: 0x%x\n",
- config->source.tpg.y_mask);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "y_delta: %d\n",
- config->source.tpg.y_delta);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "xy_mask: 0x%x\n",
- config->source.tpg.xy_mask);
- break;
case IA_CSS_INPUT_MODE_PRBS:
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "source.prbs\n");
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "id: %d\n",
diff --git a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
index 86254888f676..7b5603e4e173 100644
--- a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
+++ b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
@@ -382,17 +382,6 @@ int ia_css_ifmtr_configure(struct ia_css_stream_config *config,
vectors_per_buffer = buffer_height * buffer_width / ISP_VEC_NELEMS;
- if (config->mode == IA_CSS_INPUT_MODE_TPG &&
- ((binary && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_VIDEO) ||
- (!binary))) {
- /* !binary -> sp raw copy pipe */
- /* workaround for TPG in video mode */
- start_line = 0;
- start_column = 0;
- cropped_height -= start_line;
- width_a -= start_column;
- }
-
if_a_config.start_line = start_line;
if_a_config.start_column = start_column;
if_a_config.left_padding = left_padding / deinterleaving;
diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
index 269a81190577..52483498239d 100644
--- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
+++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
@@ -96,12 +96,6 @@ static void release_be_lut_entry(
csi_mipi_packet_type_t packet_type,
csi_rx_backend_lut_entry_t *entry);
-static bool calculate_tpg_cfg(
- input_system_channel_t *channel,
- input_system_input_port_t *input_port,
- isp2401_input_system_cfg_t *isys_cfg,
- pixelgen_tpg_cfg_t *cfg);
-
static bool calculate_prbs_cfg(
input_system_channel_t *channel,
input_system_input_port_t *input_port,
@@ -517,13 +511,6 @@ static bool calculate_input_system_input_port_cfg(
rc &= calculate_be_cfg(input_port, isys_cfg, true,
&input_port_cfg->csi_rx_cfg.md_backend_cfg);
break;
- case INPUT_SYSTEM_SOURCE_TYPE_TPG:
- rc = calculate_tpg_cfg(
- channel,
- input_port,
- isys_cfg,
- &input_port_cfg->pixelgen_cfg.tpg_cfg);
- break;
case INPUT_SYSTEM_SOURCE_TYPE_PRBS:
rc = calculate_prbs_cfg(
channel,
@@ -633,17 +620,6 @@ static void release_be_lut_entry(
ia_css_isys_csi_rx_lut_rmgr_release(backend, packet_type, entry);
}
-static bool calculate_tpg_cfg(
- input_system_channel_t *channel,
- input_system_input_port_t *input_port,
- isp2401_input_system_cfg_t *isys_cfg,
- pixelgen_tpg_cfg_t *cfg)
-{
- memcpy(cfg, &isys_cfg->tpg_port_attr, sizeof(pixelgen_tpg_cfg_t));
-
- return true;
-}
-
static bool calculate_prbs_cfg(
input_system_channel_t *channel,
input_system_input_port_t *input_port,
@@ -703,9 +679,7 @@ static bool calculate_stream2mmio_cfg(
cfg->bits_per_pixel = metadata ? isys_cfg->metadata.bits_per_pixel :
isys_cfg->input_port_resolution.bits_per_pixel;
- cfg->enable_blocking =
- ((isys_cfg->mode == INPUT_SYSTEM_SOURCE_TYPE_TPG) ||
- (isys_cfg->mode == INPUT_SYSTEM_SOURCE_TYPE_PRBS));
+ cfg->enable_blocking = isys_cfg->mode == INPUT_SYSTEM_SOURCE_TYPE_PRBS;
return true;
}
diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c
index 938a4ea89c59..42a69b26db01 100644
--- a/drivers/staging/media/atomisp/pci/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/sh_css.c
@@ -462,9 +462,6 @@ ia_css_stream_input_format_bits_per_pixel(struct ia_css_stream *stream)
return bpp;
}
-/* TODO: move define to proper file in tools */
-#define GP_ISEL_TPG_MODE 0x90058
-
static int
sh_css_config_input_network_2400(struct ia_css_stream *stream)
{
@@ -500,21 +497,16 @@ sh_css_config_input_network_2400(struct ia_css_stream *stream)
return err;
}
- if (stream->config.mode == IA_CSS_INPUT_MODE_TPG ||
- stream->config.mode == IA_CSS_INPUT_MODE_PRBS) {
- unsigned int hblank_cycles = 100,
- vblank_lines = 6,
- width,
- height,
- vblank_cycles;
- width = (stream->config.input_config.input_res.width) / (1 +
- (stream->config.pixels_per_clock == 2));
+ if (stream->config.mode == IA_CSS_INPUT_MODE_PRBS) {
+ unsigned int width, height, vblank_cycles;
+ const unsigned int hblank_cycles = 100;
+ const unsigned int vblank_lines = 6;
+
+ width = (stream->config.input_config.input_res.width) /
+ (1 + (stream->config.pixels_per_clock == 2));
height = stream->config.input_config.input_res.height;
vblank_cycles = vblank_lines * (width + hblank_cycles);
- sh_css_sp_configure_sync_gen(width, height, hblank_cycles,
- vblank_cycles);
- if (pipe->stream->config.mode == IA_CSS_INPUT_MODE_TPG)
- ia_css_device_store_uint32(GP_ISEL_TPG_MODE, 0);
+ sh_css_sp_configure_sync_gen(width, height, hblank_cycles, vblank_cycles);
}
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"sh_css_config_input_network() leave:\n");
@@ -654,16 +646,6 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_id(
rc = true;
switch (stream_cfg->mode) {
- case IA_CSS_INPUT_MODE_TPG:
-
- if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID0)
- isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT0_ID;
- else if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID1)
- isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT1_ID;
- else if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID2)
- isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT2_ID;
-
- break;
case IA_CSS_INPUT_MODE_PRBS:
if (stream_cfg->source.prbs.id == IA_CSS_PRBS_ID0)
@@ -700,11 +682,6 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_type(
rc = true;
switch (stream_cfg->mode) {
- case IA_CSS_INPUT_MODE_TPG:
-
- isys_stream_descr->mode = INPUT_SYSTEM_SOURCE_TYPE_TPG;
-
- break;
case IA_CSS_INPUT_MODE_PRBS:
isys_stream_descr->mode = INPUT_SYSTEM_SOURCE_TYPE_PRBS;
@@ -733,54 +710,6 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_attr(
rc = true;
switch (stream_cfg->mode) {
- case IA_CSS_INPUT_MODE_TPG:
- if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_RAMP)
- isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_RAMP;
- else if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_CHECKERBOARD)
- isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_CHBO;
- else if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_MONO)
- isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_MONO;
- else
- rc = false;
-
- /*
- * TODO
- * - Make "color_cfg" as part of "ia_css_tpg_config".
- */
- isys_stream_descr->tpg_port_attr.color_cfg.R1 = 51;
- isys_stream_descr->tpg_port_attr.color_cfg.G1 = 102;
- isys_stream_descr->tpg_port_attr.color_cfg.B1 = 255;
- isys_stream_descr->tpg_port_attr.color_cfg.R2 = 0;
- isys_stream_descr->tpg_port_attr.color_cfg.G2 = 100;
- isys_stream_descr->tpg_port_attr.color_cfg.B2 = 160;
-
- isys_stream_descr->tpg_port_attr.mask_cfg.h_mask =
- stream_cfg->source.tpg.x_mask;
- isys_stream_descr->tpg_port_attr.mask_cfg.v_mask =
- stream_cfg->source.tpg.y_mask;
- isys_stream_descr->tpg_port_attr.mask_cfg.hv_mask =
- stream_cfg->source.tpg.xy_mask;
-
- isys_stream_descr->tpg_port_attr.delta_cfg.h_delta =
- stream_cfg->source.tpg.x_delta;
- isys_stream_descr->tpg_port_attr.delta_cfg.v_delta =
- stream_cfg->source.tpg.y_delta;
-
- /*
- * TODO
- * - Make "sync_gen_cfg" as part of "ia_css_tpg_config".
- */
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.hblank_cycles = 100;
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.vblank_cycles = 100;
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.pixels_per_clock =
- stream_cfg->pixels_per_clock;
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.nr_of_frames = (uint32_t)~(0x0);
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.pixels_per_line =
- stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.width;
- isys_stream_descr->tpg_port_attr.sync_gen_cfg.lines_per_frame =
- stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.height;
-
- break;
case IA_CSS_INPUT_MODE_PRBS:
isys_stream_descr->prbs_port_attr.seed0 = stream_cfg->source.prbs.seed;
@@ -2903,7 +2832,6 @@ init_vf_frameinfo_defaults(struct ia_css_pipe *pipe,
assert(vf_frame);
sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->frame_info, idx);
- vf_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx, thread_id, &queue_id);
vf_frame->dynamic_queue_id = queue_id;
@@ -3081,7 +3009,6 @@ init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe,
in_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe);
ia_css_frame_info_set_width(&in_frame->frame_info,
pipe->stream->config.input_config.input_res.width, 0);
- in_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_INPUT_FRAME, thread_id, &queue_id);
in_frame->dynamic_queue_id = queue_id;
@@ -3109,7 +3036,6 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe,
assert(out_frame);
sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, idx);
- out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx, thread_id, &queue_id);
out_frame->dynamic_queue_id = queue_id;
@@ -3890,12 +3816,6 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
buffer->exp_id = ddr_buffer.payload.frame.exp_id;
frame->exp_id = ddr_buffer.payload.frame.exp_id;
frame->isp_config_id = ddr_buffer.payload.frame.isp_parameters_id;
- if (ddr_buffer.payload.frame.flashed == 1)
- frame->flash_state =
- IA_CSS_FRAME_FLASH_STATE_PARTIAL;
- if (ddr_buffer.payload.frame.flashed == 2)
- frame->flash_state =
- IA_CSS_FRAME_FLASH_STATE_FULL;
frame->valid = pipe->num_invalid_frames == 0;
if (!frame->valid)
pipe->num_invalid_frames--;
@@ -4690,6 +4610,7 @@ static int load_video_binaries(struct ia_css_pipe *pipe)
sizeof(struct ia_css_binary),
GFP_KERNEL);
if (!mycs->yuv_scaler_binary) {
+ mycs->num_yuv_scaler = 0;
err = -ENOMEM;
return err;
}
@@ -6856,8 +6777,6 @@ create_host_copy_pipeline(struct ia_css_pipe *pipe,
ia_css_pipeline_clean(me);
/* Construct out_frame info */
- out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
-
if (copy_on_sp(pipe) &&
pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
ia_css_frame_info_init(&out_frame->frame_info, JPEG_BYTES, 1,
@@ -6905,7 +6824,6 @@ create_host_isyscopy_capture_pipeline(struct ia_css_pipe *pipe)
err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, 0);
if (err)
return err;
- out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, thread_id, &queue_id);
out_frame->dynamic_queue_id = queue_id;
@@ -7560,27 +7478,6 @@ int ia_css_stream_capture(struct ia_css_stream *stream, int num_captures,
return return_err;
}
-void ia_css_stream_request_flash(struct ia_css_stream *stream)
-{
- (void)stream;
-
- assert(stream);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_stream_request_flash() enter: void\n");
-
- if (!IS_ISP2401 || sh_css_sp_is_running()) {
- if (!sh_css_write_host2sp_command(host2sp_cmd_start_flash) && IS_ISP2401) {
- IA_CSS_ERROR("Call to 'sh-css_write_host2sp_command()' failed");
- ia_css_debug_dump_sp_sw_debug_info();
- }
- } else {
- IA_CSS_LOG("SP is not running!");
- }
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_stream_request_flash() leave: return_void\n");
-}
-
static void
sh_css_init_host_sp_control_vars(void)
{
@@ -8152,23 +8049,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
if (!IS_ISP2401)
ia_css_stream_configure_rx(curr_stream);
break;
- case IA_CSS_INPUT_MODE_TPG:
- if (!IS_ISP2401) {
- IA_CSS_LOG("tpg_configuration: x_mask=%d, y_mask=%d, x_delta=%d, y_delta=%d, xy_mask=%d",
- curr_stream->config.source.tpg.x_mask,
- curr_stream->config.source.tpg.y_mask,
- curr_stream->config.source.tpg.x_delta,
- curr_stream->config.source.tpg.y_delta,
- curr_stream->config.source.tpg.xy_mask);
-
- sh_css_sp_configure_tpg(
- curr_stream->config.source.tpg.x_mask,
- curr_stream->config.source.tpg.y_mask,
- curr_stream->config.source.tpg.x_delta,
- curr_stream->config.source.tpg.y_delta,
- curr_stream->config.source.tpg.xy_mask);
- }
- break;
case IA_CSS_INPUT_MODE_PRBS:
if (!IS_ISP2401) {
IA_CSS_LOG("mode prbs");
diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h
index 2349eb4d3767..bef2b8c5132b 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_internal.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h
@@ -360,7 +360,6 @@ struct sh_css_sp_config {
} input_formatter;
sync_generator_cfg_t sync_gen;
- tpg_cfg_t tpg;
prbs_cfg_t prbs;
input_system_cfg_t input_circuit;
u8 input_circuit_cfg_changed;
diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/sh_css_mipi.c
index 6e11fd771938..80f0395cc560 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_mipi.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.c
@@ -489,7 +489,7 @@ free_mipi_frames(struct ia_css_pipe *pipe)
}
} else { /* pipe ==NULL */
/* AM TEMP: free-ing all mipi buffers just like a legacy code. */
- for (port = CSI_PORT0_ID; port < N_CSI_PORTS; port++) {
+ for (port = 0; port < N_CSI_PORTS; port++) {
unsigned int i;
for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c
index cd7f5a3fecaa..29e5bee78c20 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_sp.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c
@@ -108,11 +108,6 @@ copy_isp_stage_to_sp_stage(void)
sh_css_isp_stage.binary_info.iterator.row_stripes_overlap_lines;
sh_css_sp_stage.top_cropping = (uint16_t)
sh_css_isp_stage.binary_info.pipeline.top_cropping;
- /* moved to sh_css_sp_init_stage
- sh_css_sp_stage.enable.vf_output =
- sh_css_isp_stage.binary_info.enable.vf_veceven ||
- sh_css_isp_stage.binary_info.num_output_pins > 1;
- */
sh_css_sp_stage.enable.sdis = sh_css_isp_stage.binary_info.enable.dis;
sh_css_sp_stage.enable.s3a = sh_css_isp_stage.binary_info.enable.s3a;
}
@@ -187,7 +182,7 @@ sh_css_sp_get_debug_state(struct sh_css_sp_debug_state *state)
(void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
for (i = 0; i < sizeof(*state) / sizeof(int); i++)
- ((unsigned *)state)[i] = load_sp_array_uint(sp_output, i + offset);
+ ((unsigned int *)state)[i] = load_sp_array_uint(sp_output, i + offset);
}
#endif
@@ -411,18 +406,18 @@ sh_css_copy_buffer_attr_to_spbuffer(struct ia_css_buffer_sp *dest_buf,
*/
assert(queue_id < SH_CSS_MAX_NUM_QUEUES);
- /* Klocwork assumes assert can be disabled;
- Since we can get there with any type, and it does not
- know that frame_in->dynamic_data_index can only be set
- for one of the types in the assert) it has to assume we
- can get here for any type. however this could lead to an
- out of bounds reference when indexing buf_type about 10
- lines below. In order to satisfy KW an additional if
- has been added. This one will always yield true.
+ /*
+ * Klocwork assumes assert can be disabled;
+ * Since we can get there with any type, and it does not
+ * know that frame_in->dynamic_data_index can only be set
+ * for one of the types in the assert) it has to assume we
+ * can get here for any type. however this could lead to an
+ * out of bounds reference when indexing buf_type about 10
+ * lines below. In order to satisfy KW an additional if
+ * has been added. This one will always yield true.
*/
- if ((queue_id < SH_CSS_MAX_NUM_QUEUES)) {
+ if (queue_id < SH_CSS_MAX_NUM_QUEUES)
dest_buf->buf_src.queue_id = queue_id;
- }
} else {
assert(xmem_addr != mmgr_EXCEPTION);
dest_buf->buf_src.xmem_addr = xmem_addr;
@@ -515,7 +510,8 @@ sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out,
frame_in->planes.binary.data.offset;
break;
default:
- /* This should not happen, but in case it does,
+ /*
+ * This should not happen, but in case it does,
* nullify the planes
*/
memset(&sp_frame_out->planes, 0, sizeof(sp_frame_out->planes));
@@ -643,8 +639,6 @@ void sh_css_sp_set_if_configs(
*config_b;
sh_css_sp_group.config.input_formatter.b_changed = true;
}
-
- return;
}
void
@@ -676,20 +670,6 @@ sh_css_sp_configure_sync_gen(int width, int height,
}
void
-sh_css_sp_configure_tpg(int x_mask,
- int y_mask,
- int x_delta,
- int y_delta,
- int xy_mask)
-{
- sh_css_sp_group.config.tpg.x_mask = x_mask;
- sh_css_sp_group.config.tpg.y_mask = y_mask;
- sh_css_sp_group.config.tpg.x_delta = x_delta;
- sh_css_sp_group.config.tpg.y_delta = y_delta;
- sh_css_sp_group.config.tpg.xy_mask = xy_mask;
-}
-
-void
sh_css_sp_configure_prbs(int seed)
{
sh_css_sp_group.config.prbs.seed = seed;
@@ -733,7 +713,8 @@ sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args)
/* we don't pass this error back to the upper layer, so we add a assert here
because we actually hit the error here but it still works by accident... */
- if (err) assert(false);
+ if (err)
+ assert(false);
return err;
}
@@ -748,7 +729,8 @@ sh_css_sp_init_group(bool two_ppc,
sh_css_sp_group.config.no_isp_sync = (uint8_t)no_isp_sync;
/* decide whether the frame is processed online or offline */
- if (if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED) return;
+ if (if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED)
+ return;
if (!IS_ISP2401) {
assert(if_config_index < SH_CSS_MAX_IF_CONFIGS);
@@ -860,9 +842,9 @@ initialize_isp_states(const struct ia_css_binary *binary)
if (!binary->info->mem_offsets.offsets.state)
return;
- for (i = 0; i < IA_CSS_NUM_STATE_IDS; i++) {
+
+ for (i = 0; i < IA_CSS_NUM_STATE_IDS; i++)
ia_css_kernel_init_state[i](binary);
- }
}
static void
@@ -878,9 +860,9 @@ initialize_stage_frames(struct ia_css_frames_sp *frames)
unsigned int i;
initialize_frame_buffer_attribute(&frames->in.buf_attr);
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
initialize_frame_buffer_attribute(&frames->out[i].buf_attr);
- }
+
initialize_frame_buffer_attribute(&frames->out_vf.buf_attr);
initialize_frame_buffer_attribute(&frames->s3a_buf);
initialize_frame_buffer_attribute(&frames->dvs_buf);
@@ -952,9 +934,10 @@ sh_css_sp_init_stage(struct ia_css_binary *binary,
sh_css_sp_stage.isp_copy_output = (uint8_t)args->copy_output;
sh_css_sp_stage.enable.vf_output = (args->out_vf_frame != NULL);
- /* Copy the frame infos first, to be overwritten by the frames,
- if these are present.
- */
+ /*
+ * Copy the frame infos first, to be overwritten by the frames,
+ * if these are present.
+ */
sh_css_sp_stage.frames.effective_in_res.width = binary->effective_in_frame_res.width;
sh_css_sp_stage.frames.effective_in_res.height = binary->effective_in_frame_res.height;
@@ -1031,10 +1014,12 @@ sh_css_sp_init_stage(struct ia_css_binary *binary,
initialize_isp_states(binary);
- /* we do this only for preview pipe because in fill_binary_info function
+ /*
+ * We do this only for preview pipe because in fill_binary_info function
* we assign vf_out res to out res, but for ISP internal processing, we need
* the original out res. for video pipe, it has two output pins --- out and
- * vf_out, so it can keep these two resolutions already. */
+ * vf_out, so it can keep these two resolutions already.
+ */
if (binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW &&
(binary->vf_downscale_log2 > 0)) {
/* TODO: Remove this after preview output decimation is fixed
@@ -1070,20 +1055,23 @@ sp_init_stage(struct ia_css_pipeline_stage *stage,
*/
const char *binary_name = "";
const struct ia_css_binary_xinfo *info = NULL;
- /* note: the var below is made static as it is quite large;
- if it is not static it ends up on the stack which could
- cause issues for drivers
- */
+ /*
+ * Note: the var below is made static as it is quite large;
+ * if it is not static it ends up on the stack which could
+ * cause issues for drivers
+ */
static struct ia_css_binary tmp_binary;
const struct ia_css_blob_info *blob_info = NULL;
struct ia_css_isp_param_css_segments isp_mem_if;
- /* LA: should be ia_css_data, should not contain host pointer.
- However, CSS/DDR pointer is not available yet.
- Hack is to store it in params->ddr_ptrs and then copy it late in the SP just before vmem init.
- TODO: Call this after CSS/DDR allocation and store that pointer.
- Best is to allocate it at stage creation time together with host pointer.
- Remove vmem from params.
- */
+ /*
+ * LA: should be ia_css_data, should not contain host pointer.
+ * However, CSS/DDR pointer is not available yet.
+ * Hack is to store it in params->ddr_ptrs and then copy it late in
+ * the SP just before vmem init.
+ * TODO: Call this after CSS/DDR allocation and store that pointer.
+ * Best is to allocate it at stage creation time together with host
+ * pointer. Remove vmem from params.
+ */
struct ia_css_isp_param_css_segments *mem_if = &isp_mem_if;
int err = 0;
@@ -1123,10 +1111,12 @@ sp_init_stage(struct ia_css_pipeline_stage *stage,
} else {
/* SP stage */
assert(stage->sp_func != IA_CSS_PIPELINE_NO_FUNC);
- /* binary and blob_info are now NULL.
- These will be passed to sh_css_sp_init_stage
- and dereferenced there, so passing a NULL
- pointer is no good. return an error */
+ /*
+ * binary and blob_info are now NULL.
+ * These will be passed to sh_css_sp_init_stage
+ * and dereferenced there, so passing a NULL
+ * pointer is no good. return an error
+ */
return -EINVAL;
}
@@ -1260,8 +1250,10 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id;
}
- /* For continuous use-cases, SP copy is responsible for sampling the
- * parameters */
+ /*
+ * For continuous use-cases, SP copy is responsible for sampling the
+ * parameters
+ */
if (continuous)
sh_css_sp_group.pipe[thread_id].pipe_config = 0;
@@ -1269,9 +1261,9 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
pipe = find_pipe_by_num(pipe_num);
assert(pipe);
- if (!pipe) {
+ if (!pipe)
return;
- }
+
sh_css_sp_group.pipe[thread_id].scaler_pp_lut = sh_css_pipe_get_pp_gdc_lut(pipe);
if (md_info && md_info->size > 0) {
@@ -1543,7 +1535,8 @@ ia_css_pipe_set_irq_mask(struct ia_css_pipe *pipe,
assert(pipe);
assert(IA_CSS_PIPE_ID_NUM == NR_OF_PIPELINES);
- /* Linux kernel does not have UINT16_MAX
+ /*
+ * Linux kernel does not have UINT16_MAX
* Therefore decided to comment out these 2 asserts for Linux
* Alternatives that were not chosen:
* - add a conditional #define for UINT16_MAX
@@ -1642,7 +1635,8 @@ sh_css_sp_start_isp(void)
(unsigned int)sp_address_of(sp_sw_state),
(uint32_t)(IA_CSS_SP_SW_TERMINATED));
- /* Note 1: The sp_start_isp function contains a wait till
+ /*
+ * Note 1: The sp_start_isp function contains a wait till
* the input network is configured by the SP.
* Note 2: Not all SP binaries supports host2sp_commands.
* In case a binary does support it, the host2sp_command
@@ -1652,7 +1646,8 @@ sh_css_sp_start_isp(void)
* received, the SP starts configuring the input network.
*/
- /* we need to set sp_running before we call ia_css_mmu_invalidate_cache
+ /*
+ * We need to set sp_running before we call ia_css_mmu_invalidate_cache
* as ia_css_mmu_invalidate_cache checks on sp_running to
* avoid that it accesses dmem while the SP is not powered
*/
diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.h b/drivers/staging/media/atomisp/pci/sh_css_sp.h
index 36b693bd916a..c12f57f5befc 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_sp.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_sp.h
@@ -165,13 +165,6 @@ sh_css_sp_configure_sync_gen(int width,
int vblank_cycles);
void
-sh_css_sp_configure_tpg(int x_mask,
- int y_mask,
- int x_delta,
- int y_delta,
- int xy_mask);
-
-void
sh_css_sp_configure_prbs(int seed);
void
diff --git a/drivers/staging/media/atomisp/pci/system_global.h b/drivers/staging/media/atomisp/pci/system_global.h
index 060b924023ec..e8a29f73d67a 100644
--- a/drivers/staging/media/atomisp/pci/system_global.h
+++ b/drivers/staging/media/atomisp/pci/system_global.h
@@ -201,18 +201,6 @@ enum mipi_port_id {
#define N_RX_CHANNEL_ID 4
-/* Generic port enumeration with an internal port type ID */
-typedef enum {
- CSI_PORT0_ID = 0,
- CSI_PORT1_ID,
- CSI_PORT2_ID,
- TPG_PORT0_ID,
- PRBS_PORT0_ID,
- FIFO_PORT0_ID,
- MEMORY_PORT0_ID,
- N_INPUT_PORT_ID
-} input_port_ID_t;
-
typedef enum {
CAPTURE_UNIT0_ID = 0,
CAPTURE_UNIT1_ID,
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 426310e1ea5b..1cd48028b641 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -4,7 +4,6 @@ config VIDEO_IMX_MEDIA
depends on ARCH_MXC || COMPILE_TEST
depends on HAS_DMA
depends on VIDEO_DEV
- depends on VIDEO_DEV
depends on IMX_IPUV3_CORE
select MEDIA_CONTROLLER
select V4L2_FWNODE
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c
index 2b659b0ccca1..37482b626c3c 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.c
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.c
@@ -117,7 +117,9 @@ int imgu_css_fw_init(struct imgu_css *css)
unsigned int i, j, binary_nr;
int r;
- r = request_firmware(&css->fw, IMGU_FW_NAME_20161208, css->dev);
+ r = request_firmware(&css->fw, IMGU_FW_NAME_IPU_20161208, css->dev);
+ if (r == -ENOENT)
+ r = request_firmware(&css->fw, IMGU_FW_NAME_20161208, css->dev);
if (r == -ENOENT)
r = request_firmware(&css->fw, IMGU_FW_NAME, css->dev);
if (r)
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h
index f9403da75785..c956aa21df25 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.h
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.h
@@ -9,6 +9,8 @@
#define IMGU_FW_NAME "intel/ipu3-fw.bin"
#define IMGU_FW_NAME_20161208 \
"intel/irci_irci_ecr-master_20161208_0213_20170112_1500.bin"
+#define IMGU_FW_NAME_IPU_20161208 \
+ "intel/ipu/irci_irci_ecr-master_20161208_0213_20170112_1500.bin"
typedef u32 imgu_fw_ptr;
diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c
index 9c10f1474c35..1b0a59b78949 100644
--- a/drivers/staging/media/ipu3/ipu3-css.c
+++ b/drivers/staging/media/ipu3/ipu3-css.c
@@ -2055,7 +2055,6 @@ struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css)
return ERR_PTR(-EIO);
}
- css_pipe = &css->pipes[pipe];
dev_dbg(css->dev, "event: pipeline done 0x%8x for pipe %d\n",
event, pipe);
break;
diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c
index 18ca22c3018a..e22a9c092195 100644
--- a/drivers/staging/media/ipu3/ipu3.c
+++ b/drivers/staging/media/ipu3/ipu3.c
@@ -13,6 +13,7 @@
#include <linux/pm_runtime.h>
#include "ipu3.h"
+#include "ipu3-css-fw.h"
#include "ipu3-dmamap.h"
#include "ipu3-mmu.h"
@@ -853,10 +854,13 @@ static struct pci_driver imgu_pci_driver = {
module_pci_driver(imgu_pci_driver);
-MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
+MODULE_AUTHOR("Tuukka Toivonen");
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
-MODULE_AUTHOR("Jian Xu Zheng <jian.xu.zheng@intel.com>");
-MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
+MODULE_AUTHOR("Jian Xu Zheng");
+MODULE_AUTHOR("Yuning Pu");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel ipu3_imgu PCI driver");
+MODULE_FIRMWARE(IMGU_FW_NAME);
+MODULE_FIRMWARE(IMGU_FW_NAME_20161208);
+MODULE_FIRMWARE(IMGU_FW_NAME_IPU_20161208);
diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index c44145284aa1..ea67bcf69c9d 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Maxim MAX9286 Quad GMSL2 Deserializer Driver
+ * Maxim MAX96712 Quad GMSL2 Deserializer Driver
*
* Copyright (C) 2021 Renesas Electronics Corporation
* Copyright (C) 2021 Niklas Söderlund
diff --git a/drivers/staging/media/starfive/camss/stf-camss.c b/drivers/staging/media/starfive/camss/stf-camss.c
index a587f860101a..fecd3e67c7a1 100644
--- a/drivers/staging/media/starfive/camss/stf-camss.c
+++ b/drivers/staging/media/starfive/camss/stf-camss.c
@@ -162,6 +162,12 @@ err_isp_unregister:
static void stfcamss_unregister_devs(struct stfcamss *stfcamss)
{
+ struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
+ struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
+
+ media_entity_remove_links(&isp_dev->subdev.entity);
+ media_entity_remove_links(&cap_yuv->video.vdev.entity);
+
stf_isp_unregister(&stfcamss->isp_dev);
stf_capture_unregister(stfcamss);
}
@@ -355,7 +361,7 @@ err_cleanup_notifier:
*
* Always returns 0.
*/
-static int stfcamss_remove(struct platform_device *pdev)
+static void stfcamss_remove(struct platform_device *pdev)
{
struct stfcamss *stfcamss = platform_get_drvdata(pdev);
@@ -364,8 +370,6 @@ static int stfcamss_remove(struct platform_device *pdev)
media_device_cleanup(&stfcamss->media_dev);
v4l2_async_nf_cleanup(&stfcamss->notifier);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct of_device_id stfcamss_of_match[] = {
@@ -420,7 +424,7 @@ static const struct dev_pm_ops stfcamss_pm_ops = {
static struct platform_driver stfcamss_driver = {
.probe = stfcamss_probe,
- .remove = stfcamss_remove,
+ .remove_new = stfcamss_remove,
.driver = {
.name = "starfive-camss",
.pm = &stfcamss_pm_ops,
diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
index d50616ef351e..4e6e26736852 100644
--- a/drivers/staging/media/starfive/camss/stf-isp.c
+++ b/drivers/staging/media/starfive/camss/stf-isp.c
@@ -10,9 +10,6 @@
#include "stf-camss.h"
-#define SINK_FORMATS_INDEX 0
-#define SOURCE_FORMATS_INDEX 1
-
static int isp_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel);
@@ -94,10 +91,7 @@ static void isp_try_format(struct stf_isp_dev *isp_dev,
return;
}
- if (pad == STF_ISP_PAD_SINK)
- formats = &isp_dev->formats[SINK_FORMATS_INDEX];
- else if (pad == STF_ISP_PAD_SRC)
- formats = &isp_dev->formats[SOURCE_FORMATS_INDEX];
+ formats = &isp_dev->formats[pad];
fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
STFCAMSS_FRAME_MAX_WIDTH);
@@ -123,7 +117,7 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index >= ARRAY_SIZE(isp_formats_sink))
return -EINVAL;
- formats = &isp_dev->formats[SINK_FORMATS_INDEX];
+ formats = &isp_dev->formats[code->pad];
code->code = formats->fmts[code->index].code;
} else {
struct v4l2_mbus_framefmt *sink_fmt;
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c
index 5c0a45394cba..58f8ae92320d 100644
--- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c
@@ -386,8 +386,7 @@ static int sun6i_isp_resources_setup(struct sun6i_isp_device *isp_dev,
irq = platform_get_irq(platform_dev, 0);
if (irq < 0) {
- dev_err(dev, "failed to get interrupt\n");
- ret = -ENXIO;
+ ret = irq;
goto error_clock_rate_exclusive;
}
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index c25286772603..7b8f8f810b35 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -164,6 +164,7 @@ static void tegra20_vi_get_input_formats(struct tegra_vi_channel *chan,
unsigned int input_mbus_code = chan->fmtinfo->code;
(*main_input_format) = VI_INPUT_INPUT_FORMAT_YUV422;
+ (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_UYVY;
switch (input_mbus_code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
@@ -316,13 +317,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
/* For YVU420, we swap the locations of the U and V planes. */
- if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420) {
- unsigned long temp;
-
- temp = chan->addr_offset_u;
- chan->addr_offset_u = chan->addr_offset_v;
- chan->addr_offset_v = temp;
- }
+ if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
+ swap(chan->addr_offset_u, chan->addr_offset_v);
chan->start_offset_u = chan->addr_offset_u;
chan->start_offset_v = chan->addr_offset_v;
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index af6e3a0d8df4..57a856a21e90 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -719,11 +719,11 @@ static int tegra_channel_g_dv_timings(struct file *file, void *fh,
struct v4l2_subdev *subdev;
subdev = tegra_channel_get_remote_source_subdev(chan);
- if (!v4l2_subdev_has_op(subdev, video, g_dv_timings))
+ if (!v4l2_subdev_has_op(subdev, pad, g_dv_timings))
return -ENOTTY;
return v4l2_device_call_until_err(chan->video.v4l2_dev, 0,
- video, g_dv_timings, timings);
+ pad, g_dv_timings, 0, timings);
}
static int tegra_channel_s_dv_timings(struct file *file, void *fh,
@@ -736,7 +736,7 @@ static int tegra_channel_s_dv_timings(struct file *file, void *fh,
int ret;
subdev = tegra_channel_get_remote_source_subdev(chan);
- if (!v4l2_subdev_has_op(subdev, video, s_dv_timings))
+ if (!v4l2_subdev_has_op(subdev, pad, s_dv_timings))
return -ENOTTY;
ret = tegra_channel_g_dv_timings(file, fh, &curr_timings);
@@ -750,7 +750,7 @@ static int tegra_channel_s_dv_timings(struct file *file, void *fh,
return -EBUSY;
ret = v4l2_device_call_until_err(chan->video.v4l2_dev, 0,
- video, s_dv_timings, timings);
+ pad, s_dv_timings, 0, timings);
if (ret)
return ret;
@@ -771,11 +771,11 @@ static int tegra_channel_query_dv_timings(struct file *file, void *fh,
struct v4l2_subdev *subdev;
subdev = tegra_channel_get_remote_source_subdev(chan);
- if (!v4l2_subdev_has_op(subdev, video, query_dv_timings))
+ if (!v4l2_subdev_has_op(subdev, pad, query_dv_timings))
return -ENOTTY;
return v4l2_device_call_until_err(chan->video.v4l2_dev, 0,
- video, query_dv_timings, timings);
+ pad, query_dv_timings, 0, timings);
}
static int tegra_channel_enum_dv_timings(struct file *file, void *fh,
diff --git a/include/media/cec.h b/include/media/cec.h
index 10c9cf6058b7..d131514032f2 100644
--- a/include/media/cec.h
+++ b/include/media/cec.h
@@ -173,7 +173,7 @@ struct cec_adap_ops {
* case the transmit will finish, but will not retransmit
* and be marked as ABORTED.
* @xfer_timeout_ms: the transfer timeout in ms.
- * If 0, then timeout after 2.1 ms.
+ * If 0, then timeout after 2100 ms.
* @kthread_config: kthread used to configure a CEC adapter
* @config_completion: used to signal completion of the config kthread
* @kthread: main CEC processing thread
@@ -187,6 +187,7 @@ struct cec_adap_ops {
* in order to transmit or receive CEC messages. This is usually a HW
* limitation.
* @is_enabled: the CEC adapter is enabled
+ * @is_claiming_log_addrs: true if cec_claim_log_addrs() is running
* @is_configuring: the CEC adapter is configuring (i.e. claiming LAs)
* @must_reconfigure: while configuring, the PA changed, so reclaim LAs
* @is_configured: the CEC adapter is configured (i.e. has claimed LAs)
@@ -258,6 +259,7 @@ struct cec_adapter {
u16 phys_addr;
bool needs_hpd;
bool is_enabled;
+ bool is_claiming_log_addrs;
bool is_configuring;
bool must_reconfigure;
bool is_configured;
diff --git a/include/media/ipu6-pci-table.h b/include/media/ipu6-pci-table.h
new file mode 100644
index 000000000000..0899d9d2f978
--- /dev/null
+++ b/include/media/ipu6-pci-table.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#ifndef __IPU6_PCI_TBL_H__
+#define __IPU6_PCI_TBL_H__
+
+#include <linux/pci.h>
+
+#define PCI_DEVICE_ID_INTEL_IPU6 0x9a19
+#define PCI_DEVICE_ID_INTEL_IPU6SE 0x4e19
+#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLP 0x465d
+#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLN 0x462e
+#define PCI_DEVICE_ID_INTEL_IPU6EP_RPLP 0xa75d
+#define PCI_DEVICE_ID_INTEL_IPU6EP_MTL 0x7d19
+
+static const struct pci_device_id ipu6_pci_tbl[] = {
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6SE) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLP) },
+ { 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) },
+ { }
+};
+
+#endif /* __IPU6_PCI_TBL_H__ */
diff --git a/include/media/media-device.h b/include/media/media-device.h
index 2c146d0b2b1c..53d2a16a70b0 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -429,6 +429,9 @@ void __media_device_usb_init(struct media_device *mdev,
const char *driver_name);
#else
+static inline void media_device_init(struct media_device *mdev)
+{
+}
static inline int media_device_register(struct media_device *mdev)
{
return 0;
@@ -436,6 +439,9 @@ static inline int media_device_register(struct media_device *mdev)
static inline void media_device_unregister(struct media_device *mdev)
{
}
+static inline void media_device_cleanup(struct media_device *mdev)
+{
+}
static inline int media_device_register_entity(struct media_device *mdev,
struct media_entity *entity)
{
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 9bd326d31181..f26c323e9c96 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -310,7 +310,9 @@ void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier);
*
* @sd: pointer to &struct v4l2_subdev
*/
-int v4l2_async_register_subdev(struct v4l2_subdev *sd);
+#define v4l2_async_register_subdev(sd) \
+ __v4l2_async_register_subdev(sd, THIS_MODULE)
+int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module);
/**
* v4l2_async_register_subdev_sensor - registers a sensor sub-device to the
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index f6f111fae33c..dd897a362f36 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -156,8 +156,11 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
* An error is returned if the module is no longer loaded on any attempts
* to register it.
*/
-int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
- struct v4l2_subdev *sd);
+#define v4l2_device_register_subdev(v4l2_dev, sd) \
+ __v4l2_device_register_subdev(v4l2_dev, sd, THIS_MODULE)
+int __must_check __v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd,
+ struct module *module);
/**
* v4l2_device_unregister_subdev - Unregisters a subdev with a v4l2 device.
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index edb733f21604..bdbb7e542321 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -163,6 +163,8 @@ struct v4l2_fh;
* :ref:`VIDIOC_CREATE_BUFS <vidioc_create_bufs>` ioctl
* @vidioc_prepare_buf: pointer to the function that implements
* :ref:`VIDIOC_PREPARE_BUF <vidioc_prepare_buf>` ioctl
+ * @vidioc_remove_bufs: pointer to the function that implements
+ * :ref:`VIDIOC_REMOVE_BUFS <vidioc_remove_bufs>` ioctl
* @vidioc_overlay: pointer to the function that implements
* :ref:`VIDIOC_OVERLAY <vidioc_overlay>` ioctl
* @vidioc_g_fbuf: pointer to the function that implements
@@ -422,6 +424,8 @@ struct v4l2_ioctl_ops {
struct v4l2_create_buffers *b);
int (*vidioc_prepare_buf)(struct file *file, void *fh,
struct v4l2_buffer *b);
+ int (*vidioc_remove_bufs)(struct file *file, void *fh,
+ struct v4l2_remove_buffers *d);
int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
int (*vidioc_g_fbuf)(struct file *file, void *fh,
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index 7f1af1f7f912..0af330cf91c3 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -867,6 +867,8 @@ int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb);
int v4l2_m2m_ioctl_create_bufs(struct file *file, void *fh,
struct v4l2_create_buffers *create);
+int v4l2_m2m_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *d);
int v4l2_m2m_ioctl_querybuf(struct file *file, void *fh,
struct v4l2_buffer *buf);
int v4l2_m2m_ioctl_expbuf(struct file *file, void *fh,
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index a9e6b8146279..e30c463d90e5 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -452,14 +452,6 @@ enum v4l2_subdev_pre_streamon_flags {
*
* @g_pixelaspect: callback to return the pixelaspect ratio.
*
- * @s_dv_timings: Set custom dv timings in the sub device. This is used
- * when sub device is capable of setting detailed timing information
- * in the hardware to generate/detect the video signal.
- *
- * @g_dv_timings: Get custom dv timings in the sub device.
- *
- * @query_dv_timings: callback for VIDIOC_QUERY_DV_TIMINGS() ioctl handler code.
- *
* @s_rx_buffer: set a host allocated memory buffer for the subdev. The subdev
* can adjust @size to a lower value and must not write more data to the
* buffer starting at @data than the original value of @size.
@@ -490,12 +482,6 @@ struct v4l2_subdev_video_ops {
int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
int (*s_stream)(struct v4l2_subdev *sd, int enable);
int (*g_pixelaspect)(struct v4l2_subdev *sd, struct v4l2_fract *aspect);
- int (*s_dv_timings)(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings);
- int (*g_dv_timings)(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings);
- int (*query_dv_timings)(struct v4l2_subdev *sd,
- struct v4l2_dv_timings *timings);
int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf,
unsigned int *size);
int (*pre_streamon)(struct v4l2_subdev *sd, u32 flags);
@@ -728,12 +714,14 @@ struct v4l2_subdev_stream_configs {
/**
* struct v4l2_subdev_krouting - subdev routing table
*
+ * @len_routes: length of routes array, in routes
* @num_routes: number of routes
* @routes: &struct v4l2_subdev_route
*
* This structure contains the routing table for a subdev.
*/
struct v4l2_subdev_krouting {
+ unsigned int len_routes;
unsigned int num_routes;
struct v4l2_subdev_route *routes;
};
@@ -791,6 +779,14 @@ struct v4l2_subdev_state {
*
* @set_edid: callback for VIDIOC_SUBDEV_S_EDID() ioctl handler code.
*
+ * @s_dv_timings: Set custom dv timings in the sub device. This is used
+ * when sub device is capable of setting detailed timing information
+ * in the hardware to generate/detect the video signal.
+ *
+ * @g_dv_timings: Get custom dv timings in the sub device.
+ *
+ * @query_dv_timings: callback for VIDIOC_QUERY_DV_TIMINGS() ioctl handler code.
+ *
* @dv_timings_cap: callback for VIDIOC_SUBDEV_DV_TIMINGS_CAP() ioctl handler
* code.
*
@@ -864,6 +860,12 @@ struct v4l2_subdev_pad_ops {
struct v4l2_subdev_frame_interval *interval);
int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
+ int (*s_dv_timings)(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings);
+ int (*g_dv_timings)(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings);
+ int (*query_dv_timings)(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings);
int (*dv_timings_cap)(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap);
int (*enum_dv_timings)(struct v4l2_subdev *sd,
@@ -1725,6 +1727,46 @@ static inline void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state)
}
/**
+ * v4l2_subdev_lock_states - Lock two sub-device states
+ * @state1: One subdevice state
+ * @state2: The other subdevice state
+ *
+ * Locks the state of two sub-devices.
+ *
+ * The states must be unlocked with v4l2_subdev_unlock_states() after use.
+ *
+ * This differs from calling v4l2_subdev_lock_state() on both states so that if
+ * the states share the same lock, the lock is acquired only once (so no
+ * deadlock occurs). The caller is responsible for ensuring the locks will
+ * always be acquired in the same order.
+ */
+static inline void v4l2_subdev_lock_states(struct v4l2_subdev_state *state1,
+ struct v4l2_subdev_state *state2)
+{
+ mutex_lock(state1->lock);
+ if (state1->lock != state2->lock)
+ mutex_lock(state2->lock);
+}
+
+/**
+ * v4l2_subdev_unlock_states() - Unlock two sub-device states
+ * @state1: One subdevice state
+ * @state2: The other subdevice state
+ *
+ * Unlocks the state of two sub-devices.
+ *
+ * This differs from calling v4l2_subdev_unlock_state() on both states so that
+ * if the states share the same lock, the lock is released only once.
+ */
+static inline void v4l2_subdev_unlock_states(struct v4l2_subdev_state *state1,
+ struct v4l2_subdev_state *state2)
+{
+ mutex_unlock(state1->lock);
+ if (state1->lock != state2->lock)
+ mutex_unlock(state2->lock);
+}
+
+/**
* v4l2_subdev_get_unlocked_active_state() - Checks that the active subdev state
* is unlocked and returns it
* @sd: The subdevice
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 8b86996b2719..955237ac503d 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -346,8 +346,8 @@ struct vb2_buffer {
* describes the requested number of planes and sizes\[\]
* contains the requested plane sizes. In this case
* \*num_buffers are being allocated additionally to
- * q->num_buffers. If either \*num_planes or the requested
- * sizes are invalid callback must return %-EINVAL.
+ * the buffers already allocated. If either \*num_planes
+ * or the requested sizes are invalid callback must return %-EINVAL.
* @wait_prepare: release any locks taken while calling vb2 functions;
* it is called before an ioctl needs to wait for a new
* buffer to arrive; required to avoid a deadlock in
@@ -549,9 +549,21 @@ struct vb2_buf_ops {
* @start_streaming can be called. Used when a DMA engine
* cannot be started unless at least this number of buffers
* have been queued into the driver.
- * VIDIOC_REQBUFS will ensure at least @min_queued_buffers
+ * VIDIOC_REQBUFS will ensure at least @min_queued_buffers + 1
* buffers will be allocated. Note that VIDIOC_CREATE_BUFS will not
* modify the requested buffer count.
+ * @min_reqbufs_allocation: the minimum number of buffers to be allocated when
+ * calling VIDIOC_REQBUFS. Note that VIDIOC_CREATE_BUFS will *not*
+ * modify the requested buffer count and does not use this field.
+ * Drivers can set this if there has to be a certain number of
+ * buffers available for the hardware to work effectively.
+ * This allows calling VIDIOC_REQBUFS with a buffer count of 1 and
+ * it will be automatically adjusted to a workable buffer count.
+ * If set, then @min_reqbufs_allocation must be larger than
+ * @min_queued_buffers + 1.
+ * If this field is > 3, then it is highly recommended that the
+ * driver implements the V4L2_CID_MIN_BUFFERS_FOR_CAPTURE/OUTPUT
+ * control.
* @alloc_devs: &struct device memory type/allocator-specific per-plane device
*/
/*
@@ -559,8 +571,9 @@ struct vb2_buf_ops {
* @mmap_lock: private mutex used when buffers are allocated/freed/mmapped
* @memory: current memory type used
* @dma_dir: DMA mapping direction.
- * @bufs: videobuf2 buffer structures
- * @num_buffers: number of allocated/used buffers
+ * @bufs: videobuf2 buffer structures. If it is non-NULL then
+ * bufs_bitmap is also non-NULL.
+ * @bufs_bitmap: bitmap tracking whether each bufs[] entry is used
* @max_num_buffers: upper limit of number of allocated/used buffers.
* If set to 0 v4l2 core will change it VB2_MAX_FRAME
* for backward compatibility.
@@ -582,6 +595,7 @@ struct vb2_buf_ops {
* released. Used to prevent destroying the queue by other threads.
* @is_multiplanar: set if buffer type is multiplanar
* @is_output: set if buffer type is output
+ * @is_busy: set if at least one buffer has been allocated at some time.
* @copy_timestamp: set if vb2-core should set timestamps
* @last_buffer_dequeued: used in poll() and DQBUF to immediately return if the
* last decoded buffer was already dequeued. Set for capture queues
@@ -621,6 +635,7 @@ struct vb2_queue {
u32 timestamp_flags;
gfp_t gfp_flags;
u32 min_queued_buffers;
+ u32 min_reqbufs_allocation;
struct device *alloc_devs[VB2_MAX_PLANES];
@@ -629,7 +644,7 @@ struct vb2_queue {
unsigned int memory;
enum dma_data_direction dma_dir;
struct vb2_buffer **bufs;
- unsigned int num_buffers;
+ unsigned long *bufs_bitmap;
unsigned int max_num_buffers;
struct list_head queued_list;
@@ -647,6 +662,7 @@ struct vb2_queue {
unsigned int waiting_in_dqbuf:1;
unsigned int is_multiplanar:1;
unsigned int is_output:1;
+ unsigned int is_busy:1;
unsigned int copy_timestamp:1;
unsigned int last_buffer_dequeued:1;
@@ -811,6 +827,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* @count: requested buffer count.
* @requested_planes: number of planes requested.
* @requested_sizes: array with the size of the planes.
+ * @first_index: index of the first created buffer, all allocated buffers have
+ * indices in the range [first_index..first_index+count-1]
*
* Videobuf2 core helper to implement VIDIOC_CREATE_BUFS() operation. It is
* called internally by VB2 by an API-specific handler, like
@@ -827,7 +845,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int flags, unsigned int *count,
unsigned int requested_planes,
- const unsigned int requested_sizes[]);
+ const unsigned int requested_sizes[],
+ unsigned int *first_index);
/**
* vb2_core_prepare_buf() - Pass ownership of a buffer from userspace
@@ -852,6 +871,16 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb);
/**
+ * vb2_core_remove_bufs() -
+ * @q: pointer to &struct vb2_queue with videobuf2 queue.
+ * @start: first index of the range of buffers to remove.
+ * @count: number of buffers to remove.
+ *
+ * Return: returns zero on success; an error code otherwise.
+ */
+int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count);
+
+/**
* vb2_core_qbuf() - Queue a buffer from userspace
*
* @q: pointer to &struct vb2_queue with videobuf2 queue.
@@ -1155,7 +1184,10 @@ static inline bool vb2_fileio_is_active(struct vb2_queue *q)
*/
static inline unsigned int vb2_get_num_buffers(struct vb2_queue *q)
{
- return q->num_buffers;
+ if (q->bufs_bitmap)
+ return bitmap_weight(q->bufs_bitmap, q->max_num_buffers);
+
+ return 0;
}
/**
@@ -1166,7 +1198,7 @@ static inline unsigned int vb2_get_num_buffers(struct vb2_queue *q)
*/
static inline bool vb2_is_busy(struct vb2_queue *q)
{
- return vb2_get_num_buffers(q) > 0;
+ return !!q->is_busy;
}
/**
@@ -1264,7 +1296,7 @@ static inline struct vb2_buffer *vb2_get_buffer(struct vb2_queue *q,
if (index >= q->max_num_buffers)
return NULL;
- if (index < q->num_buffers)
+ if (test_bit(index, q->bufs_bitmap))
return q->bufs[index];
return NULL;
}
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index 5a845887850b..77ce8238ab30 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -334,6 +334,8 @@ int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i);
int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i);
int vb2_ioctl_expbuf(struct file *file, void *priv,
struct v4l2_exportbuffer *p);
+int vb2_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *p);
/* struct v4l2_file_operations helpers */
diff --git a/include/uapi/linux/dvb/frontend.h b/include/uapi/linux/dvb/frontend.h
index 7e0983b987c2..8d38c6befda8 100644
--- a/include/uapi/linux/dvb/frontend.h
+++ b/include/uapi/linux/dvb/frontend.h
@@ -854,7 +854,7 @@ struct dtv_stats {
union {
__u64 uvalue; /* for counters and relative scales */
__s64 svalue; /* for 0.001 dB measures */
- };
+ } __attribute__ ((packed));
} __attribute__ ((packed));
diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
index f05f747e444d..d4c1d991014b 100644
--- a/include/uapi/linux/media-bus-format.h
+++ b/include/uapi/linux/media-bus-format.h
@@ -174,4 +174,13 @@
*/
#define MEDIA_BUS_FMT_METADATA_FIXED 0x7001
+/* Generic line based metadata formats for serial buses. Next is 0x8008. */
+#define MEDIA_BUS_FMT_META_8 0x8001
+#define MEDIA_BUS_FMT_META_10 0x8002
+#define MEDIA_BUS_FMT_META_12 0x8003
+#define MEDIA_BUS_FMT_META_14 0x8004
+#define MEDIA_BUS_FMT_META_16 0x8005
+#define MEDIA_BUS_FMT_META_20 0x8006
+#define MEDIA_BUS_FMT_META_24 0x8007
+
#endif /* __LINUX_MEDIA_BUS_FORMAT_H */
diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h
index 6b07b73473b5..946520bc49f1 100644
--- a/include/uapi/linux/v4l2-mediabus.h
+++ b/include/uapi/linux/v4l2-mediabus.h
@@ -19,12 +19,18 @@
* @width: image width
* @height: image height
* @code: data format code (from enum v4l2_mbus_pixelcode)
- * @field: used interlacing type (from enum v4l2_field)
- * @colorspace: colorspace of the data (from enum v4l2_colorspace)
- * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding)
- * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding)
- * @quantization: quantization of the data (from enum v4l2_quantization)
- * @xfer_func: transfer function of the data (from enum v4l2_xfer_func)
+ * @field: used interlacing type (from enum v4l2_field), zero for metadata
+ * mbus codes
+ * @colorspace: colorspace of the data (from enum v4l2_colorspace), zero on
+ * metadata mbus codes
+ * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero
+ * for metadata mbus codes
+ * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding), zero for
+ * metadata mbus codes
+ * @quantization: quantization of the data (from enum v4l2_quantization), zero
+ * for metadata mbus codes
+ * @xfer_func: transfer function of the data (from enum v4l2_xfer_func), zero
+ * for metadata mbus codes
* @flags: flags (V4L2_MBUS_FRAMEFMT_*)
* @reserved: reserved bytes that can be later used
*/
diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h
index 7048c51581c6..2347e266cf75 100644
--- a/include/uapi/linux/v4l2-subdev.h
+++ b/include/uapi/linux/v4l2-subdev.h
@@ -50,6 +50,10 @@ struct v4l2_subdev_format {
* @rect: pad crop rectangle boundaries
* @stream: stream number, defined in subdev routing
* @reserved: drivers and applications must zero this array
+ *
+ * The subdev crop API is an obsolete interface and may be removed in the
+ * future. It is superseded by the selection API. No new extensions to this
+ * structure will be accepted.
*/
struct v4l2_subdev_crop {
__u32 which;
@@ -224,15 +228,19 @@ struct v4l2_subdev_route {
* struct v4l2_subdev_routing - Subdev routing information
*
* @which: configuration type (from enum v4l2_subdev_format_whence)
- * @num_routes: the total number of routes in the routes array
+ * @len_routes: the length of the routes array, in routes; set by the user, not
+ * modified by the kernel
* @routes: pointer to the routes array
+ * @num_routes: the total number of routes, possibly more than fits in the
+ * routes array
* @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_routing {
__u32 which;
- __u32 num_routes;
+ __u32 len_routes;
__u64 routes;
- __u32 reserved[6];
+ __u32 num_routes;
+ __u32 reserved[11];
};
/*
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index a8015e5e7fa4..fe6b67e83751 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -599,6 +599,8 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_Y10BPACK v4l2_fourcc('Y', '1', '0', 'B') /* 10 Greyscale bit-packed */
#define V4L2_PIX_FMT_Y10P v4l2_fourcc('Y', '1', '0', 'P') /* 10 Greyscale, MIPI RAW10 packed */
#define V4L2_PIX_FMT_IPU3_Y10 v4l2_fourcc('i', 'p', '3', 'y') /* IPU3 packed 10-bit greyscale */
+#define V4L2_PIX_FMT_Y12P v4l2_fourcc('Y', '1', '2', 'P') /* 12 Greyscale, MIPI RAW12 packed */
+#define V4L2_PIX_FMT_Y14P v4l2_fourcc('Y', '1', '4', 'P') /* 14 Greyscale, MIPI RAW14 packed */
/* Palette formats */
#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */
@@ -839,6 +841,20 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */
#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+#ifdef __KERNEL__
+/*
+ * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
+ * adding new ones!
+ */
+#define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_14 v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */
+#endif
+
/* priv field value to indicates that subsequent fields are valid. */
#define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
@@ -869,6 +885,7 @@ struct v4l2_fmtdesc {
#define V4L2_FMT_FLAG_CSC_YCBCR_ENC 0x0080
#define V4L2_FMT_FLAG_CSC_HSV_ENC V4L2_FMT_FLAG_CSC_YCBCR_ENC
#define V4L2_FMT_FLAG_CSC_QUANTIZATION 0x0100
+#define V4L2_FMT_FLAG_META_LINE_BASED 0x0200
/* Frame Size and frame rate enumeration */
/*
@@ -1036,6 +1053,7 @@ struct v4l2_requestbuffers {
#define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5)
#define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6)
#define V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS (1 << 7)
+#define V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS (1 << 8)
/**
* struct v4l2_plane - plane info for multi-planar buffers
@@ -1841,7 +1859,7 @@ struct v4l2_ext_control {
struct v4l2_ctrl_hdr10_cll_info __user *p_hdr10_cll_info;
struct v4l2_ctrl_hdr10_mastering_display __user *p_hdr10_mastering_display;
void __user *ptr;
- };
+ } __attribute__ ((packed));
} __attribute__ ((packed));
struct v4l2_ext_controls {
@@ -2415,10 +2433,19 @@ struct v4l2_sdr_format {
* struct v4l2_meta_format - metadata format definition
* @dataformat: little endian four character code (fourcc)
* @buffersize: maximum size in bytes required for data
+ * @width: number of data units of data per line (valid for line
+ * based formats only, see format documentation)
+ * @height: number of lines of data per buffer (valid for line based
+ * formats only)
+ * @bytesperline: offset between the beginnings of two adjacent lines in
+ * bytes (valid for line based formats only)
*/
struct v4l2_meta_format {
__u32 dataformat;
__u32 buffersize;
+ __u32 width;
+ __u32 height;
+ __u32 bytesperline;
} __attribute__ ((packed));
/**
@@ -2624,6 +2651,20 @@ struct v4l2_create_buffers {
__u32 reserved[5];
};
+/**
+ * struct v4l2_remove_buffers - VIDIOC_REMOVE_BUFS argument
+ * @index: the first buffer to be removed
+ * @count: number of buffers to removed
+ * @type: enum v4l2_buf_type
+ * @reserved: future extensions
+ */
+struct v4l2_remove_buffers {
+ __u32 index;
+ __u32 count;
+ __u32 type;
+ __u32 reserved[13];
+};
+
/*
* I O C T L C O D E S F O R V I D E O D E V I C E S
*
@@ -2723,6 +2764,8 @@ struct v4l2_create_buffers {
#define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info)
#define VIDIOC_QUERY_EXT_CTRL _IOWR('V', 103, struct v4l2_query_ext_ctrl)
+#define VIDIOC_REMOVE_BUFS _IOWR('V', 104, struct v4l2_remove_buffers)
+
/* Reminder: when adding new ioctls please add support for them to
drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */