summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 08:32:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 08:32:10 -0700
commit00c6649bafef628955569dd39a59e3170e48f7b5 (patch)
tree597ea4250d8e340daca614e1cc9b982391d91c2f
parent1f5ffc672165ff851063a5fd044b727ab2517ae3 (diff)
parent4fbeef21f5387234111b5d52924e77757626faa5 (diff)
Merge tag 'media/v7.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - new CSI tegra support, covering Tegra20 and Tegra30 - new camera sensor drivers: T4ka3 and ov2732 - m88ds3103: add 3103c chip support - uvcvideo: add support for Intel RealSense D436/D555 and P010 pixel format - synopsys csi2rx: add i.MX93 support - imx8-isi: add i.MX95 support - imx8mq-mipi-csi2: add i.MX8ULP support - dw100: add V4L2 requests support - support for DTV devices from Hauppauge got some improvements - media staging: dropped starfive-camss driver - media docs: document multi-committers model and improve maint profile - media core: - add v4l2_subdev_get_frame_desc_passthrough() helper - improve error handling in fwnode parsing - lots of driver fixes, cleanups and improvements * tag 'media/v7.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (251 commits) Revert "media: cx231xx: add USB ID 2040:8360 for Hauppauge WinTV-HVR-935" media: synopsys: csi2rx: add i.MX93 support media: dt-bindings: add NXP i.MX93 compatible string media: synopsys: csi2rx: Use enum and u32 array for register offsets media: synopsys: csi2rx: implement .get_frame_desc() callback media: synopsys: csi2rx: only check errors from devm_clk_bulk_get_all() media: synopsys: csi2rx: use devm_reset_control_get_optional_exclusive() media: i2c: imx283: add support for non-continuous MIPI clock mode media: i2c: ov08d10: add support for 24 MHz input clock media: i2c: ov08d10: add support for reset and power management media: i2c: ov08d10: add support for binding via device tree dt-bindings: media: i2c: document Omnivision OV08D10 CMOS image sensor media: i2c: ov08d10: add missing newline to prints media: i2c: ov08d10: fix some typos in comments media: i2c: ov08d10: remove duplicate register write media: i2c: ov08d10: fix image vertical start setting media: i2c: ov08d10: fix runtime PM handling in probe staging: media: ipu7: Update TODO media: Add t4ka3 camera sensor driver media: i2c: Add ov2732 image sensor driver ...
-rw-r--r--.mailmap1
-rw-r--r--Documentation/admin-guide/media/mgb4.rst8
-rw-r--r--Documentation/admin-guide/media/starfive_camss.rst72
-rw-r--r--Documentation/admin-guide/media/starfive_camss_graph.dot12
-rw-r--r--Documentation/admin-guide/media/v4l-drivers.rst1
-rw-r--r--Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml4
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml101
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml103
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml6
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml111
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml213
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml27
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml49
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml47
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,vdec.yaml22
-rw-r--r--Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml14
-rw-r--r--Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml180
-rw-r--r--Documentation/driver-api/media/index.rst1
-rw-r--r--Documentation/driver-api/media/maintainer-entry-profile.rst463
-rw-r--r--Documentation/driver-api/media/media-committers.rst203
-rw-r--r--Documentation/process/maintainer-pgp-guide.rst2
-rw-r--r--Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/subdev-formats.rst20
-rw-r--r--MAINTAINERS30
-rw-r--r--drivers/gpio/gpio-tps68470.c21
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c1
-rw-r--r--drivers/gpu/drm/tegra/dsi.c1
-rw-r--r--drivers/gpu/host1x/Makefile1
-rw-r--r--drivers/gpu/host1x/mipi.c592
-rw-r--r--drivers/gpu/host1x/tegra114-mipi.c483
-rw-r--r--drivers/hid/hid-picolcd_cir.c1
-rw-r--r--drivers/media/cec/core/cec-core.c2
-rw-r--r--drivers/media/cec/usb/pulse8/pulse8-cec.c7
-rw-r--r--drivers/media/common/b2c2/flexcop.c14
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c13
-rw-r--r--drivers/media/common/siano/smsir.c1
-rw-r--r--drivers/media/common/uvc.c4
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-sg.c1
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c1
-rw-r--r--drivers/media/dvb-frontends/dib8000.c4
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c426
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h46
-rw-r--r--drivers/media/dvb-frontends/si2168.c8
-rw-r--r--drivers/media/i2c/Kconfig31
-rw-r--r--drivers/media/i2c/Makefile2
-rw-r--r--drivers/media/i2c/alvium-csi2.c2
-rw-r--r--drivers/media/i2c/ar0521.c3
-rw-r--r--drivers/media/i2c/ccs-pll.c5
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c29
-rw-r--r--drivers/media/i2c/ds90ub913.c59
-rw-r--r--drivers/media/i2c/ds90ub953.c61
-rw-r--r--drivers/media/i2c/ds90ub960.c216
-rw-r--r--drivers/media/i2c/dw9768.c2
-rw-r--r--drivers/media/i2c/imx219.c3
-rw-r--r--drivers/media/i2c/imx258.c14
-rw-r--r--drivers/media/i2c/imx283.c27
-rw-r--r--drivers/media/i2c/imx355.c117
-rw-r--r--drivers/media/i2c/imx412.c8
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c2
-rw-r--r--drivers/media/i2c/max9286.c2
-rw-r--r--drivers/media/i2c/mt9m114.c35
-rw-r--r--drivers/media/i2c/mt9p031.c4
-rw-r--r--drivers/media/i2c/og01a1b.c899
-rw-r--r--drivers/media/i2c/ov02a10.c2
-rw-r--r--drivers/media/i2c/ov08d10.c246
-rw-r--r--drivers/media/i2c/ov2732.c790
-rw-r--r--drivers/media/i2c/ov5647.c12
-rw-r--r--drivers/media/i2c/ov5675.c32
-rw-r--r--drivers/media/i2c/ov8856.c23
-rw-r--r--drivers/media/i2c/ov9282.c679
-rw-r--r--drivers/media/i2c/t4ka3.c1064
-rw-r--r--drivers/media/i2c/vgxy61.c3
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c3
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c3
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c15
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c6
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c1
-rw-r--r--drivers/media/pci/cx23885/cx23885.h1
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c6
-rw-r--r--drivers/media/pci/cx88/cx88-input.c3
-rw-r--r--drivers/media/pci/dm1105/dm1105.c1
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c16
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c2
-rw-r--r--drivers/media/pci/mantis/mantis_input.c1
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c3
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_in.c18
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_out.c62
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c26
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c1
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c47
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c4
-rw-r--r--drivers/media/pci/smipcie/smipcie-ir.c1
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c1
-rw-r--r--drivers/media/pci/zoran/zoran_card.c2
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c9
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-common.h2
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c35
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c37
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c134
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h4
-rw-r--r--drivers/media/platform/broadcom/Kconfig5
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c45
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.c1
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c14
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c1
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c9
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c134
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h3
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c117
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h22
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c31
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c12
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c6
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c2
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c92
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c116
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c13
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c22
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c12
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c2
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c63
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h13
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c37
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c47
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c8
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c108
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h21
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c97
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.h2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c61
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c39
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c40
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c47
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c127
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h6
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c63
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c62
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c40
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c13
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c10
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c3
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.c46
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c6
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c11
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c475
-rw-r--r--drivers/media/platform/synopsys/Kconfig1
-rw-r--r--drivers/media/platform/synopsys/dw-mipi-csi2rx.c279
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c6
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c1
-rw-r--r--drivers/media/platform/ti/vpe/vip.c1
-rw-r--r--drivers/media/rc/ati_remote.c6
-rw-r--r--drivers/media/rc/ene_ir.c2
-rw-r--r--drivers/media/rc/fintek-cir.c3
-rw-r--r--drivers/media/rc/gpio-ir-tx.c4
-rw-r--r--drivers/media/rc/igorplugusb.c17
-rw-r--r--drivers/media/rc/iguanair.c1
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c3
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c3
-rw-r--r--drivers/media/rc/imon.c16
-rw-r--r--drivers/media/rc/ir-hix5hd2.c2
-rw-r--r--drivers/media/rc/ir_toy.c1
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/rc/mceusb.c5
-rw-r--r--drivers/media/rc/rc-ir-raw.c5
-rw-r--r--drivers/media/rc/rc-loopback.c1
-rw-r--r--drivers/media/rc/rc-main.c6
-rw-r--r--drivers/media/rc/redrat3.c4
-rw-r--r--drivers/media/rc/st_rc.c2
-rw-r--r--drivers/media/rc/streamzap.c19
-rw-r--r--drivers/media/rc/sunxi-cir.c1
-rw-r--r--drivers/media/rc/ttusbir.c15
-rw-r--r--drivers/media/rc/winbond-cir.c2
-rw-r--r--drivers/media/rc/xbox_remote.c14
-rw-r--r--drivers/media/test-drivers/vidtv/Kconfig1
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.c57
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_s302m.c6
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.c48
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.h4
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.h41
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c114
-rw-r--r--drivers/media/test-drivers/vimc/vimc-streamer.c33
-rw-r--r--drivers/media/tuners/mxl5005s.c6
-rw-r--r--drivers/media/tuners/si2157.c222
-rw-r--r--drivers/media/tuners/si2157_priv.h3
-rw-r--r--drivers/media/tuners/xc5000.c12
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c2
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c50
-rw-r--r--drivers/media/usb/au0828/au0828-cards.h2
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c1
-rw-r--r--drivers/media/usb/au0828/au0828-input.c2
-rw-r--r--drivers/media/usb/au0828/au0828-video.c25
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c7
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c5
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c1
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c6
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c258
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c159
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c322
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c207
-rw-r--r--drivers/media/usb/em28xx/em28xx.h23
-rw-r--r--drivers/media/usb/go7007/go7007-loader.c6
-rw-r--r--drivers/media/usb/gspca/gspca.c13
-rw-r--r--drivers/media/usb/hackrf/hackrf.c7
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c4
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c38
-rw-r--r--drivers/media/usb/uvc/uvc_isight.c2
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c9
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c23
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c3
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h13
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c9
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c119
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470.c1
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470.h1
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470_board_data.c107
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c4
-rw-r--r--drivers/staging/media/av7110/av7110.c2
-rw-r--r--drivers/staging/media/av7110/av7110_ir.c1
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c86
-rw-r--r--drivers/staging/media/imx/imx-media-of.c3
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c2
-rw-r--r--drivers/staging/media/imx/imx-media.h2
-rw-r--r--drivers/staging/media/ipu3/ipu3.c2
-rw-r--r--drivers/staging/media/ipu7/TODO12
-rw-r--r--drivers/staging/media/starfive/Kconfig5
-rw-r--r--drivers/staging/media/starfive/Makefile2
-rw-r--r--drivers/staging/media/starfive/camss/Kconfig18
-rw-r--r--drivers/staging/media/starfive/camss/Makefile13
-rw-r--r--drivers/staging/media/starfive/camss/TODO.txt4
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.c438
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.h134
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.c605
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.h86
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp-hw-ops.c445
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.c379
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.h428
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.c570
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.h100
-rw-r--r--drivers/staging/media/tegra-video/Makefile1
-rw-r--r--drivers/staging/media/tegra-video/csi.c64
-rw-r--r--drivers/staging/media/tegra-video/csi.h22
-rw-r--r--drivers/staging/media/tegra-video/tegra20.c820
-rw-r--r--drivers/staging/media/tegra-video/vi.c58
-rw-r--r--drivers/staging/media/tegra-video/vi.h6
-rw-r--r--drivers/staging/media/tegra-video/video.c8
-rw-r--r--drivers/staging/media/tegra-video/vip.c2
-rw-r--r--drivers/staging/media/tegra-video/vip.h2
-rw-r--r--include/linux/host1x.h10
-rw-r--r--include/linux/tegra-mipi-cal.h57
-rw-r--r--include/linux/usb/uvc.h3
-rw-r--r--include/media/rc-core.h4
-rw-r--r--include/media/v4l2-fwnode.h6
-rw-r--r--include/media/v4l2-subdev.h56
266 files changed, 10022 insertions, 7106 deletions
diff --git a/.mailmap b/.mailmap
index 8e97cc50218e..634f04850fb7 100644
--- a/.mailmap
+++ b/.mailmap
@@ -317,6 +317,7 @@ Hans de Goede <hansg@kernel.org> <hdegoede@redhat.com>
Hans Verkuil <hverkuil@kernel.org> <hverkuil@xs4all.nl>
Hans Verkuil <hverkuil@kernel.org> <hverkuil-cisco@xs4all.nl>
Hans Verkuil <hverkuil@kernel.org> <hansverk@cisco.com>
+Hans Verkuil <hverkuil@kernel.org> <hans.verkuil@cisco.com>
Hao Ge <hao.ge@linux.dev> <gehao@kylinos.cn>
Harry Yoo <harry.yoo@oracle.com> <42.hyeyoo@gmail.com>
Harry Yoo <harry@kernel.org> <harry.yoo@oracle.com>
diff --git a/Documentation/admin-guide/media/mgb4.rst b/Documentation/admin-guide/media/mgb4.rst
index 0a8a56e837f7..8e429fd77712 100644
--- a/Documentation/admin-guide/media/mgb4.rst
+++ b/Documentation/admin-guide/media/mgb4.rst
@@ -74,6 +74,7 @@ Common FPDL3/GMSL input parameters
| 0 - OLDI/JEIDA
| 1 - SPWG/VESA (default)
+ | 2 - ZDML
**link_status** (R):
Video link status. If the link is locked, chips are properly connected and
@@ -240,6 +241,13 @@ Common FPDL3/GMSL output parameters
*Note: This parameter can not be changed while the output v4l2 device is
open.*
+**color_mapping** (RW):
+ Mapping of the outgoing bits in the signal to the colour bits of the pixels.
+
+ | 0 - OLDI/JEIDA
+ | 1 - SPWG/VESA (default)
+ | 2 - ZDML
+
**frame_rate** (RW):
Output video signal frame rate limit in frames per second. Due to
the limited output pixel clock steps, the card can not always generate
diff --git a/Documentation/admin-guide/media/starfive_camss.rst b/Documentation/admin-guide/media/starfive_camss.rst
deleted file mode 100644
index ca42e9447c47..000000000000
--- a/Documentation/admin-guide/media/starfive_camss.rst
+++ /dev/null
@@ -1,72 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-.. include:: <isonum.txt>
-
-================================
-Starfive Camera Subsystem driver
-================================
-
-Introduction
-------------
-
-This file documents the driver for the Starfive Camera Subsystem found on
-Starfive JH7110 SoC. The driver is located under drivers/staging/media/starfive/
-camss.
-
-The driver implements V4L2, Media controller and v4l2_subdev interfaces. Camera
-sensor using V4L2 subdev interface in the kernel is supported.
-
-The driver has been successfully used on the Gstreamer 1.18.5 with v4l2src
-plugin.
-
-
-Starfive Camera Subsystem hardware
-----------------------------------
-
-The Starfive Camera Subsystem hardware consists of::
-
- |\ +---------------+ +-----------+
- +----------+ | \ | | | |
- | | | | | | | |
- | MIPI |----->| |----->| ISP |----->| |
- | | | | | | | |
- +----------+ | | | | | Memory |
- |MUX| +---------------+ | Interface |
- +----------+ | | | |
- | | | |---------------------------->| |
- | Parallel |----->| | | |
- | | | | | |
- +----------+ | / | |
- |/ +-----------+
-
-- MIPI: The MIPI interface, receiving data from a MIPI CSI-2 camera sensor.
-
-- Parallel: The parallel interface, receiving data from a parallel sensor.
-
-- ISP: The ISP, processing raw Bayer data from an image sensor and producing
- YUV frames.
-
-
-Topology
---------
-
-The media controller pipeline graph is as follows:
-
-.. _starfive_camss_graph:
-
-.. kernel-figure:: starfive_camss_graph.dot
- :alt: starfive_camss_graph.dot
- :align: center
-
-The driver has 2 video devices:
-
-- capture_raw: The capture device, capturing image data directly from a sensor.
-- capture_yuv: The capture device, capturing YUV frame data processed by the
- ISP module
-
-The driver has 3 subdevices:
-
-- stf_isp: is responsible for all the isp operations, outputs YUV frames.
-- cdns_csi2rx: a CSI-2 bridge supporting up to 4 CSI lanes in input, and 4
- different pixel streams in output.
-- imx219: an image sensor, image data is sent through MIPI CSI-2.
diff --git a/Documentation/admin-guide/media/starfive_camss_graph.dot b/Documentation/admin-guide/media/starfive_camss_graph.dot
deleted file mode 100644
index 8eff1f161ac7..000000000000
--- a/Documentation/admin-guide/media/starfive_camss_graph.dot
+++ /dev/null
@@ -1,12 +0,0 @@
-digraph board {
- rankdir=TB
- n00000001 [label="{{<port0> 0} | stf_isp\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
- n00000001:port1 -> n00000008 [style=dashed]
- n00000004 [label="capture_raw\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
- n00000008 [label="capture_yuv\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
- n0000000e [label="{{<port0> 0} | cdns_csi2rx.19800000.csi-bridge\n | {<port1> 1 | <port2> 2 | <port3> 3 | <port4> 4}}", shape=Mrecord, style=filled, fillcolor=green]
- n0000000e:port1 -> n00000001:port0 [style=dashed]
- n0000000e:port1 -> n00000004 [style=dashed]
- n00000018 [label="{{} | imx219 6-0010\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
- n00000018:port0 -> n0000000e:port0 [style=bold]
-}
diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
index 393f83e8dc4d..d31da8e0a54f 100644
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -33,7 +33,6 @@ Video4Linux (V4L) driver-specific documentation
si470x
si4713
si476x
- starfive_camss
vimc
visl
vivid
diff --git a/Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml b/Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml
index d3329e991d16..ae48dd4ab589 100644
--- a/Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/alliedvision,alvium-csi2.yaml
@@ -8,7 +8,7 @@ title: Allied Vision Alvium Camera
maintainers:
- Tommaso Merciai <tomm.merciai@gmail.com>
- - Martin Hecht <martin.hecht@avnet.eu>
+ - Martin Hecht <mhecht73@gmail.com>
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
diff --git a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
index dffd23ca4839..e896f4db2421 100644
--- a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m114.yaml
@@ -17,7 +17,9 @@ description: |-
properties:
compatible:
- const: onnn,mt9m114
+ enum:
+ - onnn,mt9m114
+ - aptina,mi1040
reg:
description: I2C device address
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
new file mode 100644
index 000000000000..6f2017c75125
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov08d10.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Omnivision OV08D10 1/4-Inch 8MP CMOS color image sensor
+
+maintainers:
+ - Matthias Fend <matthias.fend@emfend.at>
+
+description:
+ The Omnivision OV08D10 is a 1/4-Inch 8MP CMOS color image sensor with an
+ active array size of 3280 x 2464. It is programmable through I2C
+ interface. Image data is transmitted via MIPI CSI-2 using 2 lanes.
+
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+ compatible:
+ const: ovti,ov08d10
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: MCLK input clock (6 - 27 MHz)
+ maxItems: 1
+
+ reset-gpios:
+ description: Active low XSHUTDN pin
+ maxItems: 1
+
+ dovdd-supply:
+ description: IO power supply (1.8V)
+
+ avdd-supply:
+ description: Analog power supply (2.8V)
+
+ dvdd-supply:
+ description: Core power supply (1.2V)
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interfaces.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@36 {
+ compatible = "ovti,ov08d10";
+ reg = <0x36>;
+
+ clocks = <&ov08d10_clk>;
+
+ dovdd-supply = <&ov08d10_vdddo_1v8>;
+ avdd-supply = <&ov08d10_vdda_2v8>;
+ dvdd-supply = <&ov08d10_vddd_1v2>;
+
+ orientation = <2>;
+ rotation = <0>;
+
+ reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
+
+ port {
+ ov08d10_output: endpoint {
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <360000000 720000000>;
+ remote-endpoint = <&csi_input>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
new file mode 100644
index 000000000000..814fc568c550
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov2732.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OV2732 Image Sensor
+
+maintainers:
+ - Walter Werner Schneider <contact@schnwalter.eu>
+
+description:
+ The OmniVision OV2732 is a 2MP (1920x1080) color CMOS image sensor controlled
+ through an I2C-compatible SCCB bus.
+
+properties:
+ compatible:
+ const: ovti,ov2732
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: XVCLK clock
+
+ avdd-supply:
+ description: Analog Domain Power Supply
+
+ dovdd-supply:
+ description: I/O Domain Power Supply
+
+ dvdd-supply:
+ description: Digital Domain Power Supply
+
+ powerdown-gpios:
+ maxItems: 1
+ description: Reference to the GPIO connected to the pwdn pin. Active low.
+
+ reset-gpios:
+ maxItems: 1
+ description: Reference to the GPIO connected to the reset pin. Active low.
+
+ port:
+ description: MIPI CSI-2 transmitter port
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ items:
+ - const: 1
+ - const: 2
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd-supply
+ - dovdd-supply
+ - dvdd-supply
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ov2732: camera@36 {
+ compatible = "ovti,ov2732";
+ reg = <0x36>;
+ clocks = <&ov2732_clk>;
+
+ avdd-supply = <&ov2732_avdd>;
+ dovdd-supply = <&ov2732_dovdd>;
+ dvdd-supply = <&ov2732_dvdd>;
+
+ powerdown-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+
+ port {
+ camera_out: endpoint {
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&mipi_in_camera>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
index fa71f24823f2..d0f577363f93 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
@@ -18,6 +18,9 @@ description: |-
through I2C and two-wire SCCB. The sensor output is available via CSI-2
serial data output (up to 4-lane).
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
properties:
compatible:
const: ovti,ov8856
@@ -57,6 +60,9 @@ properties:
This corresponds to the hardware pin XSHUTDOWN which is physically
active low.
+ orientation: true
+ rotation: true
+
port:
$ref: /schemas/graph.yaml#/$defs/port-base
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
new file mode 100644
index 000000000000..6050d7e7dcfe
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx355.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX355 Sensor
+
+maintainers:
+ - Richard Acayan <mailingradian@gmail.com>
+
+description:
+ The IMX355 sensor is a 3280x2464 image sensor, commonly found as the front
+ camera in smartphones.
+
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+ compatible:
+ const: sony,imx355
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ avdd-supply:
+ description: Analog power supply.
+
+ dvdd-supply:
+ description: Digital power supply.
+
+ dovdd-supply:
+ description: Interface power supply.
+
+ reset-gpios:
+ description: Reset GPIO (active low).
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ minItems: 4
+ maxItems: 4
+
+ required:
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd-supply
+ - dvdd-supply
+ - dovdd-supply
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,camcc-sdm845.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera@1a {
+ compatible = "sony,imx355";
+ reg = <0x1a>;
+
+ clocks = <&camcc CAM_CC_MCLK2_CLK>;
+
+ assigned-clocks = <&camcc CAM_CC_MCLK2_CLK>;
+ assigned-clock-rates = <24000000>;
+
+ reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&cam_front_ldo>;
+ dvdd-supply = <&cam_front_ldo>;
+ dovdd-supply = <&cam_vio_ldo>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&cam_front_default>;
+
+ rotation = <270>;
+ orientation = <0>;
+
+ port {
+ cam_front_endpoint: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&camss_endpoint1>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
index 0539d52de422..8e2b82d6dc81 100644
--- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
@@ -13,12 +13,10 @@ description:
The TI DS90UB9XX devices are FPD-Link video deserializers with I2C and GPIO
forwarding.
-allOf:
- - $ref: /schemas/i2c/i2c-atr.yaml#
-
properties:
compatible:
enum:
+ - ti,ds90ub954-q1
- ti,ds90ub960-q1
- ti,ds90ub9702-q1
@@ -125,116 +123,127 @@ properties:
ports:
$ref: /schemas/graph.yaml#/properties/ports
+ description:
+ Ports represent FPD-Link inputs to the deserializer and CSI TX outputs
+ from the deserializer. The number of ports is model-dependent.
- properties:
- port@0:
- $ref: /schemas/graph.yaml#/$defs/port-base
- unevaluatedProperties: false
- description: FPD-Link input 0
-
- properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
- description:
- Endpoint for FPD-Link port. If the RX mode for this port is RAW,
- hsync-active and vsync-active must be defined.
-
- port@1:
- $ref: /schemas/graph.yaml#/$defs/port-base
- unevaluatedProperties: false
- description: FPD-Link input 1
-
- properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
- description:
- Endpoint for FPD-Link port. If the RX mode for this port is RAW,
- hsync-active and vsync-active must be defined.
-
- port@2:
- $ref: /schemas/graph.yaml#/$defs/port-base
- unevaluatedProperties: false
- description: FPD-Link input 2
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - ports
- properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
- description:
- Endpoint for FPD-Link port. If the RX mode for this port is RAW,
- hsync-active and vsync-active must be defined.
+$defs:
+ FPDLink-input-port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: FPD-Link input
- port@3:
- $ref: /schemas/graph.yaml#/$defs/port-base
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
- description: FPD-Link input 3
+ description:
+ Endpoint for FPD-Link port. If the RX mode for this port is RAW,
+ hsync-active and vsync-active must be defined.
- properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
- description:
- Endpoint for FPD-Link port. If the RX mode for this port is RAW,
- hsync-active and vsync-active must be defined.
+ CSI2-output-port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: CSI-2 Output
- port@4:
- $ref: /schemas/graph.yaml#/$defs/port-base
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
- description: CSI-2 Output 0
properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
-
- properties:
- data-lanes:
- minItems: 1
- maxItems: 4
- link-frequencies:
- maxItems: 1
-
- required:
- - data-lanes
- - link-frequencies
-
- port@5:
- $ref: /schemas/graph.yaml#/$defs/port-base
- unevaluatedProperties: false
- description: CSI-2 Output 1
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+ link-frequencies:
+ maxItems: 1
- properties:
- endpoint:
- $ref: /schemas/media/video-interfaces.yaml#
- unevaluatedProperties: false
-
- properties:
- data-lanes:
- minItems: 1
- maxItems: 4
- link-frequencies:
- maxItems: 1
-
- required:
- - data-lanes
- - link-frequencies
-
- required:
- - port@0
- - port@1
- - port@2
- - port@3
- - port@4
- - port@5
+ required:
+ - data-lanes
+ - link-frequencies
-required:
- - compatible
- - reg
- - clocks
- - clock-names
- - ports
+allOf:
+ - $ref: /schemas/i2c/i2c-atr.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ti,ds90ub960-q1
+ - ti,ds90ub9702-q1
+ then:
+ properties:
+ ports:
+ properties:
+ port@0:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 0
+
+ port@1:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 1
+
+ port@2:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 2
+
+ port@3:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 3
+
+ port@4:
+ $ref: '#/$defs/CSI2-output-port'
+ description: CSI-2 Output 0
+
+ port@5:
+ $ref: '#/$defs/CSI2-output-port'
+ description: CSI-2 Output 1
+
+ required:
+ - port@0
+ - port@1
+ - port@2
+ - port@3
+ - port@4
+ - port@5
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,ds90ub954-q1
+ then:
+ properties:
+ ports:
+ properties:
+ port@0:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 0
+
+ port@1:
+ $ref: '#/$defs/FPDLink-input-port'
+ description: FPD-Link input 1
+
+ port@2:
+ $ref: '#/$defs/CSI2-output-port'
+ description: CSI-2 Output 0
+
+ required:
+ - port@0
+ - port@1
+ - port@2
+
+ links:
+ properties:
+ link@2: false
+ link@3: false
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
index 001a0d9b71e0..b59c4ce30b8b 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
@@ -24,6 +24,7 @@ properties:
- fsl,imx8ulp-isi
- fsl,imx91-isi
- fsl,imx93-isi
+ - fsl,imx95-isi
reg:
maxItems: 1
@@ -50,7 +51,7 @@ properties:
interrupts:
description: Processing pipeline interrupts, one per pipeline
minItems: 1
- maxItems: 2
+ maxItems: 8
power-domains:
maxItems: 1
@@ -99,6 +100,7 @@ allOf:
then:
properties:
interrupts:
+ minItems: 2
maxItems: 2
ports:
properties:
@@ -120,6 +122,29 @@ allOf:
required:
- fsl,blk-ctrl
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx95-isi
+ then:
+ properties:
+ interrupts:
+ minItems: 8
+ ports:
+ properties:
+ port@0:
+ description: Pixel Link Slave 0
+ port@1:
+ description: Pixel Link Slave 1
+ port@2:
+ description: MIPI CSI-2 RX 0
+ port@3:
+ description: MIPI CSI-2 RX 1
+ required:
+ - port@2
+ - port@3
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
index 3389bab266a9..4fcfc4fd3565 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
@@ -20,6 +20,7 @@ properties:
- enum:
- fsl,imx8mq-mipi-csi2
- fsl,imx8qxp-mipi-csi2
+ - fsl,imx8ulp-mipi-csi2
- items:
- const: fsl,imx8qm-mipi-csi2
- const: fsl,imx8qxp-mipi-csi2
@@ -39,12 +40,16 @@ properties:
clock that the RX DPHY receives.
- description: ui is the pixel clock (phy_ref up to 333Mhz).
See the reference manual for details.
+ - description: pclk is clock for csr APB interface.
+ minItems: 3
clock-names:
items:
- const: core
- const: esc
- const: ui
+ - const: pclk
+ minItems: 3
power-domains:
maxItems: 1
@@ -130,21 +135,53 @@ allOf:
compatible:
contains:
enum:
- - fsl,imx8qxp-mipi-csi2
+ - fsl,imx8mq-mipi-csi2
+ then:
+ properties:
+ reg:
+ maxItems: 1
+ resets:
+ minItems: 3
+ clocks:
+ maxItems: 3
+ clock-names:
+ maxItems: 3
+ required:
+ - fsl,mipi-phy-gpr
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx8qxp-mipi-csi2
then:
properties:
reg:
minItems: 2
resets:
maxItems: 1
- else:
+ clocks:
+ maxItems: 3
+ clock-names:
+ maxItems: 3
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,imx8ulp-mipi-csi2
+ then:
properties:
reg:
- maxItems: 1
+ minItems: 2
resets:
- minItems: 3
- required:
- - fsl,mipi-phy-gpr
+ minItems: 2
+ maxItems: 2
+ clocks:
+ minItems: 4
+ clock-names:
+ minItems: 4
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml
index 46cc7fff1599..084b65740d53 100644
--- a/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml
@@ -124,7 +124,6 @@ properties:
maxItems: 4
required:
- - clock-lanes
- data-lanes
port@1:
@@ -147,7 +146,6 @@ properties:
maxItems: 4
required:
- - clock-lanes
- data-lanes
port@2:
@@ -170,7 +168,6 @@ properties:
maxItems: 4
required:
- - clock-lanes
- data-lanes
required:
diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
index 2c2bd87582eb..4ac4a3b6f406 100644
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
@@ -17,6 +17,7 @@ description:
properties:
compatible:
enum:
+ - fsl,imx93-mipi-csi2
- rockchip,rk3568-mipi-csi2
reg:
@@ -26,14 +27,23 @@ properties:
items:
- description: Interrupt that signals changes in CSI2HOST_ERR1.
- description: Interrupt that signals changes in CSI2HOST_ERR2.
+ minItems: 1
interrupt-names:
items:
- const: err1
- const: err2
+ minItems: 1
clocks:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: per
+ - const: pixel
+ minItems: 1
phys:
maxItems: 1
@@ -88,10 +98,43 @@ required:
- phys
- ports
- power-domains
- - resets
additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3568-mipi-csi2
+ then:
+ properties:
+ interrupts:
+ minItems: 2
+ interrupt-names:
+ minItems: 2
+ clocks:
+ maxItems: 1
+ clock-names:
+ maxItems: 1
+ required:
+ - resets
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx93-mipi-csi2
+ then:
+ properties:
+ interrupts:
+ maxItems: 1
+ interrupt-names: false
+ clocks:
+ minItems: 2
+ clock-names:
+ minItems: 2
+
examples:
- |
#include <dt-bindings/clock/rk3568-cru.h>
diff --git a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
index 809fda45b3bd..42022401d0ff 100644
--- a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
@@ -28,16 +28,20 @@ properties:
reg:
minItems: 1
- items:
- - description: The function configuration registers base
- - description: The link table configuration registers base
- - description: The cache configuration registers base
+ maxItems: 3
reg-names:
- items:
- - const: function
- - const: link
- - const: cache
+ oneOf:
+ - items:
+ - const: link
+ - const: function
+ - const: cache
+ - items:
+ - const: function
+ - const: link
+ - const: cache
+ deprecated: true
+ description: Use link,function,cache block order instead.
interrupts:
maxItems: 1
@@ -123,6 +127,8 @@ allOf:
minItems: 5
reset-names:
minItems: 5
+ required:
+ - reg-names
else:
properties:
reg:
diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
index 34147127192f..d9fbb90b0977 100644
--- a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
+++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
@@ -27,11 +27,14 @@ properties:
- const: mclk
dmas:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
dma-names:
items:
- const: tx
+ - const: mdma_tx
+ minItems: 1
resets:
maxItems: 1
@@ -40,6 +43,15 @@ properties:
minItems: 1
maxItems: 2
+ power-domains:
+ maxItems: 1
+
+ sram:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ phandle to a reserved SRAM region which is used as temporary
+ storage memory between DMA and MDMA engines.
+
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
deleted file mode 100644
index c66586d90fa2..000000000000
--- a/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
+++ /dev/null
@@ -1,180 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Starfive SoC CAMSS ISP
-
-maintainers:
- - Jack Zhu <jack.zhu@starfivetech.com>
- - Changhuang Liang <changhuang.liang@starfivetech.com>
-
-description:
- The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It
- consists of a VIN controller (Video In Controller, a top-level control unit)
- and an ISP.
-
-properties:
- compatible:
- const: starfive,jh7110-camss
-
- reg:
- maxItems: 2
-
- reg-names:
- items:
- - const: syscon
- - const: isp
-
- clocks:
- maxItems: 7
-
- clock-names:
- items:
- - const: apb_func
- - const: wrapper_clk_c
- - const: dvp_inv
- - const: axiwr
- - const: mipi_rx0_pxl
- - const: ispcore_2x
- - const: isp_axi
-
- resets:
- maxItems: 6
-
- reset-names:
- items:
- - const: wrapper_p
- - const: wrapper_c
- - const: axird
- - const: axiwr
- - const: isp_top_n
- - const: isp_top_axi
-
- power-domains:
- items:
- - description: JH7110 ISP Power Domain Switch Controller.
-
- interrupts:
- maxItems: 4
-
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
-
- properties:
- port@0:
- $ref: /schemas/graph.yaml#/$defs/port-base
- unevaluatedProperties: false
- description: Input port for receiving DVP data.
-
- properties:
- endpoint:
- $ref: video-interfaces.yaml#
- unevaluatedProperties: false
-
- properties:
- bus-type:
- enum: [5, 6]
-
- bus-width:
- enum: [8, 10, 12]
-
- data-shift:
- enum: [0, 2]
- default: 0
-
- hsync-active:
- enum: [0, 1]
- default: 1
-
- vsync-active:
- enum: [0, 1]
- default: 1
-
- required:
- - bus-type
- - bus-width
-
- port@1:
- $ref: /schemas/graph.yaml#/properties/port
- description: Input port for receiving CSI data.
-
- required:
- - port@0
- - port@1
-
-required:
- - compatible
- - reg
- - reg-names
- - clocks
- - clock-names
- - resets
- - reset-names
- - power-domains
- - interrupts
- - ports
-
-additionalProperties: false
-
-examples:
- - |
- isp@19840000 {
- compatible = "starfive,jh7110-camss";
- reg = <0x19840000 0x10000>,
- <0x19870000 0x30000>;
- reg-names = "syscon", "isp";
- clocks = <&ispcrg 0>,
- <&ispcrg 13>,
- <&ispcrg 2>,
- <&ispcrg 12>,
- <&ispcrg 1>,
- <&syscrg 51>,
- <&syscrg 52>;
- clock-names = "apb_func",
- "wrapper_clk_c",
- "dvp_inv",
- "axiwr",
- "mipi_rx0_pxl",
- "ispcore_2x",
- "isp_axi";
- resets = <&ispcrg 0>,
- <&ispcrg 1>,
- <&ispcrg 10>,
- <&ispcrg 11>,
- <&syscrg 41>,
- <&syscrg 42>;
- reset-names = "wrapper_p",
- "wrapper_c",
- "axird",
- "axiwr",
- "isp_top_n",
- "isp_top_axi";
- power-domains = <&pwrc 5>;
- interrupts = <92>, <87>, <88>, <90>;
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- vin_from_sc2235: endpoint {
- remote-endpoint = <&sc2235_to_vin>;
- bus-type = <5>;
- bus-width = <8>;
- data-shift = <2>;
- hsync-active = <1>;
- vsync-active = <0>;
- pclk-sample = <1>;
- };
- };
-
- port@1 {
- reg = <1>;
- vin_from_csi2rx: endpoint {
- remote-endpoint = <&csi2rx_to_vin>;
- };
- };
- };
- };
diff --git a/Documentation/driver-api/media/index.rst b/Documentation/driver-api/media/index.rst
index d5593182a3f9..08fc2cfc07a3 100644
--- a/Documentation/driver-api/media/index.rst
+++ b/Documentation/driver-api/media/index.rst
@@ -26,6 +26,7 @@ Documentation/userspace-api/media/index.rst
:numbered:
maintainer-entry-profile
+ media-committers
v4l2-core
dtv-core
diff --git a/Documentation/driver-api/media/maintainer-entry-profile.rst b/Documentation/driver-api/media/maintainer-entry-profile.rst
index 2127e5b15e8f..c5c00c66d85c 100644
--- a/Documentation/driver-api/media/maintainer-entry-profile.rst
+++ b/Documentation/driver-api/media/maintainer-entry-profile.rst
@@ -1,45 +1,328 @@
+.. SPDX-License-Identifier: GPL-2.0
+
Media Subsystem Profile
=======================
Overview
--------
-The media subsystem covers support for a variety of devices: stream
-capture, analog and digital TV streams, cameras, remote controllers, HDMI CEC
-and media pipeline control.
+The Linux Media Community (aka: the LinuxTV Community) is formed by
+developers working on Linux Kernel Media Subsystem, together with users
+who also play an important role in testing the code.
+
+The Media Subsystem has code to support a wide variety of media-related
+devices: stream capture, analog and digital TV streams, cameras,
+video codecs, video processing (resizers, etc.), radio, remote controllers,
+HDMI CEC and media pipeline control.
-It covers, mainly, the contents of those directories:
+The Media Subsystem consists of the following directories in the kernel
+tree:
- drivers/media
- drivers/staging/media
+ - include/media
+ - Documentation/devicetree/bindings/media/\ [1]_
- Documentation/admin-guide/media
- Documentation/driver-api/media
- Documentation/userspace-api/media
- - Documentation/devicetree/bindings/media/\ [1]_
- - include/media
.. [1] Device tree bindings are maintained by the
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS maintainers
(see the MAINTAINERS file). So, changes there must be reviewed
- by them before being merged via the media subsystem's development
+ by them before being merged into the media subsystem's development
tree.
Both media userspace and Kernel APIs are documented and the documentation
must be kept in sync with the API changes. It means that all patches that
add new features to the subsystem must also bring changes to the
-corresponding API files.
+corresponding API documentation.
+
+Media Maintainers
+-----------------
+
+Media Maintainers are not just people capable of writing code, but they
+are developers who have demonstrated their ability to collaborate with
+the team, get the most knowledgeable people to review code, contribute
+high-quality code, and follow through to fix issues (in code or tests).
+
+Due to the size and wide scope of the media subsystem, multiple layers of
+maintainers are required, each with their own areas of expertise:
+
+- **Media Driver Maintainer**:
+ Responsible for one or more drivers within the Media Subsystem. They
+ are listed in the MAINTAINERS file as maintainer for those drivers. Media
+ Driver Maintainers review patches for those drivers, provide feedback if
+ patches do not follow the subsystem rules, or are not using the
+ media kernel or userspace APIs correctly, or if they have poor code
+ quality.
+
+ If you are the patch author, you work with other Media
+ Maintainers to ensure your patches are reviewed.
+
+ Some Media Driver Maintainers have additional responsibilities. They have
+ been granted Patchwork access and keep
+ `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
+ up to date, decide when patches are ready for merging, and create Pull
+ Requests for the Media Subsystem Maintainers to merge.
+
+- **Media Core Maintainer**:
+ Media Driver Maintainers with Patchwork access who are also responsible for
+ one or more media core frameworks.
+
+ Core framework changes are done via consensus between the relevant Media
+ Core Maintainers. Media Maintainers may include core framework changes in
+ their Pull Requests if they are signed off by the relevant Media Core
+ Maintainers.
+
+- **Media Subsystem Maintainers**:
+ Media Core Maintainers who are also responsible for the subsystem as a
+ whole, with access to the entire subsystem. Responsible for merging Pull
+ Requests from other Media Maintainers.
+
+ Userspace API/ABI changes are made via consensus among Media Subsystem
+ Maintainers\ [2]_. Media Maintainers may include API/ABI changes in
+ their Pull Requests if they are signed off by all Media Subsystem
+ Maintainers.
+
+All Media Maintainers shall agree with the Kernel development process as
+described in Documentation/process/index.rst and with the Kernel development
+rules in the Kernel documentation, including its code of conduct.
+
+Media Maintainers are often reachable via the #linux-media IRC channel at OFTC.
+
+.. [2] Everything that would break backward compatibility with existing
+ non-kernel code are API/ABI changes. This includes ioctl and sysfs
+ interfaces, v4l2 controls, and their behaviors.
+
+Patchwork Access
+----------------
+
+All Media Maintainers who have been granted Patchwork access shall ensure that
+`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
+will reflect the current status, e.g. patches shall be delegated to the Media
+Maintainer who is handling them and the patch status shall be updated according
+to these rules:
+
+- ``Under Review``: Used if the patch requires a second opinion
+ or when it is part of a Pull Request;
+- ``Superseded``: There is a newer version of the patch posted to the
+ mailing list.
+- ``Duplicated``: There was another patch doing the same thing from someone
+ else that was accepted.
+- ``Not Applicable``: Use for patch series that are not merged at media.git
+ tree (e.g. drm, dmabuf, upstream merge, etc.) but were cross-posted to the
+ linux-media mailing list.
+- ``Accepted``: Once a patch is merged in the multi-committer tree. Only Media
+ Maintainers with commit rights are allowed to set this state.
+
+If Media Maintainers decide not to accept a patch, they should reply to the
+patch authors by e‑mail, explaining why it is not accepted, and
+update `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
+accordingly with one of the following statuses:
+
+- ``Changes Requested``: if a new revision was requested;
+- ``Rejected``: if the proposed change is not acceptable at all.
+
+.. Note::
+
+ Patchwork supports a couple of clients to help semi-automate
+ status updates via its REST interface:
+
+ https://patchwork.readthedocs.io/en/latest/usage/clients/
+
+For patches that fall within their area of responsibility a Media Maintainer
+also decides when those patches are ready for merging, and create Pull Requests
+for the Media Subsystem Maintainers to merge.
+
+The most important aspect of becoming a Media Maintainer with Patchwork access
+is that you have demonstrated an ability to give good code reviews. We value
+your ability to deliver thorough, constructive code reviews.
+
+As such, potential maintainers must earn enough credibility and trust from the
+Linux Media Community. To do that, developers shall be familiar with the open
+source model and have been active in the Linux Kernel community for some time,
+and, in particular, in the media subsystem.
+
+In addition to actually making the code changes, you are basically
+demonstrating your:
+
+- commitment to the project;
+- ability to collaborate with the team and communicate well;
+- understanding of how upstream and the Linux Media Community work
+ (policies, processes for testing, code review, ...)
+- reasonable knowledge about:
+
+ - the Kernel development process:
+ Documentation/process/index.rst
+
+ - the Media development profile:
+ Documentation/driver-api/media/maintainer-entry-profile.rst
+
+- understanding of the projects' code base and coding style;
+- ability to provide feedback to the patch authors;
+- ability to judge when a patch might be ready for review and to submit;
+- ability to write good code (last but certainly not least).
+
+Media Driver Maintainers that desire to get Patchwork access are encouraged
+to participate at the yearly Linux Media Summit, typically co-located with
+a Linux-related conference. These summits are announced on the linux-media
+mailing list.
+
+If you are doing such tasks and have become a valued developer, an
+existing Media Maintainer can nominate you to the Media Subsystem Maintainers.
+
+The ultimate responsibility for accepting a nominated maintainer is up to
+the subsystem's maintainers. The nominated maintainer must have earned a trust
+relationship with all Media Subsystem Maintainers, as, by being granted
+Patchwork access, you will take over part of their maintenance tasks.
+
+Media Committers
+----------------
+
+Experienced and trusted Media Maintainers may be granted commit rights
+which allow them to directly push patches to the media development tree instead
+of posting a Pull Request for the Media Subsystem Maintainers. This helps
+offloading some of the work of the Media Subsystem Maintainers.
+
+More details about Media Committers' roles and responsibilities can be
+found here: :ref:`Media Committers`.
+
+Media development sites
+-----------------------
+
+The `LinuxTV <https://linuxtv.org/>`_ web site hosts news about the subsystem,
+together with:
+
+- `Wiki pages <https://www.linuxtv.org/wiki/index.php/Main_Page>`_;
+- `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_;
+- `Linux Media documentation <https://linuxtv.org/docs.php>`_;
+- and more.
+
+The main development trees used by the media subsystem are at:
+
+- Stable tree:
+ - https://git.linuxtv.org/media.git/
+
+- Media committers tree:
+ - https://gitlab.freedesktop.org/linux-media/media-committers.git
+
+ Please note that it can be rebased, although only as a last resort.
+
+- Media development trees, including apps and CI:
+
+ - https://git.linuxtv.org/
+ - https://gitlab.freedesktop.org/linux-media/
+
+
+.. _Media development workflow:
+
+Media development workflow
+++++++++++++++++++++++++++
+
+All changes for the media subsystem shall be sent first as e-mails to the
+media mailing list, following the process documented at
+Documentation/process/index.rst.
+
+It means that patches shall be submitted as plain text only via e-mail to
+linux-media@vger.kernel.org (aka: LMML). While subscription is not mandatory,
+you can find details about how to subscribe to it and to see its archives at:
+
+ https://subspace.kernel.org/vger.kernel.org.html
+
+Emails with HTML will be automatically rejected by the mail server.
+
+It could be wise to also copy the relevant Media Maintainer(s). You should use
+``scripts/get_maintainers.pl`` to identify whom else needs to be copied.
+Please always copy driver's authors and maintainers.
+
+To minimize the chance of merge conflicts for your patch series, and make it
+easier to backport patches to stable Kernels, we recommend that you use the
+following baseline for your patch series:
-Due to the size and wide scope of the media subsystem, media's
-maintainership model is to have sub-maintainers that have a broad
-knowledge of a specific aspect of the subsystem. It is the sub-maintainers'
-task to review the patches, providing feedback to users if the patches are
-following the subsystem rules and are properly using the media kernel and
-userspace APIs.
+1. Features for the next mainline release:
-Patches for the media subsystem must be sent to the media mailing list
-at linux-media@vger.kernel.org as plain text only e-mail. Emails with
-HTML will be automatically rejected by the mail server. It could be wise
-to also copy the sub-maintainer(s).
+ - baseline shall be the ``media-committers.git next`` branch;
+
+2. Bug fixes for the next mainline release:
+
+ - baseline shall be the ``media-committers.git next`` branch. If the
+ changes depend on a fix from the ``media-committers.git fixes``
+ branch, then you can use that as baseline.
+
+3. Bug fixes for the current mainline release (-rcX):
+
+ - baseline shall be the latest mainline -rcX release or the
+ ``media-committers.git fixes`` branch if changes depend on a mainline
+ fix that is not yet merged;
+
+.. Note::
+
+ See https://www.kernel.org/category/releases.html for an overview
+ about Kernel release types.
+
+Patches with fixes shall have:
+
+- a ``Fixes:`` tag pointing to the first commit that introduced the bug;
+- when applicable, a ``Cc: stable@vger.kernel.org``.
+
+Patches that were fixing bugs publicly reported by someone at the
+linux-media@vger.kernel.org mailing list shall have:
+
+- a ``Reported-by:`` tag immediately followed by a ``Closes:`` tag.
+
+Patches that change API shall update documentation accordingly at the
+same patch series.
+
+See Documentation/process/index.rst for more details about e-mail submission.
+
+Once a patch is submitted, it may follow either one of the following
+workflows:
+
+a. Media Maintainers' workflow: Media Maintainers post the Pull Requests,
+ which are handled by the Media Subsystem Maintainers::
+
+ +-------+ +------------+ +------+ +-------+ +---------------------+
+ |e-mail |-->|picked up by|-->|code |-->|pull |-->|Subsystem Maintainers|
+ |to LMML| |Patchwork | |review| |request| |merge in |
+ | | | | | | | | |media-committers.git |
+ +-------+ +------------+ +------+ +-------+ +---------------------+
+
+ For this workflow, Pull Requests are generated by Media Maintainers with
+ Patchwork access. If you do not have Patchwork access, then please don't
+ submit Pull Requests, as they will not be processed.
+
+b. Media Committers' workflow: patches are handled by Media Maintainers with
+ commit rights::
+
+ +-------+ +------------+ +------+ +--------------------------+
+ |e-mail |-->|picked up by|-->|code |-->|Media Committers merge in |
+ |to LMML| |Patchwork | |review| |media-committers.git |
+ +-------+ +------------+ +------+ +--------------------------+
+
+When patches are picked up by
+`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
+and when merged at media-committers, Media CI bots will check for errors and
+may provide e-mail feedback about patch problems. When this happens, the patch
+submitter must fix them or explain why the errors are false positives.
+
+Patches will only be moved to the next stage in these two workflows if they
+pass on Media CI or if there are false-positives in the Media CI reports.
+
+For both workflows, all patches shall be properly reviewed at
+linux-media@vger.kernel.org (LMML) before being merged in
+``media-committers.git``. Media patches will be reviewed in a timely manner
+by the maintainers and reviewers as listed in the MAINTAINERS file.
+
+Media Maintainers shall request reviews from other Media Maintainers and
+developers where applicable, i.e. because those developers have more
+knowledge about some areas that are changed by a patch.
+
+There shall be no open issues or unresolved or conflicting feedback
+from anyone. Clear them up first. Defer to the Media Subsystem
+Maintainers if needed.
+
+Failures during e-mail submission
++++++++++++++++++++++++++++++++++
Media's workflow is heavily based on Patchwork, meaning that, once a patch
is submitted, the e-mail will first be accepted by the mailing list
@@ -47,51 +330,107 @@ server, and, after a while, it should appear at:
- https://patchwork.linuxtv.org/project/linux-media/list/
-If it doesn't automatically appear there after a few minutes, then
+If it doesn't automatically appear there after some time [3]_, then
probably something went wrong on your submission. Please check if the
-email is in plain text\ [2]_ only and if your emailer is not mangling
+email is in plain text\ [4]_ only and if your emailer is not mangling
whitespaces before complaining or submitting them again.
-You can check if the mailing list server accepted your patch, by looking at:
+To troubleshoot problems, you should first check if the mailing list
+server has accepted your patch, by looking at:
- https://lore.kernel.org/linux-media/
-.. [2] If your email contains HTML, the mailing list server will simply
+If the patch is there and not at
+`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
+it is likely that your e-mailer mangled the patch. Patchwork internally
+has logic that checks if the received e-mail contains a valid patch.
+Any whitespace and new line breakages mangling the patch won't be recognized by
+`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
+and such a patch will be rejected.
+
+.. [3] It usually takes a few minutes for the patch to arrive, but
+ the e-mail server may be busy, so it may take a longer time
+ for a patch to be picked by
+ `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_.
+
+.. [4] If your email contains HTML, the mailing list server will simply
drop it, without any further notice.
+.. _media-developers-gpg:
+
+Authentication for pull and merge requests
+++++++++++++++++++++++++++++++++++++++++++
+
+The authenticity of developers submitting Pull Requests and merge requests
+shall be validated by using the Linux Kernel Web of Trust, with PGP signing
+at some moment. See: :ref:`kernel_org_trust_repository`.
-Media maintainers
-+++++++++++++++++
+With the Pull Request workflow, Pull Requests shall use PGP-signed tags.
-At the media subsystem, we have a group of senior developers that
-are responsible for doing the code reviews at the drivers (also known as
-sub-maintainers), and another senior developer responsible for the
-subsystem as a whole. For core changes, whenever possible, multiple
-media maintainers do the review.
+With the committers' workflow, this is ensured at the time merge request
+rights will be granted to the gitlab instance used by the media-committers.git
+tree, after receiving the e-mail documented in
+:ref:`media-committer-agreement`.
+
+For more details about PGP signing, please read
+Documentation/process/maintainer-pgp-guide.rst.
+
+Maintaining media maintainer status
+-----------------------------------
+
+See :ref:`Maintain Media Status`.
+
+List of Media Maintainers
+-------------------------
-The media maintainers that work on specific areas of the subsystem are:
+The Media Maintainers listed here all have patchwork access and can
+make Pull Requests or have commit rights.
-- Remote Controllers (infrared):
- Sean Young <sean@mess.org>
+The Media Subsystem Maintainers are:
+ - Mauro Carvalho Chehab <mchehab@kernel.org>
+ - Hans Verkuil <hverkuil@kernel.org>
-- HDMI CEC:
- Hans Verkuil <hverkuil@kernel.org>
+The Media Core Maintainers are:
+ - Sakari Ailus <sakari.ailus@linux.intel.com>
-- Media controller drivers:
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ - Media controller drivers
+ - Core media controller framework
+ - ISP
+ - sensor drivers
+ - v4l2-async and v4l2-fwnode core frameworks
+ - v4l2-flash-led-class core framework
-- ISP, v4l2-async, v4l2-fwnode, v4l2-flash-led-class and Sensor drivers:
- Sakari Ailus <sakari.ailus@linux.intel.com>
+ - Mauro Carvalho Chehab <mchehab@kernel.org>
-- V4L2 drivers and core V4L2 frameworks:
- Hans Verkuil <hverkuil@kernel.org>
+ - DVB
-The subsystem maintainer is:
- Mauro Carvalho Chehab <mchehab@kernel.org>
+ - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-Media maintainers may delegate a patch to other media maintainers as needed.
-On such case, checkpatch's ``delegate`` field indicates who's currently
-responsible for reviewing a patch.
+ - Media controller drivers
+ - Core media controller framework
+ - ISP
+
+ - Hans Verkuil <hverkuil@kernel.org>
+
+ - V4L2 drivers
+ - V4L2 and videobuf2 core frameworks
+ - HDMI CEC drivers
+ - HDMI CEC core framework
+
+ - Sean Young <sean@mess.org>
+
+ - Remote Controller (infrared) drivers
+ - Remote Controller (infrared) core framework
+
+The Media Driver Maintainers responsible for specific areas are:
+ - Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+ - Codec drivers
+ - M2M driver not otherwise delegated
+
+ - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+ - Qualcomm drivers
Submit Checklist Addendum
-------------------------
@@ -106,18 +445,15 @@ that should be used in order to check if the drivers are properly
implementing the media APIs:
==================== =======================================================
-Type Tool
+Type Utility
==================== =======================================================
-V4L2 drivers\ [3]_ ``v4l2-compliance``
+V4L2 drivers\ [5]_ ``v4l2-compliance``
V4L2 virtual drivers ``contrib/test/test-media``
CEC drivers ``cec-compliance``
==================== =======================================================
-.. [3] The ``v4l2-compliance`` also covers the media controller usage inside
- V4L2 drivers.
-
-Other compliance tools are under development to check other parts of the
-subsystem.
+.. [5] The ``v4l2-compliance`` utility also covers the media controller usage
+ inside V4L2 drivers.
Those tests need to pass before the patches go upstream.
@@ -134,6 +470,8 @@ Where the check script is::
Be sure to not introduce new warnings on your patches without a
very good reason.
+Please see `Media development workflow`_ for e-mail submission rules.
+
Style Cleanup Patches
+++++++++++++++++++++
@@ -173,34 +511,35 @@ least, simply wrapping the lines.
In particular, we accept lines with more than 80 columns:
- on strings, as they shouldn't be broken due to line length limits;
- - when a function or variable name need to have a big identifier name,
- which keeps hard to honor the 80 columns limit;
+ - when a function or variable name needs to have a long identifier name,
+ which makes hard to honor the 80 columns limit;
- on arithmetic expressions, when breaking lines makes them harder to
read;
- - when they avoid a line to end with an open parenthesis or an open
+ - when they avoid a line ending with an open parenthesis or an open
bracket.
Key Cycle Dates
---------------
-New submissions can be sent at any time, but if they intend to hit the
+New submissions can be sent at any time, but if they are intended to hit the
next merge window they should be sent before -rc5, and ideally stabilized
in the linux-media branch by -rc6.
Review Cadence
--------------
-Provided that your patch is at https://patchwork.linuxtv.org, it should
-be sooner or later handled, so you don't need to re-submit a patch.
+Provided that your patch has landed in
+`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_, it
+should be sooner or later handled, so you don't need to re-submit a patch.
-Except for bug fixes, we don't usually add new patches to the development
-tree between -rc6 and the next -rc1.
+Except for important bug fixes, we don't usually add new patches to the
+development tree between -rc6 and the next -rc1.
Please notice that the media subsystem is a high traffic one, so it
could take a while for us to be able to review your patches. Feel free
to ping if you don't get a feedback in a couple of weeks or to ask
-other developers to publicly add Reviewed-by and, more importantly,
+other developers to publicly add ``Reviewed-by:`` and, more importantly,
``Tested-by:`` tags.
Please note that we expect a detailed description for ``Tested-by:``,
-identifying what boards were used at the test and what it was tested.
+identifying what boards were used during the test and what it was tested.
diff --git a/Documentation/driver-api/media/media-committers.rst b/Documentation/driver-api/media/media-committers.rst
new file mode 100644
index 000000000000..a905856f6a61
--- /dev/null
+++ b/Documentation/driver-api/media/media-committers.rst
@@ -0,0 +1,203 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _Media Committers:
+
+Media Committers
+================
+
+Who is a Media Committer?
+-------------------------
+
+A Media Committer is a Media Maintainer with patchwork access who has been
+granted commit access to push patches from other developers and their own
+patches to the
+`media-committers <https://gitlab.freedesktop.org/linux-media/media-committers>`_
+tree.
+
+These commit rights are granted with expectation of responsibility:
+committers are people who care about the Linux Kernel as a whole and
+about the Linux media subsystem and want to advance its development. It
+is also based on a trust relationship among other committers, maintainers
+and the Linux Media community.
+
+As Media Committer you have the following additional responsibilities:
+
+1. Patches you authored must have a ``Signed-off-by``, ``Reviewed-by``
+ or ``Acked-by`` from another Media Maintainer;
+2. If a patch introduces a regression, then that must be corrected as soon
+ as possible. Typically the patch is either reverted, or an additional
+ patch is committed to fix the regression;
+3. If patches are fixing bugs against already released Kernels, including
+ the reverts mentioned above, the Media Committer shall add the needed
+ tags. Please see :ref:`Media development workflow` for more details.
+4. All Media Committers are responsible for maintaining
+ `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
+ updating the state of the patches they review or merge.
+
+
+Becoming a Media Committer
+--------------------------
+
+Existing Media Committers can nominate a Media Maintainer to be granted
+commit rights. The Media Maintainer must have patchwork access,
+have been reviewing patches from third parties for some time, and has
+demonstrated a good understanding of the maintainer's duties and processes.
+
+The ultimate responsibility for accepting a nominated committer is up to
+the Media Subsystem Maintainers. The nominated committer must have earned a
+trust relationship with all Media Subsystem Maintainers, as, by granting you
+commit rights, part of their responsibilities are handed over to you.
+
+Due to that, to become a Media Committer, a consensus between all Media
+Subsystem Maintainers is required.
+
+.. Note::
+
+ In order to preserve/protect the developers that could have their commit
+ rights granted, denied or removed as well as the subsystem maintainers who
+ have the task to accept or deny commit rights, all communication related to
+ changing commit rights should happen in private as much as possible.
+
+.. _media-committer-agreement:
+
+Media Committer's agreement
+---------------------------
+
+Once a nominated committer is accepted by all Media Subsystem Maintainers,
+they will ask if the developer is interested in the nomination and discuss
+what area(s) of the media subsystem the committer will be responsible for.
+Those areas will typically be the same as the areas that the nominated
+committer is already maintaining.
+
+When the developer accepts being a committer, the new committer shall
+explicitly accept the Kernel development policies described under its
+Documentation/, and in particular to the rules in this document, by writing
+an e-mail to media-committers@linuxtv.org, with a declaration of intent
+following the model below::
+
+ I, John Doe, would like to change my status to: Committer
+
+ As Media Maintainer I accept commit rights for the following areas of
+ the media subsystem:
+
+ ...
+
+ For the purpose of committing patches to the media-committers tree,
+ I'll be using my user https://gitlab.freedesktop.org/users/<username>.
+
+Followed by a formal declaration of agreement with the Kernel development
+rules::
+
+ I agree to follow the Kernel development rules described at:
+
+ https://www.kernel.org/doc/html/latest/driver-api/media/media-committers.rst
+
+ and to the Linux Kernel development process rules.
+
+ I agree to abide by the Code of Conduct as documented in:
+ https://www.kernel.org/doc/html/latest/process/code-of-conduct.rst
+
+ I am aware that I can, at any point of time, retire. In that case, I will
+ send an e-mail to notify the Media Subsystem Maintainers for them to revoke
+ my commit rights.
+
+ I am aware that the Kernel development rules change over time.
+ By doing a new push to media-committers tree, I understand that I agree
+ to follow the rules in effect at the time of the commit.
+
+That e-mail shall be signed via the Kernel Web of trust with a PGP key cross
+signed by other Kernel and media developers. As described at
+:ref:`media-developers-gpg`, the PGP signature, together with the gitlab user
+security are fundamental components that ensure the authenticity of the merge
+requests that will happen at the media-committers.git tree.
+
+In case the kernel development process changes, by merging new commits to the
+`media-committers tree <https://gitlab.freedesktop.org/linux-media/media-committers>`_,
+the Media Committer implicitly declares their agreement with the latest
+version of the documented process including the contents of this file.
+
+If a Media Committer decides to retire, it is the committer's duty to
+notify the Media Subsystem Maintainers about that decision.
+
+.. note::
+
+ 1. Changes to the kernel media development process shall be announced in
+ the media-committers mailing list with a reasonable review period. All
+ committers are automatically subscribed to that mailing list;
+ 2. Due to the distributed nature of the Kernel development, it is
+ possible that kernel development process changes may end being
+ reviewed/merged at the Linux Docs and/or at the Linux Kernel mailing
+ lists, especially for the contents under Documentation/process and for
+ trivial typo fixes.
+
+Media Core Committers
+---------------------
+
+A Media Core Committer is a Media Core Maintainer with commit rights.
+
+As described in Documentation/driver-api/media/maintainer-entry-profile.rst,
+a Media Core Maintainer maintains media core frameworks as well, besides
+just drivers, and so is allowed to change core files and the media subsystem's
+Kernel API. The extent of the core committer's grants will be detailed by the
+Media Subsystem Maintainers when they nominate a Media Core Committer.
+
+Existing Media Committers may become Media Core Committers and vice versa.
+Such decisions will be taken in consensus among the Media Subsystem
+Maintainers.
+
+Media committers rules
+----------------------
+
+Media committers shall do their best efforts to avoid merging patches that
+would break any existing drivers. If it breaks, fixup or revert patches
+shall be merged as soon as possible, aiming to be merged at the same Kernel
+cycle the bug is reported.
+
+Media committers shall behave accordingly to the rights granted by
+the Media Subsystem Maintainers, especially with regards of the scope of changes
+they may apply directly at the media-committers tree. That scope can
+change over time on a mutual agreement between Media Committers and
+Media Subsystem Maintainers.
+
+The Media Committer workflow is described at :ref:`Media development workflow`.
+
+.. _Maintain Media Status:
+
+Maintaining Media Maintainer or Committer status
+------------------------------------------------
+
+A community of maintainers working together to move the Linux Kernel
+forward is essential to creating successful projects that are rewarding
+to work on. If there are problems or disagreements within the community,
+they can usually be solved through healthy discussion and debate.
+
+In the unhappy event that a Media Maintainer or Committer continues to
+disregard good citizenship (or actively disrupts the project), we may need
+to revoke that person's status. In such cases, if someone suggests the
+revocation with a good reason, then after discussing this among the Media
+Maintainers, the final decision is taken by the Media Subsystem Maintainers.
+
+As the decision to become a Media Maintainer or Committer comes from a
+consensus between Media Subsystem Maintainers, a single Media Subsystem
+Maintainer not trusting the Media Maintainer or Committer anymore is enough
+to revoke their maintenance, Patchwork grants and/or commit rights.
+
+Having commit rights revoked doesn't prevent Media Maintainers to keep
+contributing to the subsystem either via the pull request or via email workflow
+as documented at the :ref:`Media development workflow`.
+
+If a maintainer is inactive for more than a couple of Kernel cycles,
+maintainers will try to reach you via e-mail. If not possible, they may
+revoke their maintainer/patchwork and committer rights and update MAINTAINERS
+file entries accordingly. If you wish to resume contributing as maintainer
+later on, then contact the Media Subsystem Maintainers to ask if your
+maintenance, Patchwork grants and commit rights can be restored.
+
+References
+----------
+
+Much of this was inspired by/copied from the committer policies of:
+
+- `Chromium <https://chromium.googlesource.com/chromium/src/+/main/docs/contributing.md>`_;
+- `WebKit <https://webkit.org/commit-and-review-policy/>`_;
+- `Mozilla <https://www.mozilla.org/hacking/committer/>`_.
diff --git a/Documentation/process/maintainer-pgp-guide.rst b/Documentation/process/maintainer-pgp-guide.rst
index bfe877a1a7e4..652dfbe64102 100644
--- a/Documentation/process/maintainer-pgp-guide.rst
+++ b/Documentation/process/maintainer-pgp-guide.rst
@@ -897,6 +897,8 @@ the new default in GnuPG v2). To set it, add (or modify) the
trust-model tofu+pgp
+.. _kernel_org_trust_repository:
+
Using the kernel.org web of trust repository
--------------------------------------------
diff --git a/Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst b/Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst
index 81b762ef17c4..99ffda355204 100644
--- a/Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst
+++ b/Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst
@@ -444,7 +444,7 @@ Description
~~~~~~~~~~~
A call to `AUDIO_GET_CAPABILITIES`_ returns an unsigned integer with the
-following bits set according to the hardwares capabilities.
+following bits set according to the hardware's capabilities.
-----
diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
index 896177c5334f..c9999b929773 100644
--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
@@ -159,14 +159,18 @@ formats in memory (a raw Bayer image won't be magically converted to
JPEG just by storing it to memory), there is no one-to-one
correspondence between them.
-The media bus pixel codes document parallel formats. Should the pixel data be
-transported over a serial bus, the media bus pixel code that describes a
-parallel format that transfers a sample on a single clock cycle is used. For
-instance, both MEDIA_BUS_FMT_BGR888_1X24 and MEDIA_BUS_FMT_BGR888_3X8 are used
-on parallel busses for transferring an 8 bits per sample BGR data, whereas on
-serial busses the data in this format is only referred to using
-MEDIA_BUS_FMT_BGR888_1X24. This is because there is effectively only a single
-way to transport that format on the serial busses.
+While the media bus pixel codes are named based on how pixels are
+transmitted on parallel buses, serial buses do not define separate
+codes. By convention, they use the codes that transfer a sample on a
+single clock cycle, and whose bit orders from LSB to MSB correspond to
+the order in which colour components are transmitted on the serial bus.
+For instance, the MIPI CSI-2 24-bit RGB (RGB888) format uses the
+MEDIA_BUS_FMT_RGB888_1X24 media bus code because CSI-2 transmits the
+blue colour component first, followed by green and red, and
+MEDIA_BUS_FMT_RGB888_1X24 defines the first bit of blue at bit 0.
+While used for 24-bit RGB data on parallel buses, the
+MEDIA_BUS_FMT_RGB888_3X8 or MEDIA_BUS_FMT_BGR888_1X24 codes must not be
+used for CSI-2.
Packed RGB Formats
^^^^^^^^^^^^^^^^^^
diff --git a/MAINTAINERS b/MAINTAINERS
index e7dc9e6fad2e..8e48efe8d9fa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16136,8 +16136,9 @@ MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
S: Maintained
+P: Documentation/driver-api/media/maintainer-entry-profile.rst
W: https://linuxtv.org
-Q: http://patchwork.kernel.org/project/linux-media/list/
+Q: https://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/media.git
F: Documentation/admin-guide/media/
F: Documentation/devicetree/bindings/media/
@@ -19542,9 +19543,11 @@ F: drivers/media/i2c/ov02e10.c
OMNIVISION OV08D10 SENSOR DRIVER
M: Jimmy Su <jimmy.su@intel.com>
+R: Matthias Fend <matthias.fend@emfend.at>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
F: drivers/media/i2c/ov08d10.c
OMNIVISION OV08X40 SENSOR DRIVER
@@ -19585,6 +19588,13 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/ovti,ov2685.yaml
F: drivers/media/i2c/ov2685.c
+OMNIVISION OV2732 SENSOR DRIVER
+M: Walter Werner Schneider <contact@schnwalter.eu>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
+F: drivers/media/i2c/ov2732.c
+
OMNIVISION OV2735 SENSOR DRIVER
M: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
@@ -24586,7 +24596,6 @@ F: include/uapi/rdma/rdma_user_rxe.h
SOFTLOGIC 6x10 MPEG CODEC
M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
-M: Andrey Utkin <andrey_utkin@fastmail.com>
M: Ismael Luceno <ismael@iodev.co.uk>
L: linux-media@vger.kernel.org
S: Supported
@@ -25237,15 +25246,6 @@ M: Ion Badulescu <ionut@badula.org>
S: Odd Fixes
F: drivers/net/ethernet/adaptec/starfire*
-STARFIVE CAMERA SUBSYSTEM DRIVER
-M: Jack Zhu <jack.zhu@starfivetech.com>
-M: Changhuang Liang <changhuang.liang@starfivetech.com>
-L: linux-media@vger.kernel.org
-S: Maintained
-F: Documentation/admin-guide/media/starfive_camss.rst
-F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
-F: drivers/staging/media/starfive/camss
-
STARFIVE CRYPTO DRIVER
M: Jia Jie Ho <jiajie.ho@starfivetech.com>
M: William Qiu <william.qiu@starfivetech.com>
@@ -26816,6 +26816,12 @@ F: drivers/char/toshiba.c
F: include/linux/toshiba.h
F: include/uapi/linux/toshiba.h
+TOSHIBA T4KA3 CAMERA SENSOR DRIVER
+M: Kate Hsuan <hpa@redhat.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/t4ka3.c
+
TOSHIBA TC358743 DRIVER
M: Hans Verkuil <hverkuil@kernel.org>
L: linux-media@vger.kernel.org
@@ -27036,8 +27042,6 @@ F: drivers/platform/x86/tuxedo/
TW5864 VIDEO4LINUX DRIVER
M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
-M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
-M: Andrey Utkin <andrey_utkin@fastmail.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/pci/tw5864/
diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c
index d4fbdf90e190..8541acecfbbe 100644
--- a/drivers/gpio/gpio-tps68470.c
+++ b/drivers/gpio/gpio-tps68470.c
@@ -120,6 +120,17 @@ static int tps68470_gpio_input(struct gpio_chip *gc, unsigned int offset)
TPS68470_GPIO_MODE_MASK, 0x00);
}
+static int tps68470_enable_i2c_daisy_chain(struct gpio_chip *gc)
+{
+ int ret;
+
+ ret = tps68470_gpio_input(gc, 1);
+ if (ret)
+ return ret;
+
+ return tps68470_gpio_input(gc, 2);
+}
+
static const char *tps68470_names[TPS68470_N_GPIO] = {
"gpio.0", "gpio.1", "gpio.2", "gpio.3",
"gpio.4", "gpio.5", "gpio.6",
@@ -129,6 +140,7 @@ static const char *tps68470_names[TPS68470_N_GPIO] = {
static int tps68470_gpio_probe(struct platform_device *pdev)
{
struct tps68470_gpio_data *tps68470_gpio;
+ int ret;
tps68470_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps68470_gpio),
GFP_KERNEL);
@@ -149,7 +161,14 @@ static int tps68470_gpio_probe(struct platform_device *pdev)
tps68470_gpio->gc.base = -1;
tps68470_gpio->gc.parent = &pdev->dev;
- return devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, tps68470_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, tps68470_gpio);
+ if (ret)
+ return ret;
+
+ if (device_property_present(&pdev->dev, "daisy-chain-enable"))
+ ret = tps68470_enable_i2c_daisy_chain(&tps68470_gpio->gc);
+
+ return ret;
}
static struct platform_driver tps68470_gpio_driver = {
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index d3f238b1f2a9..982306eb4f0a 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2221,6 +2221,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
return;
rc_unregister_device(ctx->rc_dev);
+ rc_free_device(ctx->rc_dev);
}
static int sii8620_is_packing_required(struct sii8620 *ctx,
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 2c5aefe9621a..7f25c50621c9 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -14,6 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/tegra-mipi-cal.h>
#include <video/mipi_display.h>
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index ee5286ffe08d..fead483af0b4 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -9,6 +9,7 @@ host1x-y = \
job.o \
debug.o \
mipi.o \
+ tegra114-mipi.o \
fence.o \
hw/host1x01.o \
hw/host1x02.o \
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
index fea9f491df66..01513b775d89 100644
--- a/drivers/gpu/host1x/mipi.c
+++ b/drivers/gpu/host1x/mipi.c
@@ -1,215 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 NVIDIA Corporation
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * Copyright (C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <linux/clk.h>
-#include <linux/host1x.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
-#include "dev.h"
-
-#define MIPI_CAL_CTRL 0x00
-#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
-#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
-#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
-#define MIPI_CAL_CTRL_START (1 << 0)
-
-#define MIPI_CAL_AUTOCAL_CTRL 0x01
-
-#define MIPI_CAL_STATUS 0x02
-#define MIPI_CAL_STATUS_DONE (1 << 16)
-#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
-
-#define MIPI_CAL_CONFIG_CSIA 0x05
-#define MIPI_CAL_CONFIG_CSIB 0x06
-#define MIPI_CAL_CONFIG_CSIC 0x07
-#define MIPI_CAL_CONFIG_CSID 0x08
-#define MIPI_CAL_CONFIG_CSIE 0x09
-#define MIPI_CAL_CONFIG_CSIF 0x0a
-#define MIPI_CAL_CONFIG_DSIA 0x0e
-#define MIPI_CAL_CONFIG_DSIB 0x0f
-#define MIPI_CAL_CONFIG_DSIC 0x10
-#define MIPI_CAL_CONFIG_DSID 0x11
-
-#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
-#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
-#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
-#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
-#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
-#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
-#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
-
-/* for data and clock lanes */
-#define MIPI_CAL_CONFIG_SELECT (1 << 21)
-
-/* for data lanes */
-#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
-#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
-
-/* for clock lanes */
-#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG0 0x16
-#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
-#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG1 0x17
-#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
-
-#define MIPI_CAL_BIAS_PAD_CFG2 0x18
-#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
-#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
-
-struct tegra_mipi_pad {
- unsigned long data;
- unsigned long clk;
-};
-
-struct tegra_mipi_soc {
- bool has_clk_lane;
- const struct tegra_mipi_pad *pads;
- unsigned int num_pads;
-
- bool clock_enable_override;
- bool needs_vclamp_ref;
-
- /* bias pad configuration settings */
- u8 pad_drive_down_ref;
- u8 pad_drive_up_ref;
+/* only need to support one provider */
+static struct {
+ struct device_node *np;
+ const struct tegra_mipi_ops *ops;
+} provider;
- u8 pad_vclamp_level;
- u8 pad_vauxp_level;
-
- /* calibration settings for data lanes */
- u8 hspdos;
- u8 hspuos;
- u8 termos;
-
- /* calibration settings for clock lanes */
- u8 hsclkpdos;
- u8 hsclkpuos;
-};
-
-struct tegra_mipi {
- const struct tegra_mipi_soc *soc;
- struct device *dev;
- void __iomem *regs;
- struct mutex lock;
- struct clk *clk;
-
- unsigned long usage_count;
-};
-
-struct tegra_mipi_device {
- struct platform_device *pdev;
- struct tegra_mipi *mipi;
- struct device *device;
- unsigned long pads;
-};
-
-static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
- unsigned long offset)
+/**
+ * tegra_mipi_enable - Enable the Tegra MIPI calibration device.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This calls the enable sequence for the Tegra MIPI calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_enable(struct tegra_mipi_device *device)
{
- return readl(mipi->regs + (offset << 2));
-}
+ if (device->ops->enable)
+ return device->ops->enable(device);
-static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
- unsigned long offset)
-{
- writel(value, mipi->regs + (offset << 2));
+ return 0;
}
+EXPORT_SYMBOL(tegra_mipi_enable);
-static int tegra_mipi_power_up(struct tegra_mipi *mipi)
+/**
+ * tegra_mipi_disable - Disable the Tegra MIPI calibration device.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This calls the disable sequence for the Tegra MIPI calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_disable(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
-
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
- value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
-
- if (mipi->soc->needs_vclamp_ref)
- value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
-
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
-
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- clk_disable(mipi->clk);
+ if (device->ops->disable)
+ return device->ops->disable(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_disable);
-static int tegra_mipi_power_down(struct tegra_mipi *mipi)
+/**
+ * tegra_mipi_start_calibration - Start the Tegra MIPI calibration sequence.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This initiates the calibration of CSI/DSI interfaces via the Tegra MIPI
+ * calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
-
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- /*
- * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
- * supplies the DSI pads. This must be kept enabled until none of the
- * DSI lanes are used anymore.
- */
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value |= MIPI_CAL_BIAS_PAD_PDVREG;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- /*
- * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
- * control a regulator that supplies current to the pre-driver logic.
- * Powering down this regulator causes DSI to fail, so it must remain
- * powered on until none of the DSI lanes are used anymore.
- */
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+ if (device->ops->start_calibration)
+ return device->ops->start_calibration(device);
- if (mipi->soc->needs_vclamp_ref)
- value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_start_calibration);
- value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+/**
+ * tegra_mipi_finish_calibration - Finish the Tegra MIPI calibration sequence.
+ * @device: Handle to the Tegra MIPI calibration device.
+ *
+ * This completes the calibration of CSI/DSI interfaces via the Tegra MIPI
+ * calibration device.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
+{
+ if (device->ops->finish_calibration)
+ return device->ops->finish_calibration(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_finish_calibration);
+/**
+ * tegra_mipi_request - Request a Tegra MIPI calibration device.
+ * @device: Handle of the device requesting the MIPI calibration function.
+ * @np: Device node pointer of the device requesting the MIPI calibration
+ * function.
+ *
+ * This function requests a reference to a Tegra MIPI calibration device.
+ *
+ * Returns a pointer to the Tegra MIPI calibration device on success,
+ * or an ERR_PTR-encoded error code on failure.
+ */
struct tegra_mipi_device *tegra_mipi_request(struct device *device,
struct device_node *np)
{
- struct tegra_mipi_device *dev;
+ struct tegra_mipi_device *mipidev;
struct of_phandle_args args;
int err;
@@ -219,321 +114,80 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
if (err < 0)
return ERR_PTR(err);
- dev = kzalloc_obj(*dev);
- if (!dev) {
+ if (provider.np != args.np)
+ return ERR_PTR(-ENODEV);
+
+ mipidev = kzalloc_obj(*mipidev);
+ if (!mipidev) {
err = -ENOMEM;
goto out;
}
- dev->pdev = of_find_device_by_node(args.np);
- if (!dev->pdev) {
+ mipidev->pdev = of_find_device_by_node(args.np);
+ if (!mipidev->pdev) {
err = -ENODEV;
goto free;
}
- dev->mipi = platform_get_drvdata(dev->pdev);
- if (!dev->mipi) {
- err = -EPROBE_DEFER;
- goto put;
- }
-
of_node_put(args.np);
- dev->pads = args.args[0];
- dev->device = device;
+ mipidev->ops = provider.ops;
+ mipidev->pads = args.args[0];
- return dev;
+ return mipidev;
-put:
- platform_device_put(dev->pdev);
free:
- kfree(dev);
+ kfree(mipidev);
out:
of_node_put(args.np);
return ERR_PTR(err);
}
EXPORT_SYMBOL(tegra_mipi_request);
-void tegra_mipi_free(struct tegra_mipi_device *device)
+/**
+ * tegra_mipi_free - Free a Tegra MIPI calibration device.
+ * @mipidev: Handle to the Tegra MIPI calibration device.
+ *
+ * This function releases a reference to a Tegra MIPI calibration device
+ * previously requested by tegra_mipi_request().
+ */
+void tegra_mipi_free(struct tegra_mipi_device *mipidev)
{
- platform_device_put(device->pdev);
- kfree(device);
+ platform_device_put(mipidev->pdev);
+ kfree(mipidev);
}
EXPORT_SYMBOL(tegra_mipi_free);
-int tegra_mipi_enable(struct tegra_mipi_device *dev)
+static void tegra_mipi_remove_provider(void *data)
{
- int err = 0;
-
- mutex_lock(&dev->mipi->lock);
-
- if (dev->mipi->usage_count++ == 0)
- err = tegra_mipi_power_up(dev->mipi);
-
- mutex_unlock(&dev->mipi->lock);
-
- return err;
-
-}
-EXPORT_SYMBOL(tegra_mipi_enable);
-
-int tegra_mipi_disable(struct tegra_mipi_device *dev)
-{
- int err = 0;
-
- mutex_lock(&dev->mipi->lock);
-
- if (--dev->mipi->usage_count == 0)
- err = tegra_mipi_power_down(dev->mipi);
-
- mutex_unlock(&dev->mipi->lock);
-
- return err;
-
-}
-EXPORT_SYMBOL(tegra_mipi_disable);
-
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
-{
- struct tegra_mipi *mipi = device->mipi;
- void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
- u32 value;
- int err;
-
- err = readl_relaxed_poll_timeout(status_reg, value,
- !(value & MIPI_CAL_STATUS_ACTIVE) &&
- (value & MIPI_CAL_STATUS_DONE), 50,
- 250000);
- mutex_unlock(&device->mipi->lock);
- clk_disable(device->mipi->clk);
-
- return err;
+ provider.np = NULL;
+ provider.ops = NULL;
}
-EXPORT_SYMBOL(tegra_mipi_finish_calibration);
-
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
-{
- const struct tegra_mipi_soc *soc = device->mipi->soc;
- unsigned int i;
- u32 value;
- int err;
-
- err = clk_enable(device->mipi->clk);
- if (err < 0)
- return err;
-
- mutex_lock(&device->mipi->lock);
-
- value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
- MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
- value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
- value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
- value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- for (i = 0; i < soc->num_pads; i++) {
- u32 clk = 0, data = 0;
-
- if (device->pads & BIT(i)) {
- data = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
- MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
- MIPI_CAL_CONFIG_TERMOS(soc->termos);
- clk = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
- MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
- }
- tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
-
- if (soc->has_clk_lane && soc->pads[i].clk != 0)
- tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
- }
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
- value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
- value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
- value |= MIPI_CAL_CTRL_PRESCALE(0x2);
-
- if (!soc->clock_enable_override)
- value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
- else
- value |= MIPI_CAL_CTRL_CLKEN_OVR;
-
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /* clear any pending status bits */
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value |= MIPI_CAL_CTRL_START;
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /*
- * Wait for min 72uS to let calibration logic finish calibration
- * sequence codes before waiting for pads idle state to apply the
- * results.
- */
- usleep_range(75, 80);
-
- return 0;
-}
-EXPORT_SYMBOL(tegra_mipi_start_calibration);
-
-static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA },
- { .data = MIPI_CAL_CONFIG_CSIB },
- { .data = MIPI_CAL_CONFIG_CSIC },
- { .data = MIPI_CAL_CONFIG_CSID },
- { .data = MIPI_CAL_CONFIG_CSIE },
- { .data = MIPI_CAL_CONFIG_DSIA },
- { .data = MIPI_CAL_CONFIG_DSIB },
- { .data = MIPI_CAL_CONFIG_DSIC },
- { .data = MIPI_CAL_CONFIG_DSID },
-};
-
-static const struct tegra_mipi_soc tegra114_mipi_soc = {
- .has_clk_lane = false,
- .pads = tegra114_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x4,
- .termos = 0x5,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x4,
-};
-
-static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
-};
-
-static const struct tegra_mipi_soc tegra124_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x1,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_soc tegra132_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = false,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x3,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
- { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
- { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
-};
-
-static const struct tegra_mipi_soc tegra210_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra210_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x1,
- .pad_vauxp_level = 0x1,
- .hspdos = 0x0,
- .hspuos = 0x2,
- .termos = 0x0,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x2,
-};
-
-static const struct of_device_id tegra_mipi_of_match[] = {
- { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
- { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
- { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
- { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
- { },
-};
-
-static int tegra_mipi_probe(struct platform_device *pdev)
+/**
+ * devm_tegra_mipi_add_provider - Managed registration of a Tegra MIPI
+ * calibration function provider.
+ * @device: Handle to the device providing the MIPI calibration function.
+ * @np: Device node pointer of the device providing the MIPI calibration
+ * function.
+ * @ops: Operations supported by the MIPI calibration device.
+ *
+ * This registers a device that provides MIPI calibration functions.
+ * For Tegra20 and Tegra30, this is the CSI block, while Tegra114 and
+ * newer SoC generations have a dedicated hardware block for these
+ * functions.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
+ const struct tegra_mipi_ops *ops)
{
- const struct of_device_id *match;
- struct tegra_mipi *mipi;
-
- match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
- if (!match)
- return -ENODEV;
+ if (provider.np)
+ return -EBUSY;
- mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
- if (!mipi)
- return -ENOMEM;
+ provider.np = np;
+ provider.ops = ops;
- mipi->soc = match->data;
- mipi->dev = &pdev->dev;
-
- mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(mipi->regs))
- return PTR_ERR(mipi->regs);
-
- mutex_init(&mipi->lock);
-
- mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
- if (IS_ERR(mipi->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- return PTR_ERR(mipi->clk);
- }
-
- platform_set_drvdata(pdev, mipi);
-
- return 0;
+ return devm_add_action_or_reset(device, tegra_mipi_remove_provider, NULL);
}
-
-struct platform_driver tegra_mipi_driver = {
- .driver = {
- .name = "tegra-mipi",
- .of_match_table = tegra_mipi_of_match,
- },
- .probe = tegra_mipi_probe,
-};
+EXPORT_SYMBOL(devm_tegra_mipi_add_provider);
diff --git a/drivers/gpu/host1x/tegra114-mipi.c b/drivers/gpu/host1x/tegra114-mipi.c
new file mode 100644
index 000000000000..c084a09784d1
--- /dev/null
+++ b/drivers/gpu/host1x/tegra114-mipi.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
+
+#include "dev.h"
+
+#define MIPI_CAL_CTRL 0x00
+#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
+#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
+#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
+#define MIPI_CAL_CTRL_START BIT(0)
+
+#define MIPI_CAL_AUTOCAL_CTRL 0x01
+
+#define MIPI_CAL_STATUS 0x02
+#define MIPI_CAL_STATUS_DONE BIT(16)
+#define MIPI_CAL_STATUS_ACTIVE BIT(0)
+
+#define MIPI_CAL_CONFIG_CSIA 0x05
+#define MIPI_CAL_CONFIG_CSIB 0x06
+#define MIPI_CAL_CONFIG_CSIC 0x07
+#define MIPI_CAL_CONFIG_CSID 0x08
+#define MIPI_CAL_CONFIG_CSIE 0x09
+#define MIPI_CAL_CONFIG_CSIF 0x0a
+#define MIPI_CAL_CONFIG_DSIA 0x0e
+#define MIPI_CAL_CONFIG_DSIB 0x0f
+#define MIPI_CAL_CONFIG_DSIC 0x10
+#define MIPI_CAL_CONFIG_DSID 0x11
+
+#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
+#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
+#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
+#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
+#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
+#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
+#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
+
+/* for data and clock lanes */
+#define MIPI_CAL_CONFIG_SELECT BIT(21)
+
+/* for data lanes */
+#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
+
+/* for clock lanes */
+#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG0 0x16
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0)
+
+#define MIPI_CAL_BIAS_PAD_CFG1 0x17
+#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
+
+#define MIPI_CAL_BIAS_PAD_CFG2 0x18
+#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
+#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
+
+struct tegra_mipi_pad {
+ unsigned long data;
+ unsigned long clk;
+};
+
+struct tegra_mipi_soc {
+ bool has_clk_lane;
+ const struct tegra_mipi_pad *pads;
+ unsigned int num_pads;
+
+ bool clock_enable_override;
+ bool needs_vclamp_ref;
+
+ /* bias pad configuration settings */
+ u8 pad_drive_down_ref;
+ u8 pad_drive_up_ref;
+
+ u8 pad_vclamp_level;
+ u8 pad_vauxp_level;
+
+ /* calibration settings for data lanes */
+ u8 hspdos;
+ u8 hspuos;
+ u8 termos;
+
+ /* calibration settings for clock lanes */
+ u8 hsclkpdos;
+ u8 hsclkpuos;
+};
+
+struct tegra_mipi {
+ const struct tegra_mipi_soc *soc;
+ struct device *dev;
+ void __iomem *regs;
+ struct mutex lock; /* for register access */
+ struct clk *clk;
+
+ unsigned long usage_count;
+};
+
+static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
+ unsigned long offset)
+{
+ return readl(mipi->regs + (offset << 2));
+}
+
+static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
+ unsigned long offset)
+{
+ writel(value, mipi->regs + (offset << 2));
+}
+
+static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+
+ if (mipi->soc->needs_vclamp_ref)
+ value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ clk_disable(mipi->clk);
+
+ return 0;
+}
+
+static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ /*
+ * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
+ * supplies the DSI pads. This must be kept enabled until none of the
+ * DSI lanes are used anymore.
+ */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value |= MIPI_CAL_BIAS_PAD_PDVREG;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ /*
+ * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
+ * control a regulator that supplies current to the pre-driver logic.
+ * Powering down this regulator causes DSI to fail, so it must remain
+ * powered on until none of the DSI lanes are used anymore.
+ */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+
+ if (mipi->soc->needs_vclamp_ref)
+ value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+
+ value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ return 0;
+}
+
+static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (mipi->usage_count++ == 0)
+ err = tegra114_mipi_power_up(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (--mipi->usage_count == 0)
+ err = tegra114_mipi_power_down(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
+ u32 value;
+ int err;
+
+ err = readl_relaxed_poll_timeout(status_reg, value,
+ !(value & MIPI_CAL_STATUS_ACTIVE) &&
+ (value & MIPI_CAL_STATUS_DONE), 50,
+ 250000);
+ mutex_unlock(&mipi->lock);
+ clk_disable(mipi->clk);
+
+ return err;
+}
+
+static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ const struct tegra_mipi_soc *soc = mipi->soc;
+ unsigned int i;
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&mipi->lock);
+
+ value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
+ MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
+ value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
+ value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
+ value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ for (i = 0; i < soc->num_pads; i++) {
+ u32 clk = 0, data = 0;
+
+ if (mipidev->pads & BIT(i)) {
+ data = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
+ MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
+ MIPI_CAL_CONFIG_TERMOS(soc->termos);
+ clk = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
+ MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
+ }
+
+ tegra_mipi_writel(mipi, data, soc->pads[i].data);
+
+ if (soc->has_clk_lane && soc->pads[i].clk != 0)
+ tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
+ }
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
+ value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
+ value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
+ value |= MIPI_CAL_CTRL_PRESCALE(0x2);
+
+ if (!soc->clock_enable_override)
+ value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
+ else
+ value |= MIPI_CAL_CTRL_CLKEN_OVR;
+
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /* clear any pending status bits */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value |= MIPI_CAL_CTRL_START;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /*
+ * Wait for min 72uS to let calibration logic finish calibration
+ * sequence codes before waiting for pads idle state to apply the
+ * results.
+ */
+ usleep_range(75, 80);
+
+ return 0;
+}
+
+static const struct tegra_mipi_ops tegra114_mipi_ops = {
+ .enable = tegra114_mipi_enable,
+ .disable = tegra114_mipi_disable,
+ .start_calibration = tegra114_mipi_start_calibration,
+ .finish_calibration = tegra114_mipi_finish_calibration,
+};
+
+static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA },
+ { .data = MIPI_CAL_CONFIG_CSIB },
+ { .data = MIPI_CAL_CONFIG_CSIC },
+ { .data = MIPI_CAL_CONFIG_CSID },
+ { .data = MIPI_CAL_CONFIG_CSIE },
+ { .data = MIPI_CAL_CONFIG_DSIA },
+ { .data = MIPI_CAL_CONFIG_DSIB },
+ { .data = MIPI_CAL_CONFIG_DSIC },
+ { .data = MIPI_CAL_CONFIG_DSID },
+};
+
+static const struct tegra_mipi_soc tegra114_mipi_soc = {
+ .has_clk_lane = false,
+ .pads = tegra114_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x4,
+ .termos = 0x5,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x4,
+};
+
+static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+};
+
+static const struct tegra_mipi_soc tegra124_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x1,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_soc tegra132_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = false,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x3,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
+ { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
+};
+
+static const struct tegra_mipi_soc tegra210_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra210_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x1,
+ .pad_vauxp_level = 0x1,
+ .hspdos = 0x0,
+ .hspuos = 0x2,
+ .termos = 0x0,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x2,
+};
+
+static const struct of_device_id tegra_mipi_of_match[] = {
+ { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
+ { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
+ { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
+ { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
+ { },
+};
+
+static int tegra_mipi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_mipi *mipi;
+
+ match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+
+ mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
+ if (!mipi)
+ return -ENOMEM;
+
+ mipi->soc = match->data;
+ mipi->dev = &pdev->dev;
+
+ mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(mipi->regs))
+ return PTR_ERR(mipi->regs);
+
+ mutex_init(&mipi->lock);
+
+ mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(mipi->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(mipi->clk);
+ }
+
+ platform_set_drvdata(pdev, mipi);
+
+ return devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node,
+ &tegra114_mipi_ops);
+}
+
+struct platform_driver tegra_mipi_driver = {
+ .driver = {
+ .name = "tegra-mipi",
+ .of_match_table = tegra_mipi_of_match,
+ },
+ .probe = tegra_mipi_probe,
+};
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index d6faa0e00f95..6d4c636e1c9f 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
data->rc_dev = NULL;
rc_unregister_device(rdev);
+ rc_free_device(rdev);
}
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 1953ce559eca..0fcd3b5e60c8 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -338,8 +338,8 @@ int cec_register_adapter(struct cec_adapter *adap,
res = cec_devnode_register(&adap->devnode, adap->owner);
if (res) {
#ifdef CONFIG_MEDIA_CEC_RC
- /* Note: rc_unregister also calls rc_free */
rc_unregister_device(adap->rc);
+ rc_free_device(adap->rc);
adap->rc = NULL;
#endif
return res;
diff --git a/drivers/media/cec/usb/pulse8/pulse8-cec.c b/drivers/media/cec/usb/pulse8/pulse8-cec.c
index 0df3af152762..fa5df1062753 100644
--- a/drivers/media/cec/usb/pulse8/pulse8-cec.c
+++ b/drivers/media/cec/usb/pulse8/pulse8-cec.c
@@ -235,6 +235,9 @@ static int pulse8_send_and_wait_once(struct pulse8 *pulse8,
{
int err;
+ if (!pulse8->serio)
+ return -ENODEV;
+
if (debug > 1)
dev_info(pulse8->dev, "transmit %s: %*ph\n",
pulse8_msgname(cmd[0]), cmd_len, cmd);
@@ -655,6 +658,10 @@ static void pulse8_disconnect(struct serio *serio)
{
struct pulse8 *pulse8 = serio_get_drvdata(serio);
+ cancel_delayed_work_sync(&pulse8->ping_eeprom_work);
+ mutex_lock(&pulse8->lock);
+ pulse8->serio = NULL;
+ mutex_unlock(&pulse8->lock);
cec_unregister_adapter(pulse8->adap);
serio_set_drvdata(serio, NULL);
serio_close(serio);
diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c
index abd3d21a18ca..f7766a9b31da 100644
--- a/drivers/media/common/b2c2/flexcop.c
+++ b/drivers/media/common/b2c2/flexcop.c
@@ -291,20 +291,6 @@ void flexcop_device_exit(struct flexcop_device *fc)
}
EXPORT_SYMBOL(flexcop_device_exit);
-static int flexcop_module_init(void)
-{
- info(DRIVER_NAME " loaded successfully");
- return 0;
-}
-
-static void flexcop_module_cleanup(void)
-{
- info(DRIVER_NAME " unloaded successfully");
-}
-
-module_init(flexcop_module_init);
-module_exit(flexcop_module_cleanup);
-
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
index fd12ef10409c..fe5c376d397a 100644
--- a/drivers/media/common/saa7146/saa7146_fops.c
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -417,19 +417,6 @@ int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
}
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
-static int __init saa7146_vv_init_module(void)
-{
- return 0;
-}
-
-
-static void __exit saa7146_vv_cleanup_module(void)
-{
-}
-
-module_init(saa7146_vv_init_module);
-module_exit(saa7146_vv_cleanup_module);
-
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index af07fed21ae1..283770d583d5 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
void sms_ir_exit(struct smscore_device_t *coredev)
{
rc_unregister_device(coredev->ir.dev);
+ rc_free_device(coredev->ir.dev);
pr_debug("\n");
}
diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c
index 1ad4604474ac..0107cedd031e 100644
--- a/drivers/media/common/uvc.c
+++ b/drivers/media/common/uvc.c
@@ -41,6 +41,10 @@ static const struct uvc_format_desc uvc_fmts[] = {
.fcc = V4L2_PIX_FMT_M420,
},
{
+ .guid = UVC_GUID_FORMAT_P010,
+ .fcc = V4L2_PIX_FMT_P010,
+ },
+ {
.guid = UVC_GUID_FORMAT_UYVY,
.fcc = V4L2_PIX_FMT_UYVY,
},
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
index 982021d547e5..b1d0695cda26 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
@@ -345,6 +345,7 @@ static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
return err;
}
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
/*
* Use common vm_area operations to track buffer refcount.
*/
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index 58c4c489bf97..58b959b272c6 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -567,6 +567,7 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd,
case AU8522_COMPOSITE_CH1:
case AU8522_SVIDEO_CH13:
case AU8522_COMPOSITE_CH4_SIF:
+ case AU8522_COMPOSITE_CH4:
state->vid_input = input;
break;
default:
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index ebef27bcc989..d291113291e0 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -2695,7 +2695,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff)
static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
{
- s16 unit_khz_dds_val;
+ s32 unit_khz_dds_val;
u32 abs_offset_khz = abs(offset_khz);
u32 dds = state->cfg.pll->ifreq & 0x1ffffff;
u8 invert = !!(state->cfg.pll->ifreq & (1 << 25));
@@ -2716,7 +2716,7 @@ static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
dds = (1<<26) - dds;
} else {
ratio = 2;
- unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal);
+ unit_khz_dds_val = 67108864 / state->cfg.pll->internal;
if (offset_khz < 0)
unit_khz_dds_val *= -1;
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index be156180e5ef..44bee1f3c5e9 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -78,7 +78,10 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
.addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2
};
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
val = 0x11;
ret = regmap_write(dev->regmap, 0x03, val);
@@ -90,10 +93,18 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n",
dev->dt_addr, ret, reg, data);
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
return -EREMOTEIO;
}
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
dev->dt_addr, reg, data);
@@ -127,9 +138,14 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
}
};
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+ if (dev->chip_id == M88DS3103C_CHIP_ID) {
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
+ val = 0x11;
+ } else {
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+ val = 0x12;
+ }
- val = 0x12;
ret = regmap_write(dev->regmap, 0x03, val);
if (ret)
dev_dbg(&client->dev, "fail=%d\n", ret);
@@ -139,10 +155,18 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n",
dev->dt_addr, ret, reg);
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
return -EREMOTEIO;
}
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
dev->dt_addr, reg, b1[0]);
@@ -185,14 +209,25 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
switch (c->delivery_system) {
case SYS_DVBS:
- ret = regmap_read(dev->regmap, 0xd1, &utmp);
- if (ret)
- goto err;
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ ret = regmap_read(dev->regmap, 0x0d, &utmp);
+ if (ret)
+ goto err;
- if ((utmp & 0x07) == 0x07)
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC |
- FE_HAS_LOCK;
+ if ((utmp & 0xf7) == 0xf7)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ } else {
+ ret = regmap_read(dev->regmap, 0xd1, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp & 0x07) == 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
break;
case SYS_DVBS2:
ret = regmap_read(dev->regmap, 0x0d, &utmp);
@@ -201,8 +236,8 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
if ((utmp & 0x8f) == 0x8f)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC |
- FE_HAS_LOCK;
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
break;
default:
dev_dbg(&client->dev, "invalid delivery_system\n");
@@ -371,6 +406,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -428,8 +464,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
reg15 = m88ds3103b_dt_read(dev, 0x15);
- m88ds3103b_dt_write(dev, 0x05, 0x40);
- m88ds3103b_dt_write(dev, 0x11, 0x08);
+ if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+ m88ds3103b_dt_write(dev, 0x05, 0x40);
+ m88ds3103b_dt_write(dev, 0x11, 0x08);
+ }
if (big_symbol)
reg15 |= 0x02;
@@ -441,8 +479,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
usleep_range(5000, 5500);
- m88ds3103b_dt_write(dev, 0x05, 0x00);
- m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
+ if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+ m88ds3103b_dt_write(dev, 0x05, 0x00);
+ m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
+ }
usleep_range(5000, 5500);
@@ -583,12 +623,6 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
sm = N - 1;
- /* Write to registers */
- //reg15 &= 0x01;
- //reg15 |= (pll_div_fb >> 8) & 0x01;
-
- //reg16 = pll_div_fb & 0xFF;
-
reg1D &= ~0x03;
reg1D |= sm;
reg1D |= 0x80;
@@ -596,8 +630,11 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
reg1E = ((f3 << 4) + f2) & 0xFF;
reg1F = ((f1 << 4) + f0) & 0xFF;
- m88ds3103b_dt_write(dev, 0x05, 0x40);
- m88ds3103b_dt_write(dev, 0x11, 0x08);
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ m88ds3103b_dt_write(dev, 0x05, 0x40);
+ m88ds3103b_dt_write(dev, 0x11, 0x08);
+ }
+
m88ds3103b_dt_write(dev, 0x1D, reg1D);
m88ds3103b_dt_write(dev, 0x1E, reg1E);
m88ds3103b_dt_write(dev, 0x1F, reg1F);
@@ -607,14 +644,88 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
usleep_range(5000, 5500);
- m88ds3103b_dt_write(dev, 0x05, 0x00);
- m88ds3103b_dt_write(dev, 0x11, 0x0A);
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ m88ds3103b_dt_write(dev, 0x05, 0x00);
+ m88ds3103b_dt_write(dev, 0x11, 0x0A);
+ }
usleep_range(5000, 5500);
return 0;
}
+static int mt_fe_dmd_ds3103c_set_ts_out_mode(struct dvb_frontend *fe, enum m88ds3103_ts_mode mode)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+
+ unsigned int tmp, val = 0;
+
+ regmap_read(dev->regmap, 0x0b, &val);
+ val &= ~0x01;
+ regmap_write(dev->regmap, 0x0b, val);
+
+ regmap_read(dev->regmap, 0xfd, &tmp);
+ if (mode == M88DS3103_TS_PARALLEL) {
+ tmp &= ~0x01;
+ tmp &= ~0x04;
+
+ regmap_write(dev->regmap, 0xfa, 0x01);
+ regmap_write(dev->regmap, 0xf1, 0x60);
+ regmap_write(dev->regmap, 0xfa, 0x00);
+ } else if (mode == M88DS3103_TS_SERIAL) {
+ tmp &= ~0x01;
+ tmp |= 0x04;
+ } else {
+ tmp |= 0x01;
+ tmp &= ~0x04;
+
+ regmap_write(dev->regmap, 0xfa, 0x01);
+ regmap_write(dev->regmap, 0xf1, 0x60);
+ regmap_write(dev->regmap, 0xfa, 0x00);
+ }
+
+ if (dev->cfg->ts_clk_pol) {
+ tmp &= ~0xf8;
+ tmp |= 0x02;
+ } else {
+ tmp &= ~0xb8;
+ tmp |= 0x42;
+ }
+
+ tmp |= 0x80;
+ regmap_write(dev->regmap, 0xfd, tmp);
+
+ val = 0;
+ if (mode != M88DS3103_TS_SERIAL) {
+ tmp = M88DS3103_TS_CI;
+
+ val |= tmp & 0x03;
+ val |= (tmp << 2) & 0x0C;
+ val |= (tmp << 4) & 0x30;
+ val |= (tmp << 6) & 0xC0;
+ } else {
+ val = 0x00;
+ }
+
+ regmap_write(dev->regmap, 0x0a, val);
+
+ regmap_read(dev->regmap, 0x0b, &tmp);
+
+ tmp &= ~0x20;
+ tmp |= 0x01;
+
+ regmap_write(dev->regmap, 0x0b, tmp);
+
+ regmap_read(dev->regmap, 0x0c, &tmp);
+
+ regmap_write(dev->regmap, 0xf4, 0x01);
+
+ tmp &= ~0x80;
+ regmap_write(dev->regmap, 0x0c, tmp);
+
+ return 0;
+}
+
static int m88ds3103_set_frontend(struct dvb_frontend *fe)
{
struct m88ds3103_dev *dev = fe->demodulator_priv;
@@ -627,6 +738,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
u16 u16tmp;
u32 tuner_frequency_khz, target_mclk, u32tmp;
s32 s32tmp;
+ unsigned int utmp;
static const struct reg_sequence reset_buf[] = {
{0x07, 0x80}, {0x07, 0x00}
};
@@ -646,9 +758,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
+ /* Clear TS */
+ ret = regmap_write(dev->regmap, 0xf5, 0x00);
+
/* Disable demod clock path */
- if (dev->chip_id == M88RS6000_CHIP_ID) {
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chip_id == M88RS6000_CHIP_ID ||
+ dev->chip_id == M88DS3103C_CHIP_ID) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = regmap_read(dev->regmap, 0xb2, &u32tmp);
if (ret)
goto err;
@@ -688,7 +805,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
}
/* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */
- if (dev->chip_id == M88RS6000_CHIP_ID) {
+ if (dev->chip_id == M88RS6000_CHIP_ID ||
+ dev->chip_id == M88DS3103C_CHIP_ID) {
if (c->symbol_rate > 45010000)
dev->mclk = 110250000;
else
@@ -699,7 +817,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
else
target_mclk = 144000000;
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
m88ds3103b_select_mclk(dev);
m88ds3103b_set_mclk(dev, target_mclk / 1000);
}
@@ -772,6 +891,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->chip_id == M88RS6000_CHIP_ID) {
len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
init = m88rs6000_dvbs_init_reg_vals;
+ } else if (dev->chip_id == M88DS3103C_CHIP_ID) {
+ len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
+ init = m88ds3103c_dvbs_init_reg_vals;
} else {
len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
init = m88ds3103_dvbs_init_reg_vals;
@@ -781,6 +903,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->chip_id == M88RS6000_CHIP_ID) {
len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
init = m88rs6000_dvbs2_init_reg_vals;
+ } else if (dev->chip_id == M88DS3103C_CHIP_ID) {
+ len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
+ init = m88ds3103c_dvbs_init_reg_vals;
} else {
len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
init = m88ds3103_dvbs2_init_reg_vals;
@@ -799,7 +924,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
goto err;
}
- if (dev->chip_id == M88RS6000_CHIP_ID) {
+ if (dev->chip_id == M88RS6000_CHIP_ID ||
+ dev->chip_id == M88DS3103C_CHIP_ID) {
if (c->delivery_system == SYS_DVBS2 &&
c->symbol_rate <= 5000000) {
ret = regmap_write(dev->regmap, 0xc0, 0x04);
@@ -812,11 +938,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
}
- ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
- if (ret)
- goto err;
+ if (dev->chip_id != M88DS3103C_CHIP_ID) {
+ ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
+ if (ret)
+ goto err;
+ }
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
buf[0] = m88ds3103b_dt_read(dev, 0x15);
buf[1] = m88ds3103b_dt_read(dev, 0x16);
@@ -838,15 +967,22 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
m88ds3103b_dt_write(dev, 0x16, buf[1]);
regmap_read(dev->regmap, 0x30, &u32tmp);
- u32tmp &= ~0x80;
- regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
+ if (dev->chip_id == M88DS3103C_CHIP_ID) {
+ regmap_write(dev->regmap, 0x30, dev->config.agc_inv ? 0x18 : 0x08);
+ } else {
+ u32tmp &= ~0x80;
+ regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
+ }
}
- ret = regmap_write(dev->regmap, 0xf1, 0x01);
- if (ret)
- goto err;
+ if (dev->chip_id != M88DS3103C_CHIP_ID) {
+ ret = regmap_write(dev->regmap, 0xf1, 0x01);
+ if (ret)
+ goto err;
+ }
- if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chiptype != M88DS3103_CHIPTYPE_3103B &&
+ dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80);
if (ret)
goto err;
@@ -864,7 +1000,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
break;
case M88DS3103_TS_PARALLEL:
u8tmp = 0x02;
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
u8tmp = 0x01;
u8tmp1 = 0x01;
}
@@ -881,10 +1018,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->cfg->ts_clk_pol)
u8tmp |= 0x40;
- /* TS mode */
- ret = regmap_write(dev->regmap, 0xfd, u8tmp);
- if (ret)
- goto err;
+ if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+ /* TS mode */
+ ret = regmap_write(dev->regmap, 0xfd, u8tmp);
+ if (ret)
+ goto err;
+ }
switch (dev->cfg->ts_mode) {
case M88DS3103_TS_SERIAL:
@@ -918,7 +1057,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
if (ret)
goto err;
- u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+ u8tmp = 0xcb;
+ else
+ u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+
ret = regmap_write(dev->regmap, 0xea, u8tmp);
if (ret)
goto err;
@@ -930,7 +1074,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
else
u8tmp = 0x06;
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C)
m88ds3103b_set_mclk(dev, target_mclk / 1000);
ret = regmap_write(dev->regmap, 0xc3, 0x08);
@@ -949,9 +1094,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
- u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+ u16tmp = DIV_ROUND_CLOSEST_ULL((((u64)c->symbol_rate << 15) + dev->mclk / 4),
+ (dev->mclk / 2));
+ else
+ u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
+
buf[0] = (u16tmp >> 0) & 0xff;
buf[1] = (u16tmp >> 8) & 0xff;
+
ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
if (ret)
goto err;
@@ -960,7 +1111,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
- ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ ret = m88ds3103_update_bits(dev, 0x30, 0x08, dev->cfg->agc_inv << 3);
+ } else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ ret = m88ds3103_update_bits(dev, 0x08, 0x43, 0x43);
+
+ ret = m88ds3103_update_bits(dev, 0x30, 0x18, dev->cfg->agc_inv ? 0x18 : 0x08);
+ } else {
+ ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
+ }
if (ret)
goto err;
@@ -974,10 +1133,37 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
(c->delivery_system == SYS_DVBS) ? 0x10 : 0x0);
if (ret)
goto err;
+ }
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ ret = m88ds3103_update_bits(dev, 0x76, 0x80, 0x00);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0x22, 0x01, 0x01);
+ ret = m88ds3103_update_bits(dev, 0x23, 0x01, 0x00);
+ ret = m88ds3103_update_bits(dev, 0x24, 0x01, 0x00);
ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08);
if (ret)
goto err;
+
+ ret = regmap_read(dev->regmap, 0x08, &utmp);
+ if (ret)
+ goto err;
+
+ if (c->delivery_system == SYS_DVBS) {
+ utmp = (utmp & 0xfb) | 0x40;
+ regmap_write(dev->regmap, 0x08, utmp);
+ regmap_write(dev->regmap, 0xe0, 0xf8);
+ } else if (c->delivery_system == SYS_DVBS2) {
+ utmp = utmp | 0x44;
+ regmap_write(dev->regmap, 0x08, utmp);
+ } else {
+ utmp = utmp & 0xbb;
+ regmap_write(dev->regmap, 0x08, utmp);
+ regmap_write(dev->regmap, 0xe0, 0xf8);
+ }
}
dev_dbg(&client->dev, "carrier offset=%d\n",
@@ -986,8 +1172,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
/* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
+
+ usleep_range(1000, 1200);
+
buf[0] = (s32tmp >> 0) & 0xff;
buf[1] = (s32tmp >> 8) & 0xff;
+
ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
if (ret)
goto err;
@@ -1012,6 +1202,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1031,13 +1222,65 @@ static int m88ds3103_init(struct dvb_frontend *fe)
dev->warm = false;
/* wake up device from sleep */
- ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
- if (ret)
- goto err;
- ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ ret = m88ds3103_update_bits(dev, 0x0b, 0x90, 0x80); /* set dt_addr */
+
+ m88ds3103b_dt_write(dev, 0x04, 0x01); /* reset */
+ m88ds3103b_dt_write(dev, 0x04, 0x00);
+ usleep_range(800, 1200);
+
+ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */
+ m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */
+
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0x0b, 0x01, 0x01);
+ if (ret)
+ goto err;
+ /* global reset, global diseqc reset, global fec reset */
+ ret = regmap_write(dev->regmap, 0x07, 0x80);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x07, 0x00);
+ if (ret)
+ goto err;
+ usleep_range(800, 1200);
+
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+ if (ret)
+ goto err;
+ } else {
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
+ if (ret)
+ goto err;
+ }
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */
+ m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */
+ m88ds3103b_dt_write(dev, 0x24, 0x04);
+ m88ds3103b_dt_write(dev, 0x84, 0x04);
+ m88ds3103b_dt_write(dev, 0x15, 0x6c);
+ usleep_range(800, 1200);
+ }
+
+ /* global reset, global diseqc reset, global fec reset */
+ ret = regmap_write(dev->regmap, 0x07, 0xe0);
if (ret)
goto err;
- ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
+ ret = regmap_write(dev->regmap, 0x07, 0x00);
if (ret)
goto err;
@@ -1051,20 +1294,14 @@ static int m88ds3103_init(struct dvb_frontend *fe)
if (utmp)
goto warm;
- /* global reset, global diseqc reset, global fec reset */
- ret = regmap_write(dev->regmap, 0x07, 0xe0);
- if (ret)
- goto err;
- ret = regmap_write(dev->regmap, 0x07, 0x00);
- if (ret)
- goto err;
-
/* cold state - try to download firmware */
dev_info(&client->dev, "found a '%s' in cold state\n",
dev->fe.ops.info.name);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
name = M88DS3103B_FIRMWARE;
+ else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+ name = M88DS3103C_FIRMWARE;
else if (dev->chip_id == M88RS6000_CHIP_ID)
name = M88RS6000_FIRMWARE;
else
@@ -1116,6 +1353,18 @@ static int m88ds3103_init(struct dvb_frontend *fe)
dev_info(&client->dev, "firmware version: %X.%X\n",
(utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+ mt_fe_dmd_ds3103c_set_ts_out_mode(fe, dev->cfg->ts_mode);
+
+ ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv ? 0x10 : 0x00);
+ if (ret)
+ goto err;
+ }
+
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
m88ds3103b_dt_write(dev, 0x21, 0x92);
m88ds3103b_dt_write(dev, 0x15, 0x6C);
@@ -1139,6 +1388,7 @@ err_release_firmware:
release_firmware(firmware);
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1157,6 +1407,8 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
/* TS Hi-Z */
if (dev->chip_id == M88RS6000_CHIP_ID)
utmp = 0x29;
+ else if (dev->chip_id == M88DS3103C_CHIP_ID)
+ utmp = 0x0b;
else
utmp = 0x27;
ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00);
@@ -1167,6 +1419,17 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
if (ret)
goto err;
+
+ /* Internal tuner sleep */
+ if (dev->chip_id == M88DS3103C_CHIP_ID) {
+ ret = m88ds3103b_dt_write(dev, 0x10, 0x00);
+ if (ret)
+ goto err;
+ ret = m88ds3103b_dt_write(dev, 0x11, 0x00);
+ if (ret)
+ goto err;
+ }
+
ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
if (ret)
goto err;
@@ -1177,6 +1440,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1341,11 +1605,13 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe,
if (ret)
goto err;
+// dev_dbg(&client->dev, "%s() 0x%X | 0x%X\n", __func__, buf[0], buf[1]);
c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1378,7 +1644,8 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
int ret;
unsigned int utmp, tone, reg_a1_mask;
- dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode);
+ dev_dbg(&client->dev, "fe_sec_tone_mode=%s\n",
+ fe_sec_tone_mode == SEC_TONE_ON ? "ON" : "OFF");
if (!dev->warm) {
ret = -EAGAIN;
@@ -1413,6 +1680,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1463,6 +1731,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1542,6 +1811,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1621,6 +1891,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+
return ret;
}
@@ -1818,6 +2089,7 @@ static int m88ds3103_probe(struct i2c_client *client)
switch (dev->chip_id) {
case M88RS6000_CHIP_ID:
case M88DS3103_CHIP_ID:
+ case M88DS3103C_CHIP_ID:
break;
default:
ret = -ENODEV;
@@ -1847,7 +2119,8 @@ static int m88ds3103_probe(struct i2c_client *client)
/* 0x29 register is defined differently for m88rs6000. */
/* set internal tuner address to 0x21 */
- if (dev->chip_id == M88RS6000_CHIP_ID)
+ if (dev->chip_id == M88RS6000_CHIP_ID ||
+ dev->chip_id == M88DS3103C_CHIP_ID)
utmp = 0x00;
ret = regmap_write(dev->regmap, 0x29, utmp);
@@ -1882,6 +2155,9 @@ static int m88ds3103_probe(struct i2c_client *client)
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B",
sizeof(dev->fe.ops.info.name));
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+ strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103C",
+ sizeof(dev->fe.ops.info.name));
else if (dev->chip_id == M88RS6000_CHIP_ID)
strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000",
sizeof(dev->fe.ops.info.name));
@@ -1894,15 +2170,22 @@ static int m88ds3103_probe(struct i2c_client *client)
pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
- if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+ dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
/* enable i2c repeater for tuner */
- m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+ if (dev->chip_id == M88DS3103C_CHIP_ID)
+ m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+ else
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
/* get frontend address */
ret = regmap_read(dev->regmap, 0x29, &utmp);
if (ret)
goto err_del_adapters;
+
dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1;
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+ dev->dt_addr = 0x5c >> 1;
dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr);
dev->dt_client = i2c_new_dummy_device(client->adapter,
@@ -1941,6 +2224,7 @@ static const struct i2c_device_id m88ds3103_id_table[] = {
{"m88ds3103", M88DS3103_CHIPTYPE_3103},
{"m88rs6000", M88DS3103_CHIPTYPE_RS6000},
{"m88ds3103b", M88DS3103_CHIPTYPE_3103B},
+ {"m88ds3103c", M88DS3103_CHIPTYPE_3103C},
{}
};
MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index 594ad9cbc2cc..d7d16e7904da 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -17,15 +17,18 @@
#include <linux/math64.h>
#define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw"
+#define M88DS3103C_FIRMWARE "dvb-demod-m88ds3103c.fw"
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
-#define M88RS6000_CHIP_ID 0x74
-#define M88DS3103_CHIP_ID 0x70
+#define M88DS3103_CHIP_ID 0x70
+#define M88RS6000_CHIP_ID 0x74
+#define M88DS3103C_CHIP_ID 0x71
#define M88DS3103_CHIPTYPE_3103 0
#define M88DS3103_CHIPTYPE_RS6000 1
#define M88DS3103_CHIPTYPE_3103B 2
+#define M88DS3103_CHIPTYPE_3103C 3
struct m88ds3103_dev {
struct i2c_client *client;
@@ -399,4 +402,43 @@ static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
{0xb8, 0x00},
{0x29, 0x01},
};
+
+static const struct m88ds3103_reg_val m88ds3103c_dvbs_init_reg_vals[] = {
+ {0x04, 0x10},
+ {0x8a, 0x01},
+ {0x16, 0xa7},
+ {0x30, 0x08},
+ {0x32, 0x32},
+ {0x33, 0x35},
+ {0x35, 0xff},
+ {0x4a, 0x80},
+ {0x4d, 0x93},
+ {0xae, 0x09},
+ {0x22, 0x01},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x27, 0x07},
+ {0x9c, 0x31},
+ {0x9d, 0xc1},
+ {0xcb, 0xf4},
+ {0xca, 0x00},
+ {0x7f, 0x04},
+ {0x78, 0x0c},
+ {0x85, 0x08},
+ {0x08, 0x47},
+ {0xf0, 0x03},
+ {0xfa, 0x01},
+ {0xf2, 0x00},
+ {0xfa, 0x00},
+ {0xe6, 0x00},
+ {0xe7, 0xf3},
+ {0x08, 0x43},
+ {0xe0, 0xf8},
+ {0x00, 0x00},
+ {0xbd, 0x82},
+ {0x80, 0xa8},
+ {0x81, 0xea},
+ {0xbe, 0xa1}
+};
+
#endif
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index c4bbcd127cac..9c5bac8cda47 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -40,7 +40,7 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
if (cmd->rlen) {
/* wait cmd execution terminate */
- #define TIMEOUT 70
+ #define TIMEOUT 140
timeout = jiffies + msecs_to_jiffies(TIMEOUT);
while (!time_after(jiffies, timeout)) {
ret = i2c_master_recv(client, cmd->args, cmd->rlen);
@@ -54,6 +54,8 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
/* firmware ready? */
if ((cmd->args[0] >> 7) & 0x01)
break;
+
+ usleep_range(2500, 3500);
}
dev_dbg(&client->dev, "cmd execution took %d ms\n",
@@ -572,8 +574,8 @@ static int si2168_sleep(struct dvb_frontend *fe)
if (ret)
goto err;
- /* Firmware later than B 4.0-11 loses warm state during sleep */
- if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
+ /* Firmware B 4.0-11 and later lose warm state during sleep */
+ if (dev->version >= ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
dev->warm = false;
cmd_init(&cmd, "\x13", 1, 0);
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5eb1e0e0a87a..8f2ba4121586 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -355,6 +355,7 @@ config VIDEO_MT9V111
config VIDEO_OG01A1B
tristate "OmniVision OG01A1B sensor support"
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OG01A1B camera.
@@ -489,6 +490,19 @@ config VIDEO_OV2685
To compile this driver as a module, choose M here: the
module will be called ov2685.
+config VIDEO_OV2732
+ tristate "OmniVision OV2732 sensor support"
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_CCI_I2C
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV2732 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov2732.
+
config VIDEO_OV2735
tristate "OmniVision OV2735 sensor support"
select V4L2_CCI_I2C
@@ -690,6 +704,7 @@ config VIDEO_OV8865
config VIDEO_OV9282
tristate "OmniVision OV9282 sensor support"
depends on OF_GPIO
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OV9282 camera sensor.
@@ -788,6 +803,18 @@ config VIDEO_S5KJN1
To compile this driver as a module, choose M here: the
module will be called s5kjn1.
+config VIDEO_T4KA3
+ tristate "Toshiba T4KA3 sensor support"
+ depends on ACPI || COMPILE_TEST
+ depends on GPIOLIB
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for the Toshiba T4KA3 8 MP
+ camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called t4ka3.
+
config VIDEO_VD55G1
tristate "ST VD55G1 sensor support"
select V4L2_CCI_I2C
@@ -1724,8 +1751,8 @@ config VIDEO_DS90UB960
select V4L2_FWNODE
select VIDEO_V4L2_SUBDEV_API
help
- Device driver for the Texas Instruments DS90UB960
- FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer.
+ Device driver for the Texas Instruments DS90UB954, DS90UB960
+ FPD-Link III Deserializers and DS90UB9702 FPD-Link IV Deserializer.
config VIDEO_MAX96714
tristate "Maxim MAX96714 GMSL2 deserializer"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a3a6396df3c4..90b276a7417a 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
+obj-$(CONFIG_VIDEO_OV2732) += ov2732.o
obj-$(CONFIG_VIDEO_OV2735) += ov2735.o
obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
@@ -139,6 +140,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
+obj-$(CONFIG_VIDEO_T4KA3) += t4ka3.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
obj-$(CONFIG_VIDEO_TC358746) += tc358746.o
obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o
diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c
index e6fa2f961177..955b7072a560 100644
--- a/drivers/media/i2c/alvium-csi2.c
+++ b/drivers/media/i2c/alvium-csi2.c
@@ -2541,6 +2541,6 @@ module_i2c_driver(alvium_i2c_driver);
MODULE_DESCRIPTION("Allied Vision's Alvium Camera Driver");
MODULE_AUTHOR("Tommaso Merciai <tomm.merciai@gmail.com>");
-MODULE_AUTHOR("Martin Hecht <martin.hecht@avnet.eu>");
+MODULE_AUTHOR("Martin Hecht <mhecht73@gmail.com>");
MODULE_AUTHOR("Avnet Silica Software & Services EMEA");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c
index f156058500e3..ed324c2d87aa 100644
--- a/drivers/media/i2c/ar0521.c
+++ b/drivers/media/i2c/ar0521.c
@@ -1094,6 +1094,9 @@ static int ar0521_probe(struct i2c_client *client)
/* Request optional reset pin (usually active low) and assert it */
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+ "failed to get reset gpio\n");
v4l2_i2c_subdev_init(&sensor->sd, client, &ar0521_subdev_ops);
diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 4eb83636e102..1605cfa5db19 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -824,9 +824,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
op_lim_fr->min_pll_ip_clk_freq_hz));
min_op_pre_pll_clk_div =
max_t(u16, op_lim_fr->min_pre_pll_clk_div,
- clk_div_even_up(
- DIV_ROUND_UP(pll->ext_clk_freq_hz,
- op_lim_fr->max_pll_ip_clk_freq_hz)));
+ DIV_ROUND_UP(pll->ext_clk_freq_hz,
+ op_lim_fr->max_pll_ip_clk_freq_hz));
dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index a86306304330..69d5cc648c0f 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -1652,10 +1652,14 @@ static int set_v4lstd(struct i2c_client *client)
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
+ u8 pal_n = 0;
+ u8 ntsc_j = 0;
+ u8 tmp_reg = 0;
/* First tests should be against specific std */
if (state->std == V4L2_STD_NTSC_M_JP) {
fmt = 0x2;
+ ntsc_j = 0x80;
} else if (state->std == V4L2_STD_NTSC_443) {
fmt = 0x3;
} else if (state->std == V4L2_STD_PAL_M) {
@@ -1663,6 +1667,7 @@ static int set_v4lstd(struct i2c_client *client)
fmt = 0x5;
} else if (state->std == V4L2_STD_PAL_N) {
fmt = 0x6;
+ pal_n = 0x40;
} else if (state->std == V4L2_STD_PAL_Nc) {
fmt = 0x7;
} else if (state->std == V4L2_STD_PAL_60) {
@@ -1689,10 +1694,30 @@ static int set_v4lstd(struct i2c_client *client)
/* Set format to NTSC-M */
cx25840_and_or(client, 0x400, ~0xf, 1);
/* Turn off LCOMB */
- cx25840_and_or(client, 0x47b, ~6, 0);
+ cx25840_and_or(client, 0x47b, ~0x6, 0);
+ } else if (fmt == 0xc) { /* SECAM - Step 9c - toggle CKILLEN */
+ tmp_reg = cx25840_read(client, 0x401);
+ cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20);
+ cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00);
}
+
cx25840_and_or(client, 0x400, ~0xf, fmt);
- cx25840_and_or(client, 0x403, ~0x3, pal_m);
+
+ if (fmt >= 4 && fmt < 8) {
+ tmp_reg = cx25840_read(client, 0x401);
+ cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x00 : 0x40); /* CAGCEN */
+ cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x40 : 0x00);
+ cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20); /* CKILLEN */
+ cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00);
+ }
+
+ if (pal_m)
+ cx25840_and_or(client, 0x403, ~0x3, pal_m);
+ else if (pal_n) /* cx25840 datasheet table 3-19 */
+ cx25840_and_or(client, 0x403, ~0x40, pal_n);
+ else if (ntsc_j) /* cx25840 datasheet table 3-19 */
+ cx25840_and_or(client, 0x403, ~0x80, ntsc_j);
+
if (is_cx23888(state))
cx23888_std_setup(client);
else
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index e97e499b04e6..49aa5f4a172c 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -372,63 +372,6 @@ static int ub913_set_routing(struct v4l2_subdev *sd,
return _ub913_set_routing(sd, state, routing);
}
-static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
- struct v4l2_mbus_frame_desc *fd)
-{
- struct ub913_data *priv = sd_to_ub913(sd);
- const struct v4l2_subdev_krouting *routing;
- struct v4l2_mbus_frame_desc source_fd;
- struct v4l2_subdev_route *route;
- struct v4l2_subdev_state *state;
- int ret;
-
- if (pad != UB913_PAD_SOURCE)
- return -EINVAL;
-
- ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
- priv->source_sd_pad, &source_fd);
- if (ret)
- return ret;
-
- fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL;
-
- state = v4l2_subdev_lock_and_get_active_state(sd);
-
- routing = &state->routing;
-
- for_each_active_route(routing, route) {
- unsigned int i;
-
- if (route->source_pad != pad)
- continue;
-
- for (i = 0; i < source_fd.num_entries; i++) {
- if (source_fd.entry[i].stream == route->sink_stream)
- break;
- }
-
- if (i == source_fd.num_entries) {
- dev_err(&priv->client->dev,
- "Failed to find stream from source frame desc\n");
- ret = -EPIPE;
- goto out_unlock;
- }
-
- fd->entry[fd->num_entries].stream = route->source_stream;
- fd->entry[fd->num_entries].flags = source_fd.entry[i].flags;
- fd->entry[fd->num_entries].length = source_fd.entry[i].length;
- fd->entry[fd->num_entries].pixelcode =
- source_fd.entry[i].pixelcode;
-
- fd->num_entries++;
- }
-
-out_unlock:
- v4l2_subdev_unlock_state(state);
-
- return ret;
-}
-
static int ub913_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
@@ -544,7 +487,7 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = {
.enable_streams = ub913_enable_streams,
.disable_streams = ub913_disable_streams,
.set_routing = ub913_set_routing,
- .get_frame_desc = ub913_get_frame_desc,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ub913_set_fmt,
};
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index daefdb108fbf..a8ab67f4137f 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -424,65 +424,6 @@ static int ub953_set_routing(struct v4l2_subdev *sd,
return _ub953_set_routing(sd, state, routing);
}
-static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
- struct v4l2_mbus_frame_desc *fd)
-{
- struct ub953_data *priv = sd_to_ub953(sd);
- struct v4l2_mbus_frame_desc source_fd;
- struct v4l2_subdev_route *route;
- struct v4l2_subdev_state *state;
- int ret;
-
- if (pad != UB953_PAD_SOURCE)
- return -EINVAL;
-
- ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
- priv->source_sd_pad, &source_fd);
- if (ret)
- return ret;
-
- fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
-
- state = v4l2_subdev_lock_and_get_active_state(sd);
-
- for_each_active_route(&state->routing, route) {
- struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
- unsigned int i;
-
- if (route->source_pad != pad)
- continue;
-
- for (i = 0; i < source_fd.num_entries; i++) {
- if (source_fd.entry[i].stream == route->sink_stream) {
- source_entry = &source_fd.entry[i];
- break;
- }
- }
-
- if (!source_entry) {
- dev_err(&priv->client->dev,
- "Failed to find stream from source frame desc\n");
- ret = -EPIPE;
- goto out_unlock;
- }
-
- fd->entry[fd->num_entries].stream = route->source_stream;
- fd->entry[fd->num_entries].flags = source_entry->flags;
- fd->entry[fd->num_entries].length = source_entry->length;
- fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
- fd->entry[fd->num_entries].bus.csi2.vc =
- source_entry->bus.csi2.vc;
- fd->entry[fd->num_entries].bus.csi2.dt =
- source_entry->bus.csi2.dt;
-
- fd->num_entries++;
- }
-
-out_unlock:
- v4l2_subdev_unlock_state(state);
-
- return ret;
-}
static int ub953_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
@@ -694,7 +635,7 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = {
.enable_streams = ub953_enable_streams,
.disable_streams = ub953_disable_streams,
.set_routing = ub953_set_routing,
- .get_frame_desc = ub953_get_frame_desc,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ub953_set_fmt,
};
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index e133127397f4..d50e977cf6ce 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -396,6 +396,13 @@
#define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3)
#define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0)
+#define UB954_IR_RX_ANA_STROBE_SET_CLK_DATA 0x08
+#define UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY BIT(3)
+#define UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(7)
+#define UB954_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK GENMASK(2, 0)
+#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(4, 6)
+#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT 4
+
/* UB9702 Registers */
#define UB9702_SR_CSI_EXCLUSIVE_FWD2 0x3c
@@ -454,12 +461,23 @@
#define UB960_MAX_EQ_LEVEL 14
#define UB960_NUM_EQ_LEVELS (UB960_MAX_EQ_LEVEL - UB960_MIN_EQ_LEVEL + 1)
+enum chip_type {
+ UB954,
+ UB960,
+ UB9702,
+};
+
+enum chip_family {
+ FAMILY_FPD3,
+ FAMILY_FPD4,
+};
+
struct ub960_hw_data {
const char *model;
+ enum chip_type chip_type;
+ enum chip_family chip_family;
u8 num_rxports;
u8 num_txports;
- bool is_ub9702;
- bool is_fpdlink4;
};
enum ub960_rxport_mode {
@@ -982,6 +1000,10 @@ static int ub960_txport_select(struct ub960_data *priv, u8 nport)
lockdep_assert_held(&priv->reg_lock);
+ /* UB954 has only 1 CSI TX. Hence, no need to select */
+ if (priv->hw_data->chip_type == UB954)
+ return 0;
+
if (priv->reg_current.txport == nport)
return 0;
@@ -1406,10 +1428,11 @@ static int ub960_parse_dt_txport(struct ub960_data *priv,
priv->tx_link_freq[0] = vep.link_frequencies[0];
priv->tx_data_rate = priv->tx_link_freq[0] * 2;
- if (priv->tx_data_rate != MHZ(1600) &&
- priv->tx_data_rate != MHZ(1200) &&
- priv->tx_data_rate != MHZ(800) &&
- priv->tx_data_rate != MHZ(400)) {
+ if ((priv->tx_data_rate != MHZ(1600) &&
+ priv->tx_data_rate != MHZ(1200) &&
+ priv->tx_data_rate != MHZ(800) &&
+ priv->tx_data_rate != MHZ(400)) ||
+ (priv->hw_data->chip_type == UB954 && priv->tx_data_rate == MHZ(1200))) {
dev_err(dev, "tx%u: invalid 'link-frequencies' value\n", nport);
ret = -EINVAL;
goto err_free_vep;
@@ -1533,22 +1556,35 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv,
u8 clk_delay, data_delay;
int ret;
- ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
- UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL);
- if (ret)
- return ret;
+ if (priv->hw_data->chip_type == UB954) {
+ ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, &v, NULL);
+ if (ret)
+ return ret;
- clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
- 0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
+ clk_delay = (v & UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
+ 0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
- ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
- UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL);
- if (ret)
- return ret;
+ data_delay = (v & UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
+ 0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
+ } else {
+ ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL);
+ if (ret)
+ return ret;
- data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
+ clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
+ ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL);
+ if (ret)
+ return ret;
+
+ data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
+ 0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
+ }
+
ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v, NULL);
if (ret)
return ret;
@@ -1569,26 +1605,49 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv,
static int ub960_rxport_set_strobe_pos(struct ub960_data *priv,
unsigned int nport, s8 strobe_pos)
{
- u8 clk_delay, data_delay;
int ret = 0;
- clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
- data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
-
- if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
- clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
- else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
- data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY;
- else if (strobe_pos < 0)
- clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
- else if (strobe_pos > 0)
- data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
-
- ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
- UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret);
-
- ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
- UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret);
+ if (priv->hw_data->chip_type == UB954) {
+ u8 clk_data_delay;
+
+ clk_data_delay = UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY |
+ UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
+
+ if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
+ clk_data_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
+ else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
+ clk_data_delay = (strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY) <<
+ UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT;
+ else if (strobe_pos < 0)
+ clk_data_delay = abs(strobe_pos) |
+ UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
+ else if (strobe_pos > 0)
+ clk_data_delay = (strobe_pos |
+ UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) <<
+ UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT;
+
+ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, clk_data_delay, &ret);
+ } else {
+ u8 clk_delay, data_delay;
+
+ clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
+ data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
+
+ if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
+ clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
+ else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
+ data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY;
+ else if (strobe_pos < 0)
+ clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
+ else if (strobe_pos > 0)
+ data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
+
+ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret);
+ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
+ UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret);
+ }
return ret;
}
@@ -1924,7 +1983,7 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv,
if (ret)
return ret;
- if (priv->hw_data->is_ub9702) {
+ if (priv->hw_data->chip_type == UB9702) {
dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n",
nport, ((u64)v * HZ_PER_MHZ) >> 8);
} else {
@@ -2186,7 +2245,7 @@ static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport)
ser_pdata->port = nport;
ser_pdata->atr = priv->atr;
- if (priv->hw_data->is_ub9702)
+ if (priv->hw_data->chip_type == UB9702)
ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub9702(priv, rxport);
else
ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub960(priv, rxport);
@@ -2352,7 +2411,7 @@ static int ub960_init_tx_ports(struct ub960_data *priv)
{
int ret;
- if (priv->hw_data->is_ub9702)
+ if (priv->hw_data->chip_type == UB9702)
ret = ub960_init_tx_ports_ub9702(priv);
else
ret = ub960_init_tx_ports_ub960(priv);
@@ -3624,7 +3683,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv,
case RXPORT_MODE_CSI2_SYNC:
case RXPORT_MODE_CSI2_NONSYNC:
- if (!priv->hw_data->is_ub9702) {
+ if (priv->hw_data->chip_type == UB960 ||
+ priv->hw_data->chip_type == UB954) {
/* Map all VCs from this port to the same VC */
ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP,
(vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) |
@@ -4158,33 +4218,40 @@ static int ub960_log_status(struct v4l2_subdev *sd)
dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1),
v & (u8)BIT(0));
- ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport),
- &v16, NULL);
- if (ret)
- return ret;
+ /*
+ * Frame counter, frame error counter, line counter and line error counter
+ * registers are marked as reserved in the UB954 datasheet. Hence restrict
+ * the following register reads only for UB960 and UB9702.
+ */
+ if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB9702) {
+ ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport),
+ &v16, NULL);
+ if (ret)
+ return ret;
- dev_info(dev, "\tframe counter %u\n", v16);
+ dev_info(dev, "\tframe counter %u\n", v16);
- ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport),
- &v16, NULL);
- if (ret)
- return ret;
+ ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport),
+ &v16, NULL);
+ if (ret)
+ return ret;
- dev_info(dev, "\tframe error counter %u\n", v16);
+ dev_info(dev, "\tframe error counter %u\n", v16);
- ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport),
- &v16, NULL);
- if (ret)
- return ret;
+ ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport),
+ &v16, NULL);
+ if (ret)
+ return ret;
- dev_info(dev, "\tline counter %u\n", v16);
+ dev_info(dev, "\tline counter %u\n", v16);
- ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport),
- &v16, NULL);
- if (ret)
- return ret;
+ ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport),
+ &v16, NULL);
+ if (ret)
+ return ret;
- dev_info(dev, "\tline error counter %u\n", v16);
+ dev_info(dev, "\tline error counter %u\n", v16);
+ }
}
for_each_rxport(priv, it) {
@@ -4250,7 +4317,7 @@ static int ub960_log_status(struct v4l2_subdev *sd)
dev_info(dev, "\tcsi_err_counter %u\n", v);
- if (!priv->hw_data->is_ub9702) {
+ if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB954) {
ret = ub960_log_status_ub960_sp_eq(priv, nport);
if (ret)
return ret;
@@ -4408,7 +4475,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
return -EINVAL;
}
- if (!priv->hw_data->is_fpdlink4 && cdr_mode == RXPORT_CDR_FPD4) {
+ if (priv->hw_data->chip_family != FAMILY_FPD4 && cdr_mode == RXPORT_CDR_FPD4) {
dev_err(dev, "rx%u: FPD-Link 4 CDR not supported\n", nport);
return -EINVAL;
}
@@ -5010,7 +5077,12 @@ static int ub960_enable_core_hw(struct ub960_data *priv)
if (ret)
goto err_pd_gpio;
- if (priv->hw_data->is_ub9702)
+ /*
+ * UB954 REFCLK_FREQ is not synchronized, so multiple reads are recommended
+ * by the datasheet. However, a single read is practically seen to be
+ * sufficient and moreover it is only used for a debug print.
+ */
+ if (priv->hw_data->chip_type == UB9702)
ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq,
NULL);
else
@@ -5029,7 +5101,7 @@ static int ub960_enable_core_hw(struct ub960_data *priv)
goto err_pd_gpio;
/* release GPIO lock */
- if (priv->hw_data->is_ub9702) {
+ if (priv->hw_data->chip_type == UB9702) {
ret = ub960_update_bits(priv, UB960_SR_RESET,
UB960_SR_RESET_GPIO_LOCK_RELEASE,
UB960_SR_RESET_GPIO_LOCK_RELEASE,
@@ -5102,7 +5174,7 @@ static int ub960_probe(struct i2c_client *client)
if (ret)
goto err_free_ports;
- if (priv->hw_data->is_ub9702)
+ if (priv->hw_data->chip_type == UB9702)
ret = ub960_init_rx_ports_ub9702(priv);
else
ret = ub960_init_rx_ports_ub960(priv);
@@ -5169,21 +5241,32 @@ static void ub960_remove(struct i2c_client *client)
mutex_destroy(&priv->reg_lock);
}
+static const struct ub960_hw_data ds90ub954_hw = {
+ .model = "ub954",
+ .chip_type = UB954,
+ .chip_family = FAMILY_FPD3,
+ .num_rxports = 2,
+ .num_txports = 1,
+};
+
static const struct ub960_hw_data ds90ub960_hw = {
.model = "ub960",
+ .chip_type = UB960,
+ .chip_family = FAMILY_FPD3,
.num_rxports = 4,
.num_txports = 2,
};
static const struct ub960_hw_data ds90ub9702_hw = {
.model = "ub9702",
+ .chip_type = UB9702,
+ .chip_family = FAMILY_FPD4,
.num_rxports = 4,
.num_txports = 2,
- .is_ub9702 = true,
- .is_fpdlink4 = true,
};
static const struct i2c_device_id ub960_id[] = {
+ { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw },
{ "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw },
{ "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw },
{}
@@ -5191,6 +5274,7 @@ static const struct i2c_device_id ub960_id[] = {
MODULE_DEVICE_TABLE(i2c, ub960_id);
static const struct of_device_id ub960_dt_ids[] = {
+ { .compatible = "ti,ds90ub954-q1", .data = &ds90ub954_hw },
{ .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw },
{ .compatible = "ti,ds90ub9702-q1", .data = &ds90ub9702_hw },
{}
diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c
index d434721ba8ed..d037d3009c9a 100644
--- a/drivers/media/i2c/dw9768.c
+++ b/drivers/media/i2c/dw9768.c
@@ -551,6 +551,6 @@ static struct i2c_driver dw9768_i2c_driver = {
};
module_i2c_driver(dw9768_i2c_driver);
-MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>");
+MODULE_AUTHOR("Dongchun Zhu");
MODULE_DESCRIPTION("DW9768 VCM driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index fee63bc106d9..7da02ce5da15 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -1218,6 +1218,9 @@ static int imx219_probe(struct i2c_client *client)
/* Request optional enable pin */
imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
+ if (IS_ERR(imx219->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(imx219->reset_gpio),
+ "failed to get reset gpio\n");
/*
* The sensor must be powered for imx219_identify_module()
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index e50dcfd830f5..bc9ee449a87c 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -709,12 +709,16 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
v4l2_subdev_state_get_format(fh->state, 0);
struct v4l2_rect *try_crop;
+ mutex_lock(&imx258->mutex);
+
/* Initialize try_fmt */
try_fmt->width = supported_modes[0].width;
try_fmt->height = supported_modes[0].height;
try_fmt->code = imx258_get_format_code(imx258);
try_fmt->field = V4L2_FIELD_NONE;
+ mutex_unlock(&imx258->mutex);
+
/* Initialize try_crop */
try_crop = v4l2_subdev_state_get_crop(fh->state, 0);
try_crop->left = IMX258_PIXEL_ARRAY_LEFT;
@@ -839,7 +843,9 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
+ mutex_lock(&imx258->mutex);
code->code = imx258_get_format_code(imx258);
+ mutex_unlock(&imx258->mutex);
return 0;
}
@@ -849,10 +855,16 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_size_enum *fse)
{
struct imx258 *imx258 = to_imx258(sd);
+ u32 code;
+
if (fse->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
- if (fse->code != imx258_get_format_code(imx258))
+ mutex_lock(&imx258->mutex);
+ code = imx258_get_format_code(imx258);
+ mutex_unlock(&imx258->mutex);
+
+ if (fse->code != code)
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c
index 8ab63ad8f385..38ea1902f2c2 100644
--- a/drivers/media/i2c/imx283.c
+++ b/drivers/media/i2c/imx283.c
@@ -129,7 +129,8 @@
/* Master Mode Operation Control */
#define IMX283_REG_XMSTA CCI_REG8(0x3105)
-#define IMX283_XMSTA BIT(0)
+#define IMX283_XMSTA_START 0
+#define IMX283_XMSTA_STOP BIT(0)
#define IMX283_REG_SYNCDRV CCI_REG8(0x3107)
#define IMX283_SYNCDRV_XHS_XVS (0xa0 | 0x02)
@@ -149,6 +150,9 @@
#define IMX283_REG_PLSTMG02 CCI_REG8(0x36aa)
#define IMX283_PLSTMG02_VAL 0x00
+#define IMX283_REG_MIPI_CLK CCI_REG8(0x3a43)
+#define IMX283_MIPI_CLK_NONCONTINUOUS BIT(0)
+
#define IMX283_REG_EBD_X_OUT_SIZE CCI_REG16_LE(0x3a54)
/* Test pattern generator */
@@ -565,6 +569,7 @@ struct imx283 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vflip;
+ bool mipi_clk_noncontinuous;
unsigned long link_freq_bitmap;
u16 hmax;
@@ -988,6 +993,7 @@ static int imx283_set_pad_format(struct v4l2_subdev *sd,
static int imx283_standby_cancel(struct imx283 *imx283)
{
unsigned int link_freq_idx;
+ u8 mipi_clk;
int ret = 0;
cci_write(imx283->cci, IMX283_REG_STANDBY,
@@ -1007,6 +1013,10 @@ static int imx283_standby_cancel(struct imx283 *imx283)
/* Enable PLL */
cci_write(imx283->cci, IMX283_REG_STBPL, IMX283_STBPL_NORMAL, &ret);
+ /* Configure MIPI clock mode */
+ mipi_clk = imx283->mipi_clk_noncontinuous ? IMX283_MIPI_CLK_NONCONTINUOUS : 0;
+ cci_write(imx283->cci, IMX283_REG_MIPI_CLK, mipi_clk, &ret);
+
/* Configure the MIPI link speed */
link_freq_idx = __ffs(imx283->link_freq_bitmap);
cci_multi_reg_write(imx283->cci, link_freq_reglist[link_freq_idx].regs,
@@ -1023,8 +1033,6 @@ static int imx283_standby_cancel(struct imx283 *imx283)
usleep_range(19000, 20000);
cci_write(imx283->cci, IMX283_REG_CLAMP, IMX283_CLPSQRST, &ret);
- cci_write(imx283->cci, IMX283_REG_XMSTA, 0, &ret);
- cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret);
return ret;
}
@@ -1117,6 +1125,10 @@ static int imx283_start_streaming(struct imx283 *imx283,
/* Apply customized values from controls (HMAX/VMAX/SHR) */
ret = __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler);
+ /* Start master mode */
+ cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_START, &ret);
+ cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret);
+
return ret;
}
@@ -1153,12 +1165,14 @@ static int imx283_disable_streams(struct v4l2_subdev *sd,
u64 streams_mask)
{
struct imx283 *imx283 = to_imx283(sd);
- int ret;
+ int ret = 0;
if (pad != IMAGE_PAD)
return -EINVAL;
- ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL);
+ cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_STOP, &ret);
+ cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STANDBY, &ret);
+
if (ret)
dev_err(imx283->dev, "Failed to stop stream\n");
@@ -1426,6 +1440,9 @@ static int imx283_parse_endpoint(struct imx283 *imx283)
goto done_endpoint_free;
}
+ imx283->mipi_clk_noncontinuous =
+ bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
+
ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_frequencies, ARRAY_SIZE(link_frequencies),
diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c
index 776107efe386..27a5c212a527 100644
--- a/drivers/media/i2c/imx355.c
+++ b/drivers/media/i2c/imx355.c
@@ -3,9 +3,13 @@
#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/of.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
#include <media/v4l2-ctrls.h>
@@ -62,6 +66,9 @@
#define IMX355_EXT_CLK 19200000
#define IMX355_LINK_FREQ_INDEX 0
+/* number of data lanes */
+#define IMX355_DATA_LANES 4
+
struct imx355_reg {
u16 address;
u8 val;
@@ -125,6 +132,15 @@ struct imx355 {
* Protect access to sensor v4l2 controls.
*/
struct mutex mutex;
+
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+};
+
+static const struct regulator_bulk_data imx355_supplies[] = {
+ { .supply = "avdd" },
+ { .supply = "dvdd" },
+ { .supply = "dovdd" },
};
static const struct imx355_reg imx355_global_regs[] = {
@@ -1515,6 +1531,52 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = {
.open = imx355_open,
};
+static int imx355_power_off(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx355 *imx355 = to_imx355(sd);
+
+ gpiod_set_value_cansleep(imx355->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(imx355_supplies), imx355->supplies);
+ clk_disable_unprepare(imx355->clk);
+
+ return 0;
+}
+
+static int imx355_power_on(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx355 *imx355 = to_imx355(sd);
+ int ret;
+
+ ret = clk_prepare_enable(imx355->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clocks");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(imx355_supplies),
+ imx355->supplies);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to enable regulators");
+ goto error_disable_clocks;
+ }
+
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(imx355->reset_gpio, 0);
+ usleep_range(10000, 11000);
+
+ return 0;
+
+error_disable_clocks:
+ clk_disable_unprepare(imx355->clk);
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx355_pm_ops, imx355_power_off,
+ imx355_power_on, NULL);
+
/* Initialize control handlers */
static int imx355_init_controls(struct imx355 *imx355)
{
@@ -1646,6 +1708,9 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev)
if (!cfg)
goto out_err;
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES)
+ goto out_err;
+
ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_freq_menu_items,
@@ -1689,16 +1754,26 @@ static int imx355_probe(struct i2c_client *client)
"external clock %lu is not supported\n",
freq);
- /* Initialize subdev */
- v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops);
-
- /* Check module identity */
- ret = imx355_identify_module(imx355);
+ ret = devm_regulator_bulk_get_const(imx355->dev,
+ ARRAY_SIZE(imx355_supplies),
+ imx355_supplies,
+ &imx355->supplies);
if (ret) {
- dev_err(imx355->dev, "failed to find sensor: %d", ret);
+ dev_err_probe(imx355->dev, ret, "could not get regulators");
goto error_probe;
}
+ imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(imx355->reset_gpio)) {
+ ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio),
+ "failed to get gpios");
+ goto error_probe;
+ }
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops);
+
imx355->hwcfg = imx355_get_hwcfg(imx355->dev);
if (!imx355->hwcfg) {
dev_err(imx355->dev, "failed to get hwcfg");
@@ -1706,13 +1781,24 @@ static int imx355_probe(struct i2c_client *client)
goto error_probe;
}
+ ret = imx355_power_on(imx355->dev);
+ if (ret)
+ goto error_probe;
+
+ /* Check module identity */
+ ret = imx355_identify_module(imx355);
+ if (ret) {
+ dev_err(imx355->dev, "failed to find sensor: %d", ret);
+ goto error_power_off;
+ }
+
/* Set default mode to max resolution */
imx355->cur_mode = &supported_modes[0];
ret = imx355_init_controls(imx355);
if (ret) {
dev_err(imx355->dev, "failed to init controls: %d", ret);
- goto error_probe;
+ goto error_power_off;
}
/* Initialize subdev */
@@ -1752,6 +1838,9 @@ error_media_entity_runtime_pm:
error_handler_free:
v4l2_ctrl_handler_free(imx355->sd.ctrl_handler);
+error_power_off:
+ imx355_power_off(imx355->dev);
+
error_probe:
mutex_destroy(&imx355->mutex);
@@ -1768,7 +1857,11 @@ static void imx355_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(imx355->dev);
- pm_runtime_set_suspended(imx355->dev);
+
+ if (!pm_runtime_status_suspended(imx355->dev)) {
+ imx355_power_off(imx355->dev);
+ pm_runtime_set_suspended(imx355->dev);
+ }
mutex_destroy(&imx355->mutex);
}
@@ -1779,10 +1872,18 @@ static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = {
};
MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids);
+static const struct of_device_id imx355_match_table[] = {
+ { .compatible = "sony,imx355", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx355_match_table);
+
static struct i2c_driver imx355_i2c_driver = {
.driver = {
.name = "imx355",
.acpi_match_table = ACPI_PTR(imx355_acpi_ids),
+ .of_match_table = imx355_match_table,
+ .pm = &imx355_pm_ops,
},
.probe = imx355_probe,
.remove = imx355_remove,
diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c
index b3826f803547..e25e0a9ff65c 100644
--- a/drivers/media/i2c/imx412.c
+++ b/drivers/media/i2c/imx412.c
@@ -925,7 +925,7 @@ static int imx412_parse_hw_config(struct imx412 *imx412)
/* Request optional reset pin */
imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
- GPIOD_OUT_LOW);
+ GPIOD_OUT_HIGH);
if (IS_ERR(imx412->reset_gpio)) {
dev_err(imx412->dev, "failed to get reset gpio %pe\n",
imx412->reset_gpio);
@@ -1037,7 +1037,11 @@ static int imx412_power_on(struct device *dev)
goto error_reset;
}
- usleep_range(1000, 1200);
+ /*
+ * Certain Arducam IMX577 module variants require a longer reset settle
+ * time. Increasing the delay from 1ms to 10ms ensures reliable startup.
+ */
+ usleep_range(10000, 12000);
return 0;
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 5588cdd7ec20..604745317004 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
mutex_unlock(&ir->lock);
if (rc == -ENODEV) {
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
ir->rc = NULL;
return;
}
@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
i2c_unregister_device(ir->tx_c);
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
}
static const struct i2c_device_id ir_kbd_id[] = {
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index e6e214f8294b..ac0712ce1e65 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1205,7 +1205,7 @@ static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
{
struct max9286_priv *priv = gpiochip_get_data(chip);
- return priv->gpio_state & BIT(offset);
+ return !!(priv->gpio_state & BIT(offset));
}
static int max9286_register_gpio(struct max9286_priv *priv)
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 16b0ace15813..e395e2d14e97 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -368,6 +368,10 @@
* Data Structures
*/
+struct mt9m114_model_info {
+ bool state_standby_polling;
+};
+
enum mt9m114_format_flag {
MT9M114_FMT_FLAG_PARALLEL = BIT(0),
MT9M114_FMT_FLAG_CSI2 = BIT(1),
@@ -417,6 +421,8 @@ struct mt9m114 {
struct v4l2_ctrl *tpg[4];
} ifp;
+
+ const struct mt9m114_model_info *info;
};
/* -----------------------------------------------------------------------------
@@ -2284,9 +2290,11 @@ static int mt9m114_power_on(struct mt9m114 *sensor)
* reaches the standby mode (either initiated manually above in
* parallel mode, or automatically after reset in MIPI mode).
*/
- ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY);
- if (ret < 0)
- goto error_clock;
+ if (sensor->info->state_standby_polling) {
+ ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY);
+ if (ret < 0)
+ goto error_clock;
+ }
return 0;
@@ -2532,6 +2540,10 @@ static int mt9m114_probe(struct i2c_client *client)
if (ret < 0)
return ret;
+ sensor->info = device_get_match_data(dev);
+ if (!sensor->info)
+ return -ENODEV;
+
/* Acquire clocks, GPIOs and regulators. */
sensor->clk = devm_v4l2_sensor_clk_get(dev, NULL);
if (IS_ERR(sensor->clk)) {
@@ -2646,15 +2658,24 @@ static void mt9m114_remove(struct i2c_client *client)
pm_runtime_set_suspended(dev);
}
+static const struct mt9m114_model_info mt9m114_models_default = {
+ .state_standby_polling = true,
+};
+
+static const struct mt9m114_model_info mt9m114_models_aptina = {
+ .state_standby_polling = false,
+};
+
static const struct of_device_id mt9m114_of_ids[] = {
- { .compatible = "onnn,mt9m114" },
- { /* sentinel */ },
+ { .compatible = "onnn,mt9m114", .data = &mt9m114_models_default },
+ { .compatible = "aptina,mi1040", .data = &mt9m114_models_aptina },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mt9m114_of_ids);
static const struct acpi_device_id mt9m114_acpi_ids[] = {
- { "INT33F0" },
- { /* sentinel */ },
+ { "INT33F0", (kernel_ulong_t)&mt9m114_models_default },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_ids);
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 1500ee4db47e..ea5d43d925ff 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -1183,6 +1183,10 @@ static int mt9p031_probe(struct i2c_client *client)
mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_HIGH);
+ if (IS_ERR(mt9p031->reset)) {
+ ret = PTR_ERR(mt9p031->reset);
+ goto done;
+ }
ret = mt9p031_clk_setup(mt9p031);
if (ret)
diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c
index c7184de6251a..1675f0460969 100644
--- a/drivers/media/i2c/og01a1b.c
+++ b/drivers/media/i2c/og01a1b.c
@@ -9,65 +9,53 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
-#include <linux/unaligned.h>
-
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
-#define OG01A1B_REG_VALUE_08BIT 1
-#define OG01A1B_REG_VALUE_16BIT 2
-#define OG01A1B_REG_VALUE_24BIT 3
-
#define OG01A1B_LINK_FREQ_500MHZ 500000000ULL
#define OG01A1B_SCLK 120000000LL
#define OG01A1B_MCLK 19200000
#define OG01A1B_DATA_LANES 2
-#define OG01A1B_RGB_DEPTH 10
-#define OG01A1B_REG_CHIP_ID 0x300a
+#define OG01A1B_REG_CHIP_ID CCI_REG24(0x300a)
#define OG01A1B_CHIP_ID 0x470141
-#define OG01A1B_REG_MODE_SELECT 0x0100
+#define OG01A1B_REG_MODE_SELECT CCI_REG8(0x0100)
#define OG01A1B_MODE_STANDBY 0x00
#define OG01A1B_MODE_STREAMING 0x01
/* vertical-timings from sensor */
-#define OG01A1B_REG_VTS 0x380e
+#define OG01A1B_REG_VTS CCI_REG16(0x380e)
#define OG01A1B_VTS_120FPS 0x0498
#define OG01A1B_VTS_120FPS_MIN 0x0498
#define OG01A1B_VTS_MAX 0x7fff
/* horizontal-timings from sensor */
-#define OG01A1B_REG_HTS 0x380c
+#define OG01A1B_REG_HTS CCI_REG16(0x380c)
/* Exposure controls from sensor */
-#define OG01A1B_REG_EXPOSURE 0x3501
+#define OG01A1B_REG_EXPOSURE CCI_REG16(0x3501)
#define OG01A1B_EXPOSURE_MIN 1
#define OG01A1B_EXPOSURE_MAX_MARGIN 14
#define OG01A1B_EXPOSURE_STEP 1
/* Analog gain controls from sensor */
-#define OG01A1B_REG_ANALOG_GAIN 0x3508
+#define OG01A1B_REG_ANALOG_GAIN CCI_REG16(0x3508)
#define OG01A1B_ANAL_GAIN_MIN 16
#define OG01A1B_ANAL_GAIN_MAX 248 /* Max = 15.5x */
#define OG01A1B_ANAL_GAIN_STEP 1
/* Digital gain controls from sensor */
-#define OG01A1B_REG_DIG_GAIN 0x350a
+#define OG01A1B_REG_DIG_GAIN CCI_REG24(0x350a)
#define OG01A1B_DGTL_GAIN_MIN 1024
#define OG01A1B_DGTL_GAIN_MAX 16384 /* Max = 16x */
#define OG01A1B_DGTL_GAIN_STEP 1
#define OG01A1B_DGTL_GAIN_DEFAULT 1024
-/* Group Access */
-#define OG01A1B_REG_GROUP_ACCESS 0x3208
-#define OG01A1B_GROUP_HOLD_START 0x0
-#define OG01A1B_GROUP_HOLD_END 0x10
-#define OG01A1B_GROUP_HOLD_LAUNCH 0xa0
-
/* Test Pattern Control */
-#define OG01A1B_REG_TEST_PATTERN 0x5100
+#define OG01A1B_REG_TEST_PATTERN CCI_REG8(0x5100)
#define OG01A1B_TEST_PATTERN_ENABLE BIT(7)
#define OG01A1B_TEST_PATTERN_BAR_SHIFT 2
@@ -77,14 +65,9 @@ enum {
OG01A1B_LINK_FREQ_1000MBPS,
};
-struct og01a1b_reg {
- u16 address;
- u8 val;
-};
-
struct og01a1b_reg_list {
+ const struct cci_reg_sequence *regs;
u32 num_of_regs;
- const struct og01a1b_reg *regs;
};
struct og01a1b_link_freq_config {
@@ -114,275 +97,279 @@ struct og01a1b_mode {
const struct og01a1b_reg_list reg_list;
};
-static const struct og01a1b_reg mipi_data_rate_1000mbps[] = {
- {0x0103, 0x01},
- {0x0303, 0x02},
- {0x0304, 0x00},
- {0x0305, 0xd2},
- {0x0323, 0x02},
- {0x0324, 0x01},
- {0x0325, 0x77},
+static const u32 og01a1b_mbus_formats[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y8_1X8,
};
-static const struct og01a1b_reg mode_1280x1024_regs[] = {
- {0x0300, 0x0a},
- {0x0301, 0x29},
- {0x0302, 0x31},
- {0x0303, 0x02},
- {0x0304, 0x00},
- {0x0305, 0xd2},
- {0x0306, 0x00},
- {0x0307, 0x01},
- {0x0308, 0x02},
- {0x0309, 0x00},
- {0x0310, 0x00},
- {0x0311, 0x00},
- {0x0312, 0x07},
- {0x0313, 0x00},
- {0x0314, 0x00},
- {0x0315, 0x00},
- {0x0320, 0x02},
- {0x0321, 0x01},
- {0x0322, 0x01},
- {0x0323, 0x02},
- {0x0324, 0x01},
- {0x0325, 0x77},
- {0x0326, 0xce},
- {0x0327, 0x04},
- {0x0329, 0x02},
- {0x032a, 0x04},
- {0x032b, 0x04},
- {0x032c, 0x02},
- {0x032d, 0x01},
- {0x032e, 0x00},
- {0x300d, 0x02},
- {0x300e, 0x04},
- {0x3021, 0x08},
- {0x301e, 0x03},
- {0x3103, 0x00},
- {0x3106, 0x08},
- {0x3107, 0x40},
- {0x3216, 0x01},
- {0x3217, 0x00},
- {0x3218, 0xc0},
- {0x3219, 0x55},
- {0x3500, 0x00},
- {0x3501, 0x04},
- {0x3502, 0x8a},
- {0x3506, 0x01},
- {0x3507, 0x72},
- {0x3508, 0x01},
- {0x3509, 0x00},
- {0x350a, 0x01},
- {0x350b, 0x00},
- {0x350c, 0x00},
- {0x3541, 0x00},
- {0x3542, 0x40},
- {0x3605, 0xe0},
- {0x3606, 0x41},
- {0x3614, 0x20},
- {0x3620, 0x0b},
- {0x3630, 0x07},
- {0x3636, 0xa0},
- {0x3637, 0xf9},
- {0x3638, 0x09},
- {0x3639, 0x38},
- {0x363f, 0x09},
- {0x3640, 0x17},
- {0x3662, 0x04},
- {0x3665, 0x80},
- {0x3670, 0x68},
- {0x3674, 0x00},
- {0x3677, 0x3f},
- {0x3679, 0x00},
- {0x369f, 0x19},
- {0x36a0, 0x03},
- {0x36a2, 0x19},
- {0x36a3, 0x03},
- {0x370d, 0x66},
- {0x370f, 0x00},
- {0x3710, 0x03},
- {0x3715, 0x03},
- {0x3716, 0x03},
- {0x3717, 0x06},
- {0x3733, 0x00},
- {0x3778, 0x00},
- {0x37a8, 0x0f},
- {0x37a9, 0x01},
- {0x37aa, 0x07},
- {0x37bd, 0x1c},
- {0x37c1, 0x2f},
- {0x37c3, 0x09},
- {0x37c8, 0x1d},
- {0x37ca, 0x30},
- {0x37df, 0x00},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x05},
- {0x3805, 0x0f},
- {0x3806, 0x04},
- {0x3807, 0x0f},
- {0x3808, 0x05},
- {0x3809, 0x00},
- {0x380a, 0x04},
- {0x380b, 0x00},
- {0x380c, 0x03},
- {0x380d, 0x50},
- {0x380e, 0x04},
- {0x380f, 0x98},
- {0x3810, 0x00},
- {0x3811, 0x08},
- {0x3812, 0x00},
- {0x3813, 0x08},
- {0x3814, 0x11},
- {0x3815, 0x11},
- {0x3820, 0x40},
- {0x3821, 0x04},
- {0x3826, 0x00},
- {0x3827, 0x00},
- {0x382a, 0x08},
- {0x382b, 0x52},
- {0x382d, 0xba},
- {0x383d, 0x14},
- {0x384a, 0xa2},
- {0x3866, 0x0e},
- {0x3867, 0x07},
- {0x3884, 0x00},
- {0x3885, 0x08},
- {0x3893, 0x68},
- {0x3894, 0x2a},
- {0x3898, 0x00},
- {0x3899, 0x31},
- {0x389a, 0x04},
- {0x389b, 0x00},
- {0x389c, 0x0b},
- {0x389d, 0xad},
- {0x389f, 0x08},
- {0x38a0, 0x00},
- {0x38a1, 0x00},
- {0x38a8, 0x70},
- {0x38ac, 0xea},
- {0x38b2, 0x00},
- {0x38b3, 0x08},
- {0x38bc, 0x20},
- {0x38c4, 0x0c},
- {0x38c5, 0x3a},
- {0x38c7, 0x3a},
- {0x38e1, 0xc0},
- {0x38ec, 0x3c},
- {0x38f0, 0x09},
- {0x38f1, 0x6f},
- {0x38fe, 0x3c},
- {0x391e, 0x00},
- {0x391f, 0x00},
- {0x3920, 0xa5},
- {0x3921, 0x00},
- {0x3922, 0x00},
- {0x3923, 0x00},
- {0x3924, 0x05},
- {0x3925, 0x00},
- {0x3926, 0x00},
- {0x3927, 0x00},
- {0x3928, 0x1a},
- {0x3929, 0x01},
- {0x392a, 0xb4},
- {0x392b, 0x00},
- {0x392c, 0x10},
- {0x392f, 0x40},
- {0x4000, 0xcf},
- {0x4003, 0x40},
- {0x4008, 0x00},
- {0x4009, 0x07},
- {0x400a, 0x02},
- {0x400b, 0x54},
- {0x400c, 0x00},
- {0x400d, 0x07},
- {0x4010, 0xc0},
- {0x4012, 0x02},
- {0x4014, 0x04},
- {0x4015, 0x04},
- {0x4017, 0x02},
- {0x4042, 0x01},
- {0x4306, 0x04},
- {0x4307, 0x12},
- {0x4509, 0x00},
- {0x450b, 0x83},
- {0x4604, 0x68},
- {0x4608, 0x0a},
- {0x4700, 0x06},
- {0x4800, 0x64},
- {0x481b, 0x3c},
- {0x4825, 0x32},
- {0x4833, 0x18},
- {0x4837, 0x0f},
- {0x4850, 0x40},
- {0x4860, 0x00},
- {0x4861, 0xec},
- {0x4864, 0x00},
- {0x4883, 0x00},
- {0x4888, 0x90},
- {0x4889, 0x05},
- {0x488b, 0x04},
- {0x4f00, 0x04},
- {0x4f10, 0x04},
- {0x4f21, 0x01},
- {0x4f22, 0x40},
- {0x4f23, 0x44},
- {0x4f24, 0x51},
- {0x4f25, 0x41},
- {0x5000, 0x1f},
- {0x500a, 0x00},
- {0x5100, 0x00},
- {0x5111, 0x20},
- {0x3020, 0x20},
- {0x3613, 0x03},
- {0x38c9, 0x02},
- {0x5304, 0x01},
- {0x3620, 0x08},
- {0x3639, 0x58},
- {0x363a, 0x10},
- {0x3674, 0x04},
- {0x3780, 0xff},
- {0x3781, 0xff},
- {0x3782, 0x00},
- {0x3783, 0x01},
- {0x3798, 0xa3},
- {0x37aa, 0x10},
- {0x38a8, 0xf0},
- {0x38c4, 0x09},
- {0x38c5, 0xb0},
- {0x38df, 0x80},
- {0x38ff, 0x05},
- {0x4010, 0xf1},
- {0x4011, 0x70},
- {0x3667, 0x80},
- {0x4d00, 0x4a},
- {0x4d01, 0x18},
- {0x4d02, 0xbb},
- {0x4d03, 0xde},
- {0x4d04, 0x93},
- {0x4d05, 0xff},
- {0x4d09, 0x0a},
- {0x37aa, 0x16},
- {0x3606, 0x42},
- {0x3605, 0x00},
- {0x36a2, 0x17},
- {0x300d, 0x0a},
- {0x4d00, 0x4d},
- {0x4d01, 0x95},
- {0x3d8C, 0x70},
- {0x3d8d, 0xE9},
- {0x5300, 0x00},
- {0x5301, 0x10},
- {0x5302, 0x00},
- {0x5303, 0xE3},
- {0x3d88, 0x00},
- {0x3d89, 0x10},
- {0x3d8a, 0x00},
- {0x3d8b, 0xE3},
- {0x4f22, 0x00},
+static const struct cci_reg_sequence mipi_data_rate_1000mbps[] = {
+ { CCI_REG8(0x0103), 0x01 },
+ { CCI_REG8(0x0303), 0x02 },
+ { CCI_REG8(0x0304), 0x00 },
+ { CCI_REG8(0x0305), 0xd2 },
+ { CCI_REG8(0x0323), 0x02 },
+ { CCI_REG8(0x0324), 0x01 },
+ { CCI_REG8(0x0325), 0x77 },
+};
+
+static const struct cci_reg_sequence mode_1280x1024_regs[] = {
+ { CCI_REG8(0x0300), 0x0a },
+ { CCI_REG8(0x0301), 0x29 },
+ { CCI_REG8(0x0302), 0x31 },
+ { CCI_REG8(0x0303), 0x02 },
+ { CCI_REG8(0x0304), 0x00 },
+ { CCI_REG8(0x0305), 0xd2 },
+ { CCI_REG8(0x0306), 0x00 },
+ { CCI_REG8(0x0307), 0x01 },
+ { CCI_REG8(0x0308), 0x02 },
+ { CCI_REG8(0x0309), 0x00 },
+ { CCI_REG8(0x0310), 0x00 },
+ { CCI_REG8(0x0311), 0x00 },
+ { CCI_REG8(0x0312), 0x07 },
+ { CCI_REG8(0x0313), 0x00 },
+ { CCI_REG8(0x0314), 0x00 },
+ { CCI_REG8(0x0315), 0x00 },
+ { CCI_REG8(0x0320), 0x02 },
+ { CCI_REG8(0x0321), 0x01 },
+ { CCI_REG8(0x0322), 0x01 },
+ { CCI_REG8(0x0323), 0x02 },
+ { CCI_REG8(0x0324), 0x01 },
+ { CCI_REG8(0x0325), 0x77 },
+ { CCI_REG8(0x0326), 0xce },
+ { CCI_REG8(0x0327), 0x04 },
+ { CCI_REG8(0x0329), 0x02 },
+ { CCI_REG8(0x032a), 0x04 },
+ { CCI_REG8(0x032b), 0x04 },
+ { CCI_REG8(0x032c), 0x02 },
+ { CCI_REG8(0x032d), 0x01 },
+ { CCI_REG8(0x032e), 0x00 },
+ { CCI_REG8(0x300d), 0x02 },
+ { CCI_REG8(0x300e), 0x04 },
+ { CCI_REG8(0x3021), 0x08 },
+ { CCI_REG8(0x301e), 0x03 },
+ { CCI_REG8(0x3103), 0x00 },
+ { CCI_REG8(0x3106), 0x08 },
+ { CCI_REG8(0x3107), 0x40 },
+ { CCI_REG8(0x3216), 0x01 },
+ { CCI_REG8(0x3217), 0x00 },
+ { CCI_REG8(0x3218), 0xc0 },
+ { CCI_REG8(0x3219), 0x55 },
+ { CCI_REG8(0x3500), 0x00 },
+ { CCI_REG8(0x3501), 0x04 },
+ { CCI_REG8(0x3502), 0x8a },
+ { CCI_REG8(0x3506), 0x01 },
+ { CCI_REG8(0x3507), 0x72 },
+ { CCI_REG8(0x3508), 0x01 },
+ { CCI_REG8(0x3509), 0x00 },
+ { CCI_REG8(0x350a), 0x01 },
+ { CCI_REG8(0x350b), 0x00 },
+ { CCI_REG8(0x350c), 0x00 },
+ { CCI_REG8(0x3541), 0x00 },
+ { CCI_REG8(0x3542), 0x40 },
+ { CCI_REG8(0x3605), 0xe0 },
+ { CCI_REG8(0x3606), 0x41 },
+ { CCI_REG8(0x3614), 0x20 },
+ { CCI_REG8(0x3620), 0x0b },
+ { CCI_REG8(0x3630), 0x07 },
+ { CCI_REG8(0x3636), 0xa0 },
+ { CCI_REG8(0x3637), 0xf9 },
+ { CCI_REG8(0x3638), 0x09 },
+ { CCI_REG8(0x3639), 0x38 },
+ { CCI_REG8(0x363f), 0x09 },
+ { CCI_REG8(0x3640), 0x17 },
+ { CCI_REG8(0x3665), 0x80 },
+ { CCI_REG8(0x3670), 0x68 },
+ { CCI_REG8(0x3674), 0x00 },
+ { CCI_REG8(0x3677), 0x3f },
+ { CCI_REG8(0x3679), 0x00 },
+ { CCI_REG8(0x369f), 0x19 },
+ { CCI_REG8(0x36a0), 0x03 },
+ { CCI_REG8(0x36a2), 0x19 },
+ { CCI_REG8(0x36a3), 0x03 },
+ { CCI_REG8(0x370d), 0x66 },
+ { CCI_REG8(0x370f), 0x00 },
+ { CCI_REG8(0x3710), 0x03 },
+ { CCI_REG8(0x3715), 0x03 },
+ { CCI_REG8(0x3716), 0x03 },
+ { CCI_REG8(0x3717), 0x06 },
+ { CCI_REG8(0x3733), 0x00 },
+ { CCI_REG8(0x3778), 0x00 },
+ { CCI_REG8(0x37a8), 0x0f },
+ { CCI_REG8(0x37a9), 0x01 },
+ { CCI_REG8(0x37aa), 0x07 },
+ { CCI_REG8(0x37bd), 0x1c },
+ { CCI_REG8(0x37c1), 0x2f },
+ { CCI_REG8(0x37c3), 0x09 },
+ { CCI_REG8(0x37c8), 0x1d },
+ { CCI_REG8(0x37ca), 0x30 },
+ { CCI_REG8(0x37df), 0x00 },
+ { CCI_REG8(0x3800), 0x00 },
+ { CCI_REG8(0x3801), 0x00 },
+ { CCI_REG8(0x3802), 0x00 },
+ { CCI_REG8(0x3803), 0x00 },
+ { CCI_REG8(0x3804), 0x05 },
+ { CCI_REG8(0x3805), 0x0f },
+ { CCI_REG8(0x3806), 0x04 },
+ { CCI_REG8(0x3807), 0x0f },
+ { CCI_REG8(0x3808), 0x05 },
+ { CCI_REG8(0x3809), 0x00 },
+ { CCI_REG8(0x380a), 0x04 },
+ { CCI_REG8(0x380b), 0x00 },
+ { CCI_REG8(0x380c), 0x03 },
+ { CCI_REG8(0x380d), 0x50 },
+ { CCI_REG8(0x380e), 0x04 },
+ { CCI_REG8(0x380f), 0x98 },
+ { CCI_REG8(0x3810), 0x00 },
+ { CCI_REG8(0x3811), 0x08 },
+ { CCI_REG8(0x3812), 0x00 },
+ { CCI_REG8(0x3813), 0x08 },
+ { CCI_REG8(0x3814), 0x11 },
+ { CCI_REG8(0x3815), 0x11 },
+ { CCI_REG8(0x3820), 0x40 },
+ { CCI_REG8(0x3821), 0x04 },
+ { CCI_REG8(0x3826), 0x00 },
+ { CCI_REG8(0x3827), 0x00 },
+ { CCI_REG8(0x382a), 0x08 },
+ { CCI_REG8(0x382b), 0x52 },
+ { CCI_REG8(0x382d), 0xba },
+ { CCI_REG8(0x383d), 0x14 },
+ { CCI_REG8(0x384a), 0xa2 },
+ { CCI_REG8(0x3866), 0x0e },
+ { CCI_REG8(0x3867), 0x07 },
+ { CCI_REG8(0x3884), 0x00 },
+ { CCI_REG8(0x3885), 0x08 },
+ { CCI_REG8(0x3893), 0x68 },
+ { CCI_REG8(0x3894), 0x2a },
+ { CCI_REG8(0x3898), 0x00 },
+ { CCI_REG8(0x3899), 0x31 },
+ { CCI_REG8(0x389a), 0x04 },
+ { CCI_REG8(0x389b), 0x00 },
+ { CCI_REG8(0x389c), 0x0b },
+ { CCI_REG8(0x389d), 0xad },
+ { CCI_REG8(0x389f), 0x08 },
+ { CCI_REG8(0x38a0), 0x00 },
+ { CCI_REG8(0x38a1), 0x00 },
+ { CCI_REG8(0x38a8), 0x70 },
+ { CCI_REG8(0x38ac), 0xea },
+ { CCI_REG8(0x38b2), 0x00 },
+ { CCI_REG8(0x38b3), 0x08 },
+ { CCI_REG8(0x38bc), 0x20 },
+ { CCI_REG8(0x38c4), 0x0c },
+ { CCI_REG8(0x38c5), 0x3a },
+ { CCI_REG8(0x38c7), 0x3a },
+ { CCI_REG8(0x38e1), 0xc0 },
+ { CCI_REG8(0x38ec), 0x3c },
+ { CCI_REG8(0x38f0), 0x09 },
+ { CCI_REG8(0x38f1), 0x6f },
+ { CCI_REG8(0x38fe), 0x3c },
+ { CCI_REG8(0x391e), 0x00 },
+ { CCI_REG8(0x391f), 0x00 },
+ { CCI_REG8(0x3920), 0xa5 },
+ { CCI_REG8(0x3921), 0x00 },
+ { CCI_REG8(0x3922), 0x00 },
+ { CCI_REG8(0x3923), 0x00 },
+ { CCI_REG8(0x3924), 0x05 },
+ { CCI_REG8(0x3925), 0x00 },
+ { CCI_REG8(0x3926), 0x00 },
+ { CCI_REG8(0x3927), 0x00 },
+ { CCI_REG8(0x3928), 0x1a },
+ { CCI_REG8(0x3929), 0x01 },
+ { CCI_REG8(0x392a), 0xb4 },
+ { CCI_REG8(0x392b), 0x00 },
+ { CCI_REG8(0x392c), 0x10 },
+ { CCI_REG8(0x392f), 0x40 },
+ { CCI_REG8(0x4000), 0xcf },
+ { CCI_REG8(0x4003), 0x40 },
+ { CCI_REG8(0x4008), 0x00 },
+ { CCI_REG8(0x4009), 0x07 },
+ { CCI_REG8(0x400a), 0x02 },
+ { CCI_REG8(0x400b), 0x54 },
+ { CCI_REG8(0x400c), 0x00 },
+ { CCI_REG8(0x400d), 0x07 },
+ { CCI_REG8(0x4010), 0xc0 },
+ { CCI_REG8(0x4012), 0x02 },
+ { CCI_REG8(0x4014), 0x04 },
+ { CCI_REG8(0x4015), 0x04 },
+ { CCI_REG8(0x4017), 0x02 },
+ { CCI_REG8(0x4042), 0x01 },
+ { CCI_REG8(0x4306), 0x04 },
+ { CCI_REG8(0x4307), 0x12 },
+ { CCI_REG8(0x4509), 0x00 },
+ { CCI_REG8(0x450b), 0x83 },
+ { CCI_REG8(0x4604), 0x68 },
+ { CCI_REG8(0x4608), 0x0a },
+ { CCI_REG8(0x4700), 0x06 },
+ { CCI_REG8(0x4800), 0x64 },
+ { CCI_REG8(0x481b), 0x3c },
+ { CCI_REG8(0x4825), 0x32 },
+ { CCI_REG8(0x4833), 0x18 },
+ { CCI_REG8(0x4837), 0x0f },
+ { CCI_REG8(0x4850), 0x40 },
+ { CCI_REG8(0x4860), 0x00 },
+ { CCI_REG8(0x4861), 0xec },
+ { CCI_REG8(0x4864), 0x00 },
+ { CCI_REG8(0x4883), 0x00 },
+ { CCI_REG8(0x4888), 0x90 },
+ { CCI_REG8(0x4889), 0x05 },
+ { CCI_REG8(0x488b), 0x04 },
+ { CCI_REG8(0x4f00), 0x04 },
+ { CCI_REG8(0x4f10), 0x04 },
+ { CCI_REG8(0x4f21), 0x01 },
+ { CCI_REG8(0x4f22), 0x40 },
+ { CCI_REG8(0x4f23), 0x44 },
+ { CCI_REG8(0x4f24), 0x51 },
+ { CCI_REG8(0x4f25), 0x41 },
+ { CCI_REG8(0x5000), 0x1f },
+ { CCI_REG8(0x500a), 0x00 },
+ { CCI_REG8(0x5100), 0x00 },
+ { CCI_REG8(0x5111), 0x20 },
+ { CCI_REG8(0x3020), 0x20 },
+ { CCI_REG8(0x3613), 0x03 },
+ { CCI_REG8(0x38c9), 0x02 },
+ { CCI_REG8(0x5304), 0x01 },
+ { CCI_REG8(0x3620), 0x08 },
+ { CCI_REG8(0x3639), 0x58 },
+ { CCI_REG8(0x363a), 0x10 },
+ { CCI_REG8(0x3674), 0x04 },
+ { CCI_REG8(0x3780), 0xff },
+ { CCI_REG8(0x3781), 0xff },
+ { CCI_REG8(0x3782), 0x00 },
+ { CCI_REG8(0x3783), 0x01 },
+ { CCI_REG8(0x3798), 0xa3 },
+ { CCI_REG8(0x37aa), 0x10 },
+ { CCI_REG8(0x38a8), 0xf0 },
+ { CCI_REG8(0x38c4), 0x09 },
+ { CCI_REG8(0x38c5), 0xb0 },
+ { CCI_REG8(0x38df), 0x80 },
+ { CCI_REG8(0x38ff), 0x05 },
+ { CCI_REG8(0x4010), 0xf1 },
+ { CCI_REG8(0x4011), 0x70 },
+ { CCI_REG8(0x3667), 0x80 },
+ { CCI_REG8(0x4d00), 0x4a },
+ { CCI_REG8(0x4d01), 0x18 },
+ { CCI_REG8(0x4d02), 0xbb },
+ { CCI_REG8(0x4d03), 0xde },
+ { CCI_REG8(0x4d04), 0x93 },
+ { CCI_REG8(0x4d05), 0xff },
+ { CCI_REG8(0x4d09), 0x0a },
+ { CCI_REG8(0x37aa), 0x16 },
+ { CCI_REG8(0x3606), 0x42 },
+ { CCI_REG8(0x3605), 0x00 },
+ { CCI_REG8(0x36a2), 0x17 },
+ { CCI_REG8(0x300d), 0x0a },
+ { CCI_REG8(0x4d00), 0x4d },
+ { CCI_REG8(0x4d01), 0x95 },
+ { CCI_REG8(0x3d8c), 0x70 },
+ { CCI_REG8(0x3d8d), 0xe9 },
+ { CCI_REG8(0x5300), 0x00 },
+ { CCI_REG8(0x5301), 0x10 },
+ { CCI_REG8(0x5302), 0x00 },
+ { CCI_REG8(0x5303), 0xe3 },
+ { CCI_REG8(0x3d88), 0x00 },
+ { CCI_REG8(0x3d89), 0x10 },
+ { CCI_REG8(0x3d8a), 0x00 },
+ { CCI_REG8(0x3d8b), 0xe3 },
+ { CCI_REG8(0x4f22), 0x00 },
};
static const char * const og01a1b_test_pattern_menu[] = {
@@ -423,6 +410,7 @@ static const struct og01a1b_mode supported_modes[] = {
struct og01a1b {
struct device *dev;
+ struct regmap *regmap;
struct clk *xvclk;
struct gpio_desc *reset_gpio;
struct regulator *avdd;
@@ -443,102 +431,36 @@ struct og01a1b {
/* Current mode */
const struct og01a1b_mode *cur_mode;
- /* To serialize asynchronus callbacks */
- struct mutex mutex;
+ /* Selected media bus format output */
+ u32 code;
};
-static u64 to_pixel_rate(u32 f_index)
+static u64 to_pixel_rate(u32 f_index, u32 bpp)
{
u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OG01A1B_DATA_LANES;
- do_div(pixel_rate, OG01A1B_RGB_DEPTH);
+ do_div(pixel_rate, bpp);
return pixel_rate;
}
-static u64 to_pixels_per_line(u32 hts, u32 f_index)
+static u64 to_pixels_per_line(u32 hts, u32 f_index, u32 bpp)
{
- u64 ppl = hts * to_pixel_rate(f_index);
+ u64 ppl = hts * to_pixel_rate(f_index, bpp);
do_div(ppl, OG01A1B_SCLK);
return ppl;
}
-static int og01a1b_read_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd);
- struct i2c_msg msgs[2];
- u8 addr_buf[2];
- u8 data_buf[4] = {0};
- int ret;
-
- if (len > 4)
- return -EINVAL;
-
- put_unaligned_be16(reg, addr_buf);
- msgs[0].addr = client->addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(addr_buf);
- msgs[0].buf = addr_buf;
- msgs[1].addr = client->addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = &data_buf[4 - len];
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs))
- return -EIO;
-
- *val = get_unaligned_be32(data_buf);
-
- return 0;
-}
-
-static int og01a1b_write_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd);
- u8 buf[6];
-
- if (len > 4)
- return -EINVAL;
-
- put_unaligned_be16(reg, buf);
- put_unaligned_be32(val << 8 * (4 - len), buf + 2);
- if (i2c_master_send(client, buf, len + 2) != len + 2)
- return -EIO;
-
- return 0;
-}
-
-static int og01a1b_write_reg_list(struct og01a1b *og01a1b,
- const struct og01a1b_reg_list *r_list)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < r_list->num_of_regs; i++) {
- ret = og01a1b_write_reg(og01a1b, r_list->regs[i].address, 1,
- r_list->regs[i].val);
- if (ret) {
- dev_err_ratelimited(og01a1b->dev,
- "failed to write reg 0x%4.4x. error = %d",
- r_list->regs[i].address, ret);
- return ret;
- }
- }
-
- return 0;
-}
-
static int og01a1b_test_pattern(struct og01a1b *og01a1b, u32 pattern)
{
if (pattern)
pattern = (pattern - 1) << OG01A1B_TEST_PATTERN_BAR_SHIFT |
OG01A1B_TEST_PATTERN_ENABLE;
- return og01a1b_write_reg(og01a1b, OG01A1B_REG_TEST_PATTERN,
- OG01A1B_REG_VALUE_08BIT, pattern);
+ return cci_write(og01a1b->regmap, OG01A1B_REG_TEST_PATTERN,
+ pattern, NULL);
}
static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -565,26 +487,23 @@ static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
- ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_ANALOG_GAIN,
- OG01A1B_REG_VALUE_16BIT,
- ctrl->val << 4);
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_ANALOG_GAIN,
+ ctrl->val << 4, NULL);
break;
case V4L2_CID_DIGITAL_GAIN:
- ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_DIG_GAIN,
- OG01A1B_REG_VALUE_24BIT,
- ctrl->val << 6);
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_DIG_GAIN,
+ ctrl->val << 6, NULL);
break;
case V4L2_CID_EXPOSURE:
- ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_EXPOSURE,
- OG01A1B_REG_VALUE_16BIT, ctrl->val);
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_EXPOSURE,
+ ctrl->val, NULL);
break;
case V4L2_CID_VBLANK:
- ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_VTS,
- OG01A1B_REG_VALUE_16BIT,
- og01a1b->cur_mode->height + ctrl->val);
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_VTS,
+ og01a1b->cur_mode->height + ctrl->val, NULL);
break;
case V4L2_CID_TEST_PATTERN:
@@ -609,6 +528,7 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b)
{
struct v4l2_ctrl_handler *ctrl_hdlr;
s64 exposure_max, h_blank;
+ u32 bpp;
int ret;
ctrl_hdlr = &og01a1b->ctrl_handler;
@@ -616,7 +536,6 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b)
if (ret)
return ret;
- ctrl_hdlr->lock = &og01a1b->mutex;
og01a1b->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
&og01a1b_ctrl_ops,
V4L2_CID_LINK_FREQ,
@@ -626,13 +545,12 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b)
if (og01a1b->link_freq)
og01a1b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ bpp = (og01a1b->code == MEDIA_BUS_FMT_Y10_1X10 ? 10 : 8);
og01a1b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops,
- V4L2_CID_PIXEL_RATE, 0,
- to_pixel_rate
- (OG01A1B_LINK_FREQ_1000MBPS),
- 1,
- to_pixel_rate
- (OG01A1B_LINK_FREQ_1000MBPS));
+ V4L2_CID_PIXEL_RATE, 0,
+ to_pixel_rate(OG01A1B_LINK_FREQ_1000MBPS, bpp),
+ 1,
+ to_pixel_rate(OG01A1B_LINK_FREQ_1000MBPS, bpp));
og01a1b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops,
V4L2_CID_VBLANK,
og01a1b->cur_mode->vts_min -
@@ -642,7 +560,7 @@ static int og01a1b_init_controls(struct og01a1b *og01a1b)
og01a1b->cur_mode->vts_def -
og01a1b->cur_mode->height);
h_blank = to_pixels_per_line(og01a1b->cur_mode->hts,
- og01a1b->cur_mode->link_freq_index) -
+ og01a1b->cur_mode->link_freq_index, bpp) -
og01a1b->cur_mode->width;
og01a1b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops,
V4L2_CID_HBLANK, h_blank, h_blank,
@@ -682,78 +600,78 @@ static void og01a1b_update_pad_format(const struct og01a1b_mode *mode,
{
fmt->width = mode->width;
fmt->height = mode->height;
- fmt->code = MEDIA_BUS_FMT_Y10_1X10;
fmt->field = V4L2_FIELD_NONE;
}
-static int og01a1b_start_streaming(struct og01a1b *og01a1b)
+static int og01a1b_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct og01a1b *og01a1b = to_og01a1b(sd);
+ unsigned int link_freq_index = og01a1b->cur_mode->link_freq_index;
const struct og01a1b_reg_list *reg_list;
- int link_freq_index, ret;
+ int ret;
- link_freq_index = og01a1b->cur_mode->link_freq_index;
- reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = pm_runtime_resume_and_get(og01a1b->dev);
+ if (ret)
+ return ret;
- ret = og01a1b_write_reg_list(og01a1b, reg_list);
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = cci_multi_reg_write(og01a1b->regmap, reg_list->regs,
+ reg_list->num_of_regs, NULL);
if (ret) {
- dev_err(og01a1b->dev, "failed to set plls");
- return ret;
+ dev_err(og01a1b->dev, "failed to set plls: %d\n", ret);
+ goto error;
}
reg_list = &og01a1b->cur_mode->reg_list;
- ret = og01a1b_write_reg_list(og01a1b, reg_list);
+ ret = cci_multi_reg_write(og01a1b->regmap, reg_list->regs,
+ reg_list->num_of_regs, NULL);
if (ret) {
- dev_err(og01a1b->dev, "failed to set mode");
- return ret;
+ dev_err(og01a1b->dev, "failed to set mode: %d\n", ret);
+ goto error;
+ }
+
+ ret = cci_write(og01a1b->regmap, CCI_REG8(0x3662),
+ (og01a1b->code == MEDIA_BUS_FMT_Y10_1X10 ? 0x4 : 0x6),
+ NULL);
+ if (ret) {
+ dev_err(og01a1b->dev, "failed to set output format: %d\n", ret);
+ goto error;
}
ret = __v4l2_ctrl_handler_setup(og01a1b->sd.ctrl_handler);
if (ret)
- return ret;
+ goto error;
- ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT,
- OG01A1B_REG_VALUE_08BIT,
- OG01A1B_MODE_STREAMING);
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_MODE_SELECT,
+ OG01A1B_MODE_STREAMING, NULL);
if (ret) {
- dev_err(og01a1b->dev, "failed to set stream");
- return ret;
+ dev_err(og01a1b->dev, "failed to start streaming: %d\n", ret);
+ goto error;
}
return 0;
-}
-static void og01a1b_stop_streaming(struct og01a1b *og01a1b)
-{
- if (og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT,
- OG01A1B_REG_VALUE_08BIT, OG01A1B_MODE_STANDBY))
- dev_err(og01a1b->dev, "failed to set stream");
+error:
+ pm_runtime_put_autosuspend(og01a1b->dev);
+
+ return ret;
}
-static int og01a1b_set_stream(struct v4l2_subdev *sd, int enable)
+static int og01a1b_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct og01a1b *og01a1b = to_og01a1b(sd);
- int ret = 0;
-
- mutex_lock(&og01a1b->mutex);
- if (enable) {
- ret = pm_runtime_resume_and_get(og01a1b->dev);
- if (ret) {
- mutex_unlock(&og01a1b->mutex);
- return ret;
- }
+ int ret;
- ret = og01a1b_start_streaming(og01a1b);
- if (ret) {
- enable = 0;
- og01a1b_stop_streaming(og01a1b);
- pm_runtime_put(og01a1b->dev);
- }
- } else {
- og01a1b_stop_streaming(og01a1b);
- pm_runtime_put(og01a1b->dev);
- }
+ ret = cci_write(og01a1b->regmap, OG01A1B_REG_MODE_SELECT,
+ OG01A1B_MODE_STANDBY, NULL);
+ if (ret)
+ dev_err(og01a1b->dev, "failed to stop streaming: %d\n", ret);
- mutex_unlock(&og01a1b->mutex);
+ pm_runtime_put_autosuspend(og01a1b->dev);
return ret;
}
@@ -764,55 +682,38 @@ static int og01a1b_set_format(struct v4l2_subdev *sd,
{
struct og01a1b *og01a1b = to_og01a1b(sd);
const struct og01a1b_mode *mode;
- s32 vblank_def, h_blank;
+ s32 vblank_def, h_blank, bpp;
mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes), width,
height, fmt->format.width,
fmt->format.height);
- mutex_lock(&og01a1b->mutex);
og01a1b_update_pad_format(mode, &fmt->format);
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
- } else {
- og01a1b->cur_mode = mode;
- __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index);
- __v4l2_ctrl_s_ctrl_int64(og01a1b->pixel_rate,
- to_pixel_rate(mode->link_freq_index));
-
- /* Update limits and set FPS to default */
- vblank_def = mode->vts_def - mode->height;
- __v4l2_ctrl_modify_range(og01a1b->vblank,
- mode->vts_min - mode->height,
- OG01A1B_VTS_MAX - mode->height, 1,
- vblank_def);
- __v4l2_ctrl_s_ctrl(og01a1b->vblank, vblank_def);
- h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) -
- mode->width;
- __v4l2_ctrl_modify_range(og01a1b->hblank, h_blank, h_blank, 1,
- h_blank);
- }
- mutex_unlock(&og01a1b->mutex);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ goto set_format;
- return 0;
-}
+ bpp = (fmt->format.code == MEDIA_BUS_FMT_Y10_1X10 ? 10 : 8);
+ __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(og01a1b->pixel_rate,
+ to_pixel_rate(mode->link_freq_index, bpp));
-static int og01a1b_get_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct og01a1b *og01a1b = to_og01a1b(sd);
+ /* Update limits and set FPS to default */
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(og01a1b->vblank,
+ mode->vts_min - mode->height,
+ OG01A1B_VTS_MAX - mode->height, 1, vblank_def);
+ __v4l2_ctrl_s_ctrl(og01a1b->vblank, vblank_def);
+ h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index,
+ bpp) - mode->width;
+ __v4l2_ctrl_modify_range(og01a1b->hblank, h_blank, h_blank, 1, h_blank);
- mutex_lock(&og01a1b->mutex);
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- fmt->format = *v4l2_subdev_state_get_format(sd_state,
- fmt->pad);
- else
- og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format);
+ og01a1b->cur_mode = mode;
+ og01a1b->code = fmt->format.code;
- mutex_unlock(&og01a1b->mutex);
+set_format:
+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
return 0;
}
@@ -821,10 +722,10 @@ static int og01a1b_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->index > 0)
+ if (code->index > ARRAY_SIZE(og01a1b_mbus_formats) - 1)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_Y10_1X10;
+ code->code = og01a1b_mbus_formats[code->index];
return 0;
}
@@ -836,7 +737,8 @@ static int og01a1b_enum_frame_size(struct v4l2_subdev *sd,
if (fse->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_Y10_1X10)
+ if (fse->code != MEDIA_BUS_FMT_Y10_1X10 &&
+ fse->code != MEDIA_BUS_FMT_Y8_1X8)
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
@@ -847,27 +749,36 @@ static int og01a1b_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+static int og01a1b_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
{
struct og01a1b *og01a1b = to_og01a1b(sd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ .width = og01a1b->cur_mode->width,
+ .height = og01a1b->cur_mode->height,
+ .code = og01a1b->code,
+ },
+ };
- mutex_lock(&og01a1b->mutex);
- og01a1b_update_pad_format(&supported_modes[0],
- v4l2_subdev_state_get_format(fh->state, 0));
- mutex_unlock(&og01a1b->mutex);
+ og01a1b_set_format(sd, state, &fmt);
return 0;
}
static const struct v4l2_subdev_video_ops og01a1b_video_ops = {
- .s_stream = og01a1b_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops og01a1b_pad_ops = {
.set_fmt = og01a1b_set_format,
- .get_fmt = og01a1b_get_format,
+ .get_fmt = v4l2_subdev_get_fmt,
.enum_mbus_code = og01a1b_enum_mbus_code,
.enum_frame_size = og01a1b_enum_frame_size,
+ .enable_streams = og01a1b_enable_streams,
+ .disable_streams = og01a1b_disable_streams,
};
static const struct v4l2_subdev_ops og01a1b_subdev_ops = {
@@ -880,21 +791,20 @@ static const struct media_entity_operations og01a1b_subdev_entity_ops = {
};
static const struct v4l2_subdev_internal_ops og01a1b_internal_ops = {
- .open = og01a1b_open,
+ .init_state = og01a1b_init_state,
};
static int og01a1b_identify_module(struct og01a1b *og01a1b)
{
int ret;
- u32 val;
+ u64 val;
- ret = og01a1b_read_reg(og01a1b, OG01A1B_REG_CHIP_ID,
- OG01A1B_REG_VALUE_24BIT, &val);
+ ret = cci_read(og01a1b->regmap, OG01A1B_REG_CHIP_ID, &val, NULL);
if (ret)
return ret;
if (val != OG01A1B_CHIP_ID) {
- dev_err(og01a1b->dev, "chip id mismatch: %x!=%x",
+ dev_err(og01a1b->dev, "chip id mismatch: %x!=%llx",
OG01A1B_CHIP_ID, val);
return -ENXIO;
}
@@ -1042,10 +952,10 @@ static void og01a1b_remove(struct i2c_client *client)
struct og01a1b *og01a1b = to_og01a1b(sd);
v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(&og01a1b->sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(og01a1b->dev);
- mutex_destroy(&og01a1b->mutex);
}
static int og01a1b_probe(struct i2c_client *client)
@@ -1062,6 +972,11 @@ static int og01a1b_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops);
+ og01a1b->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(og01a1b->regmap))
+ return dev_err_probe(og01a1b->dev, PTR_ERR(og01a1b->regmap),
+ "failed to init CCI\n");
+
og01a1b->xvclk = devm_v4l2_sensor_clk_get(og01a1b->dev, NULL);
if (IS_ERR(og01a1b->xvclk))
return dev_err_probe(og01a1b->dev, PTR_ERR(og01a1b->xvclk),
@@ -1134,14 +1049,15 @@ static int og01a1b_probe(struct i2c_client *client)
goto power_off;
}
- mutex_init(&og01a1b->mutex);
og01a1b->cur_mode = &supported_modes[0];
+ og01a1b->code = og01a1b_mbus_formats[0];
ret = og01a1b_init_controls(og01a1b);
if (ret) {
dev_err(og01a1b->dev, "failed to init controls: %d", ret);
goto probe_error_v4l2_ctrl_handler_free;
}
+ og01a1b->sd.state_lock = og01a1b->ctrl_handler.lock;
og01a1b->sd.internal_ops = &og01a1b_internal_ops;
og01a1b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
og01a1b->sd.entity.ops = &og01a1b_subdev_entity_ops;
@@ -1153,11 +1069,18 @@ static int og01a1b_probe(struct i2c_client *client)
goto probe_error_v4l2_ctrl_handler_free;
}
+ ret = v4l2_subdev_init_finalize(&og01a1b->sd);
+ if (ret < 0) {
+ dev_err_probe(og01a1b->dev, ret,
+ "failed to finalize subdevice init\n");
+ goto probe_error_media_entity_cleanup;
+ }
+
ret = v4l2_async_register_subdev_sensor(&og01a1b->sd);
if (ret < 0) {
dev_err(og01a1b->dev, "failed to register V4L2 subdev: %d",
ret);
- goto probe_error_media_entity_cleanup;
+ goto probe_error_v4l2_subdev_cleanup;
}
/* Enable runtime PM and turn off the device */
@@ -1167,12 +1090,14 @@ static int og01a1b_probe(struct i2c_client *client)
return 0;
+probe_error_v4l2_subdev_cleanup:
+ v4l2_subdev_cleanup(&og01a1b->sd);
+
probe_error_media_entity_cleanup:
media_entity_cleanup(&og01a1b->sd.entity);
probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(og01a1b->sd.ctrl_handler);
- mutex_destroy(&og01a1b->mutex);
power_off:
og01a1b_power_off(og01a1b->dev);
diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c
index 70d9d7c43f18..143dcfe10445 100644
--- a/drivers/media/i2c/ov02a10.c
+++ b/drivers/media/i2c/ov02a10.c
@@ -998,6 +998,6 @@ static struct i2c_driver ov02a10_i2c_driver = {
};
module_i2c_driver(ov02a10_i2c_driver);
-MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>");
+MODULE_AUTHOR("Dongchun Zhu");
MODULE_DESCRIPTION("OmniVision OV02A10 sensor driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c
index 43ec2a1f2fcf..9adef5446a61 100644
--- a/drivers/media/i2c/ov08d10.c
+++ b/drivers/media/i2c/ov08d10.c
@@ -8,12 +8,12 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#define OV08D10_SCLK 144000000ULL
-#define OV08D10_XVCLK_19_2 19200000
#define OV08D10_ROWCLK 36000
#define OV08D10_DATA_LANES 2
#define OV08D10_RGB_DEPTH 10
@@ -77,8 +77,13 @@ struct ov08d10_reg_list {
const struct ov08d10_reg *regs;
};
+static const u32 ov08d10_xvclk_freqs[] = {
+ 19200000,
+ 24000000
+};
+
struct ov08d10_link_freq_config {
- const struct ov08d10_reg_list reg_list;
+ const struct ov08d10_reg_list reg_list[ARRAY_SIZE(ov08d10_xvclk_freqs)];
};
struct ov08d10_mode {
@@ -88,13 +93,13 @@ struct ov08d10_mode {
/* Frame height in pixels */
u32 height;
- /* Horizontal timining size */
+ /* Horizontal timing size */
u32 hts;
- /* Default vertical timining size */
+ /* Default vertical timing size */
u32 vts_def;
- /* Min vertical timining size */
+ /* Min vertical timing size */
u32 vts_min;
/* Link frequency needed for this resolution */
@@ -107,8 +112,8 @@ struct ov08d10_mode {
u8 data_lanes;
};
-/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes */
-static const struct ov08d10_reg mipi_data_rate_720mbps[] = {
+/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 19.2 MHz */
+static const struct ov08d10_reg mipi_data_rate_720mbps_19_2[] = {
{0xfd, 0x00},
{0x11, 0x2a},
{0x14, 0x43},
@@ -118,8 +123,8 @@ static const struct ov08d10_reg mipi_data_rate_720mbps[] = {
{0xb7, 0x02}
};
-/* 1632x1224 needs 360Mbps/lane, 2 lanes */
-static const struct ov08d10_reg mipi_data_rate_360mbps[] = {
+/* 1632x1224 needs 360Mbps/lane, 2 lanes - 19.2 MHz */
+static const struct ov08d10_reg mipi_data_rate_360mbps_19_2[] = {
{0xfd, 0x00},
{0x1a, 0x04},
{0x1b, 0xe1},
@@ -131,6 +136,30 @@ static const struct ov08d10_reg mipi_data_rate_360mbps[] = {
{0xb7, 0x02}
};
+/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 24 MHz */
+static const struct ov08d10_reg mipi_data_rate_720mbps_24_0[] = {
+ {0xfd, 0x00},
+ {0x11, 0x2a},
+ {0x14, 0x43},
+ {0x1a, 0x04},
+ {0x1b, 0xb4},
+ {0x1e, 0x13},
+ {0xb7, 0x02}
+};
+
+/* 1632x1224 needs 360Mbps/lane, 2 lanes - 24 MHz */
+static const struct ov08d10_reg mipi_data_rate_360mbps_24_0[] = {
+ {0xfd, 0x00},
+ {0x1a, 0x04},
+ {0x1b, 0xb4},
+ {0x1d, 0x00},
+ {0x1c, 0x19},
+ {0x11, 0x2a},
+ {0x14, 0x54},
+ {0x1e, 0x13},
+ {0xb7, 0x02}
+};
+
static const struct ov08d10_reg lane_2_mode_3280x2460[] = {
/* 3280x2460 resolution */
{0xfd, 0x01},
@@ -217,7 +246,7 @@ static const struct ov08d10_reg lane_2_mode_3280x2460[] = {
{0x9a, 0x30},
{0xa8, 0x02},
{0xfd, 0x02},
- {0xa1, 0x01},
+ {0xa1, 0x00},
{0xa2, 0x09},
{0xa3, 0x9c},
{0xa5, 0x00},
@@ -335,7 +364,7 @@ static const struct ov08d10_reg lane_2_mode_3264x2448[] = {
{0x9a, 0x30},
{0xa8, 0x02},
{0xfd, 0x02},
- {0xa1, 0x09},
+ {0xa1, 0x08},
{0xa2, 0x09},
{0xa3, 0x90},
{0xa5, 0x08},
@@ -381,7 +410,6 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = {
{0x07, 0x05},
{0x21, 0x02},
{0x24, 0x30},
- {0x33, 0x03},
{0x31, 0x06},
{0x33, 0x03},
{0x01, 0x03},
@@ -467,7 +495,7 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = {
{0xaa, 0xd0},
{0xab, 0x06},
{0xac, 0x68},
- {0xa1, 0x09},
+ {0xa1, 0x04},
{0xa2, 0x04},
{0xa3, 0xc8},
{0xa5, 0x04},
@@ -514,9 +542,18 @@ static const char * const ov08d10_test_pattern_menu[] = {
"Standard Color Bar",
};
+static const char *const ov08d10_supply_names[] = {
+ "dovdd", /* Digital I/O power */
+ "avdd", /* Analog power */
+ "dvdd", /* Digital core power */
+};
+
struct ov08d10 {
struct device *dev;
struct clk *clk;
+ struct reset_control *reset;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov08d10_supply_names)];
+ u8 xvclk_index;
struct v4l2_subdev sd;
struct media_pad pad;
@@ -534,7 +571,7 @@ struct ov08d10 {
/* Current mode */
const struct ov08d10_mode *cur_mode;
- /* To serialize asynchronus callbacks */
+ /* To serialize asynchronous callbacks */
struct mutex mutex;
/* lanes index */
@@ -557,17 +594,29 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = {
},
{{
.reg_list = {
+ {
.num_of_regs =
- ARRAY_SIZE(mipi_data_rate_720mbps),
- .regs = mipi_data_rate_720mbps,
- }
+ ARRAY_SIZE(mipi_data_rate_720mbps_19_2),
+ .regs = mipi_data_rate_720mbps_19_2,
+ },
+ {
+ .num_of_regs =
+ ARRAY_SIZE(mipi_data_rate_720mbps_24_0),
+ .regs = mipi_data_rate_720mbps_24_0,
+ }}
},
{
.reg_list = {
+ {
.num_of_regs =
- ARRAY_SIZE(mipi_data_rate_360mbps),
- .regs = mipi_data_rate_360mbps,
- }
+ ARRAY_SIZE(mipi_data_rate_360mbps_19_2),
+ .regs = mipi_data_rate_360mbps_19_2,
+ },
+ {
+ .num_of_regs =
+ ARRAY_SIZE(mipi_data_rate_360mbps_24_0),
+ .regs = mipi_data_rate_360mbps_24_0,
+ }}
}},
{{
.width = 3280,
@@ -613,8 +662,8 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = {
static u32 ov08d10_get_format_code(struct ov08d10 *ov08d10)
{
static const u32 codes[2][2] = {
- { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10},
- { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10},
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10 },
};
return codes[ov08d10->vflip->val][ov08d10->hflip->val];
@@ -665,7 +714,7 @@ static int ov08d10_write_reg_list(struct ov08d10 *ov08d10,
r_list->regs[i].val);
if (ret) {
dev_err_ratelimited(ov08d10->dev,
- "failed to write reg 0x%2.2x. error = %d",
+ "failed to write reg 0x%2.2x. error = %d\n",
r_list->regs[i].address, ret);
return ret;
}
@@ -864,7 +913,7 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl)
exposure_max);
}
- /* V4L2 controls values will be applied only when power is already up */
+ /* V4L2 control values will be applied only when power is already up */
if (!pm_runtime_get_if_in_use(ov08d10->dev))
return 0;
@@ -1020,37 +1069,38 @@ static int ov08d10_start_streaming(struct ov08d10 *ov08d10)
link_freq_index = ov08d10->cur_mode->link_freq_index;
reg_list =
- &ov08d10->priv_lane->link_freq_configs[link_freq_index].reg_list;
+ &ov08d10->priv_lane->link_freq_configs[link_freq_index]
+ .reg_list[ov08d10->xvclk_index];
/* soft reset */
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to reset sensor");
+ dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to reset sensor");
+ dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
usleep_range(3000, 4000);
ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to reset sensor");
+ dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
/* update sensor setting */
ret = ov08d10_write_reg_list(ov08d10, reg_list);
if (ret) {
- dev_err(ov08d10->dev, "failed to set plls");
+ dev_err(ov08d10->dev, "failed to set plls\n");
return ret;
}
reg_list = &ov08d10->cur_mode->reg_list;
ret = ov08d10_write_reg_list(ov08d10, reg_list);
if (ret) {
- dev_err(ov08d10->dev, "failed to set mode");
+ dev_err(ov08d10->dev, "failed to set mode\n");
return ret;
}
@@ -1077,19 +1127,19 @@ static void ov08d10_stop_streaming(struct ov08d10 *ov08d10)
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to stop streaming");
+ dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT,
OV08D10_MODE_STANDBY);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to stop streaming");
+ dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to stop streaming");
+ dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
}
@@ -1266,6 +1316,56 @@ static const struct v4l2_subdev_internal_ops ov08d10_internal_ops = {
.open = ov08d10_open,
};
+static int ov08d10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov08d10 *ov08d10 = to_ov08d10(sd);
+
+ reset_control_assert(ov08d10->reset);
+
+ regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies),
+ ov08d10->supplies);
+
+ clk_disable_unprepare(ov08d10->clk);
+
+ return 0;
+}
+
+static int ov08d10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov08d10 *ov08d10 = to_ov08d10(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov08d10->supplies),
+ ov08d10->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ov08d10->clk);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies),
+ ov08d10->supplies);
+
+ dev_err(dev, "failed to enable imaging clock: %d\n", ret);
+ return ret;
+ }
+
+ if (ov08d10->reset) {
+ /* Delay from DVDD stable to sensor XSHUTDN pull up: 5ms */
+ fsleep(5 * USEC_PER_MSEC);
+
+ reset_control_deassert(ov08d10->reset);
+
+ /* Delay from XSHUTDN pull up to SCCB start: 8ms */
+ fsleep(8 * USEC_PER_MSEC);
+ }
+
+ return 0;
+}
+
static int ov08d10_identify_module(struct ov08d10 *ov08d10)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
@@ -1325,7 +1425,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
/* Get number of data lanes */
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
- dev_err(dev, "number of CSI2 data lanes %d is not supported",
+ dev_err(dev, "number of CSI2 data lanes %d is not supported\n",
bus_cfg.bus.mipi_csi2.num_data_lanes);
ret = -EINVAL;
goto check_hwcfg_error;
@@ -1337,7 +1437,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
ov08d10->modes_size = ov08d10_modes_num(ov08d10);
if (!bus_cfg.nr_of_link_frequencies) {
- dev_err(dev, "no link frequencies defined");
+ dev_err(dev, "no link frequencies defined\n");
ret = -EINVAL;
goto check_hwcfg_error;
}
@@ -1350,7 +1450,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
}
if (j == bus_cfg.nr_of_link_frequencies) {
- dev_err(dev, "no link frequency %lld supported",
+ dev_err(dev, "no link frequency %lld supported\n",
ov08d10->priv_lane->link_freq_menu[i]);
ret = -EINVAL;
goto check_hwcfg_error;
@@ -1372,6 +1472,10 @@ static void ov08d10_remove(struct i2c_client *client)
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(ov08d10->dev);
+ if (!pm_runtime_status_suspended(ov08d10->dev)) {
+ ov08d10_power_off(ov08d10->dev);
+ pm_runtime_set_suspended(ov08d10->dev);
+ }
mutex_destroy(&ov08d10->mutex);
}
@@ -1379,6 +1483,7 @@ static int ov08d10_probe(struct i2c_client *client)
{
struct ov08d10 *ov08d10;
unsigned long freq;
+ unsigned int i;
int ret;
ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL);
@@ -1393,30 +1498,56 @@ static int ov08d10_probe(struct i2c_client *client)
"failed to get clock\n");
freq = clk_get_rate(ov08d10->clk);
- if (freq != OV08D10_XVCLK_19_2)
- dev_warn(ov08d10->dev,
- "external clock rate %lu is not supported\n", freq);
+ for (i = 0; i < ARRAY_SIZE(ov08d10_xvclk_freqs); i++) {
+ if (freq == ov08d10_xvclk_freqs[i])
+ break;
+ }
+ if (i >= ARRAY_SIZE(ov08d10_xvclk_freqs))
+ return dev_err_probe(ov08d10->dev, -EINVAL,
+ "external clock rate %lu is not supported\n",
+ freq);
+ ov08d10->xvclk_index = i;
ret = ov08d10_get_hwcfg(ov08d10);
if (ret) {
- dev_err(ov08d10->dev, "failed to get HW configuration: %d",
+ dev_err(ov08d10->dev, "failed to get HW configuration: %d\n",
ret);
return ret;
}
+ ov08d10->reset = devm_reset_control_get_optional_exclusive(ov08d10->dev, NULL);
+ if (IS_ERR(ov08d10->reset))
+ return dev_err_probe(ov08d10->dev, PTR_ERR(ov08d10->reset),
+ "failed to get reset\n");
+ reset_control_assert(ov08d10->reset);
+
+ for (i = 0; i < ARRAY_SIZE(ov08d10_supply_names); i++)
+ ov08d10->supplies[i].supply = ov08d10_supply_names[i];
+
+ ret = devm_regulator_bulk_get(ov08d10->dev,
+ ARRAY_SIZE(ov08d10->supplies),
+ ov08d10->supplies);
+ if (ret)
+ return dev_err_probe(ov08d10->dev, ret,
+ "failed to get regulators\n");
+
v4l2_i2c_subdev_init(&ov08d10->sd, client, &ov08d10_subdev_ops);
+ ret = ov08d10_power_on(ov08d10->dev);
+ if (ret)
+ return dev_err_probe(ov08d10->dev, ret, "failed to power on\n");
+
ret = ov08d10_identify_module(ov08d10);
if (ret) {
- dev_err(ov08d10->dev, "failed to find sensor: %d", ret);
- return ret;
+ dev_err(ov08d10->dev, "failed to find sensor: %d\n", ret);
+ goto probe_error_power_off;
}
mutex_init(&ov08d10->mutex);
ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0];
ret = ov08d10_init_controls(ov08d10);
if (ret) {
- dev_err(ov08d10->dev, "failed to init controls: %d", ret);
+ dev_err(ov08d10->dev, "failed to init controls: %d\n", ret);
goto probe_error_v4l2_ctrl_handler_free;
}
@@ -1426,37 +1557,42 @@ static int ov08d10_probe(struct i2c_client *client)
ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad);
if (ret) {
- dev_err(ov08d10->dev, "failed to init entity pads: %d", ret);
+ dev_err(ov08d10->dev, "failed to init entity pads: %d\n", ret);
goto probe_error_v4l2_ctrl_handler_free;
}
+ pm_runtime_set_active(ov08d10->dev);
+ pm_runtime_enable(ov08d10->dev);
+
ret = v4l2_async_register_subdev_sensor(&ov08d10->sd);
if (ret < 0) {
- dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d",
+ dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d\n",
ret);
goto probe_error_media_entity_cleanup;
}
- /*
- * Device is already turned on by i2c-core with ACPI domain PM.
- * Enable runtime PM and turn off the device.
- */
- pm_runtime_set_active(ov08d10->dev);
- pm_runtime_enable(ov08d10->dev);
pm_runtime_idle(ov08d10->dev);
return 0;
probe_error_media_entity_cleanup:
+ pm_runtime_disable(ov08d10->dev);
+ pm_runtime_set_suspended(ov08d10->dev);
media_entity_cleanup(&ov08d10->sd.entity);
probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(ov08d10->sd.ctrl_handler);
mutex_destroy(&ov08d10->mutex);
+probe_error_power_off:
+ ov08d10_power_off(ov08d10->dev);
+
return ret;
}
+static DEFINE_RUNTIME_DEV_PM_OPS(ov08d10_pm_ops,
+ ov08d10_power_off, ov08d10_power_on, NULL);
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id ov08d10_acpi_ids[] = {
{ "OVTI08D1" },
@@ -1466,10 +1602,18 @@ static const struct acpi_device_id ov08d10_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov08d10_acpi_ids);
#endif
+static const struct of_device_id ov08d10_of_match[] = {
+ { .compatible = "ovti,ov08d10" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov08d10_of_match);
+
static struct i2c_driver ov08d10_i2c_driver = {
.driver = {
.name = "ov08d10",
+ .pm = pm_ptr(&ov08d10_pm_ops),
.acpi_match_table = ACPI_PTR(ov08d10_acpi_ids),
+ .of_match_table = ov08d10_of_match,
},
.probe = ov08d10_probe,
.remove = ov08d10_remove,
diff --git a/drivers/media/i2c/ov2732.c b/drivers/media/i2c/ov2732.c
new file mode 100644
index 000000000000..40035320fec6
--- /dev/null
+++ b/drivers/media/i2c/ov2732.c
@@ -0,0 +1,790 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ov2732 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2025-2026 Walter Werner Schneider <contact@schnwalter.eu>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define OV2732_LANES 2
+#define OV2732_BITS_PER_SAMPLE 10
+#define OV2732_LINK_FREQ_DEFAULT 360000000
+#define OV2732_XVCLK_FREQ 24000000
+#define OV2732_PIXEL_RATE \
+ (OV2732_LINK_FREQ_DEFAULT * 2 * OV2732_LANES / OV2732_BITS_PER_SAMPLE)
+#define OV2732_NATIVE_WIDTH 1920U
+#define OV2732_NATIVE_HEIGHT 1080U
+
+/* Delay from power up to the first SCCB transaction. */
+#define OV2732_POWER_UP_DELAY_CYCLES 8192
+/* Delay from the last SCCB transaction to power down. */
+#define OV2732_POWER_DOWN_DELAY_CYCLES 512
+#define OV2732_DELAY_US(cycles) \
+ (DIV_ROUND_UP((cycles), OV2732_XVCLK_FREQ / USEC_PER_SEC))
+
+#define OV2732_REG_CHIP_ID CCI_REG24(0x300a)
+#define OV2732_CHIP_ID 0x002732
+
+#define OV2732_REG_MODE_SELECT CCI_REG8(0x0100)
+#define OV2732_MODE_STANDBY 0x00
+#define OV2732_MODE_STREAMING 0x01
+
+#define OV2732_REG_ANALOGUE_GAIN CCI_REG16(0x3508)
+#define OV2732_REG_DIGITAL_GAIN CCI_REG16(0x350a)
+#define OV2732_REG_EXPOSURE CCI_REG24(0x3500)
+#define OV2732_REG_HTS CCI_REG16(0x380c)
+#define OV2732_REG_VTS CCI_REG16(0x380e)
+#define OV2732_ANALOGUE_GAIN_MIN 0x80
+/* Max analogue gain is documented as 0x3fff, but it overflows after 0x3ff. */
+#define OV2732_ANALOGUE_GAIN_MAX 0x3ff
+#define OV2732_ANALOGUE_GAIN_DEFAULT 0x80
+#define OV2732_DIGITAL_GAIN_MIN 0x00
+#define OV2732_DIGITAL_GAIN_MAX 0x3fff
+#define OV2732_DIGITAL_GAIN_DEFAULT 0x0400
+#define OV2732_EXPOSURE_DEFAULT 0x40
+#define OV2732_EXPOSURE_MIN 0x10
+#define OV2732_EXPOSURE_OFFSET 4
+#define OV2732_HBLANK_DEFAULT 0x0068
+#define OV2732_VTS_MAX 0x7fff
+
+#define OV2732_REG_TEST_PATTERN CCI_REG8(0x5080)
+#define OV2732_TEST_PATTERN_DISABLE 0x00
+#define OV2732_TEST_PATTERN_BAR1 0x80
+#define OV2732_TEST_PATTERN_BAR2 0x84
+#define OV2732_TEST_PATTERN_BAR3 0x88
+#define OV2732_TEST_PATTERN_BAR4 0x8c
+#define OV2732_TEST_PATTERN_BAR5 0xC0
+#define OV2732_TEST_PATTERN_RANDOM 0x81
+#define OV2732_TEST_PATTERN_SQUARES_C 0x82
+#define OV2732_TEST_PATTERN_SQUARES_BW 0x92
+
+static const char * const ov2732_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+static const struct cci_reg_sequence ov2732_common_regs[] = {
+ /* PLL control, reset all registers. */
+ { CCI_REG8(0x0103), 0x01 },
+
+ /* Analog control. */
+ { CCI_REG8(0x3600), 0x55 },
+ { CCI_REG8(0x3601), 0x52 },
+ { CCI_REG8(0x3612), 0xb5 },
+ { CCI_REG8(0x3613), 0xb3 },
+ { CCI_REG8(0x3616), 0x83 },
+ { CCI_REG8(0x3621), 0x00 },
+ { CCI_REG8(0x3624), 0x06 },
+ { CCI_REG8(0x3642), 0x88 },
+ { CCI_REG8(0x3660), 0x00 },
+ { CCI_REG8(0x3661), 0x00 },
+ { CCI_REG8(0x366a), 0x64 },
+ { CCI_REG8(0x366c), 0x00 },
+ { CCI_REG8(0x366e), 0xff },
+ { CCI_REG8(0x366f), 0xff },
+ { CCI_REG8(0x3677), 0x11 },
+ { CCI_REG8(0x3678), 0x11 },
+ { CCI_REG8(0x3679), 0x0c },
+ { CCI_REG8(0x3680), 0xff },
+ { CCI_REG8(0x3681), 0x16 },
+ { CCI_REG8(0x3682), 0x16 },
+ { CCI_REG8(0x3683), 0x90 },
+ { CCI_REG8(0x3684), 0x90 },
+
+ /* ADC sync control. */
+ { CCI_REG8(0x4503), 0x00 },
+ { CCI_REG8(0x4508), 0x14 },
+ { CCI_REG8(0x450a), 0x00 },
+ { CCI_REG8(0x450b), 0x40 },
+
+ /* ISP control, enable: WIN, DPC & ISP. */
+ { CCI_REG8(0x5000), 0xa1 },
+};
+
+struct ov2732_mode {
+ u32 width;
+ u32 height;
+ u32 vts;
+};
+
+static const struct ov2732_mode supported_modes[] = {
+ {
+ .width = 1920,
+ .height = 1080,
+ .vts = 1184,
+ },
+};
+
+struct ov2732 {
+ struct device *dev;
+ struct regmap *regmap;
+
+ struct media_pad pad;
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
+
+ struct clk *xvclk;
+ u32 xvclk_freq;
+ struct gpio_desc *powerdown_gpio;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov2732_supply_names)];
+};
+
+#define to_ov2732(_sd) container_of(_sd, struct ov2732, sd)
+
+static const s64 link_freq_menu_items[] = {
+ OV2732_LINK_FREQ_DEFAULT,
+};
+
+static const char * const ov2732_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4",
+ "Vertical Color Bar Type 5",
+ "Random",
+ "Color Squares",
+ "Black and White Squares",
+};
+
+static const int ov2732_test_pattern_val[] = {
+ OV2732_TEST_PATTERN_DISABLE,
+ OV2732_TEST_PATTERN_BAR1,
+ OV2732_TEST_PATTERN_BAR2,
+ OV2732_TEST_PATTERN_BAR3,
+ OV2732_TEST_PATTERN_BAR4,
+ OV2732_TEST_PATTERN_BAR5,
+ OV2732_TEST_PATTERN_RANDOM,
+ OV2732_TEST_PATTERN_SQUARES_C,
+ OV2732_TEST_PATTERN_SQUARES_BW,
+};
+
+static int ov2732_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov2732 *ov2732 = to_ov2732(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov2732_supply_names),
+ ov2732->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ov2732->xvclk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock\n");
+ goto reg_off;
+ }
+
+ /* Wait 10ms before power up, as per datasheet. */
+ fsleep(10 * USEC_PER_MSEC);
+
+ gpiod_set_value_cansleep(ov2732->reset_gpio, 0);
+ gpiod_set_value_cansleep(ov2732->powerdown_gpio, 0);
+
+ /* Datasheet requires an 8192 cycles wait, but that isn't enough. */
+ fsleep(OV2732_DELAY_US(OV2732_POWER_UP_DELAY_CYCLES * 2));
+
+ return 0;
+
+reg_off:
+ regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names),
+ ov2732->supplies);
+
+ return ret;
+}
+
+static int ov2732_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov2732 *ov2732 = to_ov2732(sd);
+
+ clk_disable_unprepare(ov2732->xvclk);
+
+ /* Wait for 512 cycles as per datasheet. */
+ fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES));
+
+ gpiod_set_value_cansleep(ov2732->powerdown_gpio, 1);
+ gpiod_set_value_cansleep(ov2732->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names),
+ ov2732->supplies);
+
+ return 0;
+}
+
+static int ov2732_identify_chip(struct ov2732 *ov2732)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
+ int ret;
+ u64 val;
+
+ ret = cci_read(ov2732->regmap, OV2732_REG_CHIP_ID, &val, NULL);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to read chip id\n");
+
+ if (val != OV2732_CHIP_ID)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "chip id mismatch: %x!=%llx\n",
+ OV2732_CHIP_ID, val);
+
+ return 0;
+}
+
+static int ov2732_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov2732_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int ov2732_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2732 *ov2732 = to_ov2732(sd);
+ const struct ov2732_mode *mode;
+ s64 vblank_def;
+ int ret;
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+ *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ vblank_def = mode->vts - mode->height;
+ ret = __v4l2_ctrl_modify_range(ov2732->vblank, vblank_def,
+ OV2732_VTS_MAX - mode->height, 1,
+ vblank_def);
+
+ return ret;
+}
+
+static int ov2732_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, 0);
+ return 0;
+
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV2732_NATIVE_WIDTH;
+ sel->r.height = OV2732_NATIVE_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ov2732_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct ov2732 *ov2732 = to_ov2732(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Set stream off register for PLL changes. */
+ ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
+ OV2732_MODE_STANDBY, NULL);
+ if (ret)
+ goto err_put_autosuspend;
+
+ /* Send all registers that are common to all modes */
+ ret = cci_multi_reg_write(ov2732->regmap, ov2732_common_regs,
+ ARRAY_SIZE(ov2732_common_regs), NULL);
+ if (ret) {
+ dev_err(&client->dev, "failed to init registers\n");
+ goto err_put_autosuspend;
+ }
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(ov2732->sd.ctrl_handler);
+ if (ret)
+ goto err_put_autosuspend;
+
+ /* Set stream on register */
+ ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
+ OV2732_MODE_STREAMING, NULL);
+ if (ret)
+ goto err_put_autosuspend;
+
+ return 0;
+
+err_put_autosuspend:
+ pm_runtime_put_autosuspend(&client->dev);
+
+ return ret;
+}
+
+static int ov2732_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct ov2732 *ov2732 = to_ov2732(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
+ int ret;
+
+ /* set stream off register */
+ ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
+ OV2732_MODE_STANDBY, NULL);
+ if (ret)
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
+
+ /* Wait for 512 cycles as per datasheet. */
+ fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES));
+
+ pm_runtime_put_autosuspend(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops ov2732_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops ov2732_pad_ops = {
+ .enum_mbus_code = ov2732_enum_mbus_code,
+ .enum_frame_size = ov2732_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ov2732_set_fmt,
+ .get_selection = ov2732_get_selection,
+ .enable_streams = ov2732_enable_streams,
+ .disable_streams = ov2732_disable_streams,
+};
+
+static const struct v4l2_subdev_ops ov2732_subdev_ops = {
+ .video = &ov2732_video_ops,
+ .pad = &ov2732_pad_ops,
+};
+
+static int ov2732_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = 1920,
+ .height = 1080,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .field = V4L2_FIELD_NONE,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ }
+ };
+
+ return ov2732_set_fmt(sd, sd_state, &fmt);
+}
+
+static const struct v4l2_subdev_internal_ops ov2732_internal_ops = {
+ .init_state = ov2732_init_state,
+};
+
+static int ov2732_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov2732 *ov2732 =
+ container_of(ctrl->handler, struct ov2732, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_get_locked_active_state(&ov2732->sd);
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ int exposure_max, exposure_def;
+
+ exposure_max = format->height + ctrl->val -
+ OV2732_EXPOSURE_OFFSET;
+ exposure_def = exposure_max;
+ ret = __v4l2_ctrl_modify_range(ov2732->exposure,
+ OV2732_EXPOSURE_MIN,
+ exposure_max,
+ ov2732->exposure->step,
+ exposure_def);
+ if (ret)
+ return ret;
+ }
+
+ if (pm_runtime_get_if_in_use(&client->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ cci_write(ov2732->regmap, OV2732_REG_ANALOGUE_GAIN,
+ ctrl->val, &ret);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ cci_write(ov2732->regmap, OV2732_REG_DIGITAL_GAIN,
+ ctrl->val, &ret);
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* Lowest 4 bits are fraction bits. */
+ cci_write(ov2732->regmap, OV2732_REG_EXPOSURE,
+ (u32)ctrl->val << 4, &ret);
+ break;
+ case V4L2_CID_VBLANK:
+ cci_write(ov2732->regmap, OV2732_REG_VTS,
+ format->height + ctrl->val, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ cci_write(ov2732->regmap, OV2732_REG_TEST_PATTERN,
+ ov2732_test_pattern_val[ctrl->val], &ret);
+ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+ ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+};
+
+static const struct v4l2_ctrl_ops ov2732_ctrl_ops = {
+ .s_ctrl = ov2732_set_ctrl,
+};
+
+static int ov2732_init_controls(struct ov2732 *ov2732)
+{
+ const struct ov2732_mode *mode = &supported_modes[0];
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ struct v4l2_fwnode_device_properties props;
+ s64 exposure_max, vblank_def, vblank_max;
+ int ret;
+
+ handler = &ov2732->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(handler, 10);
+
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ OV2732_PIXEL_RATE, OV2732_PIXEL_RATE,
+ 1, OV2732_PIXEL_RATE);
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, &ov2732_ctrl_ops,
+ V4L2_CID_LINK_FREQ, 0, 0,
+ link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov2732->hblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
+ V4L2_CID_HBLANK,
+ OV2732_HBLANK_DEFAULT,
+ OV2732_HBLANK_DEFAULT,
+ 1, OV2732_HBLANK_DEFAULT);
+ if (ov2732->hblank)
+ ov2732->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts - mode->height;
+ vblank_max = OV2732_VTS_MAX - mode->height;
+ ov2732->vblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
+ V4L2_CID_VBLANK,
+ vblank_def, vblank_max,
+ 1, vblank_def);
+
+ exposure_max = mode->vts - OV2732_EXPOSURE_OFFSET;
+ ov2732->exposure = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV2732_EXPOSURE_MIN, exposure_max,
+ 1, OV2732_EXPOSURE_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ OV2732_ANALOGUE_GAIN_MIN, OV2732_ANALOGUE_GAIN_MAX,
+ 1, OV2732_ANALOGUE_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV2732_DIGITAL_GAIN_MIN, OV2732_DIGITAL_GAIN_MAX,
+ 1, OV2732_DIGITAL_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std_menu_items(handler, &ov2732_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov2732_test_pattern_menu) - 1,
+ 0, 0, ov2732_test_pattern_menu);
+
+ if (handler->error) {
+ ret = handler->error;
+ dev_err_probe(ov2732->dev, ret, "Control init failed\n");
+ goto err_handler_free;
+ }
+
+ ret = v4l2_fwnode_device_parse(ov2732->dev, &props);
+ if (ret)
+ goto err_handler_free;
+
+ ret = v4l2_ctrl_new_fwnode_properties(handler, &ov2732_ctrl_ops, &props);
+ if (ret)
+ goto err_handler_free;
+
+ ov2732->sd.ctrl_handler = handler;
+
+ return 0;
+
+err_handler_free:
+ v4l2_ctrl_handler_free(handler);
+
+ return ret;
+}
+
+static int ov2632_probe_dt(struct ov2732 *ov2732)
+{
+ struct fwnode_handle *ep;
+ struct fwnode_handle *fwnode = dev_fwnode(ov2732->dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ int ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret) {
+ dev_err_probe(ov2732->dev, -EINVAL, "could not parse endpoint\n");
+ goto err_probe_dt;
+ }
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV2732_LANES) {
+ dev_err(ov2732->dev, "only a 2-lane CSI2 config is supported\n");
+ ret = -EINVAL;
+ }
+
+err_probe_dt:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int ov2732_get_regulators(struct ov2732 *ov2732)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(ov2732_supply_names); i++)
+ ov2732->supplies[i].supply = ov2732_supply_names[i];
+
+ return devm_regulator_bulk_get(ov2732->dev,
+ ARRAY_SIZE(ov2732_supply_names),
+ ov2732->supplies);
+}
+
+static int ov2732_probe(struct i2c_client *client)
+{
+ struct ov2732 *ov2732;
+ int ret;
+
+ ov2732 = devm_kzalloc(&client->dev, sizeof(*ov2732), GFP_KERNEL);
+ if (!ov2732)
+ return -ENOMEM;
+
+ ov2732->dev = &client->dev;
+
+ ret = ov2632_probe_dt(ov2732);
+ if (ret)
+ return ret;
+
+ ov2732->xvclk = devm_v4l2_sensor_clk_get(ov2732->dev, NULL);
+ if (IS_ERR(ov2732->xvclk))
+ return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->xvclk),
+ "failed to get xvclk\n");
+
+ ov2732->xvclk_freq = clk_get_rate(ov2732->xvclk);
+ if (ov2732->xvclk_freq != OV2732_XVCLK_FREQ)
+ return dev_err_probe(ov2732->dev, -EINVAL,
+ "xvclk frequency not supported: %dHz\n",
+ ov2732->xvclk_freq);
+
+ ov2732->powerdown_gpio = devm_gpiod_get_optional(ov2732->dev,
+ "powerdown",
+ GPIOD_OUT_HIGH);
+
+ ov2732->reset_gpio = devm_gpiod_get_optional(ov2732->dev, "reset",
+ GPIOD_OUT_HIGH);
+
+ ov2732->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(ov2732->regmap))
+ return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->regmap),
+ "failed to init CCI\n");
+
+ ret = ov2732_get_regulators(ov2732);
+ if (ret)
+ return dev_err_probe(ov2732->dev, ret,
+ "failed to get regulators\n");
+
+ v4l2_i2c_subdev_init(&ov2732->sd, client, &ov2732_subdev_ops);
+
+ /* Device must be powered on for ov2732_identify_chip(). */
+ ret = ov2732_power_on(ov2732->dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(ov2732->dev);
+ pm_runtime_enable(ov2732->dev);
+
+ ret = ov2732_identify_chip(ov2732);
+ if (ret)
+ goto err_power_off;
+
+ ret = ov2732_init_controls(ov2732);
+ if (ret)
+ goto err_power_off;
+
+ /* Initialize subdev */
+ ov2732->sd.internal_ops = &ov2732_internal_ops;
+ ov2732->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov2732->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ ov2732->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&ov2732->sd.entity, 1, &ov2732->pad);
+ if (ret) {
+ dev_err_probe(ov2732->dev, ret, "failed to init entity pads\n");
+ goto error_handler_free;
+ }
+
+ ov2732->sd.state_lock = ov2732->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(&ov2732->sd);
+ if (ret < 0) {
+ dev_err_probe(ov2732->dev, ret, "subdev init error\n");
+ goto err_media_entity;
+ }
+
+ ret = v4l2_async_register_subdev_sensor(&ov2732->sd);
+ if (ret < 0) {
+ dev_err_probe(ov2732->dev, ret,
+ "failed to register sensor sub-device\n");
+ goto err_subdev_cleanup;
+ }
+
+ pm_runtime_set_autosuspend_delay(ov2732->dev, 1000);
+ pm_runtime_use_autosuspend(ov2732->dev);
+ pm_runtime_idle(ov2732->dev);
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&ov2732->sd);
+
+err_media_entity:
+ media_entity_cleanup(&ov2732->sd.entity);
+
+error_handler_free:
+ v4l2_ctrl_handler_free(&ov2732->ctrl_handler);
+
+err_power_off:
+ pm_runtime_disable(ov2732->dev);
+ pm_runtime_set_suspended(ov2732->dev);
+ ov2732_power_off(ov2732->dev);
+
+ return ret;
+}
+
+static void ov2732_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2732 *ov2732 = to_ov2732(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&ov2732->ctrl_handler);
+
+ pm_runtime_disable(ov2732->dev);
+ if (!pm_runtime_status_suspended(ov2732->dev)) {
+ ov2732_power_off(ov2732->dev);
+ pm_runtime_set_suspended(ov2732->dev);
+ }
+}
+
+static const struct of_device_id ov2732_of_match[] = {
+ { .compatible = "ovti,ov2732", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ov2732_of_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ov2732_pm_ops, ov2732_power_off,
+ ov2732_power_on, NULL);
+
+static struct i2c_driver ov2732_i2c_driver = {
+ .driver = {
+ .name = "ov2732",
+ .of_match_table = ov2732_of_match,
+ .pm = pm_sleep_ptr(&ov2732_pm_ops),
+ },
+ .probe = ov2732_probe,
+ .remove = ov2732_remove,
+};
+module_i2c_driver(ov2732_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov2732 sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Walter Werner Schneider <contact@schnwalter.eu>");
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index 6a46ef7233ac..db9bd2892140 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_AUTOGAIN:
/* Non-zero turns on AGC by clearing bit 1.*/
- return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1),
- ctrl->val ? 0 : BIT(1), NULL);
+ ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1),
+ ctrl->val ? 0 : BIT(1), NULL);
break;
case V4L2_CID_EXPOSURE_AUTO:
/*
* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
* clearing bit 0.
*/
- return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0),
- ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL);
+ ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0),
+ ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL);
break;
case V4L2_CID_ANALOGUE_GAIN:
/* 10 bits of gain, 2 in the high register. */
- return cci_write(sensor->regmap, OV5647_REG_GAIN,
- ctrl->val & 0x3ff, NULL);
+ ret = cci_write(sensor->regmap, OV5647_REG_GAIN,
+ ctrl->val & 0x3ff, NULL);
break;
case V4L2_CID_EXPOSURE:
/*
diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c
index ea26df328189..508149485248 100644
--- a/drivers/media/i2c/ov5675.c
+++ b/drivers/media/i2c/ov5675.c
@@ -1181,17 +1181,26 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
if (!fwnode)
return -ENXIO;
+ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
ov5675->xvclk = devm_v4l2_sensor_clk_get(dev, NULL);
- if (IS_ERR(ov5675->xvclk))
- return dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
- "failed to get xvclk: %pe\n",
- ov5675->xvclk);
+ if (IS_ERR(ov5675->xvclk)) {
+ ret = dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
+ "failed to get xvclk\n");
+ goto check_hwcfg_error;
+ }
xvclk_rate = clk_get_rate(ov5675->xvclk);
if (xvclk_rate != OV5675_XVCLK_19_2) {
dev_err(dev, "external clock rate %u is unsupported",
xvclk_rate);
- return -EINVAL;
+ ret = -EINVAL;
+ goto check_hwcfg_error;
}
ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -1199,7 +1208,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
if (IS_ERR(ov5675->reset_gpio)) {
ret = PTR_ERR(ov5675->reset_gpio);
dev_err(dev, "failed to get reset-gpios: %d\n", ret);
- return ret;
+ goto check_hwcfg_error;
}
for (i = 0; i < OV5675_NUM_SUPPLIES; i++)
@@ -1208,16 +1217,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES,
ov5675->supplies);
if (ret)
- return ret;
-
- 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;
+ goto check_hwcfg_error;
if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV5675_DATA_LANES) {
dev_err(dev, "number of CSI2 data lanes %d is not supported",
diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
index e2998cfa0d18..8bedb47cd7cf 100644
--- a/drivers/media/i2c/ov8856.c
+++ b/drivers/media/i2c/ov8856.c
@@ -1887,12 +1887,14 @@ static const struct v4l2_ctrl_ops ov8856_ctrl_ops = {
static int ov8856_init_controls(struct ov8856 *ov8856)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl_handler *ctrl_hdlr;
s64 exposure_max, h_blank;
int ret;
ctrl_hdlr = &ov8856->ctrl_handler;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
@@ -1951,12 +1953,27 @@ static int ov8856_init_controls(struct ov8856 *ov8856)
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- if (ctrl_hdlr->error)
- return ctrl_hdlr->error;
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto err_ctrl_handler_free;
+ }
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto err_ctrl_handler_free;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov8856_ctrl_ops,
+ &props);
+ if (ret)
+ goto err_ctrl_handler_free;
ov8856->sd.ctrl_handler = ctrl_hdlr;
return 0;
+
+err_ctrl_handler_free:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ return ret;
}
static void ov8856_update_pad_format(struct ov8856 *ov8856,
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index ded9b2044ff8..2167fb73ea41 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -12,38 +12,40 @@
#include <linux/math.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
/* Streaming Mode */
-#define OV9282_REG_MODE_SELECT 0x0100
+#define OV9282_REG_MODE_SELECT CCI_REG8(0x0100)
#define OV9282_MODE_STANDBY 0x00
#define OV9282_MODE_STREAMING 0x01
-#define OV9282_REG_PLL_CTRL_0D 0x030d
+#define OV9282_REG_PLL_CTRL_0D CCI_REG8(0x030d)
#define OV9282_PLL_CTRL_0D_RAW8 0x60
#define OV9282_PLL_CTRL_0D_RAW10 0x50
-#define OV9282_REG_TIMING_HTS 0x380c
+#define OV9282_REG_TIMING_HTS CCI_REG16(0x380c)
#define OV9282_TIMING_HTS_MAX 0x7fff
/* Lines per frame */
-#define OV9282_REG_LPFR 0x380e
+#define OV9282_REG_LPFR CCI_REG16(0x380e)
/* Chip ID */
-#define OV9282_REG_ID 0x300a
+#define OV9282_REG_ID CCI_REG16(0x300a)
#define OV9282_ID 0x9281
/* Output enable registers */
-#define OV9282_REG_OUTPUT_ENABLE4 0x3004
+#define OV9282_REG_OUTPUT_ENABLE4 CCI_REG8(0x3004)
#define OV9282_OUTPUT_ENABLE4_GPIO2 BIT(1)
#define OV9282_OUTPUT_ENABLE4_D9 BIT(0)
-#define OV9282_REG_OUTPUT_ENABLE5 0x3005
+#define OV9282_REG_OUTPUT_ENABLE5 CCI_REG8(0x3005)
#define OV9282_OUTPUT_ENABLE5_D8 BIT(7)
#define OV9282_OUTPUT_ENABLE5_D7 BIT(6)
#define OV9282_OUTPUT_ENABLE5_D6 BIT(5)
@@ -53,7 +55,7 @@
#define OV9282_OUTPUT_ENABLE5_D2 BIT(1)
#define OV9282_OUTPUT_ENABLE5_D1 BIT(0)
-#define OV9282_REG_OUTPUT_ENABLE6 0x3006
+#define OV9282_REG_OUTPUT_ENABLE6 CCI_REG8(0x3006)
#define OV9282_OUTPUT_ENABLE6_D0 BIT(7)
#define OV9282_OUTPUT_ENABLE6_PCLK BIT(6)
#define OV9282_OUTPUT_ENABLE6_HREF BIT(5)
@@ -62,14 +64,14 @@
#define OV9282_OUTPUT_ENABLE6_VSYNC BIT(1)
/* Exposure control */
-#define OV9282_REG_EXPOSURE 0x3500
+#define OV9282_REG_EXPOSURE CCI_REG24(0x3500)
#define OV9282_EXPOSURE_MIN 1
#define OV9282_EXPOSURE_OFFSET 25
#define OV9282_EXPOSURE_STEP 1
#define OV9282_EXPOSURE_DEFAULT 0x0282
/* AEC/AGC manual */
-#define OV9282_REG_AEC_MANUAL 0x3503
+#define OV9282_REG_AEC_MANUAL CCI_REG8(0x3503)
#define OV9282_DIGFRAC_GAIN_DELAY BIT(6)
#define OV9282_GAIN_CHANGE_DELAY BIT(5)
#define OV9282_GAIN_DELAY BIT(4)
@@ -78,28 +80,28 @@
#define OV9282_AEC_MANUAL_DEFAULT 0x00
/* Analog gain control */
-#define OV9282_REG_AGAIN 0x3509
+#define OV9282_REG_AGAIN CCI_REG8(0x3509)
#define OV9282_AGAIN_MIN 0x10
#define OV9282_AGAIN_MAX 0xff
#define OV9282_AGAIN_STEP 1
#define OV9282_AGAIN_DEFAULT 0x10
/* Group hold register */
-#define OV9282_REG_HOLD 0x3308
+#define OV9282_REG_HOLD CCI_REG8(0x3308)
-#define OV9282_REG_ANA_CORE_2 0x3662
+#define OV9282_REG_ANA_CORE_2 CCI_REG8(0x3662)
#define OV9282_ANA_CORE2_RAW8 0x07
#define OV9282_ANA_CORE2_RAW10 0x05
-#define OV9282_REG_TIMING_FORMAT_1 0x3820
-#define OV9282_REG_TIMING_FORMAT_2 0x3821
+#define OV9282_REG_TIMING_FORMAT_1 CCI_REG8(0x3820)
+#define OV9282_REG_TIMING_FORMAT_2 CCI_REG8(0x3821)
#define OV9282_FLIP_BIT BIT(2)
-#define OV9282_REG_MIPI_CTRL00 0x4800
+#define OV9282_REG_MIPI_CTRL00 CCI_REG8(0x4800)
#define OV9282_GATED_CLOCK BIT(5)
/* Flash/Strobe control registers */
-#define OV9282_REG_STROBE_FRAME_SPAN 0x3925
+#define OV9282_REG_STROBE_FRAME_SPAN CCI_REG32(0x3925)
#define OV9282_STROBE_FRAME_SPAN_DEFAULT 0x0000001a
/* Input clock rate */
@@ -140,23 +142,13 @@ static const char * const ov9282_supply_names[] = {
#define OV9282_NUM_SUPPLIES ARRAY_SIZE(ov9282_supply_names)
/**
- * struct ov9282_reg - ov9282 sensor register
- * @address: Register address
- * @val: Register value
- */
-struct ov9282_reg {
- u16 address;
- u8 val;
-};
-
-/**
* struct ov9282_reg_list - ov9282 sensor register list
* @num_of_regs: Number of registers in the list
* @regs: Pointer to register list
*/
struct ov9282_reg_list {
u32 num_of_regs;
- const struct ov9282_reg *regs;
+ const struct cci_reg_sequence *regs;
};
/**
@@ -188,6 +180,7 @@ struct ov9282_mode {
* struct ov9282 - ov9282 sensor device structure
* @dev: Pointer to generic device
* @sd: V4L2 sub-device
+ * @regmap: Regmap for sensor register access
* @pad: Media pad. Only one pad supported
* @reset_gpio: Sensor reset gpio
* @inclk: Sensor input clock
@@ -209,6 +202,7 @@ struct ov9282_mode {
struct ov9282 {
struct device *dev;
struct v4l2_subdev sd;
+ struct regmap *regmap;
struct media_pad pad;
struct gpio_desc *reset_gpio;
struct clk *inclk;
@@ -227,7 +221,6 @@ struct ov9282 {
bool noncontinuous_clock;
const struct ov9282_mode *cur_mode;
u32 code;
- struct mutex mutex;
};
static const s64 link_freq[] = {
@@ -241,73 +234,68 @@ static const s64 link_freq[] = {
* register arrays as some settings are written as part of ov9282_power_on,
* and the reset will clear them.
*/
-static const struct ov9282_reg common_regs[] = {
- {0x0302, 0x32},
- {0x030e, 0x02},
- {0x3001, 0x00},
+static const struct cci_reg_sequence common_regs[] = {
+ {CCI_REG8(0x0302), 0x32},
+ {CCI_REG8(0x030e), 0x02},
+ {CCI_REG8(0x3001), 0x00},
{OV9282_REG_OUTPUT_ENABLE4, 0x00},
{OV9282_REG_OUTPUT_ENABLE5, 0x00},
{OV9282_REG_OUTPUT_ENABLE6, OV9282_OUTPUT_ENABLE6_ILPWM},
- {0x3011, 0x0a},
- {0x3013, 0x18},
- {0x301c, 0xf0},
- {0x3022, 0x01},
- {0x3030, 0x10},
- {0x3039, 0x32},
- {0x303a, 0x00},
+ {CCI_REG8(0x3011), 0x0a},
+ {CCI_REG8(0x3013), 0x18},
+ {CCI_REG8(0x301c), 0xf0},
+ {CCI_REG8(0x3022), 0x01},
+ {CCI_REG8(0x3030), 0x10},
+ {CCI_REG8(0x3039), 0x32},
+ {CCI_REG8(0x303a), 0x00},
{OV9282_REG_AEC_MANUAL, OV9282_GAIN_PREC16_EN},
- {0x3505, 0x8c},
- {0x3507, 0x03},
- {0x3508, 0x00},
- {0x3610, 0x80},
- {0x3611, 0xa0},
- {0x3620, 0x6e},
- {0x3632, 0x56},
- {0x3633, 0x78},
- {0x3666, 0x00},
- {0x366f, 0x5a},
- {0x3680, 0x84},
- {0x3712, 0x80},
- {0x372d, 0x22},
- {0x3731, 0x80},
- {0x3732, 0x30},
- {0x377d, 0x22},
- {0x3788, 0x02},
- {0x3789, 0xa4},
- {0x378a, 0x00},
- {0x378b, 0x4a},
- {0x3799, 0x20},
- {0x3881, 0x42},
- {0x38a8, 0x02},
- {0x38a9, 0x80},
- {0x38b1, 0x00},
- {0x38c4, 0x00},
- {0x38c5, 0xc0},
- {0x38c6, 0x04},
- {0x38c7, 0x80},
- {0x3920, 0xff},
- {0x4010, 0x40},
- {0x4043, 0x40},
- {0x4307, 0x30},
- {0x4317, 0x00},
- {0x4501, 0x00},
- {0x450a, 0x08},
- {0x4601, 0x04},
- {0x470f, 0x00},
- {0x4f07, 0x00},
- {0x5000, 0x9f},
- {0x5001, 0x00},
- {0x5e00, 0x00},
- {0x5d00, 0x07},
- {0x5d01, 0x00},
- {0x0101, 0x01},
- {0x1000, 0x03},
- {0x5a08, 0x84},
-};
-
-static struct ov9282_reg_list common_regs_list = {
- .num_of_regs = ARRAY_SIZE(common_regs),
- .regs = common_regs,
+ {CCI_REG8(0x3505), 0x8c},
+ {CCI_REG8(0x3507), 0x03},
+ {CCI_REG8(0x3508), 0x00},
+ {CCI_REG8(0x3610), 0x80},
+ {CCI_REG8(0x3611), 0xa0},
+ {CCI_REG8(0x3620), 0x6e},
+ {CCI_REG8(0x3632), 0x56},
+ {CCI_REG8(0x3633), 0x78},
+ {CCI_REG8(0x3666), 0x00},
+ {CCI_REG8(0x366f), 0x5a},
+ {CCI_REG8(0x3680), 0x84},
+ {CCI_REG8(0x3712), 0x80},
+ {CCI_REG8(0x372d), 0x22},
+ {CCI_REG8(0x3731), 0x80},
+ {CCI_REG8(0x3732), 0x30},
+ {CCI_REG8(0x377d), 0x22},
+ {CCI_REG8(0x3788), 0x02},
+ {CCI_REG8(0x3789), 0xa4},
+ {CCI_REG8(0x378a), 0x00},
+ {CCI_REG8(0x378b), 0x4a},
+ {CCI_REG8(0x3799), 0x20},
+ {CCI_REG8(0x3881), 0x42},
+ {CCI_REG8(0x38a8), 0x02},
+ {CCI_REG8(0x38a9), 0x80},
+ {CCI_REG8(0x38b1), 0x00},
+ {CCI_REG8(0x38c4), 0x00},
+ {CCI_REG8(0x38c5), 0xc0},
+ {CCI_REG8(0x38c6), 0x04},
+ {CCI_REG8(0x38c7), 0x80},
+ {CCI_REG8(0x3920), 0xff},
+ {CCI_REG8(0x4010), 0x40},
+ {CCI_REG8(0x4043), 0x40},
+ {CCI_REG8(0x4307), 0x30},
+ {CCI_REG8(0x4317), 0x00},
+ {CCI_REG8(0x4501), 0x00},
+ {CCI_REG8(0x450a), 0x08},
+ {CCI_REG8(0x4601), 0x04},
+ {CCI_REG8(0x470f), 0x00},
+ {CCI_REG8(0x4f07), 0x00},
+ {CCI_REG8(0x5000), 0x9f},
+ {CCI_REG8(0x5001), 0x00},
+ {CCI_REG8(0x5e00), 0x00},
+ {CCI_REG8(0x5d00), 0x07},
+ {CCI_REG8(0x5d01), 0x00},
+ {CCI_REG8(0x0101), 0x01},
+ {CCI_REG8(0x1000), 0x03},
+ {CCI_REG8(0x5a08), 0x84},
};
#define MODE_1280_800 0
@@ -317,96 +305,96 @@ static struct ov9282_reg_list common_regs_list = {
#define DEFAULT_MODE MODE_1280_720
/* Sensor mode registers */
-static const struct ov9282_reg mode_1280x800_regs[] = {
- {0x3778, 0x00},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x05},
- {0x3805, 0x0f},
- {0x3806, 0x03},
- {0x3807, 0x2f},
- {0x3808, 0x05},
- {0x3809, 0x00},
- {0x380a, 0x03},
- {0x380b, 0x20},
- {0x3810, 0x00},
- {0x3811, 0x08},
- {0x3812, 0x00},
- {0x3813, 0x08},
- {0x3814, 0x11},
- {0x3815, 0x11},
+static const struct cci_reg_sequence mode_1280x800_regs[] = {
+ {CCI_REG8(0x3778), 0x00},
+ {CCI_REG8(0x3800), 0x00},
+ {CCI_REG8(0x3801), 0x00},
+ {CCI_REG8(0x3802), 0x00},
+ {CCI_REG8(0x3803), 0x00},
+ {CCI_REG8(0x3804), 0x05},
+ {CCI_REG8(0x3805), 0x0f},
+ {CCI_REG8(0x3806), 0x03},
+ {CCI_REG8(0x3807), 0x2f},
+ {CCI_REG8(0x3808), 0x05},
+ {CCI_REG8(0x3809), 0x00},
+ {CCI_REG8(0x380a), 0x03},
+ {CCI_REG8(0x380b), 0x20},
+ {CCI_REG8(0x3810), 0x00},
+ {CCI_REG8(0x3811), 0x08},
+ {CCI_REG8(0x3812), 0x00},
+ {CCI_REG8(0x3813), 0x08},
+ {CCI_REG8(0x3814), 0x11},
+ {CCI_REG8(0x3815), 0x11},
{OV9282_REG_TIMING_FORMAT_1, 0x40},
{OV9282_REG_TIMING_FORMAT_2, 0x00},
- {0x4003, 0x40},
- {0x4008, 0x04},
- {0x4009, 0x0b},
- {0x400c, 0x00},
- {0x400d, 0x07},
- {0x4507, 0x00},
- {0x4509, 0x00},
+ {CCI_REG8(0x4003), 0x40},
+ {CCI_REG8(0x4008), 0x04},
+ {CCI_REG8(0x4009), 0x0b},
+ {CCI_REG8(0x400c), 0x00},
+ {CCI_REG8(0x400d), 0x07},
+ {CCI_REG8(0x4507), 0x00},
+ {CCI_REG8(0x4509), 0x00},
};
-static const struct ov9282_reg mode_1280x720_regs[] = {
- {0x3778, 0x00},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x05},
- {0x3805, 0x0f},
- {0x3806, 0x02},
- {0x3807, 0xdf},
- {0x3808, 0x05},
- {0x3809, 0x00},
- {0x380a, 0x02},
- {0x380b, 0xd0},
- {0x3810, 0x00},
- {0x3811, 0x08},
- {0x3812, 0x00},
- {0x3813, 0x08},
- {0x3814, 0x11},
- {0x3815, 0x11},
+static const struct cci_reg_sequence mode_1280x720_regs[] = {
+ {CCI_REG8(0x3778), 0x00},
+ {CCI_REG8(0x3800), 0x00},
+ {CCI_REG8(0x3801), 0x00},
+ {CCI_REG8(0x3802), 0x00},
+ {CCI_REG8(0x3803), 0x00},
+ {CCI_REG8(0x3804), 0x05},
+ {CCI_REG8(0x3805), 0x0f},
+ {CCI_REG8(0x3806), 0x02},
+ {CCI_REG8(0x3807), 0xdf},
+ {CCI_REG8(0x3808), 0x05},
+ {CCI_REG8(0x3809), 0x00},
+ {CCI_REG8(0x380a), 0x02},
+ {CCI_REG8(0x380b), 0xd0},
+ {CCI_REG8(0x3810), 0x00},
+ {CCI_REG8(0x3811), 0x08},
+ {CCI_REG8(0x3812), 0x00},
+ {CCI_REG8(0x3813), 0x08},
+ {CCI_REG8(0x3814), 0x11},
+ {CCI_REG8(0x3815), 0x11},
{OV9282_REG_TIMING_FORMAT_1, 0x3c},
{OV9282_REG_TIMING_FORMAT_2, 0x84},
- {0x4003, 0x40},
- {0x4008, 0x02},
- {0x4009, 0x05},
- {0x400c, 0x00},
- {0x400d, 0x03},
- {0x4507, 0x00},
- {0x4509, 0x80},
+ {CCI_REG8(0x4003), 0x40},
+ {CCI_REG8(0x4008), 0x02},
+ {CCI_REG8(0x4009), 0x05},
+ {CCI_REG8(0x400c), 0x00},
+ {CCI_REG8(0x400d), 0x03},
+ {CCI_REG8(0x4507), 0x00},
+ {CCI_REG8(0x4509), 0x80},
};
-static const struct ov9282_reg mode_640x400_regs[] = {
- {0x3778, 0x10},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x05},
- {0x3805, 0x0f},
- {0x3806, 0x03},
- {0x3807, 0x2f},
- {0x3808, 0x02},
- {0x3809, 0x80},
- {0x380a, 0x01},
- {0x380b, 0x90},
- {0x3810, 0x00},
- {0x3811, 0x04},
- {0x3812, 0x00},
- {0x3813, 0x04},
- {0x3814, 0x31},
- {0x3815, 0x22},
+static const struct cci_reg_sequence mode_640x400_regs[] = {
+ {CCI_REG8(0x3778), 0x10},
+ {CCI_REG8(0x3800), 0x00},
+ {CCI_REG8(0x3801), 0x00},
+ {CCI_REG8(0x3802), 0x00},
+ {CCI_REG8(0x3803), 0x00},
+ {CCI_REG8(0x3804), 0x05},
+ {CCI_REG8(0x3805), 0x0f},
+ {CCI_REG8(0x3806), 0x03},
+ {CCI_REG8(0x3807), 0x2f},
+ {CCI_REG8(0x3808), 0x02},
+ {CCI_REG8(0x3809), 0x80},
+ {CCI_REG8(0x380a), 0x01},
+ {CCI_REG8(0x380b), 0x90},
+ {CCI_REG8(0x3810), 0x00},
+ {CCI_REG8(0x3811), 0x04},
+ {CCI_REG8(0x3812), 0x00},
+ {CCI_REG8(0x3813), 0x04},
+ {CCI_REG8(0x3814), 0x31},
+ {CCI_REG8(0x3815), 0x22},
{OV9282_REG_TIMING_FORMAT_1, 0x60},
{OV9282_REG_TIMING_FORMAT_2, 0x01},
- {0x4008, 0x02},
- {0x4009, 0x05},
- {0x400c, 0x00},
- {0x400d, 0x03},
- {0x4507, 0x03},
- {0x4509, 0x80},
+ {CCI_REG8(0x4008), 0x02},
+ {CCI_REG8(0x4009), 0x05},
+ {CCI_REG8(0x400c), 0x00},
+ {CCI_REG8(0x400d), 0x03},
+ {CCI_REG8(0x4507), 0x03},
+ {CCI_REG8(0x4509), 0x80},
};
/* Supported sensor mode configurations */
@@ -486,97 +474,6 @@ static inline struct ov9282 *to_ov9282(struct v4l2_subdev *subdev)
}
/**
- * ov9282_read_reg() - Read registers.
- * @ov9282: pointer to ov9282 device
- * @reg: register address
- * @len: length of bytes to read. Max supported bytes is 4
- * @val: pointer to register value to be filled.
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_read_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&ov9282->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[4 - len];
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs))
- return -EIO;
-
- *val = get_unaligned_be32(data_buf);
-
- return 0;
-}
-
-/**
- * ov9282_write_reg() - Write register
- * @ov9282: pointer to ov9282 device
- * @reg: register address
- * @len: length of bytes. Max supported bytes is 4
- * @val: register value
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_write_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd);
- u8 buf[6] = {0};
-
- if (WARN_ON(len > 4))
- return -EINVAL;
-
- put_unaligned_be16(reg, buf);
- put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
- if (i2c_master_send(client, buf, len + 2) != len + 2)
- return -EIO;
-
- return 0;
-}
-
-/**
- * ov9282_write_regs() - Write a list of registers
- * @ov9282: pointer to ov9282 device
- * @regs: list of registers to be written
- * @len: length of registers array
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_write_regs(struct ov9282 *ov9282,
- const struct ov9282_reg *regs, u32 len)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < len; i++) {
- ret = ov9282_write_reg(ov9282, regs[i].address, 1, regs[i].val);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
* ov9282_update_controls() - Update control ranges based on streaming mode
* @ov9282: pointer to ov9282 device
* @mode: pointer to ov9282_mode sensor mode
@@ -633,21 +530,21 @@ static u32 ov9282_exposure_to_us(struct ov9282 *ov9282, u32 exposure)
*/
static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain)
{
- int ret;
u32 exposure_us = ov9282_exposure_to_us(ov9282, exposure);
+ int ret, ret_hold;
dev_dbg(ov9282->dev, "Set exp %u (~%u us), analog gain %u",
exposure, exposure_us, gain);
- ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1);
+ ret = cci_write(ov9282->regmap, OV9282_REG_HOLD, 0x01, NULL);
if (ret)
return ret;
- ret = ov9282_write_reg(ov9282, OV9282_REG_EXPOSURE, 3, exposure << 4);
+ ret = cci_write(ov9282->regmap, OV9282_REG_EXPOSURE, exposure << 4, NULL);
if (ret)
goto error_release_group_hold;
- ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain);
+ ret = cci_write(ov9282->regmap, OV9282_REG_AGAIN, gain, NULL);
if (ret)
goto error_release_group_hold;
@@ -656,60 +553,9 @@ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain)
OV9282_STROBE_FRAME_SPAN_DEFAULT);
error_release_group_hold:
- ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0);
-
- return ret;
-}
+ ret_hold = cci_write(ov9282->regmap, OV9282_REG_HOLD, 0, NULL);
-static int ov9282_set_ctrl_hflip(struct ov9282 *ov9282, int value)
-{
- u32 current_val;
- int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1,
- &current_val);
- if (ret)
- return ret;
-
- if (value)
- current_val |= OV9282_FLIP_BIT;
- else
- current_val &= ~OV9282_FLIP_BIT;
-
- return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1,
- current_val);
-}
-
-static int ov9282_set_ctrl_vflip(struct ov9282 *ov9282, int value)
-{
- u32 current_val;
- int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1,
- &current_val);
- if (ret)
- return ret;
-
- if (value)
- current_val |= OV9282_FLIP_BIT;
- else
- current_val &= ~OV9282_FLIP_BIT;
-
- return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1,
- current_val);
-}
-
-static int ov9282_set_ctrl_flash_strobe_oe(struct ov9282 *ov9282, bool enable)
-{
- u32 current_val;
- int ret;
-
- ret = ov9282_read_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, &current_val);
- if (ret)
- return ret;
-
- if (enable)
- current_val |= OV9282_OUTPUT_ENABLE6_STROBE;
- else
- current_val &= ~OV9282_OUTPUT_ENABLE6_STROBE;
-
- return ov9282_write_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, current_val);
+ return ret ? ret : ret_hold;
}
static u32 ov9282_us_to_flash_duration(struct ov9282 *ov9282, u32 value)
@@ -740,30 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value)
return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR);
}
-static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value)
-{
- u32 val = ov9282_us_to_flash_duration(ov9282, value);
- int ret;
-
- ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN, 1,
- (val >> 24) & 0xff);
- if (ret)
- return ret;
-
- ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 1, 1,
- (val >> 16) & 0xff);
- if (ret)
- return ret;
-
- ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 2, 1,
- (val >> 8) & 0xff);
- if (ret)
- return ret;
-
- return ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 3, 1,
- val & 0xff);
-}
-
/**
* ov9282_set_ctrl() - Set subdevice control
* @ctrl: pointer to v4l2_ctrl structure
@@ -818,23 +640,27 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_VBLANK:
lpfr = ov9282->vblank + ov9282->cur_mode->height;
- ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr);
+ ret = cci_write(ov9282->regmap, OV9282_REG_LPFR, lpfr, NULL);
break;
case V4L2_CID_HFLIP:
- ret = ov9282_set_ctrl_hflip(ov9282, ctrl->val);
+ ret = cci_update_bits(ov9282->regmap, OV9282_REG_TIMING_FORMAT_2,
+ OV9282_FLIP_BIT, ctrl->val ? OV9282_FLIP_BIT : 0, NULL);
break;
case V4L2_CID_VFLIP:
- ret = ov9282_set_ctrl_vflip(ov9282, ctrl->val);
+ ret = cci_update_bits(ov9282->regmap, OV9282_REG_TIMING_FORMAT_1,
+ OV9282_FLIP_BIT, ctrl->val ? OV9282_FLIP_BIT : 0, NULL);
break;
case V4L2_CID_HBLANK:
- ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_HTS, 2,
- (ctrl->val + ov9282->cur_mode->width) >> 1);
+ ret = cci_write(ov9282->regmap, OV9282_REG_TIMING_HTS,
+ (ctrl->val + ov9282->cur_mode->width) >> 1, NULL);
break;
case V4L2_CID_FLASH_STROBE_OE:
- ret = ov9282_set_ctrl_flash_strobe_oe(ov9282, ctrl->val);
+ ret = cci_update_bits(ov9282->regmap, OV9282_REG_OUTPUT_ENABLE6,
+ OV9282_OUTPUT_ENABLE6_STROBE,
+ ctrl->val ? OV9282_OUTPUT_ENABLE6_STROBE : 0, NULL);
break;
case V4L2_CID_FLASH_DURATION:
- ret = ov9282_set_ctrl_flash_duration(ov9282, ctrl->val);
+ ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, ctrl->val, NULL);
break;
default:
dev_err(ov9282->dev, "Invalid control %d", ctrl->id);
@@ -968,8 +794,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd,
{
struct ov9282 *ov9282 = to_ov9282(sd);
- mutex_lock(&ov9282->mutex);
-
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
struct v4l2_mbus_framefmt *framefmt;
@@ -980,8 +804,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd,
fmt);
}
- mutex_unlock(&ov9282->mutex);
-
return 0;
}
@@ -1002,8 +824,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd,
u32 code;
int ret = 0;
- mutex_lock(&ov9282->mutex);
-
mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes),
width, height,
@@ -1029,8 +849,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd,
}
}
- mutex_unlock(&ov9282->mutex);
-
return ret;
}
@@ -1077,10 +895,8 @@ static int ov9282_get_selection(struct v4l2_subdev *sd,
case V4L2_SEL_TGT_CROP: {
struct ov9282 *ov9282 = to_ov9282(sd);
- mutex_lock(&ov9282->mutex);
sel->r = *__ov9282_get_pad_crop(ov9282, sd_state, sel->pad,
sel->which);
- mutex_unlock(&ov9282->mutex);
return 0;
}
@@ -1106,15 +922,11 @@ static int ov9282_get_selection(struct v4l2_subdev *sd,
return -EINVAL;
}
-/**
- * ov9282_start_streaming() - Start sensor stream
- * @ov9282: pointer to ov9282 device
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_start_streaming(struct ov9282 *ov9282)
+static int ov9282_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
- const struct ov9282_reg bitdepth_regs[2][2] = {
+ const struct cci_reg_sequence bitdepth_regs[2][2] = {
{
{OV9282_REG_PLL_CTRL_0D, OV9282_PLL_CTRL_0D_RAW10},
{OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW10},
@@ -1123,98 +935,74 @@ static int ov9282_start_streaming(struct ov9282 *ov9282)
{OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW8},
}
};
+ struct ov9282 *ov9282 = to_ov9282(sd);
const struct ov9282_reg_list *reg_list;
int bitdepth_index;
int ret;
+ ret = pm_runtime_resume_and_get(ov9282->dev);
+ if (ret)
+ return ret;
+
/* Write common registers */
- ret = ov9282_write_regs(ov9282, common_regs_list.regs,
- common_regs_list.num_of_regs);
+ ret = cci_multi_reg_write(ov9282->regmap, common_regs,
+ ARRAY_SIZE(common_regs), NULL);
if (ret) {
dev_err(ov9282->dev, "fail to write common registers");
- return ret;
+ goto err_pm_put;
}
bitdepth_index = ov9282->code == MEDIA_BUS_FMT_Y10_1X10 ? 0 : 1;
- ret = ov9282_write_regs(ov9282, bitdepth_regs[bitdepth_index], 2);
+ ret = cci_multi_reg_write(ov9282->regmap,
+ bitdepth_regs[bitdepth_index], 2, NULL);
if (ret) {
dev_err(ov9282->dev, "fail to write bitdepth regs");
- return ret;
+ goto err_pm_put;
}
/* Write sensor mode registers */
reg_list = &ov9282->cur_mode->reg_list;
- ret = ov9282_write_regs(ov9282, reg_list->regs, reg_list->num_of_regs);
+ ret = cci_multi_reg_write(ov9282->regmap, reg_list->regs,
+ reg_list->num_of_regs, NULL);
if (ret) {
dev_err(ov9282->dev, "fail to write initial registers");
- return ret;
+ goto err_pm_put;
}
/* Setup handler will write actual exposure and gain */
ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler);
if (ret) {
dev_err(ov9282->dev, "fail to setup handler");
- return ret;
+ goto err_pm_put;
}
/* Start streaming */
- ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
- 1, OV9282_MODE_STREAMING);
+ ret = cci_write(ov9282->regmap, OV9282_REG_MODE_SELECT,
+ OV9282_MODE_STREAMING, NULL);
if (ret) {
dev_err(ov9282->dev, "fail to start streaming");
- return ret;
+ goto err_pm_put;
}
return 0;
-}
-/**
- * ov9282_stop_streaming() - Stop sensor stream
- * @ov9282: pointer to ov9282 device
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_stop_streaming(struct ov9282 *ov9282)
-{
- return ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
- 1, OV9282_MODE_STANDBY);
+err_pm_put:
+ pm_runtime_put(ov9282->dev);
+
+ return ret;
}
-/**
- * ov9282_set_stream() - Enable sensor streaming
- * @sd: pointer to ov9282 subdevice
- * @enable: set to enable sensor streaming
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int ov9282_set_stream(struct v4l2_subdev *sd, int enable)
+static int ov9282_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct ov9282 *ov9282 = to_ov9282(sd);
int ret;
- mutex_lock(&ov9282->mutex);
-
- if (enable) {
- ret = pm_runtime_resume_and_get(ov9282->dev);
- if (ret)
- goto error_unlock;
-
- ret = ov9282_start_streaming(ov9282);
- if (ret)
- goto error_power_off;
- } else {
- ov9282_stop_streaming(ov9282);
- pm_runtime_put(ov9282->dev);
- }
-
- mutex_unlock(&ov9282->mutex);
-
- return 0;
+ ret = cci_write(ov9282->regmap, OV9282_REG_MODE_SELECT,
+ OV9282_MODE_STANDBY, NULL);
-error_power_off:
pm_runtime_put(ov9282->dev);
-error_unlock:
- mutex_unlock(&ov9282->mutex);
return ret;
}
@@ -1228,14 +1016,14 @@ error_unlock:
static int ov9282_detect(struct ov9282 *ov9282)
{
int ret;
- u32 val;
+ u64 val;
- ret = ov9282_read_reg(ov9282, OV9282_REG_ID, 2, &val);
+ ret = cci_read(ov9282->regmap, OV9282_REG_ID, &val, NULL);
if (ret)
return ret;
if (val != OV9282_ID) {
- dev_err(ov9282->dev, "chip id mismatch: %x!=%x",
+ dev_err(ov9282->dev, "chip id mismatch: %x!=%llx",
OV9282_ID, val);
return -ENXIO;
}
@@ -1346,7 +1134,7 @@ static const struct v4l2_subdev_core_ops ov9282_core_ops = {
};
static const struct v4l2_subdev_video_ops ov9282_video_ops = {
- .s_stream = ov9282_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops ov9282_pad_ops = {
@@ -1355,6 +1143,8 @@ static const struct v4l2_subdev_pad_ops ov9282_pad_ops = {
.get_fmt = ov9282_get_pad_format,
.set_fmt = ov9282_set_pad_format,
.get_selection = ov9282_get_selection,
+ .enable_streams = ov9282_enable_streams,
+ .disable_streams = ov9282_disable_streams,
};
static const struct v4l2_subdev_ops ov9282_subdev_ops = {
@@ -1397,9 +1187,8 @@ static int ov9282_power_on(struct device *dev)
usleep_range(400, 600);
- ret = ov9282_write_reg(ov9282, OV9282_REG_MIPI_CTRL00, 1,
- ov9282->noncontinuous_clock ?
- OV9282_GATED_CLOCK : 0);
+ ret = cci_write(ov9282->regmap, OV9282_REG_MIPI_CTRL00,
+ ov9282->noncontinuous_clock ? OV9282_GATED_CLOCK : 0, NULL);
if (ret) {
dev_err(ov9282->dev, "fail to write MIPI_CTRL00");
goto error_clk;
@@ -1457,9 +1246,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282)
if (ret)
return ret;
- /* Serialize controls with sensor device */
- ctrl_hdlr->lock = &ov9282->mutex;
-
/* Initialize exposure and gain */
lpfr = mode->vblank + mode->height;
ov9282->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
@@ -1576,13 +1362,15 @@ static int ov9282_probe(struct i2c_client *client)
return ret;
}
- mutex_init(&ov9282->mutex);
+ ov9282->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(ov9282->regmap))
+ return dev_err_probe(ov9282->dev, PTR_ERR(ov9282->regmap),
+ "Failed to init CCI\n");
ret = ov9282_power_on(ov9282->dev);
- if (ret) {
- dev_err(ov9282->dev, "failed to power-on the sensor");
- goto error_mutex_destroy;
- }
+ if (ret)
+ return dev_err_probe(ov9282->dev, ret,
+ "failed to power-on the sensor");
/* Check module identity */
ret = ov9282_detect(ov9282);
@@ -1615,27 +1403,34 @@ static int ov9282_probe(struct i2c_client *client)
goto error_handler_free;
}
- ret = v4l2_async_register_subdev_sensor(&ov9282->sd);
+ ov9282->sd.state_lock = ov9282->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(&ov9282->sd);
if (ret < 0) {
- dev_err(ov9282->dev,
- "failed to register async subdev: %d", ret);
+ dev_err_probe(ov9282->dev, ret, "failed to init subdev\n");
goto error_media_entity;
}
pm_runtime_set_active(ov9282->dev);
pm_runtime_enable(ov9282->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&ov9282->sd);
+ if (ret < 0)
+ goto v4l2_subdev_cleanup;
+
pm_runtime_idle(ov9282->dev);
return 0;
+v4l2_subdev_cleanup:
+ v4l2_subdev_cleanup(&ov9282->sd);
+ pm_runtime_disable(ov9282->dev);
+ pm_runtime_set_suspended(ov9282->dev);
error_media_entity:
media_entity_cleanup(&ov9282->sd.entity);
error_handler_free:
v4l2_ctrl_handler_free(ov9282->sd.ctrl_handler);
error_power_off:
ov9282_power_off(ov9282->dev);
-error_mutex_destroy:
- mutex_destroy(&ov9282->mutex);
return ret;
}
@@ -1649,9 +1444,9 @@ error_mutex_destroy:
static void ov9282_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct ov9282 *ov9282 = to_ov9282(sd);
v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
@@ -1659,8 +1454,6 @@ static void ov9282_remove(struct i2c_client *client)
if (!pm_runtime_status_suspended(&client->dev))
ov9282_power_off(&client->dev);
pm_runtime_set_suspended(&client->dev);
-
- mutex_destroy(&ov9282->mutex);
}
static const struct dev_pm_ops ov9282_pm_ops = {
diff --git a/drivers/media/i2c/t4ka3.c b/drivers/media/i2c/t4ka3.c
new file mode 100644
index 000000000000..746548868bb0
--- /dev/null
+++ b/drivers/media/i2c/t4ka3.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for T4KA3 8M camera sensor.
+ *
+ * Copyright (C) 2015 Intel Corporation. All Rights Reserved.
+ * Copyright (C) 2016 XiaoMi, Inc.
+ * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
+ * Copyright (C) 2026 Kate Hsuan <hpa@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define T4KA3_NATIVE_WIDTH 3280
+#define T4KA3_NATIVE_HEIGHT 2464
+#define T4KA3_NATIVE_START_LEFT 0
+#define T4KA3_NATIVE_START_TOP 0
+#define T4KA3_ACTIVE_WIDTH 3280
+#define T4KA3_ACTIVE_HEIGHT 2460
+#define T4KA3_ACTIVE_START_LEFT 0
+#define T4KA3_ACTIVE_START_TOP 2
+#define T4KA3_MIN_CROP_WIDTH 2
+#define T4KA3_MIN_CROP_HEIGHT 2
+
+#define T4KA3_PIXELS_PER_LINE 3440
+#define T4KA3_LINES_PER_FRAME_30FPS 2492
+#define T4KA3_FPS 30
+#define T4KA3_PIXEL_RATE \
+ (T4KA3_PIXELS_PER_LINE * T4KA3_LINES_PER_FRAME_30FPS * T4KA3_FPS)
+
+/*
+ * TODO this really should be derived from the 19.2 MHz xvclk combined
+ * with the PLL settings. But without a datasheet this is the closest
+ * approximation possible.
+ *
+ * link-freq = pixel_rate * bpp / (lanes * 2)
+ * (lanes * 2) because CSI lanes use double-data-rate (DDR) signalling.
+ * bpp = 10 and lanes = 4
+ */
+#define T4KA3_LINK_FREQ ((u64)T4KA3_PIXEL_RATE * 10 / 8)
+
+/* For enum_frame_size() full-size + binned-/quarter-size */
+#define T4KA3_FRAME_SIZES 2
+
+#define T4KA3_REG_PRODUCT_ID_HIGH CCI_REG8(0x0000)
+#define T4KA3_REG_PRODUCT_ID_LOW CCI_REG8(0x0001)
+#define T4KA3_PRODUCT_ID 0x1490
+
+#define T4KA3_REG_STREAM CCI_REG8(0x0100)
+#define T4KA3_REG_IMG_ORIENTATION CCI_REG8(0x0101)
+#define T4KA3_HFLIP_BIT BIT(0)
+#define T4KA3_VFLIP_BIT BIT(1)
+#define T4KA3_REG_PARAM_HOLD CCI_REG8(0x0104)
+#define T4KA3_REG_COARSE_INTEGRATION_TIME CCI_REG16(0x0202)
+#define T4KA3_COARSE_INTEGRATION_TIME_MARGIN 6
+#define T4KA3_REG_DIGGAIN_GREEN_R CCI_REG16(0x020e)
+#define T4KA3_REG_DIGGAIN_RED CCI_REG16(0x0210)
+#define T4KA3_REG_DIGGAIN_BLUE CCI_REG16(0x0212)
+#define T4KA3_REG_DIGGAIN_GREEN_B CCI_REG16(0x0214)
+#define T4KA3_REG_GLOBAL_GAIN CCI_REG16(0x0234)
+#define T4KA3_MIN_GLOBAL_GAIN_SUPPORTED 0x0080
+#define T4KA3_MAX_GLOBAL_GAIN_SUPPORTED 0x07ff
+#define T4KA3_REG_FRAME_LENGTH_LINES CCI_REG16(0x0340) /* aka VTS */
+/* FIXME: need a datasheet to verify the min + max vblank values */
+#define T4KA3_MIN_VBLANK 4
+#define T4KA3_MAX_VBLANK 0xffff
+#define T4KA3_REG_PIXELS_PER_LINE CCI_REG16(0x0342) /* aka HTS */
+/* These 2 being horz/vert start is a guess (no datasheet), always 0 */
+#define T4KA3_REG_HORZ_START CCI_REG16(0x0344)
+#define T4KA3_REG_VERT_START CCI_REG16(0x0346)
+/* Always 3279 (T4KA3_NATIVE_WIDTH - 1, window is used to crop */
+#define T4KA3_REG_HORZ_END CCI_REG16(0x0348)
+/* Always 2463 (T4KA3_NATIVE_HEIGHT - 1, window is used to crop */
+#define T4KA3_REG_VERT_END CCI_REG16(0x034a)
+/* Output size (after cropping/window) */
+#define T4KA3_REG_HORZ_OUTPUT_SIZE CCI_REG16(0x034c)
+#define T4KA3_REG_VERT_OUTPUT_SIZE CCI_REG16(0x034e)
+/* Window/crop start + size *after* binning */
+#define T4KA3_REG_WIN_START_X CCI_REG16(0x0408)
+#define T4KA3_REG_WIN_START_Y CCI_REG16(0x040a)
+#define T4KA3_REG_WIN_WIDTH CCI_REG16(0x040c)
+#define T4KA3_REG_WIN_HEIGHT CCI_REG16(0x040e)
+#define T4KA3_REG_TEST_PATTERN_MODE CCI_REG8(0x0601)
+/* Unknown register at address 0x0900 */
+#define T4KA3_REG_0900 CCI_REG8(0x0900)
+#define T4KA3_REG_BINNING CCI_REG8(0x0901)
+#define T4KA3_BINNING_VAL(_bin) \
+({ \
+ typeof(_bin) (b) = (_bin); \
+ ((b) << 4) | (b); \
+})
+
+#define to_t4ka3_sensor(_sd) container_of_const(_sd, \
+ struct t4ka3_data, sd)
+#define ctrl_to_t4ka3(_ctrl) container_of_const((_ctrl)->handler, \
+ struct t4ka3_data, \
+ ctrls.handler)
+
+struct t4ka3_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *test_pattern;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+};
+
+struct t4ka3_mode {
+ int binning;
+ u16 win_x;
+ u16 win_y;
+};
+
+struct t4ka3_data {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct mutex lock; /* serialize sensor's ioctl */
+ struct t4ka3_ctrls ctrls;
+ struct t4ka3_mode mode;
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *powerdown_gpio;
+ struct gpio_desc *reset_gpio;
+ int streaming;
+
+ /* MIPI lane info */
+ u32 link_freq_index;
+ u8 mipi_lanes;
+};
+
+/* init settings */
+static const struct cci_reg_sequence t4ka3_init_config[] = {
+ { CCI_REG8(0x4136), 0x13 },
+ { CCI_REG8(0x4137), 0x33 },
+ { CCI_REG8(0x3094), 0x01 },
+ { CCI_REG8(0x0233), 0x01 },
+ { CCI_REG8(0x4B06), 0x01 },
+ { CCI_REG8(0x4B07), 0x01 },
+ { CCI_REG8(0x3028), 0x01 },
+ { CCI_REG8(0x3032), 0x14 },
+ { CCI_REG8(0x305C), 0x0C },
+ { CCI_REG8(0x306D), 0x0A },
+ { CCI_REG8(0x3071), 0xFA },
+ { CCI_REG8(0x307E), 0x0A },
+ { CCI_REG8(0x307F), 0xFC },
+ { CCI_REG8(0x3091), 0x04 },
+ { CCI_REG8(0x3092), 0x60 },
+ { CCI_REG8(0x3096), 0xC0 },
+ { CCI_REG8(0x3100), 0x07 },
+ { CCI_REG8(0x3101), 0x4C },
+ { CCI_REG8(0x3118), 0xCC },
+ { CCI_REG8(0x3139), 0x06 },
+ { CCI_REG8(0x313A), 0x06 },
+ { CCI_REG8(0x313B), 0x04 },
+ { CCI_REG8(0x3143), 0x02 },
+ { CCI_REG8(0x314F), 0x0E },
+ { CCI_REG8(0x3169), 0x99 },
+ { CCI_REG8(0x316A), 0x99 },
+ { CCI_REG8(0x3171), 0x05 },
+ { CCI_REG8(0x31A1), 0xA7 },
+ { CCI_REG8(0x31A2), 0x9C },
+ { CCI_REG8(0x31A3), 0x8F },
+ { CCI_REG8(0x31A4), 0x75 },
+ { CCI_REG8(0x31A5), 0xEE },
+ { CCI_REG8(0x31A6), 0xEA },
+ { CCI_REG8(0x31A7), 0xE4 },
+ { CCI_REG8(0x31A8), 0xE4 },
+ { CCI_REG8(0x31DF), 0x05 },
+ { CCI_REG8(0x31EC), 0x1B },
+ { CCI_REG8(0x31ED), 0x1B },
+ { CCI_REG8(0x31EE), 0x1B },
+ { CCI_REG8(0x31F0), 0x1B },
+ { CCI_REG8(0x31F1), 0x1B },
+ { CCI_REG8(0x31F2), 0x1B },
+ { CCI_REG8(0x3204), 0x3F },
+ { CCI_REG8(0x3205), 0x03 },
+ { CCI_REG8(0x3210), 0x01 },
+ { CCI_REG8(0x3216), 0x68 },
+ { CCI_REG8(0x3217), 0x58 },
+ { CCI_REG8(0x3218), 0x58 },
+ { CCI_REG8(0x321A), 0x68 },
+ { CCI_REG8(0x321B), 0x60 },
+ { CCI_REG8(0x3238), 0x03 },
+ { CCI_REG8(0x3239), 0x03 },
+ { CCI_REG8(0x323A), 0x05 },
+ { CCI_REG8(0x323B), 0x06 },
+ { CCI_REG8(0x3243), 0x03 },
+ { CCI_REG8(0x3244), 0x08 },
+ { CCI_REG8(0x3245), 0x01 },
+ { CCI_REG8(0x3307), 0x19 },
+ { CCI_REG8(0x3308), 0x19 },
+ { CCI_REG8(0x3320), 0x01 },
+ { CCI_REG8(0x3326), 0x15 },
+ { CCI_REG8(0x3327), 0x0D },
+ { CCI_REG8(0x3328), 0x01 },
+ { CCI_REG8(0x3380), 0x01 },
+ { CCI_REG8(0x339E), 0x07 },
+ { CCI_REG8(0x3424), 0x00 },
+ { CCI_REG8(0x343C), 0x01 },
+ { CCI_REG8(0x3398), 0x04 },
+ { CCI_REG8(0x343A), 0x10 },
+ { CCI_REG8(0x339A), 0x22 },
+ { CCI_REG8(0x33B4), 0x00 },
+ { CCI_REG8(0x3393), 0x01 },
+ { CCI_REG8(0x33B3), 0x6E },
+ { CCI_REG8(0x3433), 0x06 },
+ { CCI_REG8(0x3433), 0x00 },
+ { CCI_REG8(0x33B3), 0x00 },
+ { CCI_REG8(0x3393), 0x03 },
+ { CCI_REG8(0x33B4), 0x03 },
+ { CCI_REG8(0x343A), 0x00 },
+ { CCI_REG8(0x339A), 0x00 },
+ { CCI_REG8(0x3398), 0x00 }
+};
+
+static const struct cci_reg_sequence t4ka3_pre_mode_set_regs[] = {
+ { CCI_REG8(0x0112), 0x0A },
+ { CCI_REG8(0x0113), 0x0A },
+ { CCI_REG8(0x0114), 0x03 },
+ { CCI_REG8(0x4136), 0x13 },
+ { CCI_REG8(0x4137), 0x33 },
+ { CCI_REG8(0x0820), 0x0A },
+ { CCI_REG8(0x0821), 0x0D },
+ { CCI_REG8(0x0822), 0x00 },
+ { CCI_REG8(0x0823), 0x00 },
+ { CCI_REG8(0x0301), 0x0A },
+ { CCI_REG8(0x0303), 0x01 },
+ { CCI_REG8(0x0305), 0x04 },
+ { CCI_REG8(0x0306), 0x02 },
+ { CCI_REG8(0x0307), 0x18 },
+ { CCI_REG8(0x030B), 0x01 },
+};
+
+static const struct cci_reg_sequence t4ka3_post_mode_set_regs[] = {
+ { CCI_REG8(0x0902), 0x00 },
+ { CCI_REG8(0x4220), 0x00 },
+ { CCI_REG8(0x4222), 0x01 },
+ { CCI_REG8(0x3380), 0x01 },
+ { CCI_REG8(0x3090), 0x88 },
+ { CCI_REG8(0x3394), 0x20 },
+ { CCI_REG8(0x3090), 0x08 },
+ { CCI_REG8(0x3394), 0x10 }
+};
+
+static const s64 link_freq_menu_items[] = {
+ T4KA3_LINK_FREQ,
+};
+
+/* T4KA3 default GRBG */
+static const int t4ka3_hv_flip_bayer_order[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+};
+
+static const struct v4l2_rect t4ka3_default_crop = {
+ .left = T4KA3_ACTIVE_START_LEFT,
+ .top = T4KA3_ACTIVE_START_TOP,
+ .width = T4KA3_ACTIVE_WIDTH,
+ .height = T4KA3_ACTIVE_HEIGHT,
+};
+
+static void t4ka3_set_bayer_order(struct t4ka3_data *sensor,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ unsigned int hv_flip = 0;
+
+ if (sensor->ctrls.vflip && sensor->ctrls.vflip->val)
+ hv_flip += 1;
+
+ if (sensor->ctrls.hflip && sensor->ctrls.hflip->val)
+ hv_flip += 2;
+
+ fmt->code = t4ka3_hv_flip_bayer_order[hv_flip];
+}
+
+static int t4ka3_update_exposure_range(struct t4ka3_data *sensor,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ int exp_max = fmt->height + sensor->ctrls.vblank->val -
+ T4KA3_COARSE_INTEGRATION_TIME_MARGIN;
+
+ return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
+ 1, exp_max);
+}
+
+static void t4ka3_fill_format(struct t4ka3_data *sensor,
+ struct v4l2_mbus_framefmt *fmt,
+ unsigned int width, unsigned int height)
+{
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->width = width;
+ fmt->height = height;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ t4ka3_set_bayer_order(sensor, fmt);
+}
+
+static void t4ka3_calc_mode(struct t4ka3_data *sensor,
+ struct v4l2_mbus_framefmt *fmt,
+ struct v4l2_rect *crop)
+{
+ int width;
+ int height;
+ int binning;
+
+ width = fmt->width;
+ height = fmt->height;
+
+ if (width <= (crop->width / 2) && height <= (crop->height / 2))
+ binning = 2;
+ else
+ binning = 1;
+
+ width *= binning;
+ height *= binning;
+
+ sensor->mode.binning = binning;
+ sensor->mode.win_x = (crop->left + (crop->width - width) / 2) & ~1;
+ sensor->mode.win_y = (crop->top + (crop->height - height) / 2) & ~1;
+ /*
+ * t4ka3's window is done after binning, but must still be a
+ * multiple of 2 ?
+ * Round up to avoid top 2 black lines in 1640x1230 (quarter res) case.
+ */
+ sensor->mode.win_x = DIV_ROUND_UP(sensor->mode.win_x, binning);
+ sensor->mode.win_y = DIV_ROUND_UP(sensor->mode.win_y, binning);
+}
+
+static void t4ka3_get_vblank_limits(struct t4ka3_data *sensor,
+ struct v4l2_subdev_state *state,
+ int *min, int *max, int *def)
+{
+ struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0);
+
+ *min = T4KA3_MIN_VBLANK + (sensor->mode.binning - 1) * fmt->height;
+ *max = T4KA3_MAX_VBLANK - fmt->height;
+ *def = T4KA3_LINES_PER_FRAME_30FPS - fmt->height;
+}
+
+static int t4ka3_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct v4l2_rect *crop =
+ v4l2_subdev_state_get_crop(sd_state, format->pad);
+ unsigned int width, height;
+ int min, max, def, ret = 0;
+
+ /* Limit set_fmt max size to crop width / height */
+ width = clamp_val(ALIGN(format->format.width, 2),
+ T4KA3_MIN_CROP_WIDTH, crop->width);
+ height = clamp_val(ALIGN(format->format.height, 2),
+ T4KA3_MIN_CROP_HEIGHT, crop->height);
+ t4ka3_fill_format(sensor, &format->format, width, height);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && sensor->streaming)
+ return -EBUSY;
+
+ *v4l2_subdev_state_get_format(sd_state, 0) = format->format;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ t4ka3_calc_mode(sensor, fmt, crop);
+
+ /* vblank range is height dependent adjust and reset to default */
+ t4ka3_get_vblank_limits(sensor, sd_state, &min, &max, &def);
+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, min, max, 1, def);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
+ if (ret)
+ return ret;
+
+ def = T4KA3_PIXELS_PER_LINE - fmt->width;
+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_s_ctrl(sensor->ctrls.hblank, def);
+}
+
+/* Horizontal or vertically flip the image */
+static int t4ka3_update_flip(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ int value, u8 flip_bit)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+ int ret;
+ u64 val;
+
+ if (sensor->streaming)
+ return -EBUSY;
+
+ val = value ? flip_bit : 0;
+
+ ret = cci_update_bits(sensor->regmap, T4KA3_REG_IMG_ORIENTATION,
+ flip_bit, val, NULL);
+ if (ret)
+ return ret;
+
+ t4ka3_set_bayer_order(sensor, fmt);
+
+ return 0;
+}
+
+static int t4ka3_test_pattern(struct t4ka3_data *sensor, s32 value)
+{
+ return cci_write(sensor->regmap, T4KA3_REG_TEST_PATTERN_MODE,
+ value, NULL);
+}
+
+static int t4ka3_detect(struct t4ka3_data *sensor, u16 *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ struct i2c_adapter *adapter = client->adapter;
+ u64 high, low;
+ int ret = 0;
+
+ /* i2c check */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ /* check sensor chip ID */
+ cci_read(sensor->regmap, T4KA3_REG_PRODUCT_ID_HIGH, &high, &ret);
+ cci_read(sensor->regmap, T4KA3_REG_PRODUCT_ID_LOW, &low, &ret);
+ if (ret)
+ return ret;
+
+ *id = (((u8)high) << 8) | (u8)low;
+ if (*id != T4KA3_PRODUCT_ID) {
+ dev_err(sensor->dev, "main sensor t4ka3 ID error\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int t4ka3_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct t4ka3_data *sensor = ctrl_to_t4ka3(ctrl);
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->sd);
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(state, 0);
+ int ret;
+
+ /* Update exposure range on vblank changes */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ ret = t4ka3_update_exposure_range(sensor, fmt);
+ 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))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TEST_PATTERN:
+ ret = t4ka3_test_pattern(sensor, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = t4ka3_update_flip(&sensor->sd, fmt,
+ ctrl->val, T4KA3_VFLIP_BIT);
+ break;
+ case V4L2_CID_HFLIP:
+ ret = t4ka3_update_flip(&sensor->sd, fmt,
+ ctrl->val, T4KA3_HFLIP_BIT);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(sensor->regmap, T4KA3_REG_FRAME_LENGTH_LINES,
+ fmt->height + ctrl->val, NULL);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(sensor->regmap,
+ T4KA3_REG_COARSE_INTEGRATION_TIME,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(sensor->regmap, T4KA3_REG_GLOBAL_GAIN,
+ ctrl->val, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(sensor->sd.dev);
+
+ return ret;
+}
+
+static int t4ka3_set_mode(struct t4ka3_data *sensor,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0);
+ int ret = 0;
+
+ cci_write(sensor->regmap, T4KA3_REG_HORZ_OUTPUT_SIZE, fmt->width, &ret);
+ /* Write mode-height - 2 otherwise things don't work, hw-bug ? */
+ cci_write(sensor->regmap, T4KA3_REG_VERT_OUTPUT_SIZE,
+ fmt->height - 2, &ret);
+
+ cci_write(sensor->regmap, T4KA3_REG_PIXELS_PER_LINE,
+ T4KA3_PIXELS_PER_LINE, &ret);
+ /* Always use the full sensor, using window to crop */
+ cci_write(sensor->regmap, T4KA3_REG_HORZ_START, 0, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_VERT_START, 0, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_HORZ_END,
+ T4KA3_NATIVE_WIDTH - 1, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_VERT_END,
+ T4KA3_NATIVE_HEIGHT - 1, &ret);
+ /* Set window */
+ cci_write(sensor->regmap, T4KA3_REG_WIN_START_X,
+ sensor->mode.win_x, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_WIN_START_Y,
+ sensor->mode.win_y, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_WIN_WIDTH, fmt->width, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_WIN_HEIGHT, fmt->height, &ret);
+ /* Write 1 to unknown register 0x0900 */
+ cci_write(sensor->regmap, T4KA3_REG_0900, 1, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_BINNING,
+ T4KA3_BINNING_VAL(sensor->mode.binning), &ret);
+
+ return ret;
+}
+
+static int t4ka3_enable_stream(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+ int ret;
+
+ ret = pm_runtime_get_sync(sensor->sd.dev);
+ if (ret < 0) {
+ dev_err(sensor->dev, "power-up err.\n");
+ goto error_powerdown;
+ }
+
+ cci_multi_reg_write(sensor->regmap, t4ka3_init_config,
+ ARRAY_SIZE(t4ka3_init_config), &ret);
+ /* enable group hold */
+ cci_write(sensor->regmap, T4KA3_REG_PARAM_HOLD, 1, &ret);
+ cci_multi_reg_write(sensor->regmap, t4ka3_pre_mode_set_regs,
+ ARRAY_SIZE(t4ka3_pre_mode_set_regs), &ret);
+ if (ret)
+ goto error_powerdown;
+
+ ret = t4ka3_set_mode(sensor, state);
+ if (ret)
+ goto error_powerdown;
+
+ ret = cci_multi_reg_write(sensor->regmap, t4ka3_post_mode_set_regs,
+ ARRAY_SIZE(t4ka3_post_mode_set_regs), NULL);
+ if (ret)
+ goto error_powerdown;
+
+ /* Restore value of all ctrls */
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+ if (ret)
+ goto error_powerdown;
+
+ /* disable group hold */
+ cci_write(sensor->regmap, T4KA3_REG_PARAM_HOLD, 0, &ret);
+ cci_write(sensor->regmap, T4KA3_REG_STREAM, 1, &ret);
+ if (ret)
+ goto error_powerdown;
+
+ sensor->streaming = 1;
+
+ return ret;
+
+error_powerdown:
+ pm_runtime_put(sensor->sd.dev);
+
+ return ret;
+}
+
+static int t4ka3_disable_stream(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+ int ret;
+
+ ret = cci_write(sensor->regmap, T4KA3_REG_STREAM, 0, NULL);
+ pm_runtime_put(sensor->sd.dev);
+ sensor->streaming = 0;
+
+ if (ret)
+ dev_err(sensor->dev,
+ "failed to disable stream with return value: %d\n",
+ ret);
+
+ return 0;
+}
+
+static int t4ka3_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
+ break;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = T4KA3_NATIVE_WIDTH;
+ sel->r.height = T4KA3_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r = t4ka3_default_crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int t4ka3_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+ struct v4l2_rect rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /*
+ * Clamp the boundaries of the crop rectangle to the size of the sensor
+ * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't
+ * disrupted.
+ */
+ rect.left = clamp_val(ALIGN(sel->r.left, 2),
+ T4KA3_NATIVE_START_LEFT, T4KA3_NATIVE_WIDTH);
+ rect.top = clamp_val(ALIGN(sel->r.top, 2),
+ T4KA3_NATIVE_START_TOP, T4KA3_NATIVE_HEIGHT);
+ rect.width = clamp_val(ALIGN(sel->r.width, 2), T4KA3_MIN_CROP_WIDTH,
+ T4KA3_NATIVE_WIDTH - rect.left);
+ rect.height = clamp_val(ALIGN(sel->r.height, 2), T4KA3_MIN_CROP_HEIGHT,
+ T4KA3_NATIVE_HEIGHT - rect.top);
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+
+ if (rect.width != crop->width || rect.height != crop->height) {
+ /*
+ * Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = v4l2_subdev_state_get_format(state, sel->pad);
+ format->width = rect.width;
+ format->height = rect.height;
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ t4ka3_calc_mode(sensor, format, crop);
+ }
+
+ sel->r = *crop = rect;
+
+ return 0;
+}
+
+static int
+t4ka3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ return 0;
+}
+
+static int t4ka3_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_rect *crop;
+
+ if (fse->index >= T4KA3_FRAME_SIZES)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(sd_state, fse->pad);
+
+ fse->min_width = crop->width / (fse->index + 1);
+ fse->min_height = crop->height / (fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int t4ka3_check_hwcfg(struct t4ka3_data *sensor)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(sensor->dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct fwnode_handle *endpoint;
+ unsigned long link_freq_bitmap;
+ int ret;
+
+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ fwnode_handle_put(endpoint);
+ if (ret)
+ return ret;
+
+ ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_freq_menu_items,
+ ARRAY_SIZE(link_freq_menu_items),
+ &link_freq_bitmap);
+
+ if (ret < 0)
+ goto out_free_bus_cfg;
+
+ sensor->link_freq_index = ffs(link_freq_bitmap) - 1;
+
+ /* 4 MIPI lanes */
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ ret = dev_err_probe(sensor->dev, -EINVAL,
+ "number of CSI2 data lanes %u is not supported\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ goto out_free_bus_cfg;
+ }
+
+ sensor->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+out_free_bus_cfg:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int t4ka3_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+
+ *v4l2_subdev_state_get_crop(sd_state, 0) = t4ka3_default_crop;
+
+ t4ka3_fill_format(sensor, v4l2_subdev_state_get_format(sd_state, 0),
+ T4KA3_ACTIVE_WIDTH, T4KA3_ACTIVE_HEIGHT);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops t4ka3_ctrl_ops = {
+ .s_ctrl = t4ka3_s_ctrl,
+};
+
+static const struct v4l2_subdev_video_ops t4ka3_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops t4ka3_pad_ops = {
+ .enum_mbus_code = t4ka3_enum_mbus_code,
+ .enum_frame_size = t4ka3_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = t4ka3_set_pad_format,
+ .get_selection = t4ka3_get_selection,
+ .set_selection = t4ka3_set_selection,
+ .enable_streams = t4ka3_enable_stream,
+ .disable_streams = t4ka3_disable_stream,
+};
+
+static const struct v4l2_subdev_ops t4ka3_ops = {
+ .video = &t4ka3_video_ops,
+ .pad = &t4ka3_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops t4ka3_internal_ops = {
+ .init_state = t4ka3_init_state,
+};
+
+static int t4ka3_init_controls(struct t4ka3_data *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &t4ka3_ctrl_ops;
+ struct t4ka3_ctrls *ctrls = &sensor->ctrls;
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ struct v4l2_fwnode_device_properties props;
+ int ret, min, max, def;
+ static const char * const test_pattern_menu[] = {
+ "Disabled",
+ "Solid White",
+ "Color Bars",
+ "Gradient",
+ "Random Data",
+ };
+
+ v4l2_ctrl_handler_init(hdl, 11);
+
+ hdl->lock = &sensor->lock;
+
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ ctrls->test_pattern =
+ v4l2_ctrl_new_std_menu_items(hdl, ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(test_pattern_menu) - 1,
+ 0, 0, test_pattern_menu);
+ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL,
+ V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
+ 0, T4KA3_PIXEL_RATE,
+ 1, T4KA3_PIXEL_RATE);
+
+ state = v4l2_subdev_lock_and_get_active_state(&sensor->sd);
+ fmt = v4l2_subdev_state_get_format(state, 0);
+ crop = v4l2_subdev_state_get_crop(state, 0);
+
+ t4ka3_calc_mode(sensor, fmt, crop);
+ t4ka3_get_vblank_limits(sensor, state, &min, &max, &def);
+
+ v4l2_subdev_unlock_state(state);
+
+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ min, max, 1, def);
+
+ def = T4KA3_PIXELS_PER_LINE - T4KA3_ACTIVE_WIDTH;
+ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ def, def, 1, def);
+
+ max = T4KA3_LINES_PER_FRAME_30FPS -
+ T4KA3_COARSE_INTEGRATION_TIME_MARGIN;
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, max, 1, max);
+
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ T4KA3_MIN_GLOBAL_GAIN_SUPPORTED,
+ T4KA3_MAX_GLOBAL_GAIN_SUPPORTED,
+ 1, T4KA3_MIN_GLOBAL_GAIN_SUPPORTED);
+
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+
+ if (hdl->error)
+ return hdl->error;
+
+ 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;
+
+ return 0;
+}
+
+static int t4ka3_pm_suspend(struct device *dev)
+{
+ struct t4ka3_data *sensor = dev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(sensor->powerdown_gpio, 1);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+
+ return 0;
+}
+
+static int t4ka3_pm_resume(struct device *dev)
+{
+ struct t4ka3_data *sensor = dev_get_drvdata(dev);
+ u16 sensor_id;
+ int ret;
+
+ usleep_range(5000, 6000);
+
+ gpiod_set_value_cansleep(sensor->powerdown_gpio, 0);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+
+ /* waiting for the sensor after powering up */
+ fsleep(20000);
+
+ ret = t4ka3_detect(sensor, &sensor_id);
+ if (ret) {
+ dev_err(sensor->dev, "sensor detect failed\n");
+ gpiod_set_value_cansleep(sensor->powerdown_gpio, 1);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(t4ka3_pm_ops, t4ka3_pm_suspend,
+ t4ka3_pm_resume, NULL);
+
+static void t4ka3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct t4ka3_data *sensor = to_t4ka3_sensor(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sensor->sd.entity);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ t4ka3_pm_suspend(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static int t4ka3_probe(struct i2c_client *client)
+{
+ struct t4ka3_data *sensor;
+ int ret;
+
+ /* allocate sensor device & init sub device */
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->dev = &client->dev;
+
+ ret = t4ka3_check_hwcfg(sensor);
+ if (ret)
+ return ret;
+
+ mutex_init(&sensor->lock);
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &t4ka3_ops);
+ sensor->sd.internal_ops = &t4ka3_internal_ops;
+
+ sensor->powerdown_gpio = devm_gpiod_get(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->powerdown_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(sensor->powerdown_gpio),
+ "getting powerdown GPIO\n");
+
+ sensor->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->reset_gpio),
+ "getting reset GPIO\n");
+
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
+
+ ret = t4ka3_pm_resume(sensor->dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret)
+ goto err_pm_disable;
+
+ sensor->sd.state_lock = sensor->ctrls.handler.lock;
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to init subdev: %d", ret);
+ goto err_media_entity;
+ }
+
+ ret = t4ka3_init_controls(sensor);
+ if (ret)
+ goto err_controls;
+
+ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
+ if (ret)
+ goto err_controls;
+
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+err_controls:
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+ v4l2_subdev_cleanup(&sensor->sd);
+
+err_media_entity:
+ media_entity_cleanup(&sensor->sd.entity);
+
+err_pm_disable:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+ t4ka3_pm_suspend(&client->dev);
+
+ return ret;
+}
+
+static const struct acpi_device_id t4ka3_acpi_match[] = {
+ { "XMCC0003" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, t4ka3_acpi_match);
+
+static struct i2c_driver t4ka3_driver = {
+ .driver = {
+ .name = "t4ka3",
+ .acpi_match_table = ACPI_PTR(t4ka3_acpi_match),
+ .pm = pm_sleep_ptr(&t4ka3_pm_ops),
+ },
+ .probe = t4ka3_probe,
+ .remove = t4ka3_remove,
+};
+module_i2c_driver(t4ka3_driver)
+
+MODULE_DESCRIPTION("A low-level driver for T4KA3 sensor");
+MODULE_AUTHOR("HARVEY LV <harvey.lv@intel.com>");
+MODULE_AUTHOR("Kate Hsuan <hpa@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c
index d64d0099e6fe..3fb2166c81ef 100644
--- a/drivers/media/i2c/vgxy61.c
+++ b/drivers/media/i2c/vgxy61.c
@@ -1802,6 +1802,9 @@ static int vgxy61_probe(struct i2c_client *client)
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+ "failed to get reset gpio\n");
ret = vgxy61_get_regulators(sensor);
if (ret) {
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
index 373b6c6817d7..f704476506e0 100644
--- a/drivers/media/pci/bt8xx/bttv-input.c
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv)
if (btv->remote == NULL)
return;
- bttv_ir_stop(btv);
rc_unregister_device(btv->remote->dev);
+ bttv_ir_stop(btv);
+ rc_free_device(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
}
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index da23e7dfeef5..95a84709d5a6 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -2443,6 +2443,9 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_VIEWCAST_460E:
case CX23885_BOARD_AVERMEDIA_CE310B:
case CX23885_BOARD_AVERMEDIA_H789C:
+ if (dev->disable_analog)
+ break;
+
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 0892a5fd137d..4a8af8b88d84 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -48,6 +48,11 @@ static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
+static unsigned int disable_analog_video[8] = { 0, 0, 0, 0, 0, 0, 0, 0};
+static int disable_analog_argc;
+module_param_array(disable_analog_video, int, &disable_analog_argc, 0644);
+MODULE_PARM_DESC(disable_analog_video, "disable analog video for card type");
+
static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");
@@ -924,6 +929,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885;
}
+ for (i = 0; i < disable_analog_argc; i++) {
+ if (disable_analog_video[i] == dev->board) {
+ pr_warn("Disabling analog for board %d\n", dev->board);
+ dev->disable_analog = 1;
+ }
+ }
+
/* If the user specific a clk freq override, apply it */
if (cx23885_boards[dev->board].clk_freq > 0)
dev->clk_freq = cx23885_boards[dev->board].clk_freq;
@@ -1043,7 +1055,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_gpio_enable(dev, 0x300, 0);
}
- if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO &&
+ !dev->disable_analog) {
if (cx23885_video_register(dev) < 0) {
pr_err("%s() Failed to register analog video adapters on VID_A\n",
__func__);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 05a7859cbe57..f240ccda40ed 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -2373,7 +2373,8 @@ static int dvb_register(struct cx23885_tsport *port)
port->i2c_client_tuner = client_tuner;
/* we only attach tuner for analog on the 888 version */
- if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) {
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB &&
+ !dev->disable_analog) {
pr_info("%s(): QUADHD_DVB analog setup\n",
__func__);
dev->ts1.analog_fe.tuner_priv = client_tuner;
@@ -2466,7 +2467,8 @@ static int dvb_register(struct cx23885_tsport *port)
port->i2c_client_tuner = client_tuner;
/* we only attach tuner for analog on the 888 version */
- if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) {
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC &&
+ !dev->disable_analog) {
pr_info("%s(): QUADHD_ATSC analog setup\n",
__func__);
dev->ts1.analog_fe.tuner_priv = client_tuner;
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index ffbbeca8a8e5..554767b8ef2b 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
if (dev->kernel_ir == NULL)
return;
rc_unregister_device(dev->kernel_ir->rc);
+ rc_free_device(dev->kernel_ir->rc);
kfree(dev->kernel_ir->phys);
kfree(dev->kernel_ir->name);
kfree(dev->kernel_ir);
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index 8ba1f306238c..8653ee1d9ba6 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -404,6 +404,7 @@ struct cx23885_dev {
unsigned char radio_addr;
struct v4l2_subdev *sd_cx25840;
struct work_struct cx25840_work;
+ unsigned int disable_analog;
/* Infrared */
struct v4l2_subdev *sd_ir;
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index 3f73391f9098..4fdc59aaed8e 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -397,7 +397,7 @@ static int dsp_buffer_free(struct cx25821_audio_dev *chip)
{
struct cx25821_riscmem *risc = &chip->buf->risc;
- BUG_ON(!chip->dma_size);
+ WARN_ON(!chip->dma_size);
dprintk(2, "Freeing buffer\n");
cx25821_alsa_dma_unmap(chip);
@@ -509,8 +509,8 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream,
chip->num_periods = params_periods(hw_params);
chip->dma_size = chip->period_size * params_periods(hw_params);
- BUG_ON(!chip->dma_size);
- BUG_ON(chip->num_periods & (chip->num_periods - 1));
+ WARN_ON(!chip->dma_size);
+ WARN_ON(chip->num_periods & (chip->num_periods - 1));
buf = kzalloc_obj(*buf);
if (NULL == buf)
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index e958eecb29c5..5d9ce4f9af01 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -509,8 +509,9 @@ int cx88_ir_fini(struct cx88_core *core)
if (!ir)
return 0;
- cx88_ir_stop(core);
rc_unregister_device(ir->dev);
+ cx88_ir_stop(core);
+ rc_free_device(ir->dev);
kfree(ir);
/* done */
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index de05d8b0f9dc..bbd24769ae56 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
static void dm1105_ir_exit(struct dm1105_dev *dm1105)
{
rc_unregister_device(dm1105->ir.dev);
+ rc_free_device(dm1105->ir.dev);
}
static int dm1105_hw_init(struct dm1105_dev *dev)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 32cc95a766b7..fc6608e33de4 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -91,6 +91,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
/* Omnivision OV2680 */
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
+ /* Omnivision OV5675 */
+ IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
/* Sony IMX471 */
@@ -107,6 +109,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9340"),
+ },
+ .driver_data = "OVTI02C1",
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9350"),
},
.driver_data = "OVTI02C1",
@@ -114,6 +123,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 9440"),
+ },
+ .driver_data = "OVTI02C1",
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 9640"),
},
.driver_data = "OVTI02C1",
diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
index 34f67f4f1bb5..d033d4618169 100644
--- a/drivers/media/pci/intel/ipu6/ipu6.c
+++ b/drivers/media/pci/intel/ipu6/ipu6.c
@@ -686,7 +686,7 @@ out_free_irq:
out_ipu6_rpm_put:
pm_runtime_put_sync(&isp->psys->auxdev.dev);
out_ipu6_bus_del_devices:
- if (isp->psys) {
+ if (!IS_ERR_OR_NULL(isp->psys)) {
ipu6_cpd_free_pkg_dir(isp->psys);
ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
}
diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
index 34c0d979240f..edb4cacf55d2 100644
--- a/drivers/media/pci/mantis/mantis_input.c
+++ b/drivers/media/pci/mantis/mantis_input.c
@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init);
void mantis_input_exit(struct mantis_pci *mantis)
{
rc_unregister_device(mantis->rc);
+ rc_free_device(mantis->rc);
}
EXPORT_SYMBOL_GPL(mantis_input_exit);
diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c
index a7351a469386..d23d854581c5 100644
--- a/drivers/media/pci/mgb4/mgb4_core.c
+++ b/drivers/media/pci/mgb4/mgb4_core.c
@@ -84,7 +84,8 @@ static int temp_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
- u32 val10, raw;
+ u32 raw;
+ int val10;
if (type != hwmon_temp || attr != hwmon_temp_input)
return -EOPNOTSUPP;
diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_in.c b/drivers/media/pci/mgb4/mgb4_sysfs_in.c
index 4cd4addcd0a5..80ff05450e98 100644
--- a/drivers/media/pci/mgb4/mgb4_sysfs_in.c
+++ b/drivers/media/pci/mgb4/mgb4_sysfs_in.c
@@ -128,7 +128,16 @@ static ssize_t color_mapping_show(struct device *dev,
u32 config = mgb4_read_reg(&vindev->mgbdev->video,
vindev->config->regs.config);
- return sprintf(buf, "%s\n", config & (1U << 8) ? "0" : "1");
+ switch ((config >> 7) & 3) {
+ case 0: /* SPWG/VESA */
+ return sprintf(buf, "1\n");
+ case 1: /* ZDML */
+ return sprintf(buf, "2\n");
+ case 2: /* OLDI/JEIDA */
+ return sprintf(buf, "0\n");
+ default:
+ return -EIO;
+ }
}
/*
@@ -151,17 +160,20 @@ static ssize_t color_mapping_store(struct device *dev,
switch (val) {
case 0: /* OLDI/JEIDA */
- fpga_data = (1U << 8);
+ fpga_data = 2;
break;
case 1: /* SPWG/VESA */
fpga_data = 0;
break;
+ case 2: /* ZDML */
+ fpga_data = 1;
+ break;
default:
return -EINVAL;
}
mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.config,
- 1U << 8, fpga_data);
+ 3U << 7, fpga_data << 7);
return count;
}
diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c
index 5769f3ca6c2f..d807218e28ca 100644
--- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c
+++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c
@@ -143,6 +143,64 @@ end:
return ret;
}
+static ssize_t color_mapping_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(dev);
+ struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
+ u32 config = mgb4_read_reg(&voutdev->mgbdev->video,
+ voutdev->config->regs.config);
+
+ switch ((config >> 6) & 3) {
+ case 0: /* SPWG/VESA */
+ return sprintf(buf, "1\n");
+ case 1: /* ZDML */
+ return sprintf(buf, "2\n");
+ case 2: /* OLDI/JEIDA */
+ return sprintf(buf, "0\n");
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Color mapping change is expected to be called on live streams. Video device
+ * locking/queue check is not needed.
+ */
+static ssize_t color_mapping_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct video_device *vdev = to_video_device(dev);
+ struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
+ u32 fpga_data;
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 0: /* OLDI/JEIDA */
+ fpga_data = 2;
+ break;
+ case 1: /* SPWG/VESA */
+ fpga_data = 0;
+ break;
+ case 2: /* ZDML */
+ fpga_data = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config,
+ 3U << 6, fpga_data << 6);
+
+ return count;
+}
+
static ssize_t display_width_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -711,6 +769,7 @@ static DEVICE_ATTR_RW(hback_porch);
static DEVICE_ATTR_RW(hfront_porch);
static DEVICE_ATTR_RW(vback_porch);
static DEVICE_ATTR_RW(vfront_porch);
+static DEVICE_ATTR_RW(color_mapping);
static DEVICE_ATTR_RW(fpdl3_output_width);
@@ -731,6 +790,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = {
&dev_attr_vback_porch.attr,
&dev_attr_vfront_porch.attr,
&dev_attr_fpdl3_output_width.attr,
+ &dev_attr_color_mapping.attr,
NULL
};
@@ -740,6 +800,7 @@ struct attribute *mgb4_gmsl3_out_attrs[] = {
&dev_attr_display_width.attr,
&dev_attr_display_height.attr,
&dev_attr_frame_rate.attr,
+ &dev_attr_color_mapping.attr,
NULL
};
@@ -759,5 +820,6 @@ struct attribute *mgb4_gmsl1_out_attrs[] = {
&dev_attr_hfront_porch.attr,
&dev_attr_vback_porch.attr,
&dev_attr_vfront_porch.attr,
+ &dev_attr_color_mapping.attr,
NULL
};
diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c
index 04e85765373e..1164e91cbb7b 100644
--- a/drivers/media/pci/saa7134/saa7134-i2c.c
+++ b/drivers/media/pci/saa7134/saa7134-i2c.c
@@ -28,7 +28,7 @@ static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
-#define i2c_dbg(level, fmt, arg...) do { \
+#define saa7134_i2c_dbg(level, fmt, arg...) do { \
if (i2c_debug == level) \
printk(KERN_DEBUG pr_fmt("i2c: " fmt), ## arg); \
} while (0)
@@ -84,20 +84,20 @@ static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev)
enum i2c_status status;
status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f;
- i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]);
+ saa7134_i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]);
return status;
}
static inline void i2c_set_status(struct saa7134_dev *dev,
enum i2c_status status)
{
- i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]);
+ saa7134_i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]);
saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status);
}
static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr)
{
- i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]);
+ saa7134_i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]);
saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6);
}
@@ -160,7 +160,7 @@ static int i2c_reset(struct saa7134_dev *dev)
enum i2c_status status;
int count;
- i2c_dbg(2, "i2c reset\n");
+ saa7134_i2c_dbg(2, "i2c reset\n");
status = i2c_get_status(dev);
if (!i2c_is_error(status))
return true;
@@ -198,7 +198,7 @@ static inline int i2c_send_byte(struct saa7134_dev *dev,
// dword |= 0x40 << 16; /* 400 kHz */
dword |= 0xf0 << 24;
saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword);
- i2c_dbg(2, "i2c data => 0x%x\n", data);
+ saa7134_i2c_dbg(2, "i2c data => 0x%x\n", data);
if (!i2c_is_busy_wait(dev))
return -EIO;
@@ -220,7 +220,7 @@ static inline int i2c_recv_byte(struct saa7134_dev *dev)
if (i2c_is_error(status))
return -EIO;
data = saa_readb(SAA7134_I2C_DATA);
- i2c_dbg(2, "i2c data <= 0x%x\n", data);
+ saa7134_i2c_dbg(2, "i2c data <= 0x%x\n", data);
return data;
}
@@ -237,12 +237,12 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
if (!i2c_reset(dev))
return -EIO;
- i2c_dbg(2, "start xfer\n");
- i2c_dbg(1, "i2c xfer:");
+ saa7134_i2c_dbg(2, "start xfer\n");
+ saa7134_i2c_dbg(1, "i2c xfer:");
for (i = 0; i < num; i++) {
if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) {
/* send address */
- i2c_dbg(2, "send address\n");
+ saa7134_i2c_dbg(2, "send address\n");
addr = msgs[i].addr << 1;
if (msgs[i].flags & I2C_M_RD)
addr |= 1;
@@ -265,7 +265,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
- i2c_dbg(2, "read bytes\n");
+ saa7134_i2c_dbg(2, "read bytes\n");
for (byte = 0; byte < msgs[i].len; byte++) {
i2c_cont(1, " =");
rc = i2c_recv_byte(dev);
@@ -286,7 +286,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
} else {
/* write bytes */
- i2c_dbg(2, "write bytes\n");
+ saa7134_i2c_dbg(2, "write bytes\n");
for (byte = 0; byte < msgs[i].len; byte++) {
data = msgs[i].buf[byte];
i2c_cont(1, " %02x", data);
@@ -296,7 +296,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
}
}
- i2c_dbg(2, "xfer done\n");
+ saa7134_i2c_dbg(2, "xfer done\n");
i2c_cont(1, " >");
i2c_set_attr(dev,STOP);
rc = -EIO;
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index 5b7101415780..7f6680de3156 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;
rc_unregister_device(dev->remote->dev);
+ rc_free_device(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
}
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index 74406d5ea0a5..6bcde506adf5 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -888,6 +888,15 @@ static int get_resources(struct saa7164_dev *dev)
return -EBUSY;
}
+static void release_resources(struct saa7164_dev *dev)
+{
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ release_mem_region(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2));
+}
+
static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
{
struct saa7164_port *port = NULL;
@@ -947,9 +956,9 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
- mutex_lock(&devlist);
- list_add_tail(&dev->devlist, &saa7164_devlist);
- mutex_unlock(&devlist);
+ scoped_guard(mutex, &devlist) {
+ list_add_tail(&dev->devlist, &saa7164_devlist);
+ }
/* board config */
dev->board = UNSET;
@@ -996,11 +1005,17 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
}
/* PCI/e allocations */
- dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
+ dev->lmmio = pci_ioremap_bar(dev->pci, 0);
+ if (!dev->lmmio) {
+ dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 0\n");
+ goto err_ioremap_bar0;
+ }
- dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
- pci_resource_len(dev->pci, 2));
+ dev->lmmio2 = pci_ioremap_bar(dev->pci, 2);
+ if (!dev->lmmio2) {
+ dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 2\n");
+ goto err_ioremap_bar2;
+ }
dev->bmmio = (u8 __iomem *)dev->lmmio;
dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
@@ -1019,17 +1034,25 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
saa7164_pci_quirks(dev);
return 0;
+
+err_ioremap_bar2:
+ iounmap(dev->lmmio);
+err_ioremap_bar0:
+ release_resources(dev);
+
+ scoped_guard(mutex, &devlist) {
+ list_del(&dev->devlist);
+ }
+ saa7164_devcount--;
+
+ return -ENODEV;
}
static void saa7164_dev_unregister(struct saa7164_dev *dev)
{
dprintk(1, "%s()\n", __func__);
- release_mem_region(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
-
- release_mem_region(pci_resource_start(dev->pci, 2),
- pci_resource_len(dev->pci, 2));
+ release_resources(dev);
if (!atomic_dec_and_test(&dev->refcount))
return;
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index cc9f384f7f1e..341cef62452f 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -10,8 +10,8 @@
#include "saa7164.h"
-#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw"
-#define SAA7164_REV2_FIRMWARE_SIZE 4019072
+#define SAA7164_REV2_FIRMWARE "v4l-saa7164-1.0.2-3.fw"
+#define SAA7164_REV2_FIRMWARE_SIZE 4038864
#define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw"
#define SAA7164_REV3_FIRMWARE_SIZE 4019072
diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c
index c0604d9c7011..0bbe4fa2d5a8 100644
--- a/drivers/media/pci/smipcie/smipcie-ir.c
+++ b/drivers/media/pci/smipcie/smipcie-ir.c
@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev)
rc_unregister_device(rc_dev);
smi_ir_stop(ir);
+ rc_free_device(rc_dev);
ir->rc_dev = NULL;
}
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 3709c0fb23b0..8b496b959d7e 100644
--- a/drivers/media/pci/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work);
rc_unregister_device(budget_ci->ir.dev);
+ rc_free_device(budget_ci->ir.dev);
}
static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
index d81facf735d9..f707bdc1fb0f 100644
--- a/drivers/media/pci/zoran/zoran_card.c
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -1373,7 +1373,7 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (zr->codec->type != zr->card.video_codec) {
pci_err(pdev, "%s - wrong codec\n", __func__);
- goto zr_unreg_videocodec;
+ goto zr_detach_codec;
}
}
if (zr->card.video_vfe != 0) {
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index 64fc88d89ccc..7cccc994fc50 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -447,17 +447,14 @@ static void vpu_m2m_device_run(void *priv)
{
}
-static void vpu_m2m_job_abort(void *priv)
+static int vpu_m2m_job_ready(void *priv)
{
- struct vpu_inst *inst = priv;
- struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx;
-
- v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx);
+ return 0;
}
static const struct v4l2_m2m_ops vpu_m2m_ops = {
.device_run = vpu_m2m_device_run,
- .job_abort = vpu_m2m_job_abort
+ .job_ready = vpu_m2m_job_ready,
};
static int vpu_vb2_queue_setup(struct vb2_queue *vq,
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-common.h b/drivers/media/platform/arm/mali-c55/mali-c55-common.h
index 31c1deaca146..13a3e9dc4243 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-common.h
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-common.h
@@ -306,5 +306,7 @@ bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55);
void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55,
enum mali_c55_config_spaces cfg_space);
void mali_c55_params_write_config(struct mali_c55 *mali_c55);
+void mali_c55_params_init_isp_config(struct mali_c55 *mali_c55,
+ const struct v4l2_subdev_state *state);
#endif /* _MALI_C55_COMMON_H */
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
index 43b834459ccf..c1a562cd214e 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
@@ -663,41 +663,6 @@ static int mali_c55_init_context(struct mali_c55 *mali_c55,
mali_c55->base + config_space_addrs[MALI_C55_CONFIG_PING],
MALI_C55_CONFIG_SPACE_SIZE);
- /*
- * Some features of the ISP need to be disabled by default and only
- * enabled at the same time as they're configured by a parameters buffer
- */
-
- /* Bypass the sqrt and square compression and expansion modules */
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1,
- MALI_C55_REG_BYPASS_1_FE_SQRT,
- MALI_C55_REG_BYPASS_1_FE_SQRT);
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
- MALI_C55_REG_BYPASS_3_SQUARE_BE,
- MALI_C55_REG_BYPASS_3_SQUARE_BE);
-
- /* Bypass the temper module */
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2,
- MALI_C55_REG_BYPASS_2_TEMPER);
-
- /* Disable the temper module's DMA read/write */
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0);
-
- /* Bypass the colour noise reduction */
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4,
- MALI_C55_REG_BYPASS_4_CNR);
-
- /* Disable the sinter module */
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG,
- MALI_C55_SINTER_ENABLE_MASK, 0);
-
- /* Disable the RGB Gamma module for each output */
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0);
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0);
-
- /* Disable the colour correction matrix */
- mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0);
-
return 0;
}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
index 497f25fbdd13..4c0fd1ec741c 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
@@ -112,9 +112,6 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55,
const struct v4l2_subdev_state *state)
{
struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
- const struct mali_c55_isp_format_info *cfg;
- const struct v4l2_mbus_framefmt *format;
- const struct v4l2_rect *crop;
u32 val;
int ret;
@@ -122,35 +119,11 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55,
MALI_C55_REG_MCU_CONFIG_WRITE_MASK,
MALI_C55_REG_MCU_CONFIG_WRITE_PING);
- /* Apply input windowing */
- crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
- format = v4l2_subdev_state_get_format(state,
- MALI_C55_ISP_PAD_SINK_VIDEO);
- cfg = mali_c55_isp_get_mbus_config_by_code(format->code);
-
- mali_c55_write(mali_c55, MALI_C55_REG_HC_START,
- MALI_C55_HC_START(crop->left));
- mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE,
- MALI_C55_HC_SIZE(crop->width));
- mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE,
- MALI_C55_VC_START(crop->top) |
- MALI_C55_VC_SIZE(crop->height));
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
- MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width);
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
- MALI_C55_REG_ACTIVE_HEIGHT_MASK,
- format->height << 16);
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER,
- MALI_C55_BAYER_ORDER_MASK, cfg->order);
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH,
- MALI_C55_INPUT_WIDTH_MASK,
- MALI_C55_INPUT_WIDTH_20BIT);
-
- mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
- MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK,
- cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK :
- 0x00);
-
+ /*
+ * Apply default ISP configuration and the apply configurations from
+ * the first available parameters buffer.
+ */
+ mali_c55_params_init_isp_config(mali_c55, state);
mali_c55_params_write_config(mali_c55);
ret = mali_c55_config_write(ctx, MALI_C55_CONFIG_PING, true);
if (ret) {
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
index be0e909bcf29..de0e9d898db7 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
@@ -43,9 +43,9 @@
* @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN
* @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and
* header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP
- * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG
- * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION
- * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS
+ * @awb_config: For header->type == MALI_C55_PARAM_BLOCK_AWB_CONFIG
+ * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG
+ * @shading_selection: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION
* @data: Allows easy initialisation of a union variable with a
* pointer into a __u8 array.
*/
@@ -732,6 +732,134 @@ void mali_c55_params_write_config(struct mali_c55 *mali_c55)
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
+void mali_c55_params_init_isp_config(struct mali_c55 *mali_c55,
+ const struct v4l2_subdev_state *state)
+{
+ const struct mali_c55_isp_format_info *cfg;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+
+ /* Apply input windowing */
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+ format = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SINK_VIDEO);
+ cfg = mali_c55_isp_get_mbus_config_by_code(format->code);
+
+ mali_c55_write(mali_c55, MALI_C55_REG_HC_START,
+ MALI_C55_HC_START(crop->left));
+ mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE,
+ MALI_C55_HC_SIZE(crop->width));
+ mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE,
+ MALI_C55_VC_START(crop->top) |
+ MALI_C55_VC_SIZE(crop->height));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
+ MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
+ MALI_C55_REG_ACTIVE_HEIGHT_MASK,
+ format->height << 16);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER,
+ MALI_C55_BAYER_ORDER_MASK, cfg->order);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH,
+ MALI_C55_INPUT_WIDTH_MASK,
+ MALI_C55_INPUT_WIDTH_20BIT);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
+ MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK,
+ cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK :
+ 0x00);
+
+ /*
+ * Some features of the ISP need to be disabled by default and only
+ * enabled at the same time as they're configured by a parameters buffer
+ */
+
+ /* Bypass the sqrt and square compression and expansion modules */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1,
+ MALI_C55_REG_BYPASS_1_FE_SQRT,
+ MALI_C55_REG_BYPASS_1_FE_SQRT);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_SQUARE_BE,
+ MALI_C55_REG_BYPASS_3_SQUARE_BE);
+
+ /* Bypass the sensor offset correction (BLS) module */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH,
+ MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH);
+
+ /* Configure 1x digital gain. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN,
+ MALI_C55_DIGITAL_GAIN_MASK, 256);
+
+ /* Set all AWB gains to 1x. at both AWB configuration points*/
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1,
+ MALI_C55_AWB_GAIN00_MASK, 256);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1,
+ MALI_C55_AWB_GAIN01_MASK,
+ MALI_C55_AWB_GAIN01(256));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2,
+ MALI_C55_AWB_GAIN10_MASK, 256);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2,
+ MALI_C55_AWB_GAIN11_MASK,
+ MALI_C55_AWB_GAIN11(256));
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1_AEXP,
+ MALI_C55_AWB_GAIN00_MASK, 256);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS1_AEXP,
+ MALI_C55_AWB_GAIN01_MASK,
+ MALI_C55_AWB_GAIN01(256));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2_AEXP,
+ MALI_C55_AWB_GAIN10_MASK, 256);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_GAINS2_AEXP,
+ MALI_C55_AWB_GAIN11_MASK,
+ MALI_C55_AWB_GAIN11(256));
+
+ /* Bypass mesh shading corrections (LSC). */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_ENABLE_MASK,
+ false);
+
+ /* Bypass the temper module */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2,
+ MALI_C55_REG_BYPASS_2_TEMPER);
+
+ /* Disable the temper module's DMA read/write */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0);
+
+ /* Disable IRIDIX module. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_IRIDIX,
+ MALI_C55_REG_BYPASS_3_IRIDIX);
+
+ /* Bypass the colour noise reduction and the PF modules */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4,
+ MALI_C55_REG_BYPASS_4_CNR |
+ MALI_C55_REG_BYPASS_4_PF_CORRECTION);
+
+ /* Disable the sinter module */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG,
+ MALI_C55_SINTER_ENABLE_MASK, 0);
+
+ /* Disable the RGB Gamma module for each output */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0);
+
+ /* Disable the colour correction matrix */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0);
+
+ /* Disable AWB stats. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AWB_DISABLE_MASK,
+ MALI_C55_AWB_DISABLE_MASK);
+
+ /* Disable auto-exposure 1024-bin histograms at both tap points. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AEXP_HIST_DISABLE_MASK,
+ MALI_C55_AEXP_HIST_DISABLE);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AEXP_IHIST_DISABLE_MASK,
+ MALI_C55_AEXP_IHIST_DISABLE);
+}
+
void mali_c55_unregister_params(struct mali_c55 *mali_c55)
{
struct mali_c55_params *params = &mali_c55->params;
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
index f5a148add1c8..f098effde7b4 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
@@ -128,8 +128,8 @@ enum mali_c55_interrupts {
#define MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH BIT(1)
#define MALI_C55_REG_BYPASS_3_MESH_SHADING BIT(3)
#define MALI_C55_REG_BYPASS_3_WHITE_BALANCE BIT(4)
-#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(5)
-#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(6)
+#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(5)
+#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(6)
#define MALI_C55_REG_BYPASS_4 0x18ec0
#define MALI_C55_REG_BYPASS_4_DEMOSAIC_RGB BIT(1)
#define MALI_C55_REG_BYPASS_4_PF_CORRECTION BIT(3)
diff --git a/drivers/media/platform/broadcom/Kconfig b/drivers/media/platform/broadcom/Kconfig
index 32b76ebfcd9a..93ba565d9a02 100644
--- a/drivers/media/platform/broadcom/Kconfig
+++ b/drivers/media/platform/broadcom/Kconfig
@@ -14,10 +14,5 @@ config VIDEO_BCM2835_UNICAM
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/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
index 53a342853be6..8d28ba0b59a3 100644
--- a/drivers/media/platform/broadcom/bcm2835-unicam.c
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -14,10 +14,6 @@
* 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
@@ -342,12 +338,12 @@ static const struct unicam_format_info unicam_image_formats[] = {
.csi_dt = MIPI_CSI2_DT_RGB565,
}, {
.fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
.depth = 24,
.csi_dt = MIPI_CSI2_DT_RGB888,
}, {
.fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
.depth = 24,
.csi_dt = MIPI_CSI2_DT_RGB888,
}, {
@@ -2148,22 +2144,45 @@ static int unicam_video_link_validate(struct media_link *link)
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);
+ fmtinfo = unicam_find_format_by_code(format->code,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
if (WARN_ON(!fmtinfo)) {
ret = -EPIPE;
goto out;
}
- if (fmtinfo->code != format->code ||
- fmt->height != format->height ||
+ /*
+ * Unicam initially associated BGR24 to BGR888_1X24 and RGB24 to
+ * RGB888_1X24.
+ *
+ * In order to allow the applications using the old behaviour to
+ * run, let's accept the old combination, but warn about it.
+ */
+ if (fmtinfo->fourcc != fmt->pixelformat) {
+ if ((fmt->pixelformat == V4L2_PIX_FMT_BGR24 &&
+ format->code == MEDIA_BUS_FMT_BGR888_1X24) ||
+ (fmt->pixelformat == V4L2_PIX_FMT_RGB24 &&
+ format->code == MEDIA_BUS_FMT_RGB888_1X24)) {
+ dev_warn_once(node->dev->dev,
+ "Incorrect pixel format %p4cc for 0x%04x. Fix your application to use %p4cc.\n",
+ &fmt->pixelformat, format->code, &fmtinfo->fourcc);
+ } else {
+ dev_dbg(node->dev->dev,
+ "image: format mismatch: 0x%04x <=> %p4cc\n",
+ format->code, &fmt->pixelformat);
+ ret = -EPIPE;
+ goto out;
+ }
+ }
+
+ if (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,
+ "image: (%u x %u) %s != (%u x %u) %s\n",
+ fmt->width, fmt->height,
v4l2_field_names[fmt->field],
- format->width, format->height, format->code,
+ format->width, format->height,
v4l2_field_names[format->field]);
ret = -EPIPE;
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
index bb13267ced38..8f71920a8a35 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
@@ -49,6 +49,7 @@ int wave5_vdi_init(struct device *dev)
if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code);
+ wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
return -EOPNOTSUPP;
}
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 80e1831a42e0..d419076d7052 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -1303,13 +1303,17 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) {
unsigned int i;
+ unsigned long flags;
for (i = 0; i < vb->num_planes; i++)
vb2_set_plane_payload(vb, i, 0);
vbuf->field = V4L2_FIELD_NONE;
+ spin_lock_irqsave(&inst->state_spinlock, flags);
send_eos_event(inst);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
} else {
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
@@ -1462,8 +1466,13 @@ static int streamoff_output(struct vb2_queue *q)
inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr;
inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr;
- if (v4l2_m2m_has_stopped(m2m_ctx))
+ if (v4l2_m2m_has_stopped(m2m_ctx)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&inst->state_spinlock, flags);
send_eos_event(inst);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ }
/* streamoff on output cancels any draining operation */
inst->eos = false;
@@ -1584,6 +1593,7 @@ static int initialize_sequence(struct vpu_instance *inst)
{
struct dec_initial_info initial_info;
int ret = 0;
+ unsigned long flags;
memset(&initial_info, 0, sizeof(struct dec_initial_info));
@@ -1605,7 +1615,9 @@ static int initialize_sequence(struct vpu_instance *inst)
return ret;
}
+ spin_lock_irqsave(&inst->state_spinlock, flags);
handle_dynamic_resolution_change(inst);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
return 0;
}
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index c01124a349f6..8c684756d5fc 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1202,6 +1202,7 @@ static int mtk_jpeg_release(struct file *file)
struct mtk_jpeg_dev *jpeg = video_drvdata(file);
struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
+ cancel_work_sync(&ctx->jpeg_work);
mutex_lock(&jpeg->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index c977ed0c09b6..4e4541b2fc8e 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -215,6 +215,15 @@ static int fops_vcodec_release(struct file *file)
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ /*
+ * Cancel any pending encode work before freeing the context.
+ * Although v4l2_m2m_ctx_release() waits for m2m job completion,
+ * the workqueue handler (mtk_venc_worker) may still be accessing
+ * the context after v4l2_m2m_job_finish() returns. Without this,
+ * a use-after-free occurs when the worker accesses ctx after kfree.
+ */
+ cancel_work_sync(&ctx->encode_work);
+
spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_del_init(&ctx->list);
spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c
index bdebbe3f4198..7787fdcdc67c 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -11,6 +11,7 @@
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irqreturn.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -74,6 +75,7 @@ struct dw100_device {
struct clk_bulk_data *clks;
int num_clks;
struct dentry *debugfs_root;
+ bool frame_failed;
};
struct dw100_q_data {
@@ -98,6 +100,7 @@ struct dw100_ctx {
unsigned int map_width;
unsigned int map_height;
bool user_map_is_set;
+ bool user_map_is_dirty;
/* Source and destination queue data */
struct dw100_q_data q_data[2];
@@ -293,11 +296,15 @@ static u32 dw100_map_format_coordinates(u16 xq, u16 yq)
return (u32)((yq << 16) | xq);
}
-static u32 *dw100_get_user_map(struct dw100_ctx *ctx)
+static void dw100_update_mapping(struct dw100_ctx *ctx)
{
struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP];
- return ctrl->p_cur.p_u32;
+ if (!ctx->user_map_is_dirty)
+ return;
+
+ memcpy(ctx->map, ctrl->p_cur.p_u32, ctx->map_size);
+ ctx->user_map_is_dirty = false;
}
/*
@@ -306,8 +313,6 @@ static u32 *dw100_get_user_map(struct dw100_ctx *ctx)
*/
static int dw100_create_mapping(struct dw100_ctx *ctx)
{
- u32 *user_map;
-
if (ctx->map)
dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size,
ctx->map, ctx->map_dma);
@@ -318,8 +323,8 @@ static int dw100_create_mapping(struct dw100_ctx *ctx)
if (!ctx->map)
return -ENOMEM;
- user_map = dw100_get_user_map(ctx);
- memcpy(ctx->map, user_map, ctx->map_size);
+ ctx->user_map_is_dirty = true;
+ dw100_update_mapping(ctx);
dev_dbg(&ctx->dw_dev->pdev->dev,
"%ux%u %s mapping created (d:%pad-c:%p) for stream %ux%u->%ux%u\n",
@@ -351,6 +356,7 @@ static int dw100_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP:
ctx->user_map_is_set = true;
+ ctx->user_map_is_dirty = true;
break;
}
@@ -405,6 +411,7 @@ static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl,
}
ctx->user_map_is_set = false;
+ ctx->user_map_is_dirty = true;
}
static const struct v4l2_ctrl_type_ops dw100_ctrl_type_ops = {
@@ -459,6 +466,15 @@ static int dw100_queue_setup(struct vb2_queue *vq,
return 0;
}
+static int dw100_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
static int dw100_buf_prepare(struct vb2_buffer *vb)
{
unsigned int i;
@@ -500,6 +516,13 @@ static void dw100_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
+static void dw100_buf_request_complete(struct vb2_buffer *vb)
+{
+ struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
static void dw100_return_all_buffers(struct vb2_queue *q,
enum vb2_buffer_state state)
{
@@ -553,11 +576,13 @@ static void dw100_stop_streaming(struct vb2_queue *q)
}
static const struct vb2_ops dw100_qops = {
- .queue_setup = dw100_queue_setup,
- .buf_prepare = dw100_buf_prepare,
- .buf_queue = dw100_buf_queue,
- .start_streaming = dw100_start_streaming,
- .stop_streaming = dw100_stop_streaming,
+ .queue_setup = dw100_queue_setup,
+ .buf_out_validate = dw100_buf_out_validate,
+ .buf_prepare = dw100_buf_prepare,
+ .buf_queue = dw100_buf_queue,
+ .start_streaming = dw100_start_streaming,
+ .stop_streaming = dw100_stop_streaming,
+ .buf_request_complete = dw100_buf_request_complete,
};
static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -575,6 +600,7 @@ static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->vq_mutex;
src_vq->dev = ctx->dw_dev->v4l2_dev.dev;
+ src_vq->supports_requests = true;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -1058,7 +1084,6 @@ static const struct v4l2_ioctl_ops dw100_ioctl_ops = {
static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error)
{
struct dw100_ctx *curr_ctx;
- struct vb2_v4l2_buffer *src_vb, *dst_vb;
enum vb2_buffer_state buf_state;
curr_ctx = v4l2_m2m_get_curr_priv(dw_dev->m2m_dev);
@@ -1069,21 +1094,16 @@ static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error)
return;
}
- src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
-
if (likely(!with_error))
buf_state = VB2_BUF_STATE_DONE;
else
buf_state = VB2_BUF_STATE_ERROR;
- v4l2_m2m_buf_done(src_vb, buf_state);
- v4l2_m2m_buf_done(dst_vb, buf_state);
-
dev_dbg(&dw_dev->pdev->dev, "Finishing transaction with%s error(s)\n",
with_error ? "" : "out");
- v4l2_m2m_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done_and_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx,
+ buf_state);
}
static void dw100_hw_reset(struct dw100_device *dw_dev)
@@ -1386,7 +1406,8 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id)
{
struct dw100_device *dw_dev = dev_id;
u32 pending_irqs, err_irqs, frame_done_irq;
- bool with_error = true;
+
+ dw_dev->frame_failed = true;
pending_irqs = dw_hw_get_pending_irqs(dw_dev);
frame_done_irq = pending_irqs & DW100_INTERRUPT_STATUS_INT_FRAME_DONE;
@@ -1394,7 +1415,7 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id)
if (frame_done_irq) {
dev_dbg(&dw_dev->pdev->dev, "Frame done interrupt\n");
- with_error = false;
+ dw_dev->frame_failed = false;
err_irqs &= ~DW100_INTERRUPT_STATUS_INT_ERR_STATUS
(DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE);
}
@@ -1407,30 +1428,57 @@ static irqreturn_t dw100_irq_handler(int irq, void *dev_id)
dw100_hw_clear_irq(dw_dev, pending_irqs |
DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT);
- dw100_job_finish(dw_dev, with_error);
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dw100_irq_thread_fn(int irq, void *dev_id)
+{
+ struct dw100_device *dw_dev = dev_id;
+
+ dw100_job_finish(dw_dev, dw_dev->frame_failed);
return IRQ_HANDLED;
}
-static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
- struct vb2_v4l2_buffer *out_vb)
+static void dw100_device_run(void *priv)
{
+ struct dw100_ctx *ctx = priv;
struct dw100_device *dw_dev = ctx->dw_dev;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
- out_vb->sequence =
- dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++;
- in_vb->sequence =
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
+ &ctx->hdl);
+
+ if (src_buf->vb2_buf.req_obj.req)
+ dw100_update_mapping(ctx);
+ else if (ctx->user_map_is_dirty)
+ dev_warn_once(&dw_dev->pdev->dev,
+ "V4L2 requests are required to update the vertex map dynamically\n");
+
+ /*
+ * As the hardware does not update any volatile controls, we can
+ * complete control handling before starting the dewarper.
+ */
+ v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
+ &ctx->hdl);
+
+ src_buf->sequence =
dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)->sequence++;
+ dst_buf->sequence =
+ dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++;
- dev_dbg(&ctx->dw_dev->pdev->dev,
+ dev_dbg(&dw_dev->pdev->dev,
"Starting queues %p->%p, sequence %u->%u\n",
v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE),
v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
- in_vb->sequence, out_vb->sequence);
+ src_buf->sequence, dst_buf->sequence);
- v4l2_m2m_buf_copy_metadata(in_vb, out_vb);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
/* Now, let's deal with hardware ... */
dw100_hw_master_bus_disable(dw_dev);
@@ -1439,10 +1487,10 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
dw100_hw_set_src_crop(dw_dev, &ctx->q_data[DW100_QUEUE_SRC],
&ctx->q_data[DW100_QUEUE_DST]);
dw100_hw_set_source(dw_dev, &ctx->q_data[DW100_QUEUE_SRC],
- &in_vb->vb2_buf);
+ &src_buf->vb2_buf);
dw100_hw_set_destination(dw_dev, &ctx->q_data[DW100_QUEUE_DST],
ctx->q_data[DW100_QUEUE_SRC].fmt,
- &out_vb->vb2_buf);
+ &dst_buf->vb2_buf);
dw100_hw_set_mapping(dw_dev, ctx->map_dma,
ctx->map_width, ctx->map_height);
dw100_hw_enable_irq(dw_dev);
@@ -1452,21 +1500,15 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
dw100_hw_master_bus_enable(dw_dev);
}
-static void dw100_device_run(void *priv)
-{
- struct dw100_ctx *ctx = priv;
- struct vb2_v4l2_buffer *src_buf, *dst_buf;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
- dw100_start(ctx, src_buf, dst_buf);
-}
-
static const struct v4l2_m2m_ops dw100_m2m_ops = {
.device_run = dw100_device_run,
};
+static const struct media_device_ops dw100_m2m_media_ops = {
+ .req_validate = vb2_request_validate,
+ .req_queue = v4l2_m2m_request_queue,
+};
+
static struct video_device *dw100_init_video_device(struct dw100_device *dw_dev)
{
struct video_device *vfd = &dw_dev->vfd;
@@ -1555,8 +1597,9 @@ static int dw100_probe(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
- ret = devm_request_irq(&pdev->dev, irq, dw100_irq_handler, IRQF_ONESHOT,
- dev_name(&pdev->dev), dw_dev);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, dw100_irq_handler,
+ dw100_irq_thread_fn, 0,
+ dev_name(&pdev->dev), dw_dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
goto err_pm;
@@ -1578,6 +1621,7 @@ static int dw100_probe(struct platform_device *pdev)
dw_dev->mdev.dev = &pdev->dev;
strscpy(dw_dev->mdev.model, "dw100", sizeof(dw_dev->mdev.model));
media_device_init(&dw_dev->mdev);
+ dw_dev->mdev.ops = &dw100_m2m_media_ops;
dw_dev->v4l2_dev.mdev = &dw_dev->mdev;
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
index adb93e977be9..e8ddfc61b4f2 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
@@ -5,6 +5,8 @@
* Copyright 2018-2019 NXP
*/
+#include <linux/bitfield.h>
+
#ifndef _MXC_JPEG_HW_H
#define _MXC_JPEG_HW_H
@@ -73,6 +75,7 @@
#define GLB_CTRL_DEC_GO (0x1 << 2)
#define GLB_CTRL_L_ENDIAN(le) ((le) << 3)
#define GLB_CTRL_SLOT_EN(slot) (0x1 << ((slot) + 4))
+#define GLB_CTRL_CUR_VERSION(r) FIELD_GET(GENMASK_U32(19, 16), r)
/* COM_STAUS fields */
#define COM_STATUS_DEC_ONGOING(r) (((r) & (1 << 31)) >> 31)
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index b442dcba02e7..725e94152884 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -64,6 +64,12 @@
#include "mxc-jpeg-hw.h"
#include "mxc-jpeg.h"
+#define call_void_jpeg_enc_ops(jpeg, op, args...) \
+ do { \
+ if ((jpeg)->enc_cfg_ops && (jpeg)->enc_cfg_ops->op) \
+ (jpeg)->enc_cfg_ops->op(args); \
+ } while (0)
+
static const struct mxc_jpeg_fmt mxc_formats[] = {
{
.name = "JPEG",
@@ -1030,11 +1036,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
if (jpeg->mode == MXC_JPEG_ENCODE &&
ctx->enc_state == MXC_JPEG_ENC_CONF) {
- q_data = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- ctx->enc_state = MXC_JPEG_ENCODING;
- dev_dbg(dev, "Encoder config finished. Start encoding...\n");
- mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality);
- mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt));
+ call_void_jpeg_enc_ops(jpeg, exit_config_mode, ctx);
goto job_unlock;
}
if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed &&
@@ -1272,6 +1274,7 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf,
jpeg_src_buf = vb2_to_mxc_buf(src_buf);
+ ctx->extseq = mxc_jpeg_is_extended_sequential(jpeg_src_buf->fmt);
/* setup the decoding descriptor */
desc->next_descpt_ptr = 0; /* end of chain */
q_data_cap = mxc_jpeg_get_q_data(ctx, cap_type);
@@ -1335,9 +1338,15 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf,
struct mxc_jpeg_q_data *q_data;
enum mxc_jpeg_image_format img_fmt;
int w, h;
+ bool extseq;
q_data = mxc_jpeg_get_q_data(ctx, src_buf->vb2_queue->type);
+ extseq = mxc_jpeg_is_extended_sequential(q_data->fmt);
+
+ ctx->extseq = extseq;
+ memset(desc, 0, sizeof(struct mxc_jpeg_desc));
+ memset(cfg_desc, 0, sizeof(struct mxc_jpeg_desc));
jpeg->slot_data.cfg_stream_size =
mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr,
q_data->fmt->fourcc,
@@ -1348,11 +1357,6 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf,
cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN;
cfg_desc->buf_base0 = jpeg->slot_data.cfg_stream_handle;
- cfg_desc->buf_base1 = 0;
- cfg_desc->line_pitch = 0;
- cfg_desc->stm_bufbase = 0; /* no output expected */
- cfg_desc->stm_bufsize = 0x0;
- cfg_desc->imgsize = 0;
cfg_desc->stm_ctrl = STM_CTRL_CONFIG_MOD(1);
cfg_desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1);
@@ -1372,11 +1376,14 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf,
desc->stm_ctrl = STM_CTRL_CONFIG_MOD(0) |
STM_CTRL_IMAGE_FORMAT(img_fmt);
desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1);
- if (mxc_jpeg_is_extended_sequential(q_data->fmt))
+ if (extseq)
desc->stm_ctrl |= STM_CTRL_PIXEL_PRECISION;
else
desc->stm_ctrl &= ~STM_CTRL_PIXEL_PRECISION;
mxc_jpeg_addrs(desc, src_buf, dst_buf, 0);
+
+ call_void_jpeg_enc_ops(jpeg, setup_desc, ctx);
+
dev_dbg(jpeg->dev, "cfg_desc:\n");
print_descriptor_info(jpeg->dev, cfg_desc);
dev_dbg(jpeg->dev, "enc desc:\n");
@@ -1388,6 +1395,54 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf,
mxc_jpeg_set_desc(cfg_desc_handle, reg, slot);
}
+static void mxc_jpeg_enc_start_config_manually(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ void __iomem *reg = jpeg->base_reg;
+ struct device *dev = jpeg->dev;
+
+ ctx->enc_state = MXC_JPEG_ENC_CONF;
+ mxc_jpeg_enc_mode_conf(dev, reg, ctx->extseq);
+}
+
+static void mxc_jpeg_enc_finish_config_manually(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ void __iomem *reg = jpeg->base_reg;
+ struct device *dev = jpeg->dev;
+
+ ctx->enc_state = MXC_JPEG_ENCODING;
+ dev_dbg(dev, "Encoder config finished. Start encoding...\n");
+ mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality);
+ mxc_jpeg_enc_mode_go(dev, reg, ctx->extseq);
+}
+
+static void mxc_jpeg_enc_configure_desc(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ struct mxc_jpeg_desc *desc = jpeg->slot_data.desc;
+ struct mxc_jpeg_desc *cfg_desc = jpeg->slot_data.cfg_desc;
+
+ ctx->enc_state = MXC_JPEG_ENCODING;
+ cfg_desc->mode = (ctx->extseq) ? 0xb0 : 0xa0;
+ cfg_desc->cfg_mode = 0x3ff;
+
+ desc->mode = (ctx->extseq) ? 0x150 : 0x140;
+ desc->cfg_mode = 0x3ff;
+ desc->quality = ctx->jpeg_quality;
+ desc->lumth = 0xffff;
+ desc->chrth = 0xffff;
+}
+
+static const struct mxc_jpeg_enc_ops mxc_jpeg_enc_cfg_ops_v0 = {
+ .enter_config_mode = mxc_jpeg_enc_start_config_manually,
+ .exit_config_mode = mxc_jpeg_enc_finish_config_manually
+};
+
+static const struct mxc_jpeg_enc_ops mxc_jpeg_enc_cfg_ops_v1 = {
+ .setup_desc = mxc_jpeg_enc_configure_desc
+};
+
static const struct mxc_jpeg_fmt *mxc_jpeg_get_sibling_format(const struct mxc_jpeg_fmt *fmt)
{
int i;
@@ -1593,12 +1648,10 @@ static void mxc_jpeg_device_run(void *priv)
if (jpeg->mode == MXC_JPEG_ENCODE) {
dev_dbg(dev, "Encoding on slot %d\n", ctx->slot);
- ctx->enc_state = MXC_JPEG_ENC_CONF;
mxc_jpeg_config_enc_desc(&dst_buf->vb2_buf, ctx,
&src_buf->vb2_buf, &dst_buf->vb2_buf);
/* start config phase */
- mxc_jpeg_enc_mode_conf(dev, reg,
- mxc_jpeg_is_extended_sequential(q_data_out->fmt));
+ call_void_jpeg_enc_ops(jpeg, enter_config_mode, ctx);
} else {
dev_dbg(dev, "Decoding on slot %d\n", ctx->slot);
print_mxc_buf(jpeg, &src_buf->vb2_buf, 0);
@@ -2842,6 +2895,14 @@ fail:
return ret;
}
+static int mxc_jpeg_get_version(void __iomem *reg)
+{
+ u32 regval;
+
+ regval = readl(reg + GLB_CTRL);
+ return GLB_CTRL_CUR_VERSION(regval);
+}
+
static int mxc_jpeg_probe(struct platform_device *pdev)
{
struct mxc_jpeg_dev *jpeg;
@@ -2974,12 +3035,35 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
jpeg->dec_vdev->minor);
platform_set_drvdata(pdev, jpeg);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable runtime PM: %d\n", ret);
+ goto err_pm;
+ }
+
+ if (mode == MXC_JPEG_ENCODE) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ goto err_pm;
+
+ if (mxc_jpeg_get_version(jpeg->base_reg) == 0)
+ jpeg->enc_cfg_ops = &mxc_jpeg_enc_cfg_ops_v0;
+ else
+ jpeg->enc_cfg_ops = &mxc_jpeg_enc_cfg_ops_v1;
+
+ pm_runtime_put_sync(dev);
+ }
return 0;
+err_pm:
+ video_unregister_device(jpeg->dec_vdev);
+ /* set NULL to prevent double-free */
+ jpeg->dec_vdev = NULL;
err_vdev_register:
- video_device_release(jpeg->dec_vdev);
+ /* Only release if allocation succeeded but registration failed */
+ if (jpeg->dec_vdev)
+ video_device_release(jpeg->dec_vdev);
err_vdev_alloc:
v4l2_m2m_release(jpeg->m2m_dev);
@@ -3050,7 +3134,6 @@ static void mxc_jpeg_remove(struct platform_device *pdev)
mxc_jpeg_free_slot_data(jpeg);
- pm_runtime_disable(&pdev->dev);
video_unregister_device(jpeg->dec_vdev);
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 9c5b4f053ded..c00c13549746 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -81,6 +81,17 @@ struct mxc_jpeg_desc {
u32 stm_bufsize;
u32 imgsize;
u32 stm_ctrl;
+ /* below parameters are valid for v1 */
+ u32 mode;
+ u32 cfg_mode;
+ u32 quality;
+ u32 rc_regs_sel;
+ u32 lumth;
+ u32 chrth;
+ u32 nomfrsize_lo;
+ u32 nomfrsize_hi;
+ u32 ofbsize_lo;
+ u32 ofbsize_hi;
} __packed;
struct mxc_jpeg_q_data {
@@ -105,6 +116,7 @@ struct mxc_jpeg_ctx {
unsigned int source_change;
bool need_initial_source_change_evt;
bool header_parsed;
+ bool extseq;
struct v4l2_ctrl_handler ctrl_handler;
u8 jpeg_quality;
struct delayed_work task_timer;
@@ -125,6 +137,15 @@ struct mxc_jpeg_slot_data {
dma_addr_t cfg_dec_daddr;
};
+struct mxc_jpeg_enc_ops {
+ /* Manual configuration (v0 hardware) - two-phase process */
+ void (*enter_config_mode)(struct mxc_jpeg_ctx *ctx);
+ void (*exit_config_mode)(struct mxc_jpeg_ctx *ctx);
+
+ /* Descriptor-based configuration (v1 hardware) - single-phase */
+ void (*setup_desc)(struct mxc_jpeg_ctx *ctx);
+};
+
struct mxc_jpeg_dev {
spinlock_t hw_lock; /* hardware access lock */
unsigned int mode;
@@ -142,6 +163,7 @@ struct mxc_jpeg_dev {
struct device **pd_dev;
struct device_link **pd_link;
struct gen_pool *sram_pool;
+ const struct mxc_jpeg_enc_ops *enc_cfg_ops;
};
/**
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index 9a43fd1eb0bc..5f9691d76434 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -1359,18 +1359,18 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0,
FWNODE_GRAPH_ENDPOINT_NEXT);
if (!ep)
- return -ENOTCONN;
+ return dev_err_probe(csis->dev, -ENOTCONN,
+ "failed to get local endpoint\n");
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
- return ret;
+ return dev_err_probe(csis->dev, ret,
+ "failed to parse endpoint\n");
for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
- if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
- dev_err(csis->dev,
- "data lanes reordering is not supported");
- return -EINVAL;
- }
+ if (vep.bus.mipi_csi2.data_lanes[i] != i + 1)
+ return dev_err_probe(csis->dev, -EINVAL,
+ "data lanes reordering is not supported\n");
}
csis->bus = vep.bus.mipi_csi2;
@@ -1382,15 +1382,22 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep,
struct v4l2_async_connection);
if (IS_ERR(asd))
- return PTR_ERR(asd);
+ return dev_err_probe(csis->dev, PTR_ERR(asd),
+ "failed to add remote fwnode to notifier\n");
csis->notifier.ops = &mipi_csis_notify_ops;
ret = v4l2_async_nf_register(&csis->notifier);
if (ret)
- return ret;
+ return dev_err_probe(csis->dev, ret,
+ "failed to register notifier\n");
- return v4l2_async_register_subdev(&csis->sd);
+ ret = v4l2_async_register_subdev(&csis->sd);
+ if (ret)
+ return dev_err_probe(csis->dev, ret,
+ "failed to register subdev\n");
+
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -1549,10 +1556,8 @@ static int mipi_csis_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &csis->sd);
ret = mipi_csis_async_register(csis);
- if (ret < 0) {
- dev_err(dev, "async register failed: %d\n", ret);
+ if (ret < 0)
goto err_cleanup;
- }
/* Initialize debugfs. */
mipi_csis_debugfs_init(csis);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index 16392420903a..4bf8570e1b9e 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -314,6 +314,17 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = {
.has_36bit_dma = true,
};
+static const struct mxc_isi_plat_data mxc_imx95_data = {
+ .model = MXC_ISI_IMX95,
+ .num_ports = 4,
+ .num_channels = 8,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_v2,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = true,
+};
+
static const struct mxc_isi_plat_data mxc_imx8qm_data = {
.model = MXC_ISI_IMX8QM,
.num_ports = 5,
@@ -557,6 +568,7 @@ static const struct of_device_id mxc_isi_of_match[] = {
{ .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
{ .compatible = "fsl,imx91-isi", .data = &mxc_imx91_data },
{ .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
+ { .compatible = "fsl,imx95-isi", .data = &mxc_imx95_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index 3cbd35305af0..14d63ec36416 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -162,6 +162,7 @@ enum model {
MXC_ISI_IMX8ULP,
MXC_ISI_IMX91,
MXC_ISI_IMX93,
+ MXC_ISI_IMX95,
};
struct mxc_isi_plat_data {
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
index 9225a7ac1c3e..0187d4ab97e8 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
@@ -309,8 +309,8 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
val = mxc_isi_read(pipe, CHNL_CTRL);
val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK |
- CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK |
- CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK);
+ CHNL_CTRL_SRC_TYPE_MASK | CHNL_CTRL_MIPI_VC_ID_MASK |
+ CHNL_CTRL_SRC_INPUT_MASK);
/*
* If no scaling or color space conversion is needed, bypass the
@@ -323,8 +323,6 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
if (pipe->chained)
val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN);
- val |= CHNL_CTRL_BLANK_PXL(0xff);
-
/* Input source (including VC configuration for CSI-2) */
if (input == MXC_ISI_INPUT_MEM) {
/*
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
index 13682bf6e9f8..1be3a728f32f 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
@@ -1410,7 +1410,7 @@ int mxc_isi_video_register(struct mxc_isi_pipe *pipe,
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct mxc_isi_buffer);
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_queued_buffers = 2;
+ q->min_queued_buffers = 0;
q->lock = &video->lock;
q->dev = pipe->isi->dev;
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index e111b1debd69..04ebed8a0493 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -72,21 +72,6 @@ enum {
ST_SUSPENDED = 4,
};
-enum imx8mq_mipi_csi_clk {
- CSI2_CLK_CORE,
- CSI2_CLK_ESC,
- CSI2_CLK_UI,
- CSI2_NUM_CLKS,
-};
-
-static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
- [CSI2_CLK_CORE] = "core",
- [CSI2_CLK_ESC] = "esc",
- [CSI2_CLK_UI] = "ui",
-};
-
-#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
-
struct imx8mq_plat_data {
int (*enable)(struct csi_state *state, u32 hs_settle);
void (*disable)(struct csi_state *state);
@@ -112,7 +97,9 @@ struct csi_state {
struct device *dev;
const struct imx8mq_plat_data *pdata;
void __iomem *regs;
- struct clk_bulk_data clks[CSI2_NUM_CLKS];
+ struct clk_bulk_data *clks;
+ struct clk *esc_clk;
+ u32 num_clks;
struct reset_control *rst;
struct regulator *mipi_phy_regulator;
@@ -352,18 +339,14 @@ static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
{
int ret;
- /*
- * these are most likely self-clearing reset bits. to make it
- * more clear, the reset-imx7 driver should implement the
- * .reset() operation.
- */
ret = reset_control_assert(state->rst);
if (ret < 0) {
dev_err(state->dev, "Failed to assert resets: %d\n", ret);
return ret;
}
- return 0;
+ /* Explicitly release reset to make sure reset bits are cleared. */
+ return reset_control_deassert(state->rst);
}
static void imx8mq_mipi_csi_set_params(struct csi_state *state)
@@ -385,24 +368,16 @@ static void imx8mq_mipi_csi_set_params(struct csi_state *state)
CSI2RX_SEND_LEVEL);
}
-static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
-{
- return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
-}
-
-static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
-{
- clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
-}
-
-static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
+static struct clk *imx8mq_mipi_csi_find_esc_clk(struct csi_state *state)
{
unsigned int i;
- for (i = 0; i < CSI2_NUM_CLKS; i++)
- state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
+ for (i = 0; i < state->num_clks; i++) {
+ if (!strcmp(state->clks[i].id, "esc"))
+ return state->clks[i].clk;
+ }
- return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
+ return ERR_PTR(-ENODEV);
}
static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
@@ -457,7 +432,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
* documentation recommends picking a value away from the boundaries.
* Let's pick the average.
*/
- esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
+ esc_clk_rate = clk_get_rate(state->esc_clk);
if (!esc_clk_rate) {
dev_err(state->dev, "Could not get esc clock rate.\n");
return -EINVAL;
@@ -727,18 +702,18 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state)
fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
FWNODE_GRAPH_ENDPOINT_NEXT);
if (!ep)
- return -ENOTCONN;
+ return dev_err_probe(state->dev, -ENOTCONN,
+ "failed to get local endpoint fwnode\n");
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
- return ret;
+ return dev_err_probe(state->dev, ret,
+ "failed to parse endpoint\n");
for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
- if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
- dev_err(state->dev,
- "data lanes reordering is not supported");
- return -EINVAL;
- }
+ if (vep.bus.mipi_csi2.data_lanes[i] != i + 1)
+ return dev_err_probe(state->dev, -EINVAL,
+ "data lanes reordering is not supported");
}
state->bus = vep.bus.mipi_csi2;
@@ -750,15 +725,22 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state)
asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
struct v4l2_async_connection);
if (IS_ERR(asd))
- return PTR_ERR(asd);
+ return dev_err_probe(state->dev, PTR_ERR(asd),
+ "failed to add fwnode to notifier\n");
state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
ret = v4l2_async_nf_register(&state->notifier);
if (ret)
- return ret;
+ return dev_err_probe(state->dev, ret,
+ "failed to register notifier\n");
- return v4l2_async_register_subdev(&state->sd);
+ ret = v4l2_async_register_subdev(&state->sd);
+ if (ret)
+ return dev_err_probe(state->dev, ret,
+ "failed to register subdev\n");
+
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -774,7 +756,7 @@ static void imx8mq_mipi_csi_pm_suspend(struct device *dev)
if (state->state & ST_POWERED) {
imx8mq_mipi_csi_stop_stream(state);
- imx8mq_mipi_csi_clk_disable(state);
+ clk_bulk_disable_unprepare(state->num_clks, state->clks);
state->state &= ~ST_POWERED;
}
@@ -792,7 +774,7 @@ static int imx8mq_mipi_csi_pm_resume(struct device *dev)
if (!(state->state & ST_POWERED)) {
state->state |= ST_POWERED;
- ret = imx8mq_mipi_csi_clk_enable(state);
+ ret = clk_bulk_prepare_enable(state->num_clks, state->clks);
}
if (state->state & ST_STREAMING) {
sd_state = v4l2_subdev_lock_and_get_active_state(sd);
@@ -1013,9 +995,16 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
if (IS_ERR(state->regs))
return PTR_ERR(state->regs);
- ret = imx8mq_mipi_csi_clk_get(state);
+ ret = devm_clk_bulk_get_all(dev, &state->clks);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ state->num_clks = ret;
+
+ state->esc_clk = imx8mq_mipi_csi_find_esc_clk(state);
+ if (IS_ERR(state->esc_clk))
+ return dev_err_probe(dev, PTR_ERR(state->esc_clk),
+ "Couldn't find esc clock\n");
platform_set_drvdata(pdev, &state->sd);
@@ -1081,6 +1070,7 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev)
static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
{ .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data },
{ .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data },
+ { .compatible = "fsl,imx8ulp-mipi-csi2", .data = &imx8qxp_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index d1b31ab8b8c4..7305cc4a04cb 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -71,10 +71,7 @@ struct rcar_csi2;
#define FLD_REG 0x1c
#define FLD_FLD_NUM(n) (((n) & 0xff) << 16)
#define FLD_DET_SEL(n) (((n) & 0x3) << 4)
-#define FLD_FLD_EN4 BIT(3)
-#define FLD_FLD_EN3 BIT(2)
-#define FLD_FLD_EN2 BIT(1)
-#define FLD_FLD_EN BIT(0)
+#define FLD_FLD_EN(ch) BIT(ch)
/* Automatic Standby Control */
#define ASTBY_REG 0x20
@@ -959,35 +956,6 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
return 0;
}
-static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
- unsigned int lanes)
-{
- struct media_pad *remote_pad;
- struct v4l2_subdev *source;
- s64 freq;
- u64 mbps;
-
- if (!priv->remote)
- return -ENODEV;
-
- source = priv->remote;
- remote_pad = &source->entity.pads[priv->remote_pad];
-
- freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes);
- if (freq < 0) {
- int ret = (int)freq;
-
- dev_err(priv->dev, "failed to get link freq for %s: %d\n",
- source->name, ret);
-
- return ret;
- }
-
- mbps = div_u64(freq * 2, MEGA);
-
- return mbps;
-}
-
static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
unsigned int *lanes)
{
@@ -1035,6 +1003,62 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
return 0;
}
+static int rcsi2_calc_mbps(struct rcar_csi2 *priv,
+ struct v4l2_subdev_state *state)
+{
+ struct media_pad *remote_pad;
+ struct v4l2_subdev *source;
+ s64 freq;
+ u64 mbps;
+
+ if (!priv->remote)
+ return -ENODEV;
+
+ source = priv->remote;
+ remote_pad = &source->entity.pads[priv->remote_pad];
+
+ /*
+ * First try to get the real link freq. If that fails, try the heuristic
+ * method with bpp and lanes (but that only works for one route).
+ */
+ freq = v4l2_get_link_freq(remote_pad, 0, 0);
+ if (freq < 0) {
+ const struct rcar_csi2_format *format;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int lanes;
+ unsigned int bpp;
+ int ret;
+
+ ret = rcsi2_get_active_lanes(priv, &lanes);
+ if (ret)
+ return ret;
+
+ fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+ if (!fmt)
+ return -EINVAL;
+
+ format = rcsi2_code_to_fmt(fmt->code);
+ if (!format)
+ return -EINVAL;
+
+ bpp = format->bpp;
+
+ freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes);
+ if (freq < 0) {
+ int ret = (int)freq;
+
+ dev_err(priv->dev, "failed to get link freq for %s: %d\n",
+ source->name, ret);
+
+ return ret;
+ }
+ }
+
+ mbps = div_u64(freq * 2, MEGA);
+
+ return mbps;
+}
+
static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
@@ -1082,8 +1106,8 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
}
if (fmt->field == V4L2_FIELD_ALTERNATE)
- fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2
- | FLD_FLD_EN;
+ fld = FLD_DET_SEL(1) | FLD_FLD_EN(3) | FLD_FLD_EN(2) |
+ FLD_FLD_EN(1) | FLD_FLD_EN(0);
/*
* Get the number of active data lanes inspecting the remote mbus
@@ -1096,7 +1120,7 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
phycnt = PHYCNT_ENABLECLK;
phycnt |= (1 << lanes) - 1;
- mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ mbps = rcsi2_calc_mbps(priv, state);
if (mbps < 0)
return mbps;
@@ -1478,23 +1502,15 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
const struct rcsi2_cphy_setting *cphy = NULL;
- const struct rcar_csi2_format *format;
- const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
int mbps;
int ret;
- /* Use the format on the sink pad to compute the receiver config. */
- fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
- format = rcsi2_code_to_fmt(fmt->code);
- if (!format)
- return -EINVAL;
-
ret = rcsi2_get_active_lanes(priv, &lanes);
if (ret)
return ret;
- mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ mbps = rcsi2_calc_mbps(priv, state);
if (mbps < 0)
return mbps;
@@ -1735,23 +1751,15 @@ static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps)
static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
- const struct rcar_csi2_format *format;
- const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
int mbps;
int ret;
- /* Calculate parameters */
- fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
- format = rcsi2_code_to_fmt(fmt->code);
- if (!format)
- return -EINVAL;
-
ret = rcsi2_get_active_lanes(priv, &lanes);
if (ret)
return ret;
- mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ mbps = rcsi2_calc_mbps(priv, state);
if (mbps < 0)
return mbps;
diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index 1eb29a0b774a..8fb2cc3b5650 100644
--- a/drivers/media/platform/renesas/rcar-isp/csisp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -28,10 +28,7 @@
#define ISPSTART_STOP 0x0000
#define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n)))
-#define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24)
-#define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16)
-#define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8)
-#define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f)
+#define ISPPROCMODE_DT_PROC_MODE_VCn(vc, pm) (((pm) & 0x3f) << (8 * (vc)))
#define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n)))
@@ -263,10 +260,10 @@ static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
/* Setup processing method. */
risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype),
- ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
+ ISPPROCMODE_DT_PROC_MODE_VCn(3, format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VCn(2, format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VCn(1, format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VCn(0, format->procmode));
/* Start ISP. */
risp_write_cs(isp, ISPSTART_REG, ISPSTART_START);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index b619d1436a41..f9af9177e02f 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -676,8 +676,30 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
if (vin->scaler)
vin->scaler(vin);
+ /*
+ * VNIS_REG has four lowest bits always 0, i.e. the stride has to be
+ * aligned to 16 bytes. This is done in rvin_format_bytesperline().
+ */
+
fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
stride = vin->format.bytesperline / fmt->bpp;
+
+ /*
+ * RAW8 format bpp is 1, but the hardware process RAW8 format in 2 pixel
+ * units, so we need to divide the stride by 2.
+ */
+ switch (vin->format.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_GREY:
+ stride /= 2;
+ break;
+ default:
+ break;
+ }
+
rvin_write(vin, stride, VNIS_REG);
}
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index 079dbaf016c2..9d45e11898c1 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -155,6 +155,18 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin,
case V4L2_PIX_FMT_NV16:
align = 0x20;
break;
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_GREY:
+ /*
+ * RAW8 format bpp is 1, but the hardware process RAW8 format in
+ * 2 pixel units, and we need to align to 32 bytes. See
+ * rvin_crop_scale_comp().
+ */
+ align = 0x20;
+ break;
default:
align = 0x10;
break;
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c
index e9857eb5b51a..355842abb24b 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c
@@ -119,7 +119,7 @@ static irqreturn_t rzv2h_ivc_isr(int irq, void *context)
* The second interrupt indicates that the post-frame transfer VBLANK
* has completed, we can now schedule a new frame transfer, if any.
*/
- queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+ rzv2h_ivc_transfer_buffer(ivc);
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
index 799453250b85..b167f1bab7ef 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
@@ -7,6 +7,7 @@
#include "rzv2h-ivc.h"
+#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/iopoll.h>
#include <linux/lockdep.h>
@@ -24,7 +25,7 @@
#include <media/videobuf2-dma-contig.h>
#define RZV2H_IVC_FIXED_HBLANK 0x20
-#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 15 + (120501 / (hts)))
+#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 70100 / (hts))
struct rzv2h_ivc_buf {
struct vb2_v4l2_buffer vb;
@@ -142,30 +143,30 @@ void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc)
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
-static void rzv2h_ivc_transfer_buffer(struct work_struct *work)
+void rzv2h_ivc_transfer_buffer(struct rzv2h_ivc *ivc)
{
- struct rzv2h_ivc *ivc = container_of(work, struct rzv2h_ivc,
- buffers.work);
struct rzv2h_ivc_buf *buf;
+ lockdep_assert_held(&ivc->spinlock);
+
+ if (ivc->vvalid_ifp)
+ return;
+
/* Setup buffers */
scoped_guard(spinlock_irqsave, &ivc->buffers.lock) {
buf = list_first_entry_or_null(&ivc->buffers.queue,
struct rzv2h_ivc_buf, queue);
- }
-
- if (!buf)
- return;
+ if (!buf)
+ return;
- list_del(&buf->queue);
+ list_del(&buf->queue);
+ ivc->buffers.curr = buf;
+ }
- ivc->buffers.curr = buf;
buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_SADDL_P0, buf->addr);
- scoped_guard(spinlock_irqsave, &ivc->spinlock) {
- ivc->vvalid_ifp = 2;
- }
+ ivc->vvalid_ifp = 2;
rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_FRCON, 0x1);
}
@@ -200,8 +201,8 @@ static void rzv2h_ivc_buf_queue(struct vb2_buffer *vb)
}
scoped_guard(spinlock_irq, &ivc->spinlock) {
- if (vb2_is_streaming(vb->vb2_queue) && !ivc->vvalid_ifp)
- queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+ if (vb2_is_streaming(vb->vb2_queue))
+ rzv2h_ivc_transfer_buffer(ivc);
}
}
@@ -214,10 +215,10 @@ static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc)
/* Currently only CRU packed pixel formats are supported */
rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PXFMT,
- RZV2H_IVC_INPUT_FMT_CRU_PACKED);
-
- rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_PXFMT,
- RZV2H_IVC_PXFMT_DTYPE, fmt->dtype);
+ FIELD_PREP(RZV2H_IVC_AXIRX_PXFMT_FIELD_DTYPE,
+ fmt->dtype) |
+ FIELD_PREP(RZV2H_IVC_AXIRX_PXFMT_FIELD_CLFMT,
+ RZV2H_IVC_CLFMT_CRU_PACKED));
rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_HSIZE, pix->width);
rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_VSIZE, pix->height);
@@ -235,8 +236,10 @@ static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc)
hts = pix->width + RZV2H_IVC_FIXED_HBLANK;
vblank = RZV2H_IVC_MIN_VBLANK(hts);
- rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_BLANK,
- RZV2H_IVC_VBLANK(vblank));
+ rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_BLANK,
+ RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK,
+ FIELD_PREP(RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK,
+ vblank));
}
static void rzv2h_ivc_return_buffers(struct rzv2h_ivc *ivc,
@@ -277,7 +280,9 @@ static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count)
rzv2h_ivc_format_configure(ivc);
- queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+ scoped_guard(spinlock_irq, &ivc->spinlock) {
+ rzv2h_ivc_transfer_buffer(ivc);
+ }
return 0;
@@ -294,9 +299,10 @@ static void rzv2h_ivc_stop_streaming(struct vb2_queue *q)
struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
u32 val = 0;
- rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, 0x1);
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP);
readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP,
- val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+ val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP),
+ 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&ivc->vdev.dev);
@@ -443,11 +449,6 @@ int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev)
spin_lock_init(&ivc->buffers.lock);
INIT_LIST_HEAD(&ivc->buffers.queue);
- INIT_WORK(&ivc->buffers.work, rzv2h_ivc_transfer_buffer);
-
- ivc->buffers.async_wq = alloc_workqueue("rzv2h-ivc", 0, 0);
- if (!ivc->buffers.async_wq)
- return -EINVAL;
/* Initialise vb2 queue */
vb2q = &ivc->vdev.vb2q;
@@ -465,7 +466,7 @@ int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev)
ret = vb2_queue_init(vb2q);
if (ret) {
dev_err(ivc->dev, "vb2 queue init failed\n");
- goto err_destroy_workqueue;
+ return ret;
}
/* Initialise Video Device */
@@ -514,8 +515,6 @@ err_cleanup_vdev_entity:
media_entity_cleanup(&vdev->entity);
err_release_vb2q:
vb2_queue_release(vb2q);
-err_destroy_workqueue:
- destroy_workqueue(ivc->buffers.async_wq);
return ret;
}
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h
index 3bcaab990b0f..6f644ba796a9 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h
@@ -24,9 +24,10 @@
#define RZV2H_IVC_ONE_EXPOSURE 0x00
#define RZV2H_IVC_TWO_EXPOSURE 0x01
#define RZV2H_IVC_REG_AXIRX_PXFMT 0x0004
-#define RZV2H_IVC_INPUT_FMT_MIPI (0 << 16)
-#define RZV2H_IVC_INPUT_FMT_CRU_PACKED BIT(16)
-#define RZV2H_IVC_PXFMT_DTYPE GENMASK(7, 0)
+#define RZV2H_IVC_AXIRX_PXFMT_FIELD_CLFMT GENMASK(17, 16)
+#define RZV2H_IVC_CLFMT_MIPI 0
+#define RZV2H_IVC_CLFMT_CRU_PACKED 1
+#define RZV2H_IVC_AXIRX_PXFMT_FIELD_DTYPE GENMASK(7, 0)
#define RZV2H_IVC_REG_AXIRX_SADDL_P0 0x0010
#define RZV2H_IVC_REG_AXIRX_SADDH_P0 0x0014
#define RZV2H_IVC_REG_AXIRX_SADDL_P1 0x0018
@@ -34,7 +35,7 @@
#define RZV2H_IVC_REG_AXIRX_HSIZE 0x0020
#define RZV2H_IVC_REG_AXIRX_VSIZE 0x0024
#define RZV2H_IVC_REG_AXIRX_BLANK 0x0028
-#define RZV2H_IVC_VBLANK(x) ((x) << 16)
+#define RZV2H_IVC_AXIRX_BLANK_FIELD_VBLANK GENMASK(25, 16)
#define RZV2H_IVC_REG_AXIRX_STRD 0x0030
#define RZV2H_IVC_REG_AXIRX_ISSU 0x0040
#define RZV2H_IVC_REG_AXIRX_ERACT 0x0048
@@ -45,6 +46,7 @@
#define RZV2H_IVC_REG_FM_MCON 0x0104
#define RZV2H_IVC_REG_FM_FRCON 0x0108
#define RZV2H_IVC_REG_FM_STOP 0x010c
+#define RZV2H_IVC_REG_FM_STOP_FSTOP BIT(20)
#define RZV2H_IVC_REG_FM_INT_EN 0x0120
#define RZV2H_IVC_VVAL_IFPE BIT(0)
#define RZV2H_IVC_REG_FM_INT_STA 0x0124
@@ -102,8 +104,6 @@ struct rzv2h_ivc {
struct {
/* Spinlock to guard buffer queue */
spinlock_t lock;
- struct workqueue_struct *async_wq;
- struct work_struct work;
struct list_head queue;
struct rzv2h_ivc_buf *curr;
unsigned int sequence;
@@ -128,3 +128,4 @@ void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc);
void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val);
void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr,
u32 mask, u32 val);
+void rzv2h_ivc_transfer_buffer(struct rzv2h_ivc *ivc);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 5fc2e5a3bb30..b1a2c68e9944 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -59,25 +59,17 @@ static const struct v4l2_ctrl_ops brx_ctrl_ops = {
* V4L2 Subdevice Operations
*/
+static const unsigned int brx_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
/*
* The BRx can't perform format conversion, all sink and source formats must be
* identical. We pick the format on the first sink pad (pad 0) and propagate it
* to all other pads.
*/
-static int brx_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes,
- ARRAY_SIZE(codes));
-}
-
static int brx_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
@@ -164,14 +156,20 @@ static int brx_set_format(struct v4l2_subdev *subdev,
compose->height = format->height;
}
- /* Propagate the format code to all pads. */
+ /*
+ * Propagate the format code to all pads, and the whole format to the
+ * source pad.
+ */
if (fmt->pad == BRX_PAD_SINK(0)) {
unsigned int i;
- for (i = 0; i <= brx->entity.source_pad; ++i) {
+ for (i = 0; i < brx->entity.source_pad; ++i) {
format = v4l2_subdev_state_get_format(state, i);
format->code = fmt->format.code;
}
+
+ format = v4l2_subdev_state_get_format(state, i);
+ *format = fmt->format;
}
done:
@@ -262,7 +260,7 @@ done:
}
static const struct v4l2_subdev_pad_ops brx_pad_ops = {
- .enum_mbus_code = brx_enum_mbus_code,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
.enum_frame_size = brx_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = brx_set_format,
@@ -271,6 +269,7 @@ static const struct v4l2_subdev_pad_ops brx_pad_ops = {
};
static const struct v4l2_subdev_ops brx_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &brx_pad_ops,
};
@@ -416,6 +415,12 @@ struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
brx->entity.ops = &brx_entity_ops;
brx->entity.type = type;
+ brx->entity.codes = brx_codes;
+ brx->entity.num_codes = ARRAY_SIZE(brx_codes);
+ brx->entity.min_width = BRX_MIN_SIZE;
+ brx->entity.max_width = BRX_MAX_SIZE;
+ brx->entity.min_height = BRX_MIN_SIZE;
+ brx->entity.max_height = BRX_MAX_SIZE;
if (type == VSP1_ENTITY_BRU) {
num_pads = vsp1->info->num_bru_inputs + 1;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
index 98645bd2a983..04c466c4da81 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
@@ -113,7 +113,7 @@ static const struct v4l2_ctrl_config clu_mode_control = {
};
/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Pad Operations
+ * V4L2 Subdevice Operations
*/
static const unsigned int clu_codes[] = {
@@ -122,46 +122,15 @@ static const unsigned int clu_codes[] = {
MEDIA_BUS_FMT_AYUV8_1X32,
};
-static int clu_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, clu_codes,
- ARRAY_SIZE(clu_codes));
-}
-
-static int clu_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- CLU_MIN_SIZE,
- CLU_MIN_SIZE, CLU_MAX_SIZE,
- CLU_MAX_SIZE);
-}
-
-static int clu_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, clu_codes,
- ARRAY_SIZE(clu_codes),
- CLU_MIN_SIZE, CLU_MIN_SIZE,
- CLU_MAX_SIZE, CLU_MAX_SIZE);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Operations
- */
-
static const struct v4l2_subdev_pad_ops clu_pad_ops = {
- .enum_mbus_code = clu_enum_mbus_code,
- .enum_frame_size = clu_enum_frame_size,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
+ .enum_frame_size = vsp1_subdev_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = clu_set_format,
+ .set_fmt = vsp1_subdev_set_pad_format,
};
static const struct v4l2_subdev_ops clu_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &clu_pad_ops,
};
@@ -247,6 +216,12 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
clu->entity.ops = &clu_entity_ops;
clu->entity.type = VSP1_ENTITY_CLU;
+ clu->entity.codes = clu_codes;
+ clu->entity.num_codes = ARRAY_SIZE(clu_codes);
+ clu->entity.min_width = CLU_MIN_SIZE;
+ clu->entity.min_height = CLU_MIN_SIZE;
+ clu->entity.max_width = CLU_MAX_SIZE;
+ clu->entity.max_height = CLU_MAX_SIZE;
ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops,
MEDIA_ENT_F_PROC_VIDEO_LUT);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 2de515c497eb..627b5046fa80 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -240,8 +240,12 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
media_device_unregister(&vsp1->media_dev);
media_device_cleanup(&vsp1->media_dev);
- if (!vsp1->info->uapi)
- vsp1_drm_cleanup(vsp1);
+ if (!vsp1->info->uapi) {
+ if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4)
+ vsp1_vspx_cleanup(vsp1);
+ else
+ vsp1_drm_cleanup(vsp1);
+ }
}
static int vsp1_create_entities(struct vsp1_device *vsp1)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index a6680d531872..1dad9589768c 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -7,11 +7,14 @@
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gfp.h>
+#include <linux/mutex.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
@@ -181,8 +184,6 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
* @subdev: V4L2 subdevice
* @sd_state: V4L2 subdev state
* @code: Media bus code enumeration
- * @codes: Array of supported media bus codes
- * @ncodes: Number of supported media bus codes
*
* This function implements the subdev enum_mbus_code pad operation for entities
* that do not support format conversion. It enumerates the given supported
@@ -191,16 +192,15 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
*/
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code,
- const unsigned int *codes, unsigned int ncodes)
+ struct v4l2_subdev_mbus_code_enum *code)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
if (code->pad == 0) {
- if (code->index >= ncodes)
+ if (code->index >= entity->num_codes)
return -EINVAL;
- code->code = codes[code->index];
+ code->code = entity->codes[code->index];
} else {
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
@@ -230,10 +230,6 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
* @subdev: V4L2 subdevice
* @sd_state: V4L2 subdev state
* @fse: Frame size enumeration
- * @min_width: Minimum image width
- * @min_height: Minimum image height
- * @max_width: Maximum image width
- * @max_height: Maximum image height
*
* This function implements the subdev enum_frame_size pad operation for
* entities that do not support scaling or cropping. It reports the given
@@ -242,47 +238,54 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
*/
int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse,
- unsigned int min_width, unsigned int min_height,
- unsigned int max_width, unsigned int max_height)
+ struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
- struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
- state = vsp1_entity_get_state(entity, sd_state, fse->which);
- if (!state)
+ if (fse->index)
return -EINVAL;
- format = v4l2_subdev_state_get_format(state, fse->pad);
+ if (fse->pad == 0) {
+ unsigned int i;
- mutex_lock(&entity->lock);
+ for (i = 0; i < entity->num_codes; ++i) {
+ if (fse->code == entity->codes[i])
+ break;
+ }
- if (fse->index || fse->code != format->code) {
- ret = -EINVAL;
- goto done;
- }
+ if (i == entity->num_codes)
+ return -EINVAL;
- if (fse->pad == 0) {
- fse->min_width = min_width;
- fse->max_width = max_width;
- fse->min_height = min_height;
- fse->max_height = max_height;
+ fse->min_width = entity->min_width;
+ fse->max_width = entity->max_width;
+ fse->min_height = entity->min_height;
+ fse->max_height = entity->max_height;
} else {
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
+
+ state = vsp1_entity_get_state(entity, sd_state, fse->which);
+ if (!state)
+ return -EINVAL;
+
/*
- * The size on the source pad are fixed and always identical to
- * the size on the sink pad.
+ * The media bus code and size on the source pad are fixed and
+ * always identical to the sink pad.
*/
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ guard(mutex)(&entity->lock);
+
+ if (fse->code != format->code)
+ return -EINVAL;
+
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
-done:
- mutex_unlock(&entity->lock);
- return ret;
+ return 0;
}
/*
@@ -290,25 +293,15 @@ done:
* @subdev: V4L2 subdevice
* @sd_state: V4L2 subdev state
* @fmt: V4L2 subdev format
- * @codes: Array of supported media bus codes
- * @ncodes: Number of supported media bus codes
- * @min_width: Minimum image width
- * @min_height: Minimum image height
- * @max_width: Maximum image width
- * @max_height: Maximum image height
*
* This function implements the subdev set_fmt pad operation for entities that
- * do not support scaling or cropping. It defaults to the first supplied media
+ * do not support scaling or cropping. It defaults to the first supported media
* bus code if the requested code isn't supported, clamps the size to the
- * supplied minimum and maximum, and propagates the sink pad format to the
- * source pad.
+ * entity's limits, and propagates the sink pad format to the source pad.
*/
int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt,
- const unsigned int *codes, unsigned int ncodes,
- unsigned int min_width, unsigned int min_height,
- unsigned int max_width, unsigned int max_height)
+ struct v4l2_subdev_format *fmt)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
struct v4l2_subdev_state *state;
@@ -337,16 +330,17 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
* Default to the first media bus code if the requested format is not
* supported.
*/
- for (i = 0; i < ncodes; ++i) {
- if (fmt->format.code == codes[i])
+ for (i = 0; i < entity->num_codes; ++i) {
+ if (fmt->format.code == entity->codes[i])
break;
}
- format->code = i < ncodes ? codes[i] : codes[0];
+ format->code = i < entity->num_codes
+ ? entity->codes[i] : entity->codes[0];
format->width = clamp_t(unsigned int, fmt->format.width,
- min_width, max_width);
+ entity->min_width, entity->max_width);
format->height = clamp_t(unsigned int, fmt->format.height,
- min_height, max_height);
+ entity->min_height, entity->max_height);
format->field = V4L2_FIELD_NONE;
format->colorspace = fmt->format.colorspace;
@@ -386,7 +380,7 @@ static int vsp1_entity_init_state(struct v4l2_subdev *subdev,
unsigned int pad;
/* Initialize all pad formats with default values. */
- for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
+ for (pad = 0; pad < subdev->entity.num_pads; ++pad) {
struct v4l2_subdev_format format = {
.pad = pad,
.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY
@@ -403,6 +397,11 @@ static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = {
.init_state = vsp1_entity_init_state,
};
+const struct v4l2_subdev_core_ops vsp1_entity_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
/* -----------------------------------------------------------------------------
* Media Operations
*/
@@ -646,6 +645,9 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
subdev->entity.ops = &vsp1->media_ops;
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ if (ops->core == &vsp1_entity_core_ops)
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+
snprintf(subdev->name, sizeof(subdev->name), "%s %s",
dev_name(vsp1->dev), name);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
index b7c72d0b7f8e..c0c1fe7d3e40 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
@@ -113,6 +113,13 @@ struct vsp1_entity {
unsigned int index;
const struct vsp1_route *route;
+ const u32 *codes;
+ unsigned int num_codes;
+ unsigned int min_width;
+ unsigned int min_height;
+ unsigned int max_width;
+ unsigned int max_height;
+
struct vsp1_pipeline *pipe;
struct list_head list_dev;
@@ -136,6 +143,8 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
return container_of(subdev, struct vsp1_entity, subdev);
}
+extern const struct v4l2_subdev_core_ops vsp1_entity_core_ops;
+
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
const char *name, unsigned int num_pads,
const struct v4l2_subdev_ops *ops, u32 function);
@@ -180,18 +189,12 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_format *fmt);
int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt,
- const unsigned int *codes, unsigned int ncodes,
- unsigned int min_width, unsigned int min_height,
- unsigned int max_width, unsigned int max_height);
+ struct v4l2_subdev_format *fmt);
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code,
- const unsigned int *codes, unsigned int ncodes);
+ struct v4l2_subdev_mbus_code_enum *code);
int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse,
- unsigned int min_w, unsigned int min_h,
- unsigned int max_w, unsigned int max_h);
+ struct v4l2_subdev_frame_size_enum *fse);
#endif /* __VSP1_ENTITY_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
index 390ea50f1595..3f87a2c9df0e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
@@ -167,16 +167,15 @@ static int histo_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- struct vsp1_histogram *histo = subdev_to_histo(subdev);
-
if (code->pad == HISTO_PAD_SOURCE) {
- code->code = MEDIA_BUS_FMT_FIXED;
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_METADATA_FIXED;
return 0;
}
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code,
- histo->formats,
- histo->num_formats);
+ return vsp1_subdev_enum_mbus_code(subdev, sd_state, code);
}
static int histo_enum_frame_size(struct v4l2_subdev *subdev,
@@ -184,12 +183,9 @@ static int histo_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->pad != HISTO_PAD_SINK)
- return -EINVAL;
+ return -ENOTTY;
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- HISTO_MIN_SIZE,
- HISTO_MIN_SIZE, HISTO_MAX_SIZE,
- HISTO_MAX_SIZE);
+ return vsp1_subdev_enum_frame_size(subdev, sd_state, fse);
}
static int histo_get_selection(struct v4l2_subdev *subdev,
@@ -354,22 +350,68 @@ static int histo_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_histogram *histo = subdev_to_histo(subdev);
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *selection;
+ unsigned int i;
- if (fmt->pad == HISTO_PAD_SOURCE) {
- fmt->format.code = MEDIA_BUS_FMT_FIXED;
- fmt->format.width = 0;
- fmt->format.height = 0;
- fmt->format.field = V4L2_FIELD_NONE;
- fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ state = vsp1_entity_get_state(entity, sd_state, fmt->which);
+ if (!state)
+ return -EINVAL;
- return 0;
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
+
+ guard(mutex)(&entity->lock);
+
+ if (fmt->pad == HISTO_PAD_SINK) {
+ /*
+ * Default to the first media bus code if the requested format
+ * is not supported.
+ */
+ for (i = 0; i < entity->num_codes; ++i) {
+ if (fmt->format.code == entity->codes[i])
+ break;
+ }
+
+ format->code = i < entity->num_codes
+ ? entity->codes[i] : entity->codes[0];
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ entity->min_width, entity->max_width);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ entity->min_height, entity->max_height);
+ format->field = V4L2_FIELD_NONE;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
+
+ /* Reset the crop and compose rectangles. */
+ selection = v4l2_subdev_state_get_crop(state, fmt->pad);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+
+ selection = v4l2_subdev_state_get_compose(state, fmt->pad);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+ } else {
+ format->code = MEDIA_BUS_FMT_METADATA_FIXED;
+ format->width = 0;
+ format->height = 0;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
}
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt,
- histo->formats, histo->num_formats,
- HISTO_MIN_SIZE, HISTO_MIN_SIZE,
- HISTO_MAX_SIZE, HISTO_MAX_SIZE);
+ fmt->format = *format;
+
+ return 0;
}
static const struct v4l2_subdev_pad_ops histo_pad_ops = {
@@ -382,6 +424,7 @@ static const struct v4l2_subdev_pad_ops histo_pad_ops = {
};
static const struct v4l2_subdev_ops histo_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &histo_pad_ops,
};
@@ -490,8 +533,6 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo,
{
int ret;
- histo->formats = formats;
- histo->num_formats = num_formats;
histo->data_size = data_size;
histo->meta_format = meta_format;
@@ -506,6 +547,12 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo,
/* Initialize the VSP entity... */
histo->entity.ops = ops;
histo->entity.type = type;
+ histo->entity.codes = formats;
+ histo->entity.num_codes = num_formats;
+ histo->entity.min_width = HISTO_MIN_SIZE;
+ histo->entity.min_height = HISTO_MIN_SIZE;
+ histo->entity.max_width = HISTO_MAX_SIZE;
+ histo->entity.max_height = HISTO_MAX_SIZE;
ret = vsp1_entity_init(vsp1, &histo->entity, name, 2, &histo_ops,
MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.h b/drivers/media/platform/renesas/vsp1/vsp1_histo.h
index 06f029846244..227db810da52 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_histo.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.h
@@ -36,8 +36,6 @@ struct vsp1_histogram {
struct video_device video;
struct media_pad pad;
- const u32 *formats;
- unsigned int num_formats;
size_t data_size;
u32 meta_format;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index 1fcd1967d3b2..830e124beb7b 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/gfp.h>
+#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
@@ -34,6 +35,11 @@ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit,
* V4L2 Subdevice Operations
*/
+static const unsigned int hsit_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+};
+
static int hsit_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -56,10 +62,50 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- HSIT_MIN_SIZE,
- HSIT_MIN_SIZE, HSIT_MAX_SIZE,
- HSIT_MAX_SIZE);
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct vsp1_hsit *hsit = to_hsit(subdev);
+ u32 code;
+
+ if (fse->index)
+ return -EINVAL;
+
+ if ((fse->pad == HSIT_PAD_SINK && !hsit->inverse) |
+ (fse->pad == HSIT_PAD_SOURCE && hsit->inverse))
+ code = MEDIA_BUS_FMT_ARGB8888_1X32;
+ else
+ code = MEDIA_BUS_FMT_AHSV8888_1X32;
+
+ if (fse->code != code)
+ return -EINVAL;
+
+ if (fse->pad == 0) {
+ fse->min_width = entity->min_width;
+ fse->max_width = entity->max_width;
+ fse->min_height = entity->min_height;
+ fse->max_height = entity->max_height;
+ } else {
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
+
+ state = vsp1_entity_get_state(entity, sd_state, fse->which);
+ if (!state)
+ return -EINVAL;
+
+ /*
+ * The size on the source pad is fixed and always identical to
+ * the sink pad.
+ */
+ format = v4l2_subdev_state_get_format(state, HSIT_PAD_SINK);
+
+ guard(mutex)(&entity->lock);
+
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
}
static int hsit_set_format(struct v4l2_subdev *subdev,
@@ -175,6 +221,13 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
else
hsit->entity.type = VSP1_ENTITY_HST;
+ hsit->entity.codes = hsit_codes;
+ hsit->entity.num_codes = ARRAY_SIZE(hsit_codes);
+ hsit->entity.min_width = HSIT_MIN_SIZE;
+ hsit->entity.min_height = HSIT_MIN_SIZE;
+ hsit->entity.max_width = HSIT_MAX_SIZE;
+ hsit->entity.max_height = HSIT_MAX_SIZE;
+
ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst",
2, &hsit_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
index 5dd62bebbe8c..d44c04e140bc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_iif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
@@ -36,38 +36,11 @@ static const unsigned int iif_codes[] = {
MEDIA_BUS_FMT_METADATA_FIXED
};
-static int iif_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes,
- ARRAY_SIZE(iif_codes));
-}
-
-static int iif_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
- IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
-}
-
-static int iif_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes,
- ARRAY_SIZE(iif_codes),
- IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
- IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
-}
-
static const struct v4l2_subdev_pad_ops iif_pad_ops = {
- .enum_mbus_code = iif_enum_mbus_code,
- .enum_frame_size = iif_enum_frame_size,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
+ .enum_frame_size = vsp1_subdev_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = iif_set_format,
+ .set_fmt = vsp1_subdev_set_pad_format,
};
static const struct v4l2_subdev_ops iif_ops = {
@@ -106,6 +79,12 @@ struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1)
iif->entity.ops = &iif_entity_ops;
iif->entity.type = VSP1_ENTITY_IIF;
+ iif->entity.codes = iif_codes;
+ iif->entity.num_codes = ARRAY_SIZE(iif_codes);
+ iif->entity.min_width = IIF_MIN_WIDTH;
+ iif->entity.min_height = IIF_MIN_HEIGHT;
+ iif->entity.max_width = IIF_MAX_WIDTH;
+ iif->entity.max_height = IIF_MAX_HEIGHT;
/*
* The IIF is never exposed to userspace, but media entity registration
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
index b3d83d1c5306..1ebb88b3e4c9 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
@@ -39,39 +39,11 @@ static const unsigned int lif_codes[] = {
MEDIA_BUS_FMT_AYUV8_1X32,
};
-static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lif_codes,
- ARRAY_SIZE(lif_codes));
-}
-
-static int lif_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- LIF_MIN_SIZE,
- LIF_MIN_SIZE, LIF_MAX_SIZE,
- LIF_MAX_SIZE);
-}
-
-static int lif_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lif_codes,
- ARRAY_SIZE(lif_codes),
- LIF_MIN_SIZE, LIF_MIN_SIZE,
- LIF_MAX_SIZE, LIF_MAX_SIZE);
-}
-
static const struct v4l2_subdev_pad_ops lif_pad_ops = {
- .enum_mbus_code = lif_enum_mbus_code,
- .enum_frame_size = lif_enum_frame_size,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
+ .enum_frame_size = vsp1_subdev_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = lif_set_format,
+ .set_fmt = vsp1_subdev_set_pad_format,
};
static const struct v4l2_subdev_ops lif_ops = {
@@ -162,6 +134,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1, unsigned int index)
lif->entity.ops = &lif_entity_ops;
lif->entity.type = VSP1_ENTITY_LIF;
lif->entity.index = index;
+ lif->entity.codes = lif_codes;
+ lif->entity.num_codes = ARRAY_SIZE(lif_codes);
+ lif->entity.min_width = LIF_MIN_SIZE;
+ lif->entity.min_height = LIF_MIN_SIZE;
+ lif->entity.max_width = LIF_MAX_SIZE;
+ lif->entity.max_height = LIF_MAX_SIZE;
/*
* The LIF is never exposed to userspace, but media entity registration
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
index dd264e6532e0..94bdedcc5c92 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
@@ -89,7 +89,7 @@ static const struct v4l2_ctrl_config lut_table_control = {
};
/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Pad Operations
+ * V4L2 Subdevice Operations
*/
static const unsigned int lut_codes[] = {
@@ -98,46 +98,15 @@ static const unsigned int lut_codes[] = {
MEDIA_BUS_FMT_AYUV8_1X32,
};
-static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lut_codes,
- ARRAY_SIZE(lut_codes));
-}
-
-static int lut_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- LUT_MIN_SIZE,
- LUT_MIN_SIZE, LUT_MAX_SIZE,
- LUT_MAX_SIZE);
-}
-
-static int lut_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lut_codes,
- ARRAY_SIZE(lut_codes),
- LUT_MIN_SIZE, LUT_MIN_SIZE,
- LUT_MAX_SIZE, LUT_MAX_SIZE);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Operations
- */
-
static const struct v4l2_subdev_pad_ops lut_pad_ops = {
- .enum_mbus_code = lut_enum_mbus_code,
- .enum_frame_size = lut_enum_frame_size,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
+ .enum_frame_size = vsp1_subdev_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = lut_set_format,
+ .set_fmt = vsp1_subdev_set_pad_format,
};
static const struct v4l2_subdev_ops lut_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &lut_pad_ops,
};
@@ -208,6 +177,12 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
lut->entity.ops = &lut_entity_ops;
lut->entity.type = VSP1_ENTITY_LUT;
+ lut->entity.codes = lut_codes;
+ lut->entity.num_codes = ARRAY_SIZE(lut_codes);
+ lut->entity.min_width = LUT_MIN_SIZE;
+ lut->entity.min_height = LUT_MIN_SIZE;
+ lut->entity.max_width = LUT_MAX_SIZE;
+ lut->entity.max_height = LUT_MAX_SIZE;
ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops,
MEDIA_ENT_F_PROC_VIDEO_LUT);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
index 811f2b7c5cc5..34b9095c9011 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
@@ -425,12 +425,13 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
if (rpf == NULL)
return ERR_PTR(-ENOMEM);
- rpf->max_width = RPF_MAX_WIDTH;
- rpf->max_height = RPF_MAX_HEIGHT;
-
rpf->entity.ops = &rpf_entity_ops;
rpf->entity.type = VSP1_ENTITY_RPF;
rpf->entity.index = index;
+ rpf->entity.min_width = RWPF_MIN_WIDTH;
+ rpf->entity.min_height = RWPF_MIN_HEIGHT;
+ rpf->entity.max_width = RPF_MAX_WIDTH;
+ rpf->entity.max_height = RPF_MAX_HEIGHT;
sprintf(name, "rpf.%u", index);
ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &vsp1_rwpf_subdev_ops,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index 9c8085d5d306..c72518b29f84 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
@@ -14,30 +14,53 @@
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
-#define RWPF_MIN_WIDTH 1
-#define RWPF_MIN_HEIGHT 1
-
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
+/* Keep HSV last. */
+static const unsigned int rwpf_codes[] = {
+ MEDIA_BUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+};
+
static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AHSV8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
- if (code->index >= ARRAY_SIZE(codes))
+ if (code->pad == RWPF_PAD_SINK)
+ return vsp1_subdev_enum_mbus_code(subdev, sd_state, code);
+
+ state = vsp1_entity_get_state(entity, sd_state, code->which);
+ if (!state)
return -EINVAL;
- code->code = codes[code->index];
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+
+ guard(mutex)(&entity->lock);
- if (code->pad == RWPF_PAD_SOURCE &&
- code->code == MEDIA_BUS_FMT_AYUV8_1X32)
+ /*
+ * The RWPF supports conversion between RGB and YUV formats, but HSV
+ * formats can't be converted.
+ */
+ if (format->code == MEDIA_BUS_FMT_AHSV8888_1X32) {
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_AHSV8888_1X32;
+ } else {
+ if (code->index >= ARRAY_SIZE(rwpf_codes) - 1)
+ return -EINVAL;
+
+ code->code = rwpf_codes[code->index];
+ }
+
+ if (code->code == MEDIA_BUS_FMT_AYUV8_1X32)
code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC
| V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
@@ -48,12 +71,42 @@ static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
- struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- RWPF_MIN_WIDTH,
- RWPF_MIN_HEIGHT, rwpf->max_width,
- rwpf->max_height);
+ if (fse->pad == RWPF_PAD_SINK)
+ return vsp1_subdev_enum_frame_size(subdev, sd_state, fse);
+
+ if (fse->index)
+ return -EINVAL;
+
+ state = vsp1_entity_get_state(entity, sd_state, fse->which);
+ if (!state)
+ return -EINVAL;
+
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+
+ guard(mutex)(&entity->lock);
+
+ /*
+ * The RWPF supports conversion between RGB and YUV formats, but
+ * HSV formats can't be converted.
+ */
+ if ((format->code == MEDIA_BUS_FMT_AHSV8888_1X32) !=
+ (fse->code == MEDIA_BUS_FMT_AHSV8888_1X32))
+ return -EINVAL;
+
+ /*
+ * The size on the source pad is fixed and always identical to
+ * the sink pad.
+ */
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+
+ return 0;
}
static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
@@ -126,9 +179,9 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
- RWPF_MIN_WIDTH, rwpf->max_width);
+ RWPF_MIN_WIDTH, rwpf->entity.max_width);
format->height = clamp_t(unsigned int, fmt->format.height,
- RWPF_MIN_HEIGHT, rwpf->max_height);
+ RWPF_MIN_HEIGHT, rwpf->entity.max_height);
format->field = V4L2_FIELD_NONE;
format->colorspace = fmt->format.colorspace;
@@ -216,6 +269,8 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
+ unsigned int min_width = RWPF_MIN_WIDTH;
+ unsigned int min_height = RWPF_MIN_HEIGHT;
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
@@ -244,21 +299,39 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
/*
- * Restrict the crop rectangle coordinates to multiples of 2 to avoid
- * shifting the color plane.
+ * For YUV formats, restrict the crop rectangle coordinates to multiples
+ * of 2 to avoid shifting the color plane.
*/
if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
sel->r.left = ALIGN(sel->r.left, 2);
sel->r.top = ALIGN(sel->r.top, 2);
sel->r.width = round_down(sel->r.width, 2);
sel->r.height = round_down(sel->r.height, 2);
+
+ /*
+ * The RPF doesn't enforces the alignment constraint on the sink
+ * pad format, which could have an odd size, possibly down to
+ * 1x1. In that case, the minimum width and height would be
+ * smaller than the sink pad format, leading to a negative upper
+ * bound in the left and top clamping. Clamp the minimum width
+ * and height to the format width and height to avoid this.
+ *
+ * In such a situation, odd values for the crop rectangle size
+ * would be accepted when clamping the width and height below.
+ * While that would create an invalid hardware configuration,
+ * the video device enforces proper alignment of the pixel
+ * format, and the mismatch will then result in link validation
+ * failure. Incorrect operation of the hardware is not possible.
+ */
+ min_width = min(ALIGN(min_width, 2), format->width);
+ min_height = min(ALIGN(min_height, 2), format->height);
}
- sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
- sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
- sel->r.width = min_t(unsigned int, sel->r.width,
+ sel->r.left = clamp_t(int, sel->r.left, 0, format->width - min_width);
+ sel->r.top = clamp_t(int, sel->r.top, 0, format->height - min_height);
+ sel->r.width = clamp(sel->r.width, min_width,
format->width - sel->r.left);
- sel->r.height = min_t(unsigned int, sel->r.height,
+ sel->r.height = clamp(sel->r.height, min_height,
format->height - sel->r.top);
crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
@@ -284,6 +357,7 @@ static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
};
const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &vsp1_rwpf_pad_ops,
};
@@ -311,6 +385,9 @@ static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
{
+ rwpf->entity.codes = rwpf_codes;
+ rwpf->entity.num_codes = ARRAY_SIZE(rwpf_codes);
+
v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
index 5ac9f0a6fafc..89c1c8e8bb6d 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
@@ -21,6 +21,9 @@
#define RWPF_PAD_SINK 0
#define RWPF_PAD_SOURCE 1
+#define RWPF_MIN_WIDTH 1
+#define RWPF_MIN_HEIGHT 1
+
struct v4l2_ctrl;
struct vsp1_dl_manager;
struct vsp1_rwpf;
@@ -36,9 +39,6 @@ struct vsp1_rwpf {
struct vsp1_video *video;
- unsigned int max_width;
- unsigned int max_height;
-
struct v4l2_pix_format_mplane format;
const struct vsp1_format_info *fmtinfo;
unsigned int brx_input;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index bba2872afaf2..94149da0c900 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
@@ -7,8 +7,10 @@
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gfp.h>
+#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
@@ -106,47 +108,35 @@ static const struct v4l2_ctrl_config sru_intensity_control = {
* V4L2 Subdevice Operations
*/
-static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes,
- ARRAY_SIZE(codes));
-}
+static const unsigned int sru_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
static int sru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_sru *sru = to_sru(subdev);
- struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
+ int ret;
- state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which);
- if (!state)
- return -EINVAL;
+ ret = vsp1_subdev_enum_frame_size(subdev, sd_state, fse);
+ if (ret)
+ return ret;
- format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
+ if (fse->pad == SRU_PAD_SOURCE) {
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
- mutex_lock(&sru->entity.lock);
+ state = vsp1_entity_get_state(&sru->entity, sd_state,
+ fse->which);
+ if (!state)
+ return -EINVAL;
- if (fse->index || fse->code != format->code) {
- ret = -EINVAL;
- goto done;
- }
+ format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
+
+ guard(mutex)(&sru->entity.lock);
- if (fse->pad == SRU_PAD_SINK) {
- fse->min_width = SRU_MIN_SIZE;
- fse->max_width = SRU_MAX_SIZE;
- fse->min_height = SRU_MIN_SIZE;
- fse->max_height = SRU_MAX_SIZE;
- } else {
fse->min_width = format->width;
fse->min_height = format->height;
if (format->width <= SRU_MAX_SIZE / 2 &&
@@ -159,9 +149,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
}
}
-done:
- mutex_unlock(&sru->entity.lock);
- return ret;
+ return 0;
}
static void sru_try_format(struct vsp1_sru *sru,
@@ -257,13 +245,14 @@ done:
}
static const struct v4l2_subdev_pad_ops sru_pad_ops = {
- .enum_mbus_code = sru_enum_mbus_code,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
.enum_frame_size = sru_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = sru_set_format,
};
static const struct v4l2_subdev_ops sru_ops = {
+ .core = &vsp1_entity_core_ops,
.pad = &sru_pad_ops,
};
@@ -370,6 +359,12 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
sru->entity.ops = &sru_entity_ops;
sru->entity.type = VSP1_ENTITY_SRU;
+ sru->entity.codes = sru_codes;
+ sru->entity.num_codes = ARRAY_SIZE(sru_codes);
+ sru->entity.min_width = SRU_MIN_SIZE;
+ sru->entity.max_width = SRU_MAX_SIZE;
+ sru->entity.min_height = SRU_MIN_SIZE;
+ sru->entity.max_height = SRU_MAX_SIZE;
ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
MEDIA_ENT_F_PROC_VIDEO_SCALER);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index 2db473b6f83c..dd4722315c56 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
@@ -7,8 +7,10 @@
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gfp.h>
+#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
@@ -111,56 +113,42 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
* V4L2 Subdevice Pad Operations
*/
-static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes,
- ARRAY_SIZE(codes));
-}
+static const unsigned int uds_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
static int uds_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_uds *uds = to_uds(subdev);
- struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
+ int ret;
- state = vsp1_entity_get_state(&uds->entity, sd_state, fse->which);
- if (!state)
- return -EINVAL;
+ ret = vsp1_subdev_enum_frame_size(subdev, sd_state, fse);
+ if (ret)
+ return ret;
- format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
+ if (fse->pad == UDS_PAD_SOURCE) {
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *format;
- mutex_lock(&uds->entity.lock);
+ state = vsp1_entity_get_state(&uds->entity, sd_state,
+ fse->which);
+ if (!state)
+ return -EINVAL;
- if (fse->index || fse->code != format->code) {
- ret = -EINVAL;
- goto done;
- }
+ format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
+
+ guard(mutex)(&uds->entity.lock);
- if (fse->pad == UDS_PAD_SINK) {
- fse->min_width = UDS_MIN_SIZE;
- fse->max_width = UDS_MAX_SIZE;
- fse->min_height = UDS_MIN_SIZE;
- fse->max_height = UDS_MAX_SIZE;
- } else {
uds_output_limits(format->width, &fse->min_width,
&fse->max_width);
uds_output_limits(format->height, &fse->min_height,
&fse->max_height);
}
-done:
- mutex_unlock(&uds->entity.lock);
- return ret;
+ return 0;
}
static void uds_try_format(struct vsp1_uds *uds,
@@ -244,7 +232,7 @@ done:
*/
static const struct v4l2_subdev_pad_ops uds_pad_ops = {
- .enum_mbus_code = uds_enum_mbus_code,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
.enum_frame_size = uds_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = uds_set_format,
@@ -410,6 +398,12 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
uds->entity.ops = &uds_entity_ops;
uds->entity.type = VSP1_ENTITY_UDS;
uds->entity.index = index;
+ uds->entity.codes = uds_codes;
+ uds->entity.num_codes = ARRAY_SIZE(uds_codes);
+ uds->entity.min_width = UDS_MIN_SIZE;
+ uds->entity.max_width = UDS_MAX_SIZE;
+ uds->entity.min_height = UDS_MIN_SIZE;
+ uds->entity.max_height = UDS_MAX_SIZE;
sprintf(name, "uds.%u", index);
ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
index edaf28b544d2..3aefe5c9d421 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
@@ -53,34 +53,6 @@ static const unsigned int uif_codes[] = {
MEDIA_BUS_FMT_AYUV8_1X32,
};
-static int uif_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, uif_codes,
- ARRAY_SIZE(uif_codes));
-}
-
-static int uif_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
- UIF_MIN_SIZE,
- UIF_MIN_SIZE, UIF_MAX_SIZE,
- UIF_MAX_SIZE);
-}
-
-static int uif_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, uif_codes,
- ARRAY_SIZE(uif_codes),
- UIF_MIN_SIZE, UIF_MIN_SIZE,
- UIF_MAX_SIZE, UIF_MAX_SIZE);
-}
-
static int uif_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
@@ -171,10 +143,10 @@ done:
*/
static const struct v4l2_subdev_pad_ops uif_pad_ops = {
- .enum_mbus_code = uif_enum_mbus_code,
- .enum_frame_size = uif_enum_frame_size,
+ .enum_mbus_code = vsp1_subdev_enum_mbus_code,
+ .enum_frame_size = vsp1_subdev_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = uif_set_format,
+ .set_fmt = vsp1_subdev_set_pad_format,
.get_selection = uif_get_selection,
.set_selection = uif_set_selection,
};
@@ -250,6 +222,12 @@ struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index)
uif->entity.ops = &uif_entity_ops;
uif->entity.type = VSP1_ENTITY_UIF;
uif->entity.index = index;
+ uif->entity.codes = uif_codes;
+ uif->entity.num_codes = ARRAY_SIZE(uif_codes);
+ uif->entity.min_width = UIF_MIN_SIZE;
+ uif->entity.min_height = UIF_MIN_SIZE;
+ uif->entity.max_width = UIF_MAX_SIZE;
+ uif->entity.max_height = UIF_MAX_SIZE;
/* The datasheet names the two UIF instances UIF4 and UIF5. */
sprintf(name, "uif.%u", index + 4);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index 30662cfdf837..cd6c5592221b 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
@@ -531,7 +531,7 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity,
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
- return wpf->flip.rotate ? 256 : wpf->max_width;
+ return wpf->flip.rotate ? 256 : wpf->entity.max_width;
}
static void wpf_partition(struct vsp1_entity *entity,
@@ -567,12 +567,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
if (wpf == NULL)
return ERR_PTR(-ENOMEM);
+ wpf->entity.min_width = RWPF_MIN_WIDTH;
+ wpf->entity.min_height = RWPF_MIN_HEIGHT;
+
if (vsp1->info->gen == 2) {
- wpf->max_width = WPF_GEN2_MAX_WIDTH;
- wpf->max_height = WPF_GEN2_MAX_HEIGHT;
+ wpf->entity.max_width = WPF_GEN2_MAX_WIDTH;
+ wpf->entity.max_height = WPF_GEN2_MAX_HEIGHT;
} else {
- wpf->max_width = WPF_GEN3_MAX_WIDTH;
- wpf->max_height = WPF_GEN3_MAX_HEIGHT;
+ wpf->entity.max_width = WPF_GEN3_MAX_WIDTH;
+ wpf->entity.max_height = WPF_GEN3_MAX_HEIGHT;
}
wpf->entity.ops = &wpf_entity_ops;
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
index 1b81bcc067ef..9e67160a16e4 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
@@ -489,8 +489,8 @@ static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface,
block = interface->index - RKCIF_MIPI_BASE;
- if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
- WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX))
+ if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) ||
+ WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs)))
return RKCIF_REGISTER_NOTSUPPORTED;
offset = rkcif->match_data->mipi->blocks[block].offset;
@@ -510,9 +510,9 @@ static inline unsigned int rkcif_mipi_id_get_reg(struct rkcif_stream *stream,
block = stream->interface->index - RKCIF_MIPI_BASE;
id = stream->id;
- if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
- WARN_ON_ONCE(id > RKCIF_ID_MAX) ||
- WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX))
+ if (WARN_ON_ONCE(block >= ARRAY_SIZE(rkcif->match_data->mipi->blocks)) ||
+ WARN_ON_ONCE(id >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id)) ||
+ WARN_ON_ONCE(index >= ARRAY_SIZE(rkcif->match_data->mipi->regs_id[id])))
return RKCIF_REGISTER_NOTSUPPORTED;
offset = rkcif->match_data->mipi->blocks[block].offset;
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
index 523103872b7a..414a9980cf2e 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
@@ -378,7 +378,8 @@ int rkcif_interface_register(struct rkcif_device *rkcif,
snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d",
interface->index - RKCIF_MIPI_BASE);
- pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads);
if (ret)
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
index e00010a91e8b..3130d420ad55 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
@@ -106,42 +106,42 @@ static int rkcif_stream_init_buffers(struct rkcif_stream *stream)
{
struct v4l2_pix_format_mplane *pix = &stream->pix;
- stream->buffers[0] = rkcif_stream_pop_buffer(stream);
- if (!stream->buffers[0])
- goto err_buff_0;
-
- stream->buffers[1] = rkcif_stream_pop_buffer(stream);
- if (!stream->buffers[1])
- goto err_buff_1;
-
- if (stream->queue_buffer) {
- stream->queue_buffer(stream, 0);
- stream->queue_buffer(stream, 1);
- }
-
stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage;
stream->dummy.vaddr =
dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size,
&stream->dummy.buffer.buff_addr[0], GFP_KERNEL,
DMA_ATTR_NO_KERNEL_MAPPING);
if (!stream->dummy.vaddr)
- goto err_dummy;
+ return -ENOMEM;
for (unsigned int i = 1; i < pix->num_planes; i++)
stream->dummy.buffer.buff_addr[i] =
stream->dummy.buffer.buff_addr[i - 1] +
pix->plane_fmt[i - 1].bytesperline * pix->height;
- return 0;
+ stream->buffers[0] = rkcif_stream_pop_buffer(stream);
+ if (!stream->buffers[0])
+ goto err_dummy_free;
+
+ stream->buffers[1] = rkcif_stream_pop_buffer(stream);
+ if (!stream->buffers[1]) {
+ stream->buffers[1] = &stream->dummy.buffer;
+ stream->buffers[1]->is_dummy = true;
+ }
-err_dummy:
- rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED);
- stream->buffers[1] = NULL;
+ if (stream->queue_buffer) {
+ stream->queue_buffer(stream, 0);
+ stream->queue_buffer(stream, 1);
+ }
+
+ return 0;
-err_buff_1:
- rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED);
- stream->buffers[0] = NULL;
-err_buff_0:
+err_dummy_free:
+ dma_free_attrs(stream->rkcif->dev, stream->dummy.size,
+ stream->dummy.vaddr,
+ stream->dummy.buffer.buff_addr[0],
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ stream->dummy.vaddr = NULL;
return -EINVAL;
}
@@ -555,7 +555,7 @@ int rkcif_stream_register(struct rkcif_device *rkcif,
vdev->vfl_dir = VFL_DIR_RX;
video_set_drvdata(vdev, stream);
- stream->pad.flags = MEDIA_PAD_FL_SINK;
+ stream->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
stream->pix.height = CIF_MIN_HEIGHT;
stream->pix.width = CIF_MIN_WIDTH;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
index 867cdddf9f89..20d88865081c 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -1380,6 +1380,9 @@ static int rkisp1_enum_framesizes(struct file *file, void *fh,
};
struct rkisp1_capture *cap = video_drvdata(file);
+ if (!rkisp1_find_fmt_cfg(cap, fsize->pixel_format))
+ return -EINVAL;
+
if (fsize->index != 0)
return -EINVAL;
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
index 2c9edd0a559b..57e573949e54 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
@@ -1020,11 +1020,15 @@ err_sclk:
static int fimc_runtime_resume(struct device *dev)
{
struct fimc_dev *fimc = dev_get_drvdata(dev);
+ int ret;
dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
/* Enable clocks and perform basic initialization */
- clk_enable(fimc->clock[CLK_GATE]);
+ ret = clk_enable(fimc->clock[CLK_GATE]);
+ if (ret)
+ return ret;
+
fimc_hw_reset(fimc);
/* Resume the capture or mem-to-mem device */
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index 0827fdaf455a..b7d9bfedd590 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
@@ -200,22 +200,19 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
static int fimc_is_register_subdevs(struct fimc_is *is)
{
- struct device_node *i2c_bus, *child;
int ret, index = 0;
ret = fimc_isp_subdev_create(&is->isp);
if (ret < 0)
return ret;
- for_each_compatible_node(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") {
- for_each_available_child_of_node(i2c_bus, child) {
+ for_each_compatible_node_scoped(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") {
+ for_each_available_child_of_node_scoped(i2c_bus, child) {
ret = fimc_is_parse_sensor_config(is, index, child);
- if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
- of_node_put(child);
- of_node_put(i2c_bus);
+ if (ret < 0 || index >= FIMC_IS_SENSORS_NUM)
return ret;
- }
+
index++;
}
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 13762861b769..e5663fbe6422 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -15,6 +15,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
+#include <linux/genalloc.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -113,6 +114,9 @@ struct dcmi_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
struct sg_table sgt;
+ struct sg_table sgt_mdma;
+ struct dma_async_tx_descriptor *dma_desc;
+ struct dma_async_tx_descriptor *mdma_desc;
size_t size;
struct list_head list;
};
@@ -158,14 +162,20 @@ struct stm32_dcmi {
struct dma_chan *dma_chan;
dma_cookie_t dma_cookie;
u32 dma_max_burst;
+
+ /* Elements for the MDMA - DMA chaining */
+ struct gen_pool *sram_pool;
+ struct dma_chan *mdma_chan;
+ void *sram_buf;
+ u32 sram_buf_size;
+ dma_addr_t sram_dma_buf;
+ dma_cookie_t mdma_cookie;
+
u32 misr;
int errors_count;
int overrun_count;
int buffers_count;
- /* Ensure DMA operations atomicity */
- struct mutex dma_lock;
-
struct media_device mdev;
struct media_pad vid_cap_pad;
struct media_pipeline pipeline;
@@ -229,127 +239,42 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
{
struct dcmi_buf *buf;
- spin_lock_irq(&dcmi->irqlock);
-
- if (dcmi->state != RUNNING) {
- spin_unlock_irq(&dcmi->irqlock);
- return -EINVAL;
- }
+ /* Nothing to do if we are not running */
+ if (dcmi->state != RUNNING)
+ return 0;
/* Restart a new DMA transfer with next buffer */
if (list_empty(&dcmi->buffers)) {
dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n");
dcmi->state = WAIT_FOR_BUFFER;
- spin_unlock_irq(&dcmi->irqlock);
return 0;
}
buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list);
dcmi->active = buf;
- spin_unlock_irq(&dcmi->irqlock);
-
return dcmi_start_capture(dcmi, buf);
}
-static void dcmi_dma_callback(void *param)
-{
- struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
- struct dma_tx_state state;
- enum dma_status status;
- struct dcmi_buf *buf = dcmi->active;
-
- spin_lock_irq(&dcmi->irqlock);
-
- /* Check DMA status */
- status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
-
- switch (status) {
- case DMA_IN_PROGRESS:
- dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__);
- break;
- case DMA_PAUSED:
- dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__);
- break;
- case DMA_ERROR:
- dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__);
-
- /* Return buffer to V4L2 in error state */
- dcmi_buffer_done(dcmi, buf, 0, -EIO);
- break;
- case DMA_COMPLETE:
- dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
-
- /* Return buffer to V4L2 */
- dcmi_buffer_done(dcmi, buf, buf->size, 0);
-
- spin_unlock_irq(&dcmi->irqlock);
-
- /* Restart capture */
- if (dcmi_restart_capture(dcmi))
- dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n",
- __func__);
- return;
- default:
- dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
- break;
- }
-
- spin_unlock_irq(&dcmi->irqlock);
-}
-
static int dcmi_start_dma(struct stm32_dcmi *dcmi,
struct dcmi_buf *buf)
{
- struct dma_async_tx_descriptor *desc = NULL;
- struct dma_slave_config config;
- int ret;
-
- memset(&config, 0, sizeof(config));
-
- config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
- config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_maxburst = 4;
-
- /* Configure DMA channel */
- ret = dmaengine_slave_config(dcmi->dma_chan, &config);
- if (ret < 0) {
- dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n",
- __func__, ret);
- return ret;
- }
-
- /*
- * Avoid call of dmaengine_terminate_sync() between
- * dmaengine_prep_slave_single() and dmaengine_submit()
- * by locking the whole DMA submission sequence
- */
- mutex_lock(&dcmi->dma_lock);
-
- /* Prepare a DMA transaction */
- desc = dmaengine_prep_slave_sg(dcmi->dma_chan, buf->sgt.sgl, buf->sgt.nents,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!desc) {
- dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
- mutex_unlock(&dcmi->dma_lock);
- return -EINVAL;
+ /* Push current DMA transaction in the pending queue */
+ if (dcmi->mdma_chan) {
+ dcmi->mdma_cookie = dmaengine_submit(buf->mdma_desc);
+ if (dma_submit_error(dcmi->mdma_cookie)) {
+ dev_err(dcmi->dev, "%s: MDMA submission failed\n", __func__);
+ return -ENXIO;
+ }
}
- /* Set completion callback routine for notification */
- desc->callback = dcmi_dma_callback;
- desc->callback_param = dcmi;
-
- /* Push current DMA transaction in the pending queue */
- dcmi->dma_cookie = dmaengine_submit(desc);
+ dcmi->dma_cookie = dmaengine_submit(buf->dma_desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
- mutex_unlock(&dcmi->dma_lock);
return -ENXIO;
}
- mutex_unlock(&dcmi->dma_lock);
-
+ if (dcmi->mdma_chan)
+ dma_async_issue_pending(dcmi->mdma_chan);
dma_async_issue_pending(dcmi->dma_chan);
return 0;
@@ -396,9 +321,11 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
reg_set(dcmi->regs, DCMI_CR, CR_CROP);
}
-static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
+static void dcmi_process_frame(struct stm32_dcmi *dcmi)
{
- struct dma_tx_state state;
+ struct dma_tx_state state, state_dma;
+ size_t bytes_used;
+
enum dma_status status;
struct dcmi_buf *buf = dcmi->active;
@@ -406,38 +333,48 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
return;
/*
- * Because of variable JPEG buffer size sent by sensor,
- * DMA transfer never completes due to transfer size never reached.
- * In order to ensure that all the JPEG data are transferred
- * in active buffer memory, DMA is drained.
- * Then DMA tx status gives the amount of data transferred
- * to memory, which is then returned to V4L2 through the active
- * buffer payload.
+ * Pause the DMA transfer, leading to trigger of the MDMA channel read while
+ * keeping a valid residue value on the dma channel
*/
+ if (dcmi->mdma_chan) {
+ spin_unlock_irq(&dcmi->irqlock);
+ dmaengine_pause(dcmi->dma_chan);
+ spin_lock_irq(&dcmi->irqlock);
+
+ do {
+ status = dmaengine_tx_status(dcmi->mdma_chan, dcmi->mdma_cookie, &state);
+ cpu_relax();
+ } while (status != DMA_ERROR && status != DMA_COMPLETE &&
+ state.residue && state.in_flight_bytes);
+ } else {
+ status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
+ }
- /* Drain DMA */
- dmaengine_synchronize(dcmi->dma_chan);
-
- /* Get DMA residue to get JPEG size */
- status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
if (status != DMA_ERROR && state.residue < buf->size) {
- /* Return JPEG buffer to V4L2 with received JPEG buffer size */
- dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0);
+ bytes_used = buf->size - state.residue;
+
+ if (state.residue && dcmi->mdma_chan) {
+ dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state_dma);
+ /* Getting full size residue equal to no residue */
+ if (state_dma.residue == dcmi->sram_buf_size)
+ state_dma.residue = 0;
+ bytes_used -= state_dma.residue;
+ }
+
+ /* Return buffer to V4L2 with received data size */
+ dcmi_buffer_done(dcmi, buf, bytes_used, 0);
} else {
dcmi->errors_count++;
- dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n",
- __func__);
- /* Return JPEG buffer to V4L2 in ERROR state */
+ dev_err(dcmi->dev, "%s: DMA error. status: 0x%x, residue: %d\n",
+ __func__, status, state.residue);
+ /* Return buffer to V4L2 in ERROR state */
dcmi_buffer_done(dcmi, buf, 0, -EIO);
}
/* Abort DMA operation */
- dmaengine_terminate_sync(dcmi->dma_chan);
-
- /* Restart capture */
- if (dcmi_restart_capture(dcmi))
- dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n",
- __func__);
+ dmaengine_terminate_async(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_async(dcmi->mdma_chan);
}
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
@@ -447,19 +384,31 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
spin_lock_irq(&dcmi->irqlock);
if (dcmi->misr & IT_OVR) {
+ /* Disable capture */
+ reg_clear(dcmi->regs, DCMI_CR, CR_CAPTURE);
+
dcmi->overrun_count++;
+
if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD)
dcmi->errors_count++;
+
+ dmaengine_terminate_async(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_async(dcmi->mdma_chan);
+
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
+ spin_unlock_irq(&dcmi->irqlock);
+ return IRQ_HANDLED;
}
+
if (dcmi->misr & IT_ERR)
dcmi->errors_count++;
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG &&
- dcmi->misr & IT_FRAME) {
- /* JPEG received */
- spin_unlock_irq(&dcmi->irqlock);
- dcmi_process_jpeg(dcmi);
- return IRQ_HANDLED;
+ if (dcmi->misr & IT_FRAME) {
+ dcmi_process_frame(dcmi);
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
}
spin_unlock_irq(&dcmi->irqlock);
@@ -539,31 +488,134 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(vb, 0, size);
if (!buf->prepared) {
+ u32 max_size = dcmi->dma_max_burst;
+ unsigned int dma_nents;
+
/* Get memory addresses */
buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
- if (buf->size > dcmi->dma_max_burst)
- num_sgs = DIV_ROUND_UP(buf->size, dcmi->dma_max_burst);
+ if (dcmi->mdma_chan)
+ max_size = dcmi->sram_buf_size / 2;
+
+ if (buf->size > max_size)
+ num_sgs = DIV_ROUND_UP(buf->size, max_size);
+
+ /*
+ * If we use MDMA chaining, DMA is used in cyclic mode and the scatterlist
+ * maximum size is thus 2
+ */
+ dma_nents = num_sgs;
+ if (dcmi->mdma_chan)
+ dma_nents = min_t(unsigned int, num_sgs, 2);
- ret = sg_alloc_table(&buf->sgt, num_sgs, GFP_ATOMIC);
+ ret = sg_alloc_table(&buf->sgt, dma_nents, GFP_ATOMIC);
if (ret) {
- dev_err(dcmi->dev, "sg table alloc failed\n");
+ dev_err(dcmi->dev, "sg table alloc failed for DMA\n");
return ret;
}
+ if (dcmi->mdma_chan) {
+ ret = sg_alloc_table(&buf->sgt_mdma, num_sgs, GFP_ATOMIC);
+ if (ret) {
+ dev_err(dcmi->dev, "sg table alloc failed for MDMA\n");
+ return ret;
+ }
+ }
+
dma_buf = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n",
vb->index, &dma_buf, buf->size);
- for_each_sg(buf->sgt.sgl, sg, num_sgs, i) {
- size_t bytes = min_t(size_t, size, dcmi->dma_max_burst);
+ for_each_sg(buf->sgt.sgl, sg, dma_nents, i) {
+ size_t bytes = min_t(size_t, size, max_size);
+
+ if (!dcmi->mdma_chan) {
+ sg_dma_address(sg) = dma_buf;
+ dma_buf += bytes;
+ } else {
+ /* Targets the beginning = first half of the sram_buf */
+ sg_dma_address(sg) = dcmi->sram_dma_buf;
+ /*
+ * Targets the second half of the sram_bubf
+ * for odd indexes of the item of the sg_list
+ */
+ if (i & 1)
+ sg->dma_address += (dcmi->sram_buf_size / 2);
+ }
+ /*
+ * In case of DMA-MDMA chaining, since the DMA is working in cyclic mode,
+ * we need to provide two linked-list node of same size in order to have
+ * a correct residue value computed.
+ */
+ if (!dcmi->mdma_chan)
+ sg_dma_len(sg) = bytes;
+ else
+ sg_dma_len(sg) = dcmi->sram_buf_size / 2;
- sg_dma_address(sg) = dma_buf;
- sg_dma_len(sg) = bytes;
- dma_buf += bytes;
size -= bytes;
}
+ /* Prepare a DMA transaction */
+ buf->dma_desc = dmaengine_prep_slave_sg(dcmi->dma_chan,
+ buf->sgt.sgl,
+ buf->sgt.nents,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!buf->dma_desc) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
+ if (dcmi->mdma_chan)
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
+ /* Mark the descriptor as reusable to avoid having to prepare it */
+ ret = dmaengine_desc_set_reuse(buf->dma_desc);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_desc_set_reuse failed\n", __func__);
+ dmaengine_desc_free(buf->dma_desc);
+ if (dcmi->mdma_chan)
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
+ if (dcmi->mdma_chan) {
+ size = dcmi->fmt.fmt.pix.sizeimage;
+ for_each_sg(buf->sgt_mdma.sgl, sg, num_sgs, i) {
+ size_t bytes = min_t(size_t, size, max_size);
+
+ sg_dma_address(sg) = dma_buf;
+ sg_dma_len(sg) = bytes;
+ dma_buf += bytes;
+ size -= bytes;
+ }
+
+ buf->mdma_desc = dmaengine_prep_slave_sg(dcmi->mdma_chan, buf->sgt_mdma.sgl,
+ buf->sgt_mdma.nents,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!buf->mdma_desc) {
+ dev_err(dcmi->dev, "%s: failed dmaengine_prep_slave_sg for MDMA\n",
+ __func__);
+ dmaengine_desc_free(buf->dma_desc);
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
+ ret = dmaengine_desc_set_reuse(buf->mdma_desc);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: failed to set reuse for mdma desc\n",
+ __func__);
+ dmaengine_desc_free(buf->mdma_desc);
+ dmaengine_desc_free(buf->dma_desc);
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+ }
+
buf->prepared = true;
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
@@ -572,6 +624,31 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
return 0;
}
+static void dcmi_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+ int ret;
+
+ if (!buf->prepared)
+ return;
+
+ if (dcmi->mdma_chan) {
+ ret = dmaengine_desc_free(buf->mdma_desc);
+ if (ret)
+ dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n",
+ __func__, ret);
+ sg_free_table(&buf->sgt_mdma);
+ }
+
+ ret = dmaengine_desc_free(buf->dma_desc);
+ if (ret)
+ dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n",
+ __func__, ret);
+ sg_free_table(&buf->sgt);
+}
+
static void dcmi_buf_queue(struct vb2_buffer *vb)
{
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
@@ -590,11 +667,9 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
- spin_unlock_irq(&dcmi->irqlock);
if (dcmi_start_capture(dcmi, buf))
dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
__func__);
- return;
}
spin_unlock_irq(&dcmi->irqlock);
@@ -773,10 +848,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
if (dcmi->do_crop)
dcmi_set_crop(dcmi);
- /* Enable jpeg capture */
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
- reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
-
/* Enable dcmi */
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
@@ -803,19 +874,18 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
- spin_unlock_irq(&dcmi->irqlock);
ret = dcmi_start_capture(dcmi, buf);
if (ret) {
dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n",
__func__);
+ spin_unlock_irq(&dcmi->irqlock);
goto err_pipeline_stop;
}
/* Enable interruptions */
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
- reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
- else
- reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR);
+ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ spin_unlock_irq(&dcmi->irqlock);
return 0;
@@ -876,9 +946,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&dcmi->irqlock);
/* Stop all pending DMA operations */
- mutex_lock(&dcmi->dma_lock);
dmaengine_terminate_sync(dcmi->dma_chan);
- mutex_unlock(&dcmi->dma_lock);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_sync(dcmi->mdma_chan);
pm_runtime_put(dcmi->dev);
@@ -895,6 +965,7 @@ static const struct vb2_ops dcmi_video_qops = {
.queue_setup = dcmi_queue_setup,
.buf_init = dcmi_buf_init,
.buf_prepare = dcmi_buf_prepare,
+ .buf_cleanup = dcmi_buf_cleanup,
.buf_queue = dcmi_buf_queue,
.start_streaming = dcmi_start_streaming,
.stop_streaming = dcmi_stop_streaming,
@@ -1886,8 +1957,9 @@ static int dcmi_probe(struct platform_device *pdev)
struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
struct stm32_dcmi *dcmi;
struct vb2_queue *q;
- struct dma_chan *chan;
+ struct dma_chan *chan, *mdma_chan;
struct dma_slave_caps caps;
+ struct dma_slave_config dma_config, mdma_config;
struct clk *mclk;
int ret = 0;
@@ -1949,14 +2021,73 @@ static int dcmi_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(chan),
"Failed to request DMA channel\n");
+ mdma_chan = dma_request_chan(&pdev->dev, "mdma_tx");
+ if (IS_ERR(mdma_chan)) {
+ ret = PTR_ERR(mdma_chan);
+ if (ret != -ENODEV)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n");
+ mdma_chan = NULL;
+ }
+
+ /* Configure the DMA channel */
+ memset(&dma_config, 0, sizeof(dma_config));
+
+ dma_config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
+ dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ if (mdma_chan)
+ dma_config.peripheral_size = 1; /* Indicates chaining */
+
+ /* Configure DMA channel */
+ ret = dmaengine_slave_config(chan, &dma_config);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n",
+ __func__, ret);
+ goto err_dma_slave_config;
+ }
+
+ /* If we want MDMA, we also need a sram pool */
+ if (mdma_chan) {
+ dcmi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (!dcmi->sram_pool) {
+ dev_info(&pdev->dev, "No SRAM pool, can't use MDMA chaining\n");
+ goto err_dma_slave_config;
+ }
+
+ dev_info(&pdev->dev, "SRAM pool: %zu KiB for DMA/MDMA chaining\n",
+ gen_pool_size(dcmi->sram_pool) / 1024);
+
+ dcmi->sram_buf_size = gen_pool_size(dcmi->sram_pool);
+ dcmi->sram_buf = gen_pool_dma_zalloc(dcmi->sram_pool, dcmi->sram_buf_size,
+ &dcmi->sram_dma_buf);
+ if (!dcmi->sram_buf) {
+ dev_err(dcmi->dev, "Failed to allocate from SRAM\n");
+ goto err_dma_slave_config;
+ }
+
+ /* Configure the MDMA Channel */
+ memset(&mdma_config, 0, sizeof(mdma_config));
+ mdma_config.direction = DMA_DEV_TO_MEM;
+ mdma_config.src_addr = dcmi->sram_dma_buf;
+ mdma_config.peripheral_size = dma_config.peripheral_size;
+ mdma_config.peripheral_config = dma_config.peripheral_config;
+ ret = dmaengine_slave_config(mdma_chan, &mdma_config);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: MDMA channel config failed (%d)\n",
+ __func__, ret);
+ goto err_mdma_slave_config;
+ }
+ }
+
dcmi->dma_max_burst = UINT_MAX;
- ret = dma_get_slave_caps(chan, &caps);
- if (!ret && caps.max_sg_burst)
- dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+ /* In case of using DMA-MDMA chaining we consider the maximum infini */
+ if (!mdma_chan) {
+ ret = dma_get_slave_caps(chan, &caps);
+ if (!ret && caps.max_sg_burst)
+ dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
- mutex_init(&dcmi->dma_lock);
init_completion(&dcmi->complete);
INIT_LIST_HEAD(&dcmi->buffers);
@@ -1964,6 +2095,7 @@ static int dcmi_probe(struct platform_device *pdev)
dcmi->mclk = mclk;
dcmi->state = STOPPED;
dcmi->dma_chan = chan;
+ dcmi->mdma_chan = mdma_chan;
q = &dcmi->queue;
@@ -2072,7 +2204,13 @@ err_device_unregister:
v4l2_device_unregister(&dcmi->v4l2_dev);
err_media_device_cleanup:
media_device_cleanup(&dcmi->mdev);
+err_mdma_slave_config:
+ if (dcmi->mdma_chan)
+ gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size);
+err_dma_slave_config:
dma_release_channel(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dma_release_channel(mdma_chan);
return ret;
}
@@ -2090,9 +2228,13 @@ static void dcmi_remove(struct platform_device *pdev)
media_device_cleanup(&dcmi->mdev);
dma_release_channel(dcmi->dma_chan);
+ if (dcmi->mdma_chan) {
+ gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size);
+ dma_release_channel(dcmi->mdma_chan);
+ }
}
-static __maybe_unused int dcmi_runtime_suspend(struct device *dev)
+static int dcmi_runtime_suspend(struct device *dev)
{
struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
@@ -2101,7 +2243,7 @@ static __maybe_unused int dcmi_runtime_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int dcmi_runtime_resume(struct device *dev)
+static int dcmi_runtime_resume(struct device *dev)
{
struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
int ret;
@@ -2113,7 +2255,7 @@ static __maybe_unused int dcmi_runtime_resume(struct device *dev)
return ret;
}
-static __maybe_unused int dcmi_suspend(struct device *dev)
+static int dcmi_suspend(struct device *dev)
{
/* disable clock */
pm_runtime_force_suspend(dev);
@@ -2124,7 +2266,7 @@ static __maybe_unused int dcmi_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int dcmi_resume(struct device *dev)
+static int dcmi_resume(struct device *dev)
{
/* restore pinctl default state */
pinctrl_pm_select_default_state(dev);
@@ -2136,9 +2278,8 @@ static __maybe_unused int dcmi_resume(struct device *dev)
}
static const struct dev_pm_ops dcmi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume)
- SET_RUNTIME_PM_OPS(dcmi_runtime_suspend,
- dcmi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume)
+ RUNTIME_PM_OPS(dcmi_runtime_suspend, dcmi_runtime_resume, NULL)
};
static struct platform_driver stm32_dcmi_driver = {
@@ -2147,7 +2288,7 @@ static struct platform_driver stm32_dcmi_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(stm32_dcmi_of_match),
- .pm = &dcmi_pm_ops,
+ .pm = pm_ptr(&dcmi_pm_ops),
},
};
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
index bf2ac092fbb3..b109de2c8111 100644
--- a/drivers/media/platform/synopsys/Kconfig
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -4,6 +4,7 @@ source "drivers/media/platform/synopsys/hdmirx/Kconfig"
config VIDEO_DW_MIPI_CSI2RX
tristate "Synopsys DesignWare MIPI CSI-2 Receiver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on VIDEO_DEV
depends on V4L_PLATFORM_DRIVERS
depends on PM && COMMON_CLK
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index 4d96171a650b..02eb4a6cafad 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -7,6 +7,7 @@
* Copyright (C) 2026 Collabora, Ltd.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -24,15 +25,6 @@
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
-#define DW_MIPI_CSI2RX_N_LANES 0x04
-#define DW_MIPI_CSI2RX_RESETN 0x10
-#define DW_MIPI_CSI2RX_PHY_STATE 0x14
-#define DW_MIPI_CSI2RX_ERR1 0x20
-#define DW_MIPI_CSI2RX_ERR2 0x24
-#define DW_MIPI_CSI2RX_MSK1 0x28
-#define DW_MIPI_CSI2RX_MSK2 0x2c
-#define DW_MIPI_CSI2RX_CONTROL 0x40
-
#define SW_CPHY_EN(x) ((x) << 0)
#define SW_DSI_EN(x) ((x) << 4)
#define SW_DATATYPE_FS(x) ((x) << 8)
@@ -40,7 +32,47 @@
#define SW_DATATYPE_LS(x) ((x) << 20)
#define SW_DATATYPE_LE(x) ((x) << 26)
-#define DW_MIPI_CSI2RX_CLKS_MAX 1
+#define DW_REG_EXIST BIT(31)
+#define DW_REG(x) (DW_REG_EXIST | (x))
+
+#define DPHY_TEST_CTRL0_TEST_CLR BIT(0)
+
+#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x))
+#define IPI_VCID_VC_0_1(x) FIELD_PREP(GENMASK(3, 2), (x))
+#define IPI_VCID_VC_2 BIT(4)
+
+#define IPI_DATA_TYPE_DT(x) FIELD_PREP(GENMASK(5, 0), (x))
+#define IPI_DATA_TYPE_EMB_DATA_EN BIT(8)
+
+#define IPI_MODE_CONTROLLER BIT(1)
+#define IPI_MODE_COLOR_MODE16 BIT(8)
+#define IPI_MODE_CUT_THROUGH BIT(16)
+#define IPI_MODE_ENABLE BIT(24)
+
+#define IPI_MEM_FLUSH_AUTO BIT(8)
+
+enum dw_mipi_csi2rx_regs_index {
+ DW_MIPI_CSI2RX_N_LANES,
+ DW_MIPI_CSI2RX_RESETN,
+ DW_MIPI_CSI2RX_PHY_STATE,
+ DW_MIPI_CSI2RX_ERR1,
+ DW_MIPI_CSI2RX_ERR2,
+ DW_MIPI_CSI2RX_MSK1,
+ DW_MIPI_CSI2RX_MSK2,
+ DW_MIPI_CSI2RX_CONTROL,
+ /* imx93 (v150) new register */
+ DW_MIPI_CSI2RX_DPHY_RSTZ,
+ DW_MIPI_CSI2RX_PHY_TST_CTRL0,
+ DW_MIPI_CSI2RX_PHY_TST_CTRL1,
+ DW_MIPI_CSI2RX_PHY_SHUTDOWNZ,
+ DW_MIPI_CSI2RX_IPI_DATATYPE,
+ DW_MIPI_CSI2RX_IPI_MEM_FLUSH,
+ DW_MIPI_CSI2RX_IPI_MODE,
+ DW_MIPI_CSI2RX_IPI_SOFTRSTN,
+ DW_MIPI_CSI2RX_IPI_VCID,
+
+ DW_MIPI_CSI2RX_MAX,
+};
enum {
DW_MIPI_CSI2RX_PAD_SINK,
@@ -48,6 +80,15 @@ enum {
DW_MIPI_CSI2RX_PAD_MAX,
};
+struct dw_mipi_csi2rx_device;
+
+struct dw_mipi_csi2rx_drvdata {
+ const u32 *regs;
+ void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2);
+ void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2);
+ void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2);
+};
+
struct dw_mipi_csi2rx_format {
u32 code;
u8 depth;
@@ -72,6 +113,38 @@ struct dw_mipi_csi2rx_device {
enum v4l2_mbus_type bus_type;
u32 lanes_num;
+
+ const struct dw_mipi_csi2rx_drvdata *drvdata;
+};
+
+static const u32 rk3568_regs[DW_MIPI_CSI2RX_MAX] = {
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x10),
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x14),
+ [DW_MIPI_CSI2RX_ERR1] = DW_REG(0x20),
+ [DW_MIPI_CSI2RX_ERR2] = DW_REG(0x24),
+ [DW_MIPI_CSI2RX_MSK1] = DW_REG(0x28),
+ [DW_MIPI_CSI2RX_MSK2] = DW_REG(0x2c),
+ [DW_MIPI_CSI2RX_CONTROL] = DW_REG(0x40),
+};
+
+static const struct dw_mipi_csi2rx_drvdata rk3568_drvdata = {
+ .regs = rk3568_regs,
+};
+
+static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
+ [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
+ [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
+ [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80),
+ [DW_MIPI_CSI2RX_IPI_VCID] = DW_REG(0x84),
+ [DW_MIPI_CSI2RX_IPI_DATATYPE] = DW_REG(0x88),
+ [DW_MIPI_CSI2RX_IPI_MEM_FLUSH] = DW_REG(0x8c),
+ [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
};
static const struct v4l2_mbus_framefmt default_format = {
@@ -186,16 +259,50 @@ static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
return container_of(sd, struct dw_mipi_csi2rx_device, sd);
}
+static bool dw_mipi_csi2rx_has_reg(struct dw_mipi_csi2rx_device *csi2,
+ enum dw_mipi_csi2rx_regs_index index)
+{
+ if (index < DW_MIPI_CSI2RX_MAX &&
+ (csi2->drvdata->regs[index] & DW_REG_EXIST))
+ return true;
+
+ return false;
+}
+
+static void __iomem *
+dw_mipi_csi2rx_get_regaddr(struct dw_mipi_csi2rx_device *csi2,
+ enum dw_mipi_csi2rx_regs_index index)
+{
+ u32 off = (~DW_REG_EXIST) & csi2->drvdata->regs[index];
+
+ return csi2->base_addr + off;
+}
+
static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2,
- unsigned int addr, u32 val)
+ enum dw_mipi_csi2rx_regs_index index,
+ u32 val)
{
- writel(val, csi2->base_addr + addr);
+ if (!dw_mipi_csi2rx_has_reg(csi2, index)) {
+ dev_err_once(csi2->dev,
+ "write to non-existent register index: %d\n",
+ index);
+ return;
+ }
+
+ writel(val, dw_mipi_csi2rx_get_regaddr(csi2, index));
}
static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2,
- unsigned int addr)
+ enum dw_mipi_csi2rx_regs_index index)
{
- return readl(csi2->base_addr + addr);
+ if (!dw_mipi_csi2rx_has_reg(csi2, index)) {
+ dev_err_once(csi2->dev,
+ "read non-existent register index: %d\n", index);
+ /* return 0 for non-existent registers */
+ return 0;
+ }
+
+ return readl(dw_mipi_csi2rx_get_regaddr(csi2, index));
}
static const struct dw_mipi_csi2rx_format *
@@ -260,14 +367,32 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
return -EINVAL;
}
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
+
+ if (csi2->drvdata->dphy_assert_reset)
+ csi2->drvdata->dphy_assert_reset(csi2);
+
control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
+
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_CONTROL))
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
+
+ ret = phy_power_on(csi2->phy);
+ if (ret)
+ return ret;
+
+ if (csi2->drvdata->dphy_deassert_reset)
+ csi2->drvdata->dphy_deassert_reset(csi2);
+
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
- return phy_power_on(csi2->phy);
+ if (csi2->drvdata->ipi_enable)
+ csi2->drvdata->ipi_enable(csi2);
+
+ return 0;
}
static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
@@ -275,8 +400,12 @@ static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
phy_power_off(csi2->phy);
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
- dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
+
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK1))
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
+
+ if (dw_mipi_csi2rx_has_reg(csi2, DW_MIPI_CSI2RX_MSK2))
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
}
static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
@@ -431,10 +560,31 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
return ret;
}
+static int
+dw_mipi_csi2rx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
+ struct v4l2_subdev *remote_sd;
+ struct media_pad *remote_pad;
+
+ remote_pad = media_pad_remote_pad_unique(&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
+ if (IS_ERR(remote_pad)) {
+ dev_err(csi2->dev, "can't get remote source pad\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ return v4l2_subdev_call(remote_sd, pad, get_frame_desc,
+ remote_pad->index, fd);
+}
+
static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
.enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = dw_mipi_csi2rx_set_fmt,
+ .get_frame_desc = dw_mipi_csi2rx_get_frame_desc,
.set_routing = dw_mipi_csi2rx_set_routing,
.enable_streams = dw_mipi_csi2rx_enable_streams,
.disable_streams = dw_mipi_csi2rx_disable_streams,
@@ -605,9 +755,93 @@ static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
v4l2_async_nf_cleanup(&csi2->notifier);
}
+static void imx93_csi2rx_dphy_assert_reset(struct dw_mipi_csi2rx_device *csi2)
+{
+ u32 val;
+
+ /* Release Synopsys DPHY test codes from reset */
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0);
+
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
+ val &= ~DPHY_TEST_CTRL0_TEST_CLR;
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
+
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0);
+ /* Wait for at least 15ns */
+ ndelay(15);
+ val |= DPHY_TEST_CTRL0_TEST_CLR;
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_TST_CTRL0, val);
+}
+
+static void imx93_csi2rx_dphy_deassert_reset(struct dw_mipi_csi2rx_device *csi2)
+{
+ /* Release PHY from reset */
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, 0x1);
+ /*
+ * ndelay() is not necessary have MMIO operation, need dummy read to
+ * ensure that the write operation above reaches its target.
+ */
+ dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ);
+ ndelay(5);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ, 0x1);
+
+ dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_DPHY_RSTZ);
+ ndelay(5);
+}
+
+static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2)
+{
+ int dt = csi2->formats->csi_dt;
+ u32 val;
+
+ /* Do IPI soft reset */
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x0);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_SOFTRSTN, 0x1);
+
+ /* Select virtual channel and data type to be processed by IPI */
+ val = IPI_DATA_TYPE_DT(dt);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_DATATYPE, val);
+
+ /* Set virtual channel 0 as default */
+ val = IPI_VCID_VC(0);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_VCID, val);
+
+ /*
+ * Select IPI camera timing mode and allow the pixel stream
+ * to be non-continuous when pixel interface FIFO is empty
+ */
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
+ val &= ~IPI_MODE_CONTROLLER;
+ val &= ~IPI_MODE_COLOR_MODE16;
+ val |= IPI_MODE_CUT_THROUGH;
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
+
+ /* Memory is automatically flushed at each Frame Start */
+ val = IPI_MEM_FLUSH_AUTO;
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, val);
+
+ /* Enable IPI */
+ val = dw_mipi_csi2rx_read(csi2, DW_MIPI_CSI2RX_IPI_MODE);
+ val |= IPI_MODE_ENABLE;
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
+}
+
+static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
+ .regs = imx93_regs,
+ .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
+ .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
+ .ipi_enable = imx93_csi2rx_dphy_ipi_enable,
+};
+
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
{
+ .compatible = "fsl,imx93-mipi-csi2",
+ .data = &imx93_drvdata,
+ },
+ {
.compatible = "rockchip,rk3568-mipi-csi2",
+ .data = &rk3568_drvdata,
},
{}
};
@@ -629,8 +863,13 @@ static int dw_mipi_csi2rx_probe(struct platform_device *pdev)
if (IS_ERR(csi2->base_addr))
return PTR_ERR(csi2->base_addr);
+ csi2->drvdata = device_get_match_data(dev);
+ if (!csi2->drvdata)
+ return dev_err_probe(dev, -EINVAL,
+ "failed to get driver data\n");
+
ret = devm_clk_bulk_get_all(dev, &csi2->clks);
- if (ret != DW_MIPI_CSI2RX_CLKS_MAX)
+ if (ret < 0)
return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
csi2->clks_num = ret;
@@ -639,7 +878,7 @@ static int dw_mipi_csi2rx_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(csi2->phy),
"failed to get MIPI CSI-2 PHY\n");
- csi2->reset = devm_reset_control_get_exclusive(dev, NULL);
+ csi2->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(csi2->reset))
return dev_err_probe(dev, PTR_ERR(csi2->reset),
"failed to get reset\n");
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 9cceffa4ce25..61ad20b18b8d 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -232,7 +232,7 @@ static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
for (i = 0; i < 10; i++) {
usleep_range(1000, 1100);
- val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ val = gpiod_get_value_cansleep(hdmirx_dev->detect_5v_gpio);
if (val > 0)
cnt++;
if (cnt >= detection_threshold)
@@ -2252,10 +2252,6 @@ static void hdmirx_delayed_work_res_change(struct work_struct *work)
static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
{
struct snps_hdmirx_dev *hdmirx_dev = dev_id;
- u32 val;
-
- val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
- v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
queue_delayed_work(system_unbound_wq,
&hdmirx_dev->delayed_work_hotplug,
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index 64e76e3576a8..b946c8087c77 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -1403,6 +1403,7 @@ static int isp_video_open(struct file *file)
ret = vb2_queue_init(&handle->queue);
if (ret < 0) {
+ v4l2_pipeline_pm_put(&video->video.entity);
omap3isp_put(video->isp);
goto done;
}
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index a4b616a5ece7..0e91e87bda9b 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -3641,6 +3641,7 @@ static void vip_remove(struct platform_device *pdev)
}
v4l2_ctrl_handler_free(&shared->ctrl_handler);
+ v4l2_device_unregister(&shared->v4l2_dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 78abe810a88e..51d85de24fae 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface,
input_free_device(input_dev);
exit_unregister_device:
rc_unregister_device(rc_dev);
- rc_dev = NULL;
exit_kill_urbs:
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface)
struct ati_remote *ati_remote;
ati_remote = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
if (!ati_remote) {
dev_warn(&interface->dev, "%s - null device?\n", __func__);
return;
}
+ rc_unregister_device(ati_remote->rdev);
+ usb_set_intfdata(interface, NULL);
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
if (ati_remote->idev)
input_unregister_device(ati_remote->idev);
- rc_unregister_device(ati_remote->rdev);
ati_remote_free_buffers(ati_remote);
+ rc_free_device(ati_remote->rdev);
kfree(ati_remote);
}
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index f8120605501a..6f7dccc965e7 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1090,7 +1090,6 @@ exit_release_hw_io:
release_region(dev->hw_io, ENE_IO_SIZE);
exit_unregister_device:
rc_unregister_device(rdev);
- rdev = NULL;
exit_free_dev_rdev:
rc_free_device(rdev);
kfree(dev);
@@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev)
ene_rx_restore_hw_buffer(dev);
spin_unlock_irqrestore(&dev->hw_lock, flags);
+ rc_free_device(dev->rdev);
free_irq(dev->irq, dev);
release_region(dev->hw_io, ENE_IO_SIZE);
kfree(dev);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index f7cfa8a073eb..5055dfc3f465 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev)
struct fintek_dev *fintek = pnp_get_drvdata(pdev);
unsigned long flags;
+ rc_unregister_device(fintek->rdev);
spin_lock_irqsave(&fintek->fintek_lock, flags);
/* disable CIR */
fintek_disable_cir(fintek);
@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev)
free_irq(fintek->cir_irq, fintek);
release_region(fintek->cir_addr, fintek->cir_port_len);
- rc_unregister_device(fintek->rdev);
+ rc_free_device(fintek->rdev);
kfree(fintek);
}
diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c
index e185ead40464..d15fb17fa0fa 100644
--- a/drivers/media/rc/gpio-ir-tx.c
+++ b/drivers/media/rc/gpio-ir-tx.c
@@ -51,7 +51,7 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
static void delay_until(ktime_t until)
{
/*
- * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
+ * delta should never exceed 1 second (IR_MAX_DURATION) and on
* m68k ndelay(s64) does not compile; so use s32 rather than s64.
*/
s32 delta;
@@ -95,7 +95,7 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf,
{
ktime_t edge;
/*
- * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
+ * delta should never exceed 1 second (IR_MAX_DURATION) and on
* m68k ndelay(s64) does not compile; so use s32 rather than s64.
*/
s32 delta;
diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
index e034c93d57cf..3e10f6fe89f8 100644
--- a/drivers/media/rc/igorplugusb.c
+++ b/drivers/media/rc/igorplugusb.c
@@ -34,7 +34,7 @@ struct igorplugusb {
struct device *dev;
struct urb *urb;
- struct usb_ctrlrequest request;
+ struct usb_ctrlrequest *request;
struct timer_list timer;
@@ -122,7 +122,7 @@ static void igorplugusb_cmd(struct igorplugusb *ir, int cmd)
{
int ret;
- ir->request.bRequest = cmd;
+ ir->request->bRequest = cmd;
ir->urb->transfer_flags = 0;
ret = usb_submit_urb(ir->urb, GFP_ATOMIC);
if (ret && ret != -EPERM)
@@ -164,13 +164,17 @@ static int igorplugusb_probe(struct usb_interface *intf,
if (!ir)
return -ENOMEM;
+ ir->request = kzalloc_obj(*ir->request, GFP_KERNEL);
+ if (!ir->request)
+ goto fail;
+
ir->dev = &intf->dev;
timer_setup(&ir->timer, igorplugusb_timer, 0);
- ir->request.bRequest = GET_INFRACODE;
- ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
- ir->request.wLength = cpu_to_le16(MAX_PACKET);
+ ir->request->bRequest = GET_INFRACODE;
+ ir->request->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
+ ir->request->wLength = cpu_to_le16(MAX_PACKET);
ir->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ir->urb)
@@ -228,6 +232,7 @@ fail:
usb_free_urb(ir->urb);
rc_free_device(ir->rc);
kfree(ir->buf_in);
+ kfree(ir->request);
return ret;
}
@@ -242,7 +247,9 @@ static void igorplugusb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
usb_unpoison_urb(ir->urb);
usb_free_urb(ir->urb);
+ rc_free_device(ir->rc);
kfree(ir->buf_in);
+ kfree(ir->request);
}
static const struct usb_device_id igorplugusb_table[] = {
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index c508f2536243..0c5b8befb0af 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
usb_kill_urb(ir->urb_in);
usb_kill_urb(ir->urb_out);
+ rc_free_device(ir->rc);
usb_free_urb(ir->urb_in);
usb_free_urb(ir->urb_out);
usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 63f6f5b36838..f30adf4d8444 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -1118,9 +1118,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv)
struct rc_dev *rdev = hw->rdev;
if (!rdev)
return;
+ rc_unregister_device(rdev);
img_ir_set_decoder(priv, NULL, 0);
hw->rdev = NULL;
- rc_unregister_device(rdev);
+ rc_free_device(rdev);
#ifdef CONFIG_COMMON_CLK
if (!IS_ERR(priv->clk))
clk_notifier_unregister(priv->clk, &hw->clk_nb);
diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
index 92fb7b555a0f..f1460d4acf3e 100644
--- a/drivers/media/rc/img-ir/img-ir-raw.c
+++ b/drivers/media/rc/img-ir/img-ir-raw.c
@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
if (!rdev)
return;
+ rc_unregister_device(rdev);
/* switch off and disable raw (edge) interrupts */
spin_lock_irq(&priv->lock);
raw->rdev = NULL;
@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
spin_unlock_irq(&priv->lock);
- rc_unregister_device(rdev);
+ rc_free_device(rdev);
timer_delete_sync(&raw->timer);
}
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 7e92161105d5..9bb27ba8240f 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -2250,7 +2250,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf,
mutex_lock(&ictx->lock);
ictx->dev = dev;
- ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
+ ictx->usbdev_intf0 = interface_to_usbdev(intf);
ictx->rx_urb_intf0 = rx_urb;
ictx->tx_urb = tx_urb;
ictx->rf_device = false;
@@ -2308,7 +2308,6 @@ idev_setup_failed:
usb_kill_urb(ictx->rx_urb_intf0);
urb_submit_failed:
find_endpoint_failed:
- usb_put_dev(ictx->usbdev_intf0);
mutex_unlock(&ictx->lock);
usb_free_urb(tx_urb);
tx_urb_alloc_failed:
@@ -2338,7 +2337,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
timer_setup(&ictx->ttimer, imon_touch_display_timeout, 0);
}
- ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
+ ictx->usbdev_intf1 = interface_to_usbdev(intf);
ictx->rx_urb_intf1 = rx_urb;
ret = -ENODEV;
@@ -2377,7 +2376,6 @@ urb_submit_failed:
input_unregister_device(ictx->touch);
touch_setup_failed:
find_endpoint_failed:
- usb_put_dev(ictx->usbdev_intf1);
ictx->usbdev_intf1 = NULL;
mutex_unlock(&ictx->lock);
usb_free_urb(rx_urb);
@@ -2426,7 +2424,7 @@ static int imon_probe(struct usb_interface *interface,
struct imon_context *ictx = NULL;
u16 vendor, product;
- usbdev = usb_get_dev(interface_to_usbdev(interface));
+ usbdev = interface_to_usbdev(interface);
iface_desc = interface->cur_altsetting;
ifnum = iface_desc->desc.bInterfaceNumber;
vendor = le16_to_cpu(usbdev->descriptor.idVendor);
@@ -2495,12 +2493,9 @@ static int imon_probe(struct usb_interface *interface,
vendor, product, ifnum,
usbdev->bus->busnum, usbdev->devnum);
- usb_put_dev(usbdev);
-
return 0;
fail:
- usb_put_dev(usbdev);
dev_err(dev, "unable to register, err %d\n", ret);
return ret;
@@ -2541,16 +2536,16 @@ static void imon_disconnect(struct usb_interface *interface)
if (ifnum == 0) {
ictx->dev_present_intf0 = false;
+ rc_unregister_device(ictx->rdev);
usb_kill_urb(ictx->rx_urb_intf0);
input_unregister_device(ictx->idev);
- rc_unregister_device(ictx->rdev);
+ rc_free_device(ictx->rdev);
if (ictx->display_supported) {
if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
usb_deregister_dev(interface, &imon_lcd_class);
else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD)
usb_deregister_dev(interface, &imon_vfd_class);
}
- usb_put_dev(ictx->usbdev_intf0);
} else {
ictx->dev_present_intf1 = false;
usb_kill_urb(ictx->rx_urb_intf1);
@@ -2558,7 +2553,6 @@ static void imon_disconnect(struct usb_interface *interface)
timer_delete_sync(&ictx->ttimer);
input_unregister_device(ictx->touch);
}
- usb_put_dev(ictx->usbdev_intf1);
}
if (refcount_dec_and_test(&ictx->users))
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index edc46828509c..1b061e4a3dcf 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
regerr:
rc_unregister_device(rdev);
- rdev = NULL;
clkerr:
clk_disable_unprepare(priv->clock);
err:
@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clock);
rc_unregister_device(priv->rdev);
+ rc_free_device(priv->rdev);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
index d6472de5da87..089833e41178 100644
--- a/drivers/media/rc/ir_toy.c
+++ b/drivers/media/rc/ir_toy.c
@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf)
usb_free_urb(ir->urb_out);
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
+ rc_free_device(ir->rc);
kfree(ir->in);
kfree(ir->out);
kfree(ir);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index bf544517c67a..bde2a7051231 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1414,7 +1414,6 @@ exit_release_cir_addr:
release_region(itdev->cir_addr, itdev->params->io_region_size);
exit_unregister_device:
rc_unregister_device(rdev);
- rdev = NULL;
exit_free_dev_rdev:
rc_free_device(rdev);
kfree(itdev);
@@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev)
release_region(dev->cir_addr, dev->params->io_region_size);
rc_unregister_device(dev->rdev);
+ rc_free_device(dev->rdev);
kfree(dev);
}
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index ed55e9ec3c57..6a9e4382a224 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1729,7 +1729,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
goto urb_in_alloc_fail;
ir->usbintf = intf;
- ir->usbdev = usb_get_dev(dev);
+ ir->usbdev = dev;
ir->dev = &intf->dev;
ir->len_in = maxp;
ir->flags.microsoft_gen1 = is_microsoft_gen1;
@@ -1817,7 +1817,6 @@ static int mceusb_dev_probe(struct usb_interface *intf,
/* Error-handling path */
rc_dev_fail:
cancel_work_sync(&ir->kevent);
- usb_put_dev(ir->usbdev);
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
urb_in_alloc_fail:
@@ -1849,7 +1848,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
- usb_put_dev(dev);
+ rc_free_device(ir->rc);
kfree(ir);
}
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 2e269ef5e26b..ba24c2f22d39 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -648,9 +648,6 @@ int ir_raw_event_register(struct rc_dev *dev)
void ir_raw_event_free(struct rc_dev *dev)
{
- if (!dev)
- return;
-
kfree(dev->raw);
dev->raw = NULL;
}
@@ -674,8 +671,6 @@ void ir_raw_event_unregister(struct rc_dev *dev)
lirc_bpf_free(dev);
- ir_raw_event_free(dev);
-
/*
* A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
* ensure that the raw member is null on unlock; this is how
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 78ac09b3cbd3..53d0540717b3 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -263,6 +263,7 @@ static int __init loop_init(void)
static void __exit loop_exit(void)
{
rc_unregister_device(loopdev.dev);
+ rc_free_device(loopdev.dev);
}
module_init(loop_init);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 821607504008..dda3479ea3ad 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device)
{
struct rc_dev *dev = to_rc_dev(device);
+ ir_raw_event_free(dev);
kfree(dev);
}
@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev,
}
rc->dev.parent = dev;
- rc->managed_alloc = true;
*dr = rc;
devres_add(dev, dr);
@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev)
device_del(&dev->dev);
ida_free(&rc_ida, dev->minor);
-
- if (!dev->managed_alloc)
- rc_free_device(dev);
}
-
EXPORT_SYMBOL_GPL(rc_unregister_device);
/*
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 3b917a2a8918..3f828a564e19 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -1131,11 +1131,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+ struct rc_dev *rc = rr3->rc;
usb_set_intfdata(intf, NULL);
- rc_unregister_device(rr3->rc);
+ rc_unregister_device(rc);
led_classdev_unregister(&rr3->led);
redrat3_delete(rr3, udev);
+ rc_free_device(rc);
}
static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index 6b70bac5f45d..0ba06bfc9e14 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, false);
clk_disable_unprepare(rc_dev->sys_clock);
rc_unregister_device(rc_dev->rdev);
+ rc_free_device(rc_dev->rdev);
}
static int st_rc_open(struct rc_dev *rdev)
@@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev)
return ret;
rcerr:
rc_unregister_device(rdev);
- rdev = NULL;
clkerr:
clk_disable_unprepare(rc_dev->sys_clock);
err:
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index 5a18603f9a95..307985d74fe8 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -219,9 +219,8 @@ static void streamzap_callback(struct urb *urb)
case -ESHUTDOWN:
/*
* this urb is terminated, clean up.
- * sz might already be invalid at this point
*/
- dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+ dev_dbg(sz->dev, "urb terminated, status: %d\n", urb->status);
return;
default:
break;
@@ -358,11 +357,16 @@ static int streamzap_probe(struct usb_interface *intf,
usb_set_intfdata(intf, sz);
- if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+ retval = usb_submit_urb(sz->urb_in, GFP_ATOMIC);
+ if (retval < 0) {
dev_err(sz->dev, "urb submit failed\n");
+ goto rc_submit_fail;
+ }
return 0;
-
+rc_submit_fail:
+ rc_free_device(sz->rdev);
+ usb_set_intfdata(intf, NULL);
rc_dev_fail:
usb_free_urb(sz->urb_in);
free_buf_in:
@@ -388,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface)
struct streamzap_ir *sz = usb_get_intfdata(interface);
struct usb_device *usbdev = interface_to_usbdev(interface);
- usb_set_intfdata(interface, NULL);
-
if (!sz)
return;
- usb_kill_urb(sz->urb_in);
rc_unregister_device(sz->rdev);
+ usb_set_intfdata(interface, NULL);
+
+ usb_kill_urb(sz->urb_in);
usb_free_urb(sz->urb_in);
usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+ rc_free_device(sz->rdev);
kfree(sz);
}
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 92ef4e7c6f69..cb4c56bf0752 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -371,6 +371,7 @@ static void sunxi_ir_remove(struct platform_device *pdev)
struct sunxi_ir *ir = platform_get_drvdata(pdev);
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
sunxi_ir_hw_exit(&pdev->dev);
}
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
index 110a46900114..3848ad3a6b85 100644
--- a/drivers/media/rc/ttusbir.c
+++ b/drivers/media/rc/ttusbir.c
@@ -32,7 +32,7 @@ struct ttusbir {
struct led_classdev led;
struct urb *bulk_urb;
- uint8_t bulk_buffer[5];
+ u8 *bulk_buffer;
int bulk_out_endp, iso_in_endp;
bool led_on, is_led_on;
atomic_t led_complete;
@@ -186,13 +186,16 @@ static int ttusbir_probe(struct usb_interface *intf,
struct rc_dev *rc;
int i, j, ret;
int altsetting = -1;
+ u8 *buffer;
tt = kzalloc_obj(*tt);
+ buffer = kzalloc(5, GFP_KERNEL);
rc = rc_allocate_device(RC_DRIVER_IR_RAW);
- if (!tt || !rc) {
+ if (!tt || !rc || buffer) {
ret = -ENOMEM;
goto out;
}
+ tt->bulk_buffer = buffer;
/* find the correct alt setting */
for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) {
@@ -281,8 +284,8 @@ static int ttusbir_probe(struct usb_interface *intf,
tt->bulk_buffer[3] = 0x01;
usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev,
- tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer),
- ttusbir_bulk_complete, tt);
+ tt->bulk_out_endp), tt->bulk_buffer, 5,
+ ttusbir_bulk_complete, tt);
tt->led.name = "ttusbir:green:power";
tt->led.default_trigger = "rc-feedback";
@@ -333,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf,
return 0;
out3:
rc_unregister_device(rc);
- rc = NULL;
out2:
led_classdev_unregister(&tt->led);
out:
@@ -351,6 +353,7 @@ out:
kfree(tt);
}
rc_free_device(rc);
+ kfree(buffer);
return ret;
}
@@ -373,6 +376,8 @@ static void ttusbir_disconnect(struct usb_interface *intf)
}
usb_kill_urb(tt->bulk_urb);
usb_free_urb(tt->bulk_urb);
+ rc_free_device(tt->rc);
+ kfree(tt->bulk_buffer);
usb_set_intfdata(intf, NULL);
kfree(tt);
}
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 515469dd82d4..8e804661a621 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -1132,7 +1132,6 @@ exit_release_wbase:
release_region(data->wbase, WAKEUP_IOMEM_LEN);
exit_unregister_device:
rc_unregister_device(data->dev);
- data->dev = NULL;
exit_free_rc:
rc_free_device(data->dev);
exit_unregister_led:
@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device)
wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
rc_unregister_device(data->dev);
+ rc_free_device(data->dev);
led_classdev_unregister(&data->led);
diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c
index 3e3da70cf8da..d2cb88b8f1ca 100644
--- a/drivers/media/rc/xbox_remote.c
+++ b/drivers/media/rc/xbox_remote.c
@@ -55,7 +55,7 @@ struct xbox_remote {
struct usb_interface *interface;
struct urb *irq_urb;
- unsigned char inbuf[DATA_BUFSIZE] __aligned(sizeof(u16));
+ u8 *inbuf;
char rc_name[NAME_BUFSIZE];
char rc_phys[NAME_BUFSIZE];
@@ -218,6 +218,10 @@ static int xbox_remote_probe(struct usb_interface *interface,
if (!xbox_remote || !rc_dev)
goto exit_free_dev_rdev;
+ xbox_remote->inbuf = kzalloc(DATA_BUFSIZE, GFP_KERNEL);
+ if (!xbox_remote->inbuf)
+ goto exit_free_inbuf;
+
/* Allocate URB buffer */
xbox_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!xbox_remote->irq_urb)
@@ -262,6 +266,8 @@ exit_kill_urbs:
usb_kill_urb(xbox_remote->irq_urb);
exit_free_buffers:
usb_free_urb(xbox_remote->irq_urb);
+exit_free_inbuf:
+ kfree(xbox_remote->inbuf);
exit_free_dev_rdev:
rc_free_device(rc_dev);
kfree(xbox_remote);
@@ -277,15 +283,17 @@ static void xbox_remote_disconnect(struct usb_interface *interface)
struct xbox_remote *xbox_remote;
xbox_remote = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
if (!xbox_remote) {
dev_warn(&interface->dev, "%s - null device?\n", __func__);
return;
}
- usb_kill_urb(xbox_remote->irq_urb);
rc_unregister_device(xbox_remote->rdev);
+ usb_set_intfdata(interface, NULL);
+ usb_kill_urb(xbox_remote->irq_urb);
+ rc_free_device(xbox_remote->rdev);
usb_free_urb(xbox_remote->irq_urb);
+ kfree(xbox_remote->inbuf);
kfree(xbox_remote);
}
diff --git a/drivers/media/test-drivers/vidtv/Kconfig b/drivers/media/test-drivers/vidtv/Kconfig
index e511e51c0b5b..8cb10b75603b 100644
--- a/drivers/media/test-drivers/vidtv/Kconfig
+++ b/drivers/media/test-drivers/vidtv/Kconfig
@@ -2,6 +2,7 @@
config DVB_VIDTV
tristate "Virtual DVB Driver (vidtv)"
depends on DVB_CORE && MEDIA_SUPPORT && I2C
+ select CRC32
help
The virtual DVB test driver serves as a reference DVB driver and helps
validate the existing APIs in the media subsystem. It can also aid developers
diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
index b6203e10e37a..a8a76434989c 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
@@ -237,8 +237,10 @@ static int vidtv_start_feed(struct dvb_demux_feed *feed)
if (dvb->nfeeds == 1) {
ret = vidtv_start_streaming(dvb);
- if (ret < 0)
+ if (ret < 0) {
+ dvb->nfeeds--;
rc = ret;
+ }
}
mutex_unlock(&dvb->feed_lock);
diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c
index da20657adc74..5f8c3af87171 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_channel.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c
@@ -341,6 +341,10 @@ vidtv_channel_pmt_match_sections(struct vidtv_channel *channels,
tail = vidtv_psi_pmt_stream_init(tail,
s->type,
e_pid);
+ if (!tail) {
+ vidtv_psi_pmt_stream_destroy(head);
+ return;
+ }
if (!head)
head = tail;
diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c
index 403fbedb8663..f0134e38a1fb 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_mux.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c
@@ -233,7 +233,7 @@ static u32 vidtv_mux_push_pcr(struct vidtv_mux *m)
/* the 27Mhz clock will feed both parts of the PCR bitfield */
args.pcr = m->timing.clk;
- nbytes += vidtv_ts_pcr_write_into(args);
+ nbytes += vidtv_ts_pcr_write_into(&args);
m->mux_buf_offset += nbytes;
m->num_streamed_pcr++;
@@ -363,7 +363,7 @@ static u32 vidtv_mux_pad_with_nulls(struct vidtv_mux *m, u32 npkts)
args.continuity_counter = &ctx->cc;
for (i = 0; i < npkts; ++i) {
- m->mux_buf_offset += vidtv_ts_null_write_into(args);
+ m->mux_buf_offset += vidtv_ts_null_write_into(&args);
args.dest_offset = m->mux_buf_offset;
}
diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c
index 685a1f7b2fb1..1b6225d65ef3 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_psi.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c
@@ -31,61 +31,6 @@
#define INITIAL_CRC 0xffffffff
#define ISO_LANGUAGE_CODE_LEN 3
-static const u32 CRC_LUT[256] = {
- /* from libdvbv5 */
- 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
- 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
- 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
- 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
- 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
- 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
- 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
- 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
- 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
- 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
- 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
- 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
- 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
- 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
- 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
- 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
- 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
- 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
- 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
- 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
- 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
- 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
- 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
- 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
- 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
- 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
- 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
- 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
- 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
- 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
- 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
- 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
- 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
- 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
- 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
- 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
- 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
- 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
- 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
- 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
- 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
- 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
- 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
-};
-
-static u32 dvb_crc32(u32 crc, u8 *data, u32 len)
-{
- /* from libdvbv5 */
- while (len--)
- crc = (crc << 8) ^ CRC_LUT[((crc >> 24) ^ *data++) & 0xff];
- return crc;
-}
-
static void vidtv_psi_update_version_num(struct vidtv_psi_table_header *h)
{
h->version++;
@@ -175,7 +120,7 @@ static u32 vidtv_psi_ts_psi_write_into(struct psi_write_args *args)
pr_warn_ratelimited("Missing CRC for chunk\n");
if (args->crc)
- *args->crc = dvb_crc32(*args->crc, args->from, args->len);
+ *args->crc = crc32_be(*args->crc, args->from, args->len);
if (args->new_psi_section && !aligned) {
pr_warn_ratelimited("Cannot write a new PSI section in a misaligned buffer\n");
diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.c b/drivers/media/test-drivers/vidtv/vidtv_s302m.c
index 581497644940..3c08e442b2d1 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_s302m.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.c
@@ -45,7 +45,7 @@
#define FF_S302M_DEFAULT_PTS_OFFSET 100000
/* Used by the tone generator: number of samples for PI */
-#define PI 180
+#define PI_SAMPLES 180
static const u8 reverse[256] = {
/* from ffmpeg */
@@ -259,10 +259,10 @@ static u16 vidtv_s302m_get_sample(struct vidtv_encoder *e)
if (!ctx->last_tone)
return 0x8000;
- pos = (2 * PI * ctx->note_offset * ctx->last_tone) / S302M_SAMPLING_RATE_HZ;
+ pos = (2 * PI_SAMPLES * ctx->note_offset * ctx->last_tone) / S302M_SAMPLING_RATE_HZ;
ctx->note_offset++;
- return (fixp_sin32(pos % (2 * PI)) >> 16) + 0x8000;
+ return (fixp_sin32(pos % (2 * PI_SAMPLES)) >> 16) + 0x8000;
}
/* bug somewhere */
diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.c b/drivers/media/test-drivers/vidtv/vidtv_ts.c
index ca4bb9c40b78..cbe9aff9ffb5 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_ts.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_ts.c
@@ -48,7 +48,7 @@ void vidtv_ts_inc_cc(u8 *continuity_counter)
*continuity_counter = 0;
}
-u32 vidtv_ts_null_write_into(struct null_packet_write_args args)
+u32 vidtv_ts_null_write_into(const struct null_packet_write_args *args)
{
u32 nbytes = 0;
struct vidtv_mpeg_ts ts_header = {};
@@ -56,21 +56,21 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args)
ts_header.sync_byte = TS_SYNC_BYTE;
ts_header.bitfield = cpu_to_be16(TS_NULL_PACKET_PID);
ts_header.payload = 1;
- ts_header.continuity_counter = *args.continuity_counter;
+ ts_header.continuity_counter = *args->continuity_counter;
/* copy TS header */
- nbytes += vidtv_memcpy(args.dest_buf,
- args.dest_offset + nbytes,
- args.buf_sz,
+ nbytes += vidtv_memcpy(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->buf_sz,
&ts_header,
sizeof(ts_header));
- vidtv_ts_inc_cc(args.continuity_counter);
+ vidtv_ts_inc_cc(args->continuity_counter);
/* fill the rest with empty data */
- nbytes += vidtv_memset(args.dest_buf,
- args.dest_offset + nbytes,
- args.buf_sz,
+ nbytes += vidtv_memset(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->buf_sz,
TS_FILL_BYTE,
TS_PACKET_LEN - nbytes);
@@ -83,17 +83,17 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args)
return nbytes;
}
-u32 vidtv_ts_pcr_write_into(struct pcr_write_args args)
+u32 vidtv_ts_pcr_write_into(const struct pcr_write_args *args)
{
u32 nbytes = 0;
struct vidtv_mpeg_ts ts_header = {};
struct vidtv_mpeg_ts_adaption ts_adap = {};
ts_header.sync_byte = TS_SYNC_BYTE;
- ts_header.bitfield = cpu_to_be16(args.pid);
+ ts_header.bitfield = cpu_to_be16(args->pid);
ts_header.scrambling = 0;
/* cc is not incremented, but it is needed. see 13818-1 clause 2.4.3.3 */
- ts_header.continuity_counter = *args.continuity_counter;
+ ts_header.continuity_counter = *args->continuity_counter;
ts_header.payload = 0;
ts_header.adaptation_field = 1;
@@ -102,27 +102,27 @@ u32 vidtv_ts_pcr_write_into(struct pcr_write_args args)
ts_adap.PCR = 1;
/* copy TS header */
- nbytes += vidtv_memcpy(args.dest_buf,
- args.dest_offset + nbytes,
- args.buf_sz,
+ nbytes += vidtv_memcpy(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->buf_sz,
&ts_header,
sizeof(ts_header));
/* write the adap after the TS header */
- nbytes += vidtv_memcpy(args.dest_buf,
- args.dest_offset + nbytes,
- args.buf_sz,
+ nbytes += vidtv_memcpy(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->buf_sz,
&ts_adap,
sizeof(ts_adap));
/* write the PCR optional */
- nbytes += vidtv_ts_write_pcr_bits(args.dest_buf,
- args.dest_offset + nbytes,
- args.pcr);
+ nbytes += vidtv_ts_write_pcr_bits(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->pcr);
- nbytes += vidtv_memset(args.dest_buf,
- args.dest_offset + nbytes,
- args.buf_sz,
+ nbytes += vidtv_memset(args->dest_buf,
+ args->dest_offset + nbytes,
+ args->buf_sz,
TS_FILL_BYTE,
TS_PACKET_LEN - nbytes);
diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.h b/drivers/media/test-drivers/vidtv/vidtv_ts.h
index 09b4ffd02829..3606398e160d 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_ts.h
+++ b/drivers/media/test-drivers/vidtv/vidtv_ts.h
@@ -90,7 +90,7 @@ void vidtv_ts_inc_cc(u8 *continuity_counter);
*
* Return: The number of bytes written into the buffer.
*/
-u32 vidtv_ts_null_write_into(struct null_packet_write_args args);
+u32 vidtv_ts_null_write_into(const struct null_packet_write_args *args);
/**
* vidtv_ts_pcr_write_into - Write a PCR packet into a buffer.
@@ -101,6 +101,6 @@ u32 vidtv_ts_null_write_into(struct null_packet_write_args args);
*
* Return: The number of bytes written into the buffer.
*/
-u32 vidtv_ts_pcr_write_into(struct pcr_write_args args);
+u32 vidtv_ts_pcr_write_into(const struct pcr_write_args *args);
#endif //VIDTV_TS_H
diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h
index 7a45a2117748..861b334ffc65 100644
--- a/drivers/media/test-drivers/vimc/vimc-common.h
+++ b/drivers/media/test-drivers/vimc/vimc-common.h
@@ -12,6 +12,8 @@
#include <linux/slab.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
+#include <media/tpg/v4l2-tpg.h>
+#include <media/v4l2-ctrls.h>
#define VIMC_PDEV_NAME "vimc"
@@ -27,6 +29,15 @@
#define VIMC_FRAME_MIN_WIDTH 16
#define VIMC_FRAME_MIN_HEIGHT 16
+#define VIMC_PIXEL_RATE_FIXED 160000000 /* 160 MHz */
+#define VIMC_HBLANK_FIXED 800
+/* VBLANK - vertical blanking (primary FPS control) */
+#define VIMC_VBLANK_MIN 4
+#define VIMC_VBLANK_MAX 65535
+#define VIMC_VBLANK_STEP 1
+#define VIMC_VBLANK_DEFAULT 3223 /* 30fps vga */
+#define VIMC_PIXELS_THRESHOLD_30FPS (1920 * 1080) /* 2073600 pixels */
+
#define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp)
/* Source and sink pad checks */
@@ -159,6 +170,36 @@ struct vimc_ent_config {
const struct vimc_ent_type *type;
};
+enum vimc_sensor_osd_mode {
+ VIMC_SENSOR_OSD_SHOW_ALL = 0,
+ VIMC_SENSOR_OSD_SHOW_COUNTERS = 1,
+ VIMC_SENSOR_OSD_SHOW_NONE = 2
+};
+
+struct vimc_sensor_device {
+ struct vimc_ent_device ved;
+ struct v4l2_subdev sd;
+ struct tpg_data tpg;
+ struct v4l2_ctrl_handler hdl;
+ struct media_pad pad;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+
+ u8 *frame;
+
+ /*
+ * Virtual "hardware" configuration, filled when the stream starts or
+ * when controls are set.
+ */
+ struct {
+ struct v4l2_area size;
+ enum vimc_sensor_osd_mode osd_value;
+ u64 start_stream_ts;
+ unsigned long fps_jiffies;
+ } hw;
+};
+
/**
* vimc_is_source - returns true if the entity has only source pads
*
diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c
index 69e714d4f228..5deebcc78a33 100644
--- a/drivers/media/test-drivers/vimc/vimc-sensor.c
+++ b/drivers/media/test-drivers/vimc/vimc-sensor.c
@@ -14,32 +14,6 @@
#include "vimc-common.h"
-enum vimc_sensor_osd_mode {
- VIMC_SENSOR_OSD_SHOW_ALL = 0,
- VIMC_SENSOR_OSD_SHOW_COUNTERS = 1,
- VIMC_SENSOR_OSD_SHOW_NONE = 2
-};
-
-struct vimc_sensor_device {
- struct vimc_ent_device ved;
- struct v4l2_subdev sd;
- struct tpg_data tpg;
- struct v4l2_ctrl_handler hdl;
- struct media_pad pad;
-
- u8 *frame;
-
- /*
- * Virtual "hardware" configuration, filled when the stream starts or
- * when controls are set.
- */
- struct {
- struct v4l2_area size;
- enum vimc_sensor_osd_mode osd_value;
- u64 start_stream_ts;
- } hw;
-};
-
static const struct v4l2_mbus_framefmt fmt_default = {
.width = 640,
.height = 480,
@@ -51,10 +25,15 @@ static const struct v4l2_mbus_framefmt fmt_default = {
static int vimc_sensor_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
+ struct vimc_sensor_device *vsensor =
+ container_of(sd, struct vimc_sensor_device, sd);
+
struct v4l2_mbus_framefmt *mf;
mf = v4l2_subdev_state_get_format(sd_state, 0);
*mf = fmt_default;
+ vsensor->hw.size.width = fmt_default.width;
+ vsensor->hw.size.height = fmt_default.height;
return 0;
}
@@ -113,6 +92,26 @@ static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor,
tpg_s_xfer_func(&vsensor->tpg, format->xfer_func);
}
+static int vimc_sensor_update_frame_timing(struct v4l2_subdev *sd,
+ u32 width, u32 height)
+{
+ struct vimc_sensor_device *vsensor =
+ container_of(sd, struct vimc_sensor_device, sd);
+ u64 pixel_rate = vsensor->pixel_rate->val;
+ u32 hts = width + vsensor->hblank->val;
+ u32 vts = height + vsensor->vblank->val;
+ u64 total_pixels = (u64)hts * vts;
+ u64 frame_interval_ns;
+
+ frame_interval_ns = total_pixels * NSEC_PER_SEC;
+ do_div(frame_interval_ns, pixel_rate);
+ vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns);
+ if (vsensor->hw.fps_jiffies == 0)
+ vsensor->hw.fps_jiffies = 1;
+
+ return 0;
+}
+
static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
{
const struct vimc_pix_map *vpix;
@@ -134,6 +133,24 @@ static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
vimc_colorimetry_clamp(fmt);
}
+static u32 vimc_calc_vblank(u32 width, u32 height,
+ s64 pixel_rate, s32 hblank)
+{
+ u32 hts = width + hblank;
+ u32 target_fps;
+ u64 vts;
+
+ target_fps = (width * height <= VIMC_PIXELS_THRESHOLD_30FPS) ? 30 : 10;
+
+ vts = (u64)pixel_rate;
+ do_div(vts, target_fps * hts);
+
+ if (vts > height)
+ return clamp((u32)(vts - height), VIMC_VBLANK_MIN, VIMC_VBLANK_MAX);
+
+ return VIMC_VBLANK_MIN;
+}
+
static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -163,6 +180,20 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
fmt->format.xfer_func, fmt->format.ycbcr_enc);
*mf = fmt->format;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ u32 vblank_def = vimc_calc_vblank(fmt->format.width,
+ fmt->format.height,
+ vsensor->pixel_rate->val,
+ vsensor->hblank->val);
+ vsensor->hw.size.width = fmt->format.width;
+ vsensor->hw.size.height = fmt->format.height;
+ __v4l2_ctrl_modify_range(vsensor->vblank,
+ VIMC_VBLANK_MIN,
+ VIMC_VBLANK_MAX,
+ VIMC_VBLANK_STEP,
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(vsensor->vblank, vblank_def);
+ }
return 0;
}
@@ -248,6 +279,8 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable)
vsensor->hw.size.width = format->width;
vsensor->hw.size.height = format->height;
+ vimc_sensor_update_frame_timing(sd, format->width,
+ format->height);
v4l2_subdev_unlock_state(state);
@@ -319,6 +352,15 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl)
case VIMC_CID_OSD_TEXT_MODE:
vsensor->hw.osd_value = ctrl->val;
break;
+ case V4L2_CID_PIXEL_RATE:
+ break;
+ case V4L2_CID_HBLANK:
+ break;
+ case V4L2_CID_VBLANK:
+ vimc_sensor_update_frame_timing(&vsensor->sd,
+ vsensor->hw.size.width,
+ vsensor->hw.size.height);
+ break;
default:
return -EINVAL;
}
@@ -403,6 +445,26 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc,
V4L2_CID_HUE, -128, 127, 1, 0);
v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 128);
+ /* Timing controls for frame interval configuration */
+ vsensor->pixel_rate = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ VIMC_PIXEL_RATE_FIXED, VIMC_PIXEL_RATE_FIXED,
+ 1, VIMC_PIXEL_RATE_FIXED);
+ if (vsensor->pixel_rate)
+ vsensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vsensor->hblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
+ V4L2_CID_HBLANK,
+ VIMC_HBLANK_FIXED, VIMC_HBLANK_FIXED,
+ 1, VIMC_HBLANK_FIXED);
+ if (vsensor->hblank)
+ vsensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vsensor->vblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
+ V4L2_CID_VBLANK,
+ VIMC_VBLANK_MIN, VIMC_VBLANK_MAX,
+ VIMC_VBLANK_STEP, VIMC_VBLANK_DEFAULT);
+
vsensor->sd.ctrl_handler = &vsensor->hdl;
if (vsensor->hdl.error) {
ret = vsensor->hdl.error;
diff --git a/drivers/media/test-drivers/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c
index 15d863f97cbf..3ebf5798fa3d 100644
--- a/drivers/media/test-drivers/vimc/vimc-streamer.c
+++ b/drivers/media/test-drivers/vimc/vimc-streamer.c
@@ -140,6 +140,29 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
}
/**
+ * vimc_streamer_get_sensor() - Get sensor from pipeline
+ * @stream: the pipeline
+ *
+ * Helper function to find the sensor device in the pipeline.
+ * Returns pointer to sensor device or NULL if not found.
+ */
+static struct vimc_sensor_device *vimc_streamer_get_sensor(struct vimc_stream *stream)
+{
+ int i;
+
+ for (i = 0; i < stream->pipe_size; i++) {
+ struct vimc_ent_device *ved = stream->ved_pipeline[i];
+
+ if (ved && ved->ent &&
+ ved->ent->function == MEDIA_ENT_F_CAM_SENSOR) {
+ return container_of(ved, struct vimc_sensor_device, ved);
+ }
+ }
+
+ return NULL;
+}
+
+/**
* vimc_streamer_thread - Process frames through the pipeline
*
* @data: vimc_stream struct of the current stream
@@ -154,25 +177,31 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
static int vimc_streamer_thread(void *data)
{
struct vimc_stream *stream = data;
+ struct vimc_sensor_device *vsensor;
u8 *frame = NULL;
int i;
+ unsigned long fps_jiffies;
+ const unsigned long default_jiffies = HZ / 30;
set_freezable();
+ vsensor = vimc_streamer_get_sensor(stream);
for (;;) {
try_to_freeze();
if (kthread_should_stop())
break;
+ /* Read from hardware configuration */
+ fps_jiffies = vsensor ? vsensor->hw.fps_jiffies : default_jiffies;
+
for (i = stream->pipe_size - 1; i >= 0; i--) {
frame = stream->ved_pipeline[i]->process_frame(
stream->ved_pipeline[i], frame);
if (!frame || IS_ERR(frame))
break;
}
- //wait for 60hz
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ / 60);
+ schedule_timeout(fps_jiffies);
}
return 0;
diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c
index ba22bf594ac7..d57570290e93 100644
--- a/drivers/media/tuners/mxl5005s.c
+++ b/drivers/media/tuners/mxl5005s.c
@@ -1174,7 +1174,12 @@ static u16 MXL5005_ControlInit(struct dvb_frontend *fe)
state->Init_Ctrl[39].bit[0] = 3;
state->Init_Ctrl[39].val[0] = 1;
+ return 0;
+}
+static u16 MXL5005_ControlInitCH(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
state->CH_Ctrl_Num = CHCTRL_NUM ;
state->CH_Ctrl[0].Ctrl_Num = DN_POLY ;
@@ -1663,6 +1668,7 @@ static void InitTunerControls(struct dvb_frontend *fe)
{
MXL5005_RegisterInit(fe);
MXL5005_ControlInit(fe);
+ MXL5005_ControlInitCH(fe);
#ifdef _MXL_INTERNAL
MXL5005_MXLControlInit(fe);
#endif
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index 4ca42114da8a..4d67e347c22f 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -638,20 +638,27 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
color = 0x10;
}
}
- } else if (params->std & V4L2_STD_MN) {
+ } else if (params->std & (V4L2_STD_MN | V4L2_STD_NTSC_443)) {
std = "MN";
bandwidth = 6000000;
if_frequency = 5400000;
system = 2;
+ if (params->std & V4L2_STD_PAL_N) {
+ std = "palN";
+ color = 0x10;
+ } else if (params->std & V4L2_STD_PAL_Nc) {
+ std = "palNc";
+ color = 0x10;
+ }
} else if (params->std & V4L2_STD_PAL_I) {
std = "palI";
bandwidth = 8000000;
- if_frequency = 7250000; /* TODO: does not work yet */
+ if_frequency = 7250000;
system = 4;
} else if (params->std & V4L2_STD_DK) {
std = "palDK";
bandwidth = 8000000;
- if_frequency = 6900000; /* TODO: does not work yet */
+ if_frequency = 6900000;
system = 5;
if (params->std & V4L2_STD_SECAM_DK) {
std = "secamDK";
@@ -660,7 +667,7 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
} else if (params->std & V4L2_STD_SECAM_L) {
std = "secamL";
bandwidth = 8000000;
- if_frequency = 6750000; /* TODO: untested */
+ if_frequency = 6900000;
system = 6;
color = 0x10;
} else if (params->std & V4L2_STD_SECAM_LC) {
@@ -680,60 +687,103 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
params->mode, system, std, params->frequency,
freq, if_frequency, bandwidth);
- /* set analog IF port */
- memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
- /* in using dev->if_port, we assume analog and digital IF's */
- /* are always on different ports */
- /* assumes if_port definition is 0 or 1 for digital out */
- cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
- /* Analog AGC assumed external */
- cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
- cmd.wlen = 6;
- cmd.rlen = 4;
- ret = si2157_cmd_execute(client, &cmd);
- if (ret)
- goto err;
+ if (dev->part_id != SI2177) {
+ /* AGC speed */
+ memcpy(cmd.args, "\x14\x00\x11\x06\x00\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
- /* set analog IF output config */
- memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
- cmd.wlen = 6;
- cmd.rlen = 4;
- ret = si2157_cmd_execute(client, &cmd);
- if (ret)
- goto err;
+ /* set analog IF port */
+ memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
+ /* in using dev->if_port, we assume analog and digital IF's */
+ /* are always on different ports */
+ /* assumes if_port definition is 0 or 1 for digital out */
+ cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
+ /* Analog AGC assumed external */
+ cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
- /* make this distinct from a digital IF */
- dev->if_frequency = if_frequency | 1;
+ /* set analog IF output config */
+ memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
- /* calc and set tuner analog if center frequency */
- if_frequency = if_frequency + 1250000 - (bandwidth / 2);
- dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
+ /* make this distinct from a digital IF */
+ dev->if_frequency = if_frequency | 1;
- memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
- cmd.args[4] = (if_frequency / 1000) & 0xff;
- cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
- cmd.wlen = 6;
- cmd.rlen = 4;
- ret = si2157_cmd_execute(client, &cmd);
- if (ret)
- goto err;
+ /* calc and set tuner analog if center frequency */
+ if_frequency = if_frequency + 1250000 - (bandwidth / 2);
+ dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
- /* set analog AGC config */
- memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
- cmd.wlen = 6;
- cmd.rlen = 4;
- ret = si2157_cmd_execute(client, &cmd);
- if (ret)
- goto err;
+ memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
+ cmd.args[4] = (if_frequency / 1000) & 0xff;
+ cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* set analog AGC config */
+ memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
- /* set analog video mode */
- memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
- cmd.args[4] = system | color;
- /* can use dev->inversion if assumed applies to both digital/analog */
- if (invert_analog)
- cmd.args[5] |= 0x02;
+ /* set analog video mode */
+ memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+ cmd.args[4] = system | color;
+ /* can use dev->inversion if assumed applies to both digital/analog */
+ if (invert_analog)
+ cmd.args[5] |= 0x02;
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+ } else {
+ /* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+ memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* analog CVBS output properties - Si2177_ATV_CVBS_OUT_FINE_PROP */
+ memcpy(cmd.args, "\x14\x00\x14\x06\x00\x64", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ dev_err(&client->dev, "%s() Settings HSYNC\n", __func__);
+ /* HSYNC output - Si2177_ATV_HSYNC_OUT_PROP */
+ memcpy(cmd.args, "\x14\x00\x27\x06\xa8\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+ }
+
+ /* AFC qcuisition range 1.5MHz */
+ memcpy(cmd.args, "\x14\x00\x10\x06\xdc\x05", 6);
cmd.wlen = 6;
- cmd.rlen = 1;
+ cmd.rlen = 4;
ret = si2157_cmd_execute(client, &cmd);
if (ret)
goto err;
@@ -750,6 +800,76 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
if (ret)
goto err;
+ if (dev->part_id == SI2177) {
+ /* Ref driver tunes, resets registers, then retunes, leaving steps as is */
+ /* set analog video mode - Si2158_ATV_VIDEO_MODE_PROP */
+ memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+ cmd.args[4] = system | color;
+ /* can use dev->inversion if assumed applies to both digital/analog */
+ if (invert_analog)
+ cmd.args[5] |= 0x02;
+
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* Si2177_ATV_AUDIO_MODE_PROP */
+ memcpy(cmd.args, "\x14\x00\x02\x06\x20\x0f", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* af out - Si2177_ATV_AF_OUT_PROP */ /* BRL */
+ memcpy(cmd.args, "\x14\x00\x0b\x06\x30\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* analog CVBS output enable - Si2177_ATV_CVBS_OUT_PROP */
+ memcpy(cmd.args, "\x14\x00\x09\x06\x19\x99", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+ memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* ATV restart */
+ memcpy(cmd.args, "\x51\x00", 2);
+ cmd.wlen = 2;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ usleep_range(10000, 11000);
+
+ /* set analog frequency */
+ memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8);
+ cmd.args[4] = (freq >> 0) & 0xff;
+ cmd.args[5] = (freq >> 8) & 0xff;
+ cmd.args[6] = (freq >> 16) & 0xff;
+ cmd.args[7] = (freq >> 24) & 0xff;
+ cmd.wlen = 8;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+ }
+
dev->bandwidth = bandwidth;
si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index 8579e80f7af7..aaada2bb0f21 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/drivers/media/tuners/si2157_priv.h
@@ -72,7 +72,8 @@ struct si2157_cmd {
((dev)->part_id == SI2177))
#define SUPPORTS_ATV_IF(dev) (((dev)->part_id == SI2157) || \
- ((dev)->part_id == SI2158))
+ ((dev)->part_id == SI2158) || \
+ ((dev)->part_id == SI2177))
/* Old firmware namespace */
#define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw"
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
index a28481edd22e..1a9c4574a8e8 100644
--- a/drivers/media/tuners/xc5000.c
+++ b/drivers/media/tuners/xc5000.c
@@ -1333,6 +1333,15 @@ static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg)
}
+static int xc5000_get_rf_strength(struct dvb_frontend *fe, u16 *rssi)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ return xc_get_lock_status(priv, rssi);
+}
+
static const struct dvb_tuner_ops xc5000_tuner_ops = {
.info = {
.name = "Xceive XC5000",
@@ -1353,7 +1362,8 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = {
.get_frequency = xc5000_get_frequency,
.get_if_frequency = xc5000_get_if_frequency,
.get_bandwidth = xc5000_get_bandwidth,
- .get_status = xc5000_get_status
+ .get_status = xc5000_get_status,
+ .get_rf_strength = xc5000_get_rf_strength,
};
struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c
index 8e480ab78f9b..a11024451ceb 100644
--- a/drivers/media/usb/as102/as102_usb_drv.c
+++ b/drivers/media/usb/as102/as102_usb_drv.c
@@ -403,7 +403,9 @@ static int as102_usb_probe(struct usb_interface *intf,
failed_dvb:
as102_free_usb_stream_buffer(as102_dev);
failed_stream:
+ usb_set_intfdata(intf, NULL);
usb_deregister_dev(intf, &as102_usb_class_driver);
+ return ret;
failed:
usb_put_dev(as102_dev->bus_adap.usb_dev);
usb_set_intfdata(intf, NULL);
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index 9ee21f8bf6fa..9929ce5dcdcd 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -105,6 +105,46 @@ struct au0828_board au0828_boards[] = {
.tuner_addr = 0x60,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
+ [AU0828_BOARD_HAUPPAUGE_IMPACTVCBE] = {
+ .name = "Hauppauge Impact VCB-e",
+ .tuner_type = TUNER_ABSENT,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_COMPOSITE,
+ .vmux = AU8522_COMPOSITE_CH4,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR1265] = {
+ .name = "Hauppauge HVR1265",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .has_ir_i2c = 1,
+ .has_analog = 1,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_TELEVISION,
+ .vmux = AU8522_COMPOSITE_CH4_SIF,
+ .amux = AU8522_AUDIO_SIF,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
};
/* Tuner callback function for au0828 boards. Currently only needed
@@ -120,6 +160,8 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg)
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_HVR1265:
+ case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE:
case AU0828_BOARD_DVICO_FUSIONHDTV7:
if (command == 0) {
/* Tuner Reset Command from xc5000 */
@@ -190,6 +232,8 @@ void au0828_card_setup(struct au0828_dev *dev)
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_HVR1265:
+ case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
if (dev->i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xa0);
@@ -248,6 +292,8 @@ void au0828_gpio_setup(struct au0828_dev *dev)
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ case AU0828_BOARD_HAUPPAUGE_HVR1265:
+ case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE:
/* GPIO's
* 4 - CS5340
* 5 - AU8522 Demodulator
@@ -340,6 +386,10 @@ struct usb_device_id au0828_usb_id_table[] = {
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7270),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x72b0),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_IMPACTVCBE },
+ { USB_DEVICE(0x2040, 0x72a0),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR1265 },
{ },
};
diff --git a/drivers/media/usb/au0828/au0828-cards.h b/drivers/media/usb/au0828/au0828-cards.h
index a9cdf85f98f5..a438aeb334a0 100644
--- a/drivers/media/usb/au0828/au0828-cards.h
+++ b/drivers/media/usb/au0828/au0828-cards.h
@@ -11,3 +11,5 @@
#define AU0828_BOARD_DVICO_FUSIONHDTV7 3
#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4
#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5
+#define AU0828_BOARD_HAUPPAUGE_IMPACTVCBE 6
+#define AU0828_BOARD_HAUPPAUGE_HVR1265 7
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 5d0447ff7d06..31123e6f9fc3 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -571,6 +571,7 @@ int au0828_dvb_register(struct au0828_dev *dev)
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR1265:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
index 7dec1a360da6..b156afb1a0ae 100644
--- a/drivers/media/usb/au0828/au0828-input.c
+++ b/drivers/media/usb/au0828/au0828-input.c
@@ -300,6 +300,7 @@ int au0828_rc_register(struct au0828_dev *dev)
if (dev->board.has_ir_i2c) { /* external i2c device */
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR1265:
rc->map_name = RC_MAP_HAUPPAUGE;
ir->get_key_i2c = au0828_get_key_au8522;
break;
@@ -357,6 +358,7 @@ void au0828_rc_unregister(struct au0828_dev *dev)
return;
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
/* done */
kfree(ir);
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index fbaa542c8259..3c53105f3d2b 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -1671,6 +1671,27 @@ static int vidioc_log_status(struct file *file, void *fh)
return 0;
}
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct au0828_dev *dev = video_drvdata(file);
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* Workaround for a bug in the au0828 hardware design that
+ * sometimes results in the colorspace being inverted
+ */
+ if (dev->greenscreen_detected == 1) {
+ dprintk(1, "Detected green frame. Resetting stream...\n");
+ au0828_analog_stream_reset(dev);
+ dev->greenscreen_detected = 0;
+ }
+
+ return vb2_ioctl_dqbuf(file, priv, b);
+}
+
void au0828_v4l2_suspend(struct au0828_dev *dev)
{
struct urb *urb;
@@ -1764,8 +1785,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.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_dqbuf = vidioc_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std,
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 1cfec76b72f3..0a5c635da040 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -2027,10 +2027,9 @@ int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
0x000035e8);
- status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600);
- status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT,
- 0xC2262600);
- status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600);
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600);
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, 0xC2260000);
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2260000);
/* Save the Spec Inversion value */
dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index b64a37d1acf4..b75535d6abaf 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1369,8 +1369,6 @@ void cx231xx_release_resources(struct cx231xx *dev)
cx231xx_unregister_media_device(dev);
- usb_put_dev(dev->udev);
-
/* Mark device as unused */
clear_bit(dev->devno, &cx231xx_devused);
}
@@ -1719,7 +1717,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
}
} while (test_and_set_bit(nr, &cx231xx_devused));
- udev = usb_get_dev(interface_to_usbdev(interface));
+ udev = interface_to_usbdev(interface);
/* allocate memory for our device state and initialize it */
dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
@@ -1915,7 +1913,6 @@ err_v4l2:
err_media_init:
usb_set_intfdata(interface, NULL);
err_if:
- usb_put_dev(udev);
clear_bit(nr, &cx231xx_devused);
return retval;
}
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index 600cff8a4abd..bd86d250433d 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
if (d->rc_dev) {
cancel_delayed_work_sync(&d->rc_query_work);
rc_unregister_device(d->rc_dev);
+ rc_free_device(d->rc_dev);
d->rc_dev = NULL;
}
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
index 65e2c9e2cdc9..6dc11718dfb9 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d)
{
if (d->state & DVB_USB_STATE_REMOTE) {
cancel_delayed_work_sync(&d->rc_query_work);
- if (d->props.rc.mode == DVB_RC_LEGACY)
+ if (d->props.rc.mode == DVB_RC_LEGACY) {
input_unregister_device(d->input_dev);
- else
+ } else {
rc_unregister_device(d->rc_dev);
+ rc_free_device(d->rc_dev);
+ }
}
d->state &= ~DVB_USB_STATE_REMOTE;
return 0;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 59a2e4db75b7..fbfb74eab475 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -387,14 +387,14 @@ static const struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
* GPIO 7 = P07_LED (green LED)
*/
static const struct em28xx_reg_seq pctv_461e[] = {
- {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0},
- {0x0d, 0xff, 0xff, 0},
- {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */
- {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 200}, /* reset demod */
- {0x0d, 0x42, 0xff, 0},
- {EM2874_R80_GPIO_P0_CTRL, 0xeb, 0xff, 0},
- {EM2874_R5F_TS_ENABLE, 0x84, 0x84, 0}, /* parallel? | null discard */
- { -1, -1, -1, -1},
+ {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0},
+ {0x0d, 0xff, 0xff, 0},
+ {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */
+ {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 200}, /* reset demod */
+ {0x0d, 0x42, 0xff, 0},
+ {EM2874_R80_GPIO_P0_CTRL, 0xeb, 0xff, 0},
+ {EM2874_R5F_TS_ENABLE, 0x84, 0x84, 0}, /* parallel? | null discard */
+ {-1, -1, -1, -1},
};
#if 0
@@ -553,6 +553,36 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = {
{EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50},
{-1, -1, -1, -1},
};
+/* Hauppauge HVR-935 \ HVR-955 / HVR-975 V2 */
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2[] = {
+ {EM2874_R80_GPIO_P0_CTRL, 0xdc, 0xff, 50},
+ {EM2874_R5F_TS_ENABLE, 0x00, 0xff, 50}, /* disable TS filters */
+ {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50},
+ {-1, -1, -1, -1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_comp[] = {
+ {0x0b, 0x00, 0xff, 100},
+ {0x0b, 0x96, 0xff, 100},
+ {0x0b, 0x00, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_5, 10},
+ {-1, -1, -1, -1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_television[] = {
+ {0x0b, 0x00, 0xff, 100},
+ {0x0b, 0x96, 0xff, 100},
+ {0x0b, 0x00, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_5, EM_GPIO_5, 10},
+ {-1, -1, -1, -1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_dvb[] = {
+ {0x0b, 0x80, 0xff, 100},
+ {0x0b, 0x96, 0xff, 100},
+ {0x0b, 0x80, 0xff, 100},
+ {-1, -1, -1, -1},
+};
/*
* MyGica USB TV Box
@@ -689,6 +719,16 @@ static struct em28xx_led hauppauge_usb_quadhd_leds[] = {
{-1, 0, 0, 0},
};
+static struct em28xx_led hauppauge_9x5_v2_leds[] = {
+ {
+ .role = EM28XX_LED_DIGITAL_CAPTURING,
+ .gpio_reg = EM2874_R80_GPIO_P0_CTRL,
+ .gpio_mask = EM_GPIO_0,
+ .inverted = 0,
+ },
+ {-1, 0, 0, 0},
+};
+
/*
* Board definitions
*/
@@ -2457,6 +2497,20 @@ const struct em28xx_board em28xx_boards[] = {
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
/*
+ * 2013:x462 PCTV DVB-S2 Stick (461e_v3)
+ * Empia EM28178, Montage M88DS3103c, Montage M88TS2022, Allegro A8293
+ */
+ [EM28178_BOARD_PCTV_461E_V3] = {
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ .name = "PCTV DVB-S2 Stick (461e v3)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_461e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /*
* 2013:025f PCTV tripleStick (292e).
* Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157
*/
@@ -2522,17 +2576,12 @@ const struct em28xx_board em28xx_boards[] = {
.def_i2c_bus = 1,
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
- .tuner_type = TUNER_SI2157,
+ .tuner_type = TUNER_ABSENT,
.tuner_gpio = hauppauge_dualhd_dvb,
.has_dvb = 1,
.has_dual_ts = 1,
.ir_codes = RC_MAP_HAUPPAUGE,
.leds = hauppauge_dualhd_leds,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
},
/*
* 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Isoc.
@@ -2628,6 +2677,126 @@ const struct em28xx_board em28xx_boards[] = {
.gpio = mygica_utv3_tuner_audio_gpio,
} },
},
+ [EM2828X_BOARD_HAUPPAUGE_USB_LIVE2] = {
+ .name = "Hauppauge USB Live2",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 0,
+ .decoder = EM28XX_BUILTIN,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+
+ /* 2040:8360 Hauppauge HVR-935
+ * Empia EM2828X, si2168 demod, si2177 tuner
+ * Composite input, s-video input, analog TV, stereo audio input
+ */
+ [EM2828X_BOARD_HAUPPAUGE_935_V2] = {
+ .name = "Hauppauge WinTV-HVR-935",
+ .def_i2c_bus = 1,
+ .has_dvb = 1,
+ .vchannels = 3,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_BUILTIN,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_gpio = hauppauge_hvr_9x5_v2,
+ .dvb_gpio = hauppauge_hvr_9x5_v2_dvb,
+ .leds = hauppauge_9x5_v2_leds,
+ .xclk = 0x8f,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = 2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_television,
+
+ } },
+ },
+ /* 2040:8366 Hauppauge HVR-955
+ * Empia EM2828X, lgdt3306a demod, si2177 tuner
+ * Composite input, s-video input, analog TV, stereo audio input
+ */
+ [EM2828X_BOARD_HAUPPAUGE_955_V2] = {
+ .name = "Hauppauge WinTV-HVR-955",
+ .def_i2c_bus = 1,
+ .has_dvb = 1,
+ .vchannels = 3,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_BUILTIN,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_gpio = hauppauge_hvr_9x5_v2,
+ .dvb_gpio = hauppauge_hvr_9x5_v2_dvb,
+ .leds = hauppauge_9x5_v2_leds,
+ .xclk = 0x8f,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = 2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_television,
+
+ } },
+ },
+ /* 2040:836a Hauppauge HVR-975
+ * Empia EM2828X, si2168 demod, lgdt3306a demod, si2177 tuner
+ * Composite input, s-video input, analog TV, stereo audio input
+ */
+ [EM2828X_BOARD_HAUPPAUGE_975_V2] = {
+ .name = "Hauppauge WinTV-HVR-975",
+ .def_i2c_bus = 1,
+ .has_dvb = 1,
+ .vchannels = 3,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_BUILTIN,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_gpio = hauppauge_hvr_9x5_v2,
+ .dvb_gpio = hauppauge_hvr_9x5_v2_dvb,
+ .leds = hauppauge_9x5_v2_leds,
+ .xclk = 0x8f,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_comp,
+ }, {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = 2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_hvr_9x5_v2_television,
+
+ } },
+ },
};
EXPORT_SYMBOL_GPL(em28xx_boards);
@@ -2757,12 +2926,38 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
{ USB_DEVICE(0x2040, 0x8265),
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+ { USB_DEVICE(0x2040, 0x8269),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+ { USB_DEVICE(0x2040, 0x8278),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
{ USB_DEVICE(0x2040, 0x026d),
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
{ USB_DEVICE(0x2040, 0x826d),
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x2040, 0x826e),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x2040, 0x826f),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x2040, 0x8270),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x2040, 0x8271),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
{ USB_DEVICE(0x2040, 0x846d),
.driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD },
+ { USB_DEVICE(0x2040, 0xc220),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 },
+ { USB_DEVICE(0x2040, 0x0360),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+ { USB_DEVICE(0x2040, 0x8360),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+ { USB_DEVICE(0x2040, 0x0366),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+ { USB_DEVICE(0x2040, 0x8366),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+ { USB_DEVICE(0x2040, 0x036a),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
+ { USB_DEVICE(0x2040, 0x836a),
+ .driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
{ USB_DEVICE(0x0438, 0xb002),
.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
{ USB_DEVICE(0x2001, 0xf112),
@@ -2831,6 +3026,10 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM28178_BOARD_PCTV_461E_V2 },
{ USB_DEVICE(0x2013, 0x0259),
.driver_info = EM28178_BOARD_PCTV_461E_V2 },
+ { USB_DEVICE(0x2013, 0x0462),
+ .driver_info = EM28178_BOARD_PCTV_461E_V3 },
+ { USB_DEVICE(0x2013, 0x8462), /* Bulk transport 461e v3 */
+ .driver_info = EM28178_BOARD_PCTV_461E_V3 },
{ USB_DEVICE(0x2013, 0x025f),
.driver_info = EM28178_BOARD_PCTV_292E },
{ USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */
@@ -3253,6 +3452,10 @@ static void em28xx_card_setup(struct em28xx *dev)
case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
+ case EM2828X_BOARD_HAUPPAUGE_USB_LIVE2:
+ case EM2828X_BOARD_HAUPPAUGE_935_V2:
+ case EM2828X_BOARD_HAUPPAUGE_955_V2:
+ case EM2828X_BOARD_HAUPPAUGE_975_V2:
{
struct tveeprom tv;
@@ -3626,6 +3829,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
}
/* NOTE: the em2820 is used in webcams, too ! */
break;
+ case CHIP_ID_EM2828X:
+ chip_name = "em2828X";
+ dev->wait_after_write = 0;
+ dev->eeprom_addrwidth_16bit = 1;
+ break;
case CHIP_ID_EM2840:
chip_name = "em2840";
break;
@@ -3784,6 +3992,7 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev,
* 0x84 bulk => analog or digital**
* 0x85 isoc => digital TS2
* 0x85 bulk => digital TS2
+ * 0x8a isoc => digital video
* (*: audio should always be isoc)
* (**: analog, if ep 0x82 is isoc, otherwise digital)
*
@@ -3807,6 +4016,8 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev,
/* Only inspect input endpoints */
switch (e->bEndpointAddress) {
+ case 0x81: /* unknown function */
+ return;
case 0x82:
*has_video = true;
if (usb_endpoint_xfer_isoc(e)) {
@@ -3824,7 +4035,10 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev,
"error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
return;
case 0x84:
- if (*has_video && (usb_endpoint_xfer_bulk(e))) {
+ if (*has_dvb && (usb_endpoint_xfer_bulk(e))) {
+ *has_dvb = true;
+ dev->dvb_ep_bulk = e->bEndpointAddress;
+ } else if (*has_video && (usb_endpoint_xfer_bulk(e))) {
dev->analog_ep_bulk = e->bEndpointAddress;
} else {
if (usb_endpoint_xfer_isoc(e)) {
@@ -3858,7 +4072,17 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev,
dev->dvb_ep_bulk_ts2 = e->bEndpointAddress;
}
return;
- }
+ case 0x8a:
+ *has_video = true;
+ *has_dvb = true;
+ if (usb_endpoint_xfer_isoc(e)) {
+ dev->analog_ep_isoc = e->bEndpointAddress;
+ dev->alt_max_pkt_size_isoc[alt] = size;
+ } else if (usb_endpoint_xfer_bulk(e)) {
+ dev->analog_ep_bulk = e->bEndpointAddress;
+ }
+ return;
+ };
}
/*
@@ -4040,6 +4264,8 @@ static int em28xx_usb_probe(struct usb_interface *intf,
try_bulk = 1;
else
try_bulk = 0;
+ } else if (dev->board.decoder == EM28XX_BUILTIN && dev->analog_xfer_mode) {
+ try_bulk = 1;
} else {
try_bulk = usb_xfer_mode > 0;
}
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 29a7f3f19b56..5bbb082dbed9 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -499,7 +499,8 @@ int em28xx_audio_setup(struct em28xx *dev)
if (dev->chip_id == CHIP_ID_EM2870 ||
dev->chip_id == CHIP_ID_EM2874 ||
dev->chip_id == CHIP_ID_EM28174 ||
- dev->chip_id == CHIP_ID_EM28178) {
+ dev->chip_id == CHIP_ID_EM28178 ||
+ dev->chip_id == CHIP_ID_EM2828X) {
/* Digital only device - don't load any alsa module */
dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
@@ -619,6 +620,65 @@ const struct em28xx_led *em28xx_find_led(struct em28xx *dev,
}
EXPORT_SYMBOL_GPL(em28xx_find_led);
+void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin)
+{
+ switch (vin) {
+ case EM2828X_TELEVISION:
+ dev_dbg(&dev->intf->dev, "EM2828X_TELEVISION\n");
+ break;
+ case EM2828X_COMPOSITE:
+ dev_dbg(&dev->intf->dev, "EM2828X_COMPOSITE\n");
+ break;
+ default:
+ dev_dbg(&dev->intf->dev, "EM2828X_SVIDEO\n");
+ break;
+ };
+
+ em28xx_write_reg(dev, 0x24, 0x00);
+ em28xx_write_reg(dev, 0x25, 0x02);
+ em28xx_write_reg(dev, 0x2E, 0x00);
+
+ if (vin == EM2828X_TELEVISION) {
+ em28xx_write_reg(dev, 0x7A0B, 0xfc);
+ em28xx_write_reg(dev, 0xB6, 0x8F);
+ em28xx_write_reg(dev, 0xB8, 0x01);
+ } else {
+ em28xx_write_reg(dev, 0x7A0B, 0x00);
+ em28xx_write_reg(dev, 0xB6, 0x8F);
+ em28xx_write_reg(dev, 0xB8, 0x00);
+ }
+
+ em28xx_write_reg(dev, 0x7A1C, 0x1E);
+ em28xx_write_reg(dev, 0x7A1D, 0x99);
+ em28xx_write_reg(dev, 0x7A1E, 0x99);
+ em28xx_write_reg(dev, 0x7A1F, 0x9A);
+ em28xx_write_reg(dev, 0x7A20, 0x3d);
+ em28xx_write_reg(dev, 0x7A21, 0x3e);
+ em28xx_write_reg(dev, 0x7A29, 0x00);
+ em28xx_write_reg(dev, 0x7A2F, 0x52);
+ em28xx_write_reg(dev, 0x7A40, 0x05);
+ em28xx_write_reg(dev, 0x7A51, 0x00);
+ em28xx_write_reg(dev, 0x7AC1, 0x1B);
+
+ if (vin == EM2828X_COMPOSITE || vin == EM2828X_TELEVISION) {
+ em28xx_write_reg(dev, 0x38, 0x01);
+ em28xx_write_reg(dev, 0xB1, 0x70);
+ em28xx_write_reg(dev, 0xB3, 0x00);
+ em28xx_write_reg(dev, 0xB5, 0x00);
+ em28xx_write_reg(dev, 0x7A02, 0x4f);
+ } else { /* EM2828X_SVIDEO */
+ em28xx_write_reg(dev, 0x38, 0x00);
+ em28xx_write_reg(dev, 0xB1, 0x60);
+ em28xx_write_reg(dev, 0xB3, 0x10);
+ em28xx_write_reg(dev, 0xB5, 0x10);
+ em28xx_write_reg(dev, 0x7A02, 0x4e);
+ }
+
+ em28xx_write_reg(dev, 0x7A3F, 0x01);
+ em28xx_write_reg(dev, 0x7A3F, 0x00);
+}
+EXPORT_SYMBOL_GPL(em2828X_decoder_vmux);
+
int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
@@ -646,12 +706,16 @@ int em28xx_capture_start(struct em28xx *dev, int start)
rc = em28xx_write_reg_bits(dev,
EM2874_R5F_TS_ENABLE,
start ? EM2874_TS1_CAPTURE_ENABLE : 0x00,
- EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD);
+ EM2874_TS1_CAPTURE_ENABLE |
+ EM2874_TS1_FILTER_ENABLE |
+ EM2874_TS1_NULL_DISCARD);
else
rc = em28xx_write_reg_bits(dev,
EM2874_R5F_TS_ENABLE,
start ? EM2874_TS2_CAPTURE_ENABLE : 0x00,
- EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD);
+ EM2874_TS2_CAPTURE_ENABLE |
+ EM2874_TS2_FILTER_ENABLE |
+ EM2874_TS2_NULL_DISCARD);
} else {
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
@@ -664,26 +728,93 @@ int em28xx_capture_start(struct em28xx *dev, int start)
if (dev->is_webcam)
rc = em28xx_write_reg(dev, 0x13, 0x0c);
- /* Enable video capture */
- rc = em28xx_write_reg(dev, 0x48, 0x00);
- if (rc < 0)
- return rc;
+ if (dev->mode == EM28XX_ANALOG_MODE) {
+ /* Enable video capture */
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
+ if (rc < 0)
+ return rc;
- if (dev->mode == EM28XX_ANALOG_MODE)
rc = em28xx_write_reg(dev,
EM28XX_R12_VINENABLE,
0x67);
- else
- rc = em28xx_write_reg(dev,
- EM28XX_R12_VINENABLE,
- 0x37);
+
+ } else if (dev->chip_id == CHIP_ID_EM2828X) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (dev->dvb_xfer_bulk) {
+ /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ 0xff);
+ } else {
+ /* ISOC Maximum Transfer Size = 188 * 5 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ dev->dvb_max_pkt_size_isoc / 188);
+ }
+
+ if (dev->ts == PRIMARY_TS)
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS1_CAPTURE_ENABLE : 0x00,
+ EM2874_TS1_CAPTURE_ENABLE |
+ EM2874_TS1_FILTER_ENABLE |
+ EM2874_TS1_NULL_DISCARD);
+ else
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS2_CAPTURE_ENABLE : 0x00,
+ EM2874_TS2_CAPTURE_ENABLE |
+ EM2874_TS2_FILTER_ENABLE |
+ EM2874_TS2_NULL_DISCARD);
+ } else {
+ /* Enable video capture */
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
+ if (rc < 0)
+ return rc;
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+ }
+
if (rc < 0)
return rc;
usleep_range(10000, 11000);
} else {
- /* disable video capture */
- rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
+ if (dev->mode == EM28XX_DIGITAL_MODE && dev->chip_id == CHIP_ID_EM2828X) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (dev->dvb_xfer_bulk) {
+ /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ 0xff);
+ } else {
+ /* ISOC Maximum Transfer Size = 188 * 5 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ dev->dvb_max_pkt_size_isoc / 188);
+ }
+
+ if (dev->ts == PRIMARY_TS)
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS1_CAPTURE_ENABLE : 0x00,
+ EM2874_TS1_CAPTURE_ENABLE |
+ EM2874_TS1_FILTER_ENABLE |
+ EM2874_TS1_NULL_DISCARD);
+ else
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS2_CAPTURE_ENABLE : 0x00,
+ EM2874_TS2_CAPTURE_ENABLE |
+ EM2874_TS2_FILTER_ENABLE |
+ EM2874_TS2_NULL_DISCARD);
+ } else {
+ /* disable video capture */
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
+ }
}
}
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 2eb9a88e595e..938f1980d448 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -97,7 +97,7 @@ struct em28xx_dvb {
struct semaphore pll_mutex;
bool dont_attach_fe1;
int lna_gpio;
- struct i2c_client *i2c_client_demod;
+ struct i2c_client *i2c_client_demod[2];
struct i2c_client *i2c_client_tuner;
struct i2c_client *i2c_client_sec;
};
@@ -296,6 +296,21 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
return em28xx_set_mode(dev, EM28XX_SUSPEND);
}
+static int em28xx_set_analog_freq(struct em28xx *dev, u32 freq)
+{
+ const struct dvb_tuner_ops *dops = &dev->dvb->fe[0]->ops.tuner_ops;
+
+ if (dops->set_analog_params) {
+ struct analog_parameters params;
+
+ params.frequency = freq;
+ params.std = dev->v4l2->norm;
+ params.mode = 0;
+
+ dops->set_analog_params(dev->dvb->fe[0], &params);
+ }
+ return 0;
+}
/* ------------------------------------------------------------------ */
static struct lgdt330x_config em2880_lgdt3303_dev = {
@@ -839,14 +854,14 @@ static void px_bcud_init(struct em28xx *dev)
};
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x46);
/* sleeping ISDB-T */
- dev->dvb->i2c_client_demod->addr = 0x14;
+ dev->dvb->i2c_client_demod[0]->addr = 0x14;
for (i = 0; i < ARRAY_SIZE(regs1); i++)
- i2c_master_send(dev->dvb->i2c_client_demod,
+ i2c_master_send(dev->dvb->i2c_client_demod[0],
regs1[i].r, regs1[i].len);
/* sleeping ISDB-S */
- dev->dvb->i2c_client_demod->addr = 0x15;
+ dev->dvb->i2c_client_demod[0]->addr = 0x15;
for (i = 0; i < ARRAY_SIZE(regs2); i++)
- i2c_master_send(dev->dvb->i2c_client_demod, regs2[i].r,
+ i2c_master_send(dev->dvb->i2c_client_demod[0], regs2[i].r,
regs2[i].len);
for (i = 0; i < ARRAY_SIZE(gpio); i++) {
em28xx_write_reg_bits(dev, gpio[i].reg, gpio[i].val,
@@ -1140,13 +1155,13 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
tda10071_pdata.pll_multiplier = 20;
tda10071_pdata.tuner_i2c_addr = 0x14;
- dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118",
+ dvb->i2c_client_demod[0] = dvb_module_probe("tda10071", "tda10071_cx24118",
&dev->i2c_adap[dev->def_i2c_bus],
0x55, &tda10071_pdata);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
- dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod);
+ dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
/* attach SEC */
a8293_pdata.dvb_frontend = dvb->fe[0];
@@ -1155,7 +1170,7 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
&dev->i2c_adap[dev->def_i2c_bus],
0x08, &a8293_pdata);
if (!dvb->i2c_client_sec) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1178,14 +1193,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
m88ds3103_pdata.ts_clk_pol = 1;
m88ds3103_pdata.agc = 0x99;
- dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
0x68, &m88ds3103_pdata);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
- dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
- i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+ dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
/* attach tuner */
ts2020_config.fe = dvb->fe[0];
@@ -1194,7 +1209,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
i2c_adapter,
0x60, &ts2020_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1215,14 +1230,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
0x08, &a8293_pdata);
if (!dvb->i2c_client_sec) {
dvb_module_release(dvb->i2c_client_tuner);
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
return 0;
}
-static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
+static int em28178_dvb_init_pctv_461e_vX(struct em28xx *dev, int version)
{
struct em28xx_dvb *dvb = dev->dvb;
struct i2c_adapter *i2c_adapter;
@@ -1239,15 +1254,25 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
m88ds3103_pdata.agc = 0x99;
m88ds3103_pdata.agc_inv = 0;
m88ds3103_pdata.spec_inv = 0;
- dvb->i2c_client_demod = dvb_module_probe("m88ds3103", "m88ds3103b",
- &dev->i2c_adap[dev->def_i2c_bus],
- 0x6a, &m88ds3103_pdata);
- if (!dvb->i2c_client_demod)
+ if (version == 2) {
+ dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103b",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x6a, &m88ds3103_pdata);
+ } else {
+ m88ds3103_pdata.lnb_hv_pol = 1;
+ m88ds3103_pdata.lnb_en_pol = 1;
+
+ dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103c",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x6a, &m88ds3103_pdata);
+ }
+
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
- dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
- i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+ dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
/* attach tuner */
ts2020_config.fe = dvb->fe[0];
@@ -1255,7 +1280,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
i2c_adapter,
0x60, &ts2020_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1270,7 +1295,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
0x08, &a8293_pdata);
if (!dvb->i2c_client_sec) {
dvb_module_release(dvb->i2c_client_tuner);
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1290,10 +1315,10 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
si2168_config.ts_mode = SI2168_TS_PARALLEL;
si2168_config.spectral_inversion = true;
- dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
0x64, &si2168_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
/* attach tuner */
@@ -1306,7 +1331,7 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
adapter,
0x60, &si2157_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
@@ -1326,10 +1351,10 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
si2168_config.fe = &dvb->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
- dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
0x64, &si2168_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
/* attach tuner */
@@ -1343,7 +1368,7 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
adapter,
0x60, &si2157_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1357,10 +1382,10 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
struct qm1d1c0042_config qm1d1c0042_config = {};
/* attach demod */
- dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat",
+ dvb->i2c_client_demod[0] = dvb_module_probe("tc90522", "tc90522sat",
&dev->i2c_adap[dev->def_i2c_bus],
0x15, &tc90522_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
/* attach tuner */
@@ -1371,7 +1396,7 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
tc90522_config.tuner_i2c,
0x61, &qm1d1c0042_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1396,10 +1421,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
si2168_config.spectral_inversion = true;
addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67;
- dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
addr, &si2168_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
/* attach tuner */
@@ -1415,7 +1440,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
adapter,
addr, &si2157_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1436,10 +1461,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
lgdt3306a_config.i2c_adapter = &adapter;
addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e;
- dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
addr, &lgdt3306a_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
return -ENODEV;
/* attach tuner */
@@ -1455,7 +1480,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
adapter,
addr, &si2157_config);
if (!dvb->i2c_client_tuner) {
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
}
@@ -1473,11 +1498,199 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev)
mxl692_config.fe = &dvb->fe[0];
addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63;
- dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL,
+ dvb->i2c_client_demod[0] = dvb_module_probe("mxl692", NULL,
&dev->i2c_adap[dev->def_i2c_bus],
addr, &mxl692_config);
- if (!dvb->i2c_client_demod)
+ if (!dvb->i2c_client_demod[0])
+ return -ENODEV;
+
+ return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_935_v2(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+
+ /* Hauppauge HVR-9x5 V2 */
+ static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {0x90, EM_GPIO_5, EM_GPIO_5, 50},
+ { -1, -1, -1, -1},
+ };
+
+ em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+ /* attach demod */
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ si2168_config.ts_clock_inv = true;
+
+ dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x64, &si2168_config);
+ if (!dvb->i2c_client_demod[0]) {
+ dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+ return -ENODEV;
+ }
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ si2157_config.if_port = 0;
+ si2157_config.inversion = true;
+ si2157_config.dont_load_firmware = 1;
+
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+ dvb_module_release(dvb->i2c_client_demod[0]);
+ return -ENODEV;
+ }
+
+ dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+ return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_955_v2(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct lgdt3306a_config lgdt3306a_config = {};
+ struct si2157_config si2157_config = {};
+
+ /* Hauppauge HVR-9x5 V2 */
+ static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {0x90, EM_GPIO_5, EM_GPIO_5, 50},
+ { -1, -1, -1, -1},
+ };
+
+ em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+ /* attach demod */
+ lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+ lgdt3306a_config.fe = &dvb->fe[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+
+ dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x59, &lgdt3306a_config);
+ if (!dvb->i2c_client_demod[0]) {
+ dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+ return -ENODEV;
+ }
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ si2157_config.if_port = 0;
+ si2157_config.inversion = true;
+ si2157_config.dont_load_firmware = 1;
+
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+ dvb_module_release(dvb->i2c_client_demod[0]);
+ return -ENODEV;
+ }
+
+ dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+ return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_975_v2(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct i2c_adapter *adapter2;
+ struct lgdt3306a_config lgdt3306a_config = {};
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+
+ /* Hauppauge HVR-9x5 V2 */
+ static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0, EM_GPIO_6, 50},
+ {EM2874_R80_GPIO_P0_CTRL, EM_GPIO_6, EM_GPIO_6, 50},
+ {0x90, EM_GPIO_5, EM_GPIO_5, 50},
+ {-1, -1, -1, -1},
+ };
+
+ em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+ /* attach demod */
+ lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+ lgdt3306a_config.fe = &dvb->fe[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+
+ dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x59, &lgdt3306a_config);
+ if (!dvb->i2c_client_demod[0]) {
+ dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+ return -ENODEV;
+ }
+
+ /* attach demod */
+ si2168_config.i2c_adapter = &adapter2;
+ si2168_config.fe = &dvb->fe[1];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ si2168_config.ts_clock_inv = true;
+
+ dvb->i2c_client_demod[1] = dvb_module_probe("si2168", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x64, &si2168_config);
+ if (!dvb->i2c_client_demod[1]) {
+ dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+ dvb_module_release(dvb->i2c_client_demod[0]);
return -ENODEV;
+ }
+
+ dvb->fe[1]->id = 1;
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ si2157_config.if_port = 0;
+ si2157_config.inversion = true;
+ si2157_config.dont_load_firmware = 1;
+
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+ dvb_module_release(dvb->i2c_client_demod[1]);
+ dvb_module_release(dvb->i2c_client_demod[0]);
+ return -ENODEV;
+ }
+
+ dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv;
+
+ memcpy(&dvb->fe[1]->ops.tuner_ops,
+ &dvb->fe[0]->ops.tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
return 0;
}
@@ -1507,6 +1720,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
dev->dvb = dvb;
dvb->fe[0] = NULL;
dvb->fe[1] = NULL;
+ dvb->i2c_client_demod[0] = NULL;
+ dvb->i2c_client_demod[1] = NULL;
/* pre-allocate DVB usb transfer buffers */
if (dev->dvb_xfer_bulk) {
@@ -1939,7 +2154,12 @@ static int em28xx_dvb_init(struct em28xx *dev)
goto out_free;
break;
case EM28178_BOARD_PCTV_461E_V2:
- result = em28178_dvb_init_pctv_461e_v2(dev);
+ result = em28178_dvb_init_pctv_461e_vX(dev, 2);
+ if (result)
+ goto out_free;
+ break;
+ case EM28178_BOARD_PCTV_461E_V3:
+ result = em28178_dvb_init_pctv_461e_vX(dev, 3);
if (result)
goto out_free;
break;
@@ -1973,6 +2193,21 @@ static int em28xx_dvb_init(struct em28xx *dev)
if (result)
goto out_free;
break;
+ case EM2828X_BOARD_HAUPPAUGE_935_V2:
+ result = em2828X_dvb_init_hauppauge_wintv_935_v2(dev);
+ if (result)
+ goto out_free;
+ break;
+ case EM2828X_BOARD_HAUPPAUGE_955_V2:
+ result = em2828X_dvb_init_hauppauge_wintv_955_v2(dev);
+ if (result)
+ goto out_free;
+ break;
+ case EM2828X_BOARD_HAUPPAUGE_975_V2:
+ result = em2828X_dvb_init_hauppauge_wintv_975_v2(dev);
+ if (result)
+ goto out_free;
+ break;
default:
dev_err(&dev->intf->dev,
"The frontend of your DVB/ATSC card isn't supported yet\n");
@@ -2068,7 +2303,8 @@ static int em28xx_dvb_fini(struct em28xx *dev)
/* release I2C module bindings */
dvb_module_release(dvb->i2c_client_sec);
dvb_module_release(dvb->i2c_client_tuner);
- dvb_module_release(dvb->i2c_client_demod);
+ dvb_module_release(dvb->i2c_client_demod[1]);
+ dvb_module_release(dvb->i2c_client_demod[0]);
kfree(dvb);
dev->dvb = NULL;
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index a7eb11f7fb34..f0a901c3b49a 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -864,6 +864,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned int bus,
le16_to_cpu(dev_config->string2),
le16_to_cpu(dev_config->string3));
+ dev->analog_xfer_mode = data[67] & 0x01;
+
return 0;
error:
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 20fdd59b5518..ab61d9a29b10 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev)
goto ref_put;
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
kfree(ir->i2c_client);
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index d7c60862874a..68a0fcc2fa72 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -283,6 +283,7 @@ enum em28xx_chip_id {
CHIP_ID_EM2884 = 68,
CHIP_ID_EM28174 = 113,
CHIP_ID_EM28178 = 114,
+ CHIP_ID_EM2828X = 148,
};
/*
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index b0c184f237a7..4a0ce9c5ee4b 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -161,13 +161,26 @@ static int em28xx_vbi_supported(struct em28xx *dev)
/* FIXME: check subdevices for VBI support */
if (dev->chip_id == CHIP_ID_EM2860 ||
- dev->chip_id == CHIP_ID_EM2883)
+ dev->chip_id == CHIP_ID_EM2883 ||
+ dev->board.decoder == EM28XX_BUILTIN)
return 1;
/* Version of em28xx that does not support VBI */
return 0;
}
+static int em28xx_analogtv_supported(struct em28xx *dev)
+{
+ switch (dev->model) {
+ case EM2828X_BOARD_HAUPPAUGE_935_V2:
+ case EM2828X_BOARD_HAUPPAUGE_955_V2:
+ case EM2828X_BOARD_HAUPPAUGE_975_V2:
+ return 1;
+ default:
+ return 0;
+ };
+}
+
/*
* em28xx_wake_i2c()
* configure i2c attached devices
@@ -344,6 +357,109 @@ static int em28xx_resolution_set(struct em28xx *dev)
return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale);
}
+static void em2828X_decoder_set_std(struct em28xx *dev, v4l2_std_id norm)
+{
+ if (norm & V4L2_STD_525_60) {
+ dev_dbg(&dev->intf->dev, "V4L2_STD_525_60");
+ em28xx_write_reg(dev, 0x7A01, 0x0d); // 0x05
+ em28xx_write_reg(dev, 0x7A04, 0xDD);
+ em28xx_write_reg(dev, 0x7A07, 0x60);
+ em28xx_write_reg(dev, 0x7A08, 0x7A);
+ em28xx_write_reg(dev, 0x7A09, 0x02);
+ em28xx_write_reg(dev, 0x7A0A, 0x7C);
+ em28xx_write_reg(dev, 0x7A0C, 0x8A);
+ em28xx_write_reg(dev, 0x7A0F, 0x1C);
+ em28xx_write_reg(dev, 0x7A18, 0x20);
+ em28xx_write_reg(dev, 0x7A19, 0x74);
+ em28xx_write_reg(dev, 0x7A1A, 0x5D);
+ em28xx_write_reg(dev, 0x7A1B, 0x17);
+ em28xx_write_reg(dev, 0x7A2E, 0x85);
+ em28xx_write_reg(dev, 0x7A31, 0x63);
+ em28xx_write_reg(dev, 0x7A82, 0x42);
+ em28xx_write_reg(dev, 0x7AC0, 0xD4);
+
+ if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) {
+ em28xx_write_reg(dev, 0x7A00, 0x00);
+ em28xx_write_reg(dev, 0x7A03, 0x00);
+ em28xx_write_reg(dev, 0x7A30, 0x22);
+ em28xx_write_reg(dev, 0x7A80, 0x03);
+ } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) {
+ em28xx_write_reg(dev, 0x7A17, 0xc3);
+ em28xx_write_reg(dev, 0x7A31, 0x62); // BRL 0x63
+ em28xx_write_reg(dev, 0x7A82, 0x42);
+ em28xx_write_reg(dev, 0x7AC0, 0xD4);
+ em28xx_write_reg(dev, 0x7A00, 0x00);
+ em28xx_write_reg(dev, 0x7A03, 0x00);
+ em28xx_write_reg(dev, 0x7A30, 0x20);
+ em28xx_write_reg(dev, 0x7A80, 0x00);
+
+ em28xx_write_reg(dev, 0x7A50, 0xdd);
+ em28xx_write_reg(dev, 0x7A5d, 0x0e);
+ em28xx_write_reg(dev, 0x7A5e, 0xea);
+ em28xx_write_reg(dev, 0x7A60, 0x64);
+ em28xx_write_reg(dev, 0x7A67, 0x5a);
+ } else {
+ em28xx_write_reg(dev, 0x7A00, 0x01);
+ em28xx_write_reg(dev, 0x7A03, 0x03);
+ em28xx_write_reg(dev, 0x7A30, 0x20);
+ em28xx_write_reg(dev, 0x7A80, 0x04);
+ }
+ } else if (norm & V4L2_STD_625_50) {
+ dev_dbg(&dev->intf->dev, "V4L2_STD_625_50");
+ em28xx_write_reg(dev, 0x7A04, 0xDC);
+ em28xx_write_reg(dev, 0x7A0C, 0x67);
+ em28xx_write_reg(dev, 0x7A0F, 0x1C);
+ em28xx_write_reg(dev, 0x7A18, 0x28);
+ em28xx_write_reg(dev, 0x7A19, 0x32);
+ em28xx_write_reg(dev, 0x7A1A, 0xB9);
+ em28xx_write_reg(dev, 0x7A1B, 0x86);
+ em28xx_write_reg(dev, 0x7A31, 0xC3);
+ em28xx_write_reg(dev, 0x7A82, 0x52);
+
+ if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) {
+ em28xx_write_reg(dev, 0x7A00, 0x32);
+ em28xx_write_reg(dev, 0x7A01, 0x10);
+ em28xx_write_reg(dev, 0x7A03, 0x06);
+ em28xx_write_reg(dev, 0x7A07, 0x2f);
+ em28xx_write_reg(dev, 0x7A08, 0x77);
+ em28xx_write_reg(dev, 0x7A09, 0x0f);
+ em28xx_write_reg(dev, 0x7A0A, 0x8c);
+ em28xx_write_reg(dev, 0x7A20, 0x3d);
+ em28xx_write_reg(dev, 0x7A2E, 0x88);
+ em28xx_write_reg(dev, 0x7A30, 0x2c);
+ em28xx_write_reg(dev, 0x7A80, 0x07);
+ } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) {
+ em28xx_write_reg(dev, 0x7A00, 0x32);
+ em28xx_write_reg(dev, 0x7A03, 0x09);
+ em28xx_write_reg(dev, 0x7A30, 0x2a);
+ em28xx_write_reg(dev, 0x7A80, 0x03);
+ em28xx_write_reg(dev, 0x7A20, 0x35);
+ em28xx_write_reg(dev, 0x7A2e, 0x88);
+ em28xx_write_reg(dev, 0x7A53, 0xcc);
+ em28xx_write_reg(dev, 0x7A5d, 0x16);
+ em28xx_write_reg(dev, 0x7A5e, 0x50);
+ em28xx_write_reg(dev, 0x7A60, 0xb4);
+ em28xx_write_reg(dev, 0x7A67, 0x64);
+ } else {
+ em28xx_write_reg(dev, 0x7A00, 0x33);
+ em28xx_write_reg(dev, 0x7A01, 0x04);
+ em28xx_write_reg(dev, 0x7A03, 0x04);
+ em28xx_write_reg(dev, 0x7A07, 0x20);
+ em28xx_write_reg(dev, 0x7A08, 0x6a);
+ em28xx_write_reg(dev, 0x7A09, 0x16);
+ em28xx_write_reg(dev, 0x7A0A, 0x80);
+ em28xx_write_reg(dev, 0x7A2E, 0x8a);
+ em28xx_write_reg(dev, 0x7A30, 0x26);
+ em28xx_write_reg(dev, 0x7A80, 0x08);
+ }
+ } else {
+ dev_err(&dev->intf->dev, "%s() Unsupported STD: %X", __func__, (unsigned int)norm);
+ }
+
+ em28xx_write_reg(dev, 0x7A3F, 0x01);
+ em28xx_write_reg(dev, 0x7A3F, 0x00);
+}
+
/* Set USB alternate setting for analog video */
static int em28xx_set_alternate(struct em28xx *dev)
{
@@ -880,6 +996,12 @@ static void em28xx_v4l2_media_release(struct em28xx *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
int i;
+ if (dev->board.decoder == EM28XX_BUILTIN) {
+ media_device_unregister_entity(dev->v4l2->decoder);
+ kfree(dev->v4l2->decoder);
+ dev->v4l2->decoder = NULL;
+ }
+
for (i = 0; i < MAX_EM28XX_INPUT; i++) {
if (!INPUT(i)->type)
return;
@@ -1003,7 +1125,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev)
ent->function = MEDIA_ENT_F_CONN_SVIDEO;
break;
default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */
- if (dev->tuner_type != TUNER_ABSENT)
+ if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev))
ent->function = MEDIA_ENT_F_CONN_RF;
break;
}
@@ -1018,6 +1140,27 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev)
dev_err(&dev->intf->dev,
"failed to register input entity %d!\n", i);
}
+
+ if (dev->board.decoder == EM28XX_BUILTIN) {
+ v4l2->decoder_pads[EM2828X_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
+ v4l2->decoder_pads[EM2828X_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG;
+ v4l2->decoder_pads[EM2828X_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ v4l2->decoder_pads[EM2828X_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
+
+ v4l2->decoder = kzalloc_obj(*v4l2->decoder);
+ v4l2->decoder->name = "em2828x_builtin";
+ v4l2->decoder->function = MEDIA_ENT_F_ATV_DECODER;
+
+ ret = media_entity_pads_init(v4l2->decoder, EM2828X_NUM_PADS,
+ &v4l2->decoder_pads[0]);
+ if (ret < 0)
+ dev_err(&dev->intf->dev, "failed to initialize decoder pads %d!\n", ret);
+
+ ret = media_device_register_entity(dev->media_dev, v4l2->decoder);
+ if (ret < 0)
+ dev_err(&dev->intf->dev, "failed to register decoder entity %d!\n", ret);
+ }
+
#endif
}
@@ -1297,6 +1440,13 @@ static void video_mux(struct em28xx *dev, int index)
MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
}
+ if (dev->board.decoder == EM28XX_BUILTIN) {
+ em2828X_decoder_vmux(dev, INPUT(index)->vmux);
+ em2828X_decoder_set_std(dev, dev->v4l2->norm);
+
+ em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
+ }
+
if (dev->board.adecoder != EM28XX_NOADECODER) {
v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
dev->ctl_ainput, dev->ctl_aoutput, 0);
@@ -1586,6 +1736,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
em28xx_resolution_set(dev);
v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
+ if (dev->board.decoder == EM28XX_BUILTIN)
+ em2828X_decoder_set_std(dev, v4l2->norm);
+
return 0;
}
@@ -1829,6 +1982,11 @@ static int vidioc_g_tuner(struct file *file, void *priv,
strscpy(t->name, "Tuner", sizeof(t->name));
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->signal = 0xffff; /* LOCKED */
+
v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
return 0;
}
@@ -1868,7 +2026,18 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (f->tuner != 0)
return -EINVAL;
- v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+ switch (dev->model) {
+ case EM2828X_BOARD_HAUPPAUGE_935_V2:
+ case EM2828X_BOARD_HAUPPAUGE_955_V2:
+ case EM2828X_BOARD_HAUPPAUGE_975_V2:
+ if (dev->em28xx_set_analog_freq)
+ dev->em28xx_set_analog_freq(dev, f->frequency);
+ break;
+ default:
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+ break;
+ }
+
v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq);
v4l2->frequency = new_freq.frequency;
@@ -1978,7 +2147,7 @@ static int vidioc_querycap(struct file *file, void *priv,
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE)
cap->capabilities |= V4L2_CAP_AUDIO;
- if (dev->tuner_type != TUNER_ABSENT)
+ if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev))
cap->capabilities |= V4L2_CAP_TUNER;
if (video_is_registered(&v4l2->vbi_dev))
cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
@@ -2126,7 +2295,7 @@ static int em28xx_v4l2_open(struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
struct em28xx *dev = video_drvdata(filp);
- struct em28xx_v4l2 *v4l2 = dev->v4l2;
+ struct em28xx_v4l2 *v4l2;
enum v4l2_buf_type fh_type = 0;
int ret;
@@ -2143,13 +2312,19 @@ static int em28xx_v4l2_open(struct file *filp)
return -EINVAL;
}
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+
+ v4l2 = dev->v4l2;
+ if (!v4l2) {
+ mutex_unlock(&dev->lock);
+ return -ENODEV;
+ }
+
em28xx_videodbg("open dev=%s type=%s users=%d\n",
video_device_node_name(vdev), v4l2_type_names[fh_type],
v4l2->users);
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
-
ret = v4l2_fh_open(filp);
if (ret) {
dev_err(&dev->intf->dev,
@@ -2549,7 +2724,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
}
hdl = &v4l2->ctrl_handler;
- v4l2_ctrl_handler_init(hdl, 8);
+ v4l2_ctrl_handler_init(hdl, 9);
v4l2->v4l2_dev.ctrl_handler = hdl;
if (dev->is_webcam)
@@ -2675,7 +2850,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
}
/* set default norm */
- v4l2->norm = V4L2_STD_PAL;
+ v4l2->norm = -1;
v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT;
@@ -2755,10 +2930,9 @@ static int em28xx_v4l2_init(struct em28xx *dev)
V4L2_CAP_STREAMING;
if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE)
v4l2->vdev.device_caps |= V4L2_CAP_AUDIO;
- if (dev->tuner_type != TUNER_ABSENT)
+ if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev))
v4l2->vdev.device_caps |= V4L2_CAP_TUNER;
-
/* disable inapplicable ioctls */
if (dev->is_webcam) {
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD);
@@ -2767,7 +2941,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
} else {
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM);
}
- if (dev->tuner_type == TUNER_ABSENT) {
+ if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) {
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER);
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER);
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY);
@@ -2778,6 +2952,9 @@ static int em28xx_v4l2_init(struct em28xx *dev)
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO);
}
+ if (dev->chip_id == CHIP_ID_EM2828X || dev->board.decoder == EM28XX_BUILTIN)
+ v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_ENUM_FRAMESIZES);
+
/* register v4l2 video video_device */
ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO,
video_nr[dev->devno]);
@@ -2796,12 +2973,12 @@ static int em28xx_v4l2_init(struct em28xx *dev)
v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock;
v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
- if (dev->tuner_type != TUNER_ABSENT)
+ if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0)
v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER;
/* disable inapplicable ioctls */
v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM);
- if (dev->tuner_type == TUNER_ABSENT) {
+ if ((v4l2->vbi_dev.device_caps & V4L2_CAP_TUNER) == 0) {
v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER);
v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER);
v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index f3449c240d21..21c912403efc 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -144,6 +144,11 @@
#define EM2860_BOARD_MYGICA_IGRABBER 105
#define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106
#define EM2860_BOARD_MYGICA_UTV3 107
+#define EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 108
+#define EM2828X_BOARD_HAUPPAUGE_935_V2 109
+#define EM2828X_BOARD_HAUPPAUGE_955_V2 110
+#define EM2828X_BOARD_HAUPPAUGE_975_V2 111
+#define EM28178_BOARD_PCTV_461E_V3 112
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -425,8 +430,14 @@ enum em28xx_decoder {
EM28XX_NODECODER = 0,
EM28XX_TVP5150,
EM28XX_SAA711X,
+ EM28XX_BUILTIN,
};
+/* Built in decoder capture options */
+#define EM2828X_COMPOSITE 0
+#define EM2828X_SVIDEO 1
+#define EM2828X_TELEVISION 2
+
enum em28xx_sensor {
EM28XX_NOSENSOR = 0,
EM28XX_MT9V011,
@@ -469,6 +480,12 @@ struct em28xx_button {
bool inverted;
};
+enum em2828x_media_pads {
+ EM2828X_PAD_INPUT,
+ EM2828X_PAD_VID_OUT,
+ EM2828X_NUM_PADS
+};
+
struct em28xx_board {
char *name;
int vchannels;
@@ -593,6 +610,7 @@ struct em28xx_v4l2 {
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad video_pad, vbi_pad;
+ struct media_pad decoder_pads[EM2828X_NUM_PADS];
struct media_entity *decoder;
#endif
};
@@ -752,6 +770,8 @@ struct em28xx {
char *buf, int len);
int (*em28xx_read_reg_req)(struct em28xx *dev, u8 req, u16 reg);
+ int (*em28xx_set_analog_freq)(struct em28xx *dev, u32 freq);
+
enum em28xx_mode mode;
// Button state polling
@@ -763,6 +783,7 @@ struct em28xx {
// Snapshot button input device
char snapshot_button_path[30]; // path of the input dev
struct input_dev *sbutton_input_dev;
+ int analog_xfer_mode;
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device *media_dev;
@@ -811,6 +832,8 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
int em28xx_audio_analog_set(struct em28xx *dev);
int em28xx_audio_setup(struct em28xx *dev);
+void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin);
+
const struct em28xx_led *em28xx_find_led(struct em28xx *dev,
enum em28xx_led_role role);
int em28xx_capture_start(struct em28xx *dev, int start);
diff --git a/drivers/media/usb/go7007/go7007-loader.c b/drivers/media/usb/go7007/go7007-loader.c
index 243aa0ad074c..5747e6e4f2d6 100644
--- a/drivers/media/usb/go7007/go7007-loader.c
+++ b/drivers/media/usb/go7007/go7007-loader.c
@@ -41,9 +41,7 @@ static int go7007_loader_probe(struct usb_interface *interface,
int ret;
int i;
- usbdev = usb_get_dev(interface_to_usbdev(interface));
- if (!usbdev)
- goto failed2;
+ usbdev = interface_to_usbdev(interface);
if (usbdev->descriptor.bNumConfigurations != 1) {
dev_err(&interface->dev, "can't handle multiple config\n");
@@ -96,7 +94,6 @@ static int go7007_loader_probe(struct usb_interface *interface,
return 0;
failed2:
- usb_put_dev(usbdev);
dev_err(&interface->dev, "probe failed\n");
return -ENODEV;
}
@@ -104,7 +101,6 @@ failed2:
static void go7007_loader_disconnect(struct usb_interface *interface)
{
dev_info(&interface->dev, "disconnect\n");
- usb_put_dev(interface_to_usbdev(interface));
usb_set_intfdata(interface, NULL);
}
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index 3fc15d16df8e..f3d3f441c851 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1701,19 +1701,6 @@ int gspca_resume(struct usb_interface *intf)
EXPORT_SYMBOL(gspca_resume);
#endif
-/* -- module insert / remove -- */
-static int __init gspca_init(void)
-{
- pr_info("v" GSPCA_VERSION " registered\n");
- return 0;
-}
-static void __exit gspca_exit(void)
-{
-}
-
-module_init(gspca_init);
-module_exit(gspca_exit);
-
module_param_named(debug, gspca_debug, int, 0644);
MODULE_PARM_DESC(debug,
"1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo");
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index 94d356fba612..a15829a60e88 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -1485,7 +1485,7 @@ static int hackrf_probe(struct usb_interface *intf,
if (ret) {
dev_err(dev->dev,
"Failed to register as video device (%d)\n", ret);
- goto err_v4l2_device_unregister;
+ goto err_v4l2_device_put;
}
dev_info(dev->dev, "Registered as %s\n",
video_device_node_name(&dev->rx_vdev));
@@ -1513,8 +1513,9 @@ static int hackrf_probe(struct usb_interface *intf,
return 0;
err_video_unregister_device_rx:
video_unregister_device(&dev->rx_vdev);
-err_v4l2_device_unregister:
- v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_device_put:
+ v4l2_device_put(&dev->v4l2_dev);
+ return ret;
err_v4l2_ctrl_handler_free_tx:
v4l2_ctrl_handler_free(&dev->tx_ctrl_handler);
err_v4l2_ctrl_handler_free_rx:
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
index c4bfadbe63c9..6c4facf4f41a 100644
--- a/drivers/media/usb/usbtv/usbtv-core.c
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -91,7 +91,7 @@ static int usbtv_probe(struct usb_interface *intf,
if (usbtv == NULL)
return -ENOMEM;
usbtv->dev = dev;
- usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
+ usbtv->udev = interface_to_usbdev(intf);
usbtv->iso_size = size;
@@ -119,7 +119,6 @@ usbtv_audio_fail:
usbtv_video_fail:
usb_set_intfdata(intf, NULL);
- usb_put_dev(usbtv->udev);
kfree(usbtv);
return ret;
@@ -137,7 +136,6 @@ static void usbtv_disconnect(struct usb_interface *intf)
usbtv_audio_free(usbtv);
usbtv_video_free(usbtv);
- usb_put_dev(usbtv->udev);
usbtv->udev = NULL;
/* the usbtv structure will be deallocated when v4l2 will be
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index b0ca81d924b6..31b4ac3b48c1 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2033,7 +2033,7 @@ int uvc_register_video_device(struct uvc_device *dev,
int ret;
/* Initialize the video buffers queue. */
- ret = uvc_queue_init(queue, type);
+ ret = uvc_queue_init(stream, queue, type);
if (ret)
return ret;
@@ -3167,7 +3167,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) },
- /* Intel D410/ASR depth camera */
+ /* Intel Realsense D410/ASR depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3176,7 +3176,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D415/ASRC depth camera */
+ /* Intel Realsense D415/ASRC depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3185,7 +3185,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D430/AWG depth camera */
+ /* Intel Realsense D430/AWG depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3194,7 +3194,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel RealSense D4M */
+ /* Intel Realsense RealSense D4M */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3203,7 +3203,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D435/AWGC depth camera */
+ /* Intel Realsense D435/AWGC depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3212,7 +3212,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D435i depth camera */
+ /* Intel Realsense D435i depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3221,7 +3221,16 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D405 Depth Camera */
+ /* Intel Realsense D555 Depth Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x8086,
+ .idProduct = 0x0b56,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
+ .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
+ /* Intel Realsense D405 Depth Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3230,7 +3239,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D455 Depth Camera */
+ /* Intel Realsense D455 Depth Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3239,7 +3248,7 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
- /* Intel D421 Depth Module */
+ /* Intel Realsense D421 Depth Module */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
@@ -3248,6 +3257,15 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
+ /* Intel Realsense D436 Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x8086,
+ .idProduct = 0x1156,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = UVC_PC_PROTOCOL_UNDEFINED,
+ .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
index 43cda5e760a3..bb3e13c0d5a6 100644
--- a/drivers/media/usb/uvc/uvc_isight.c
+++ b/drivers/media/usb/uvc/uvc_isight.c
@@ -41,7 +41,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
0xde, 0xad, 0xfa, 0xce
};
- struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ struct uvc_streaming *stream = queue->stream;
unsigned int maxlen, nbytes;
u8 *mem;
int is_header = 0;
diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c
index 0a906ae3f971..9de8aba1229e 100644
--- a/drivers/media/usb/uvc/uvc_metadata.c
+++ b/drivers/media/usb/uvc/uvc_metadata.c
@@ -50,7 +50,7 @@ static int uvc_meta_v4l2_get_format(struct file *file, void *priv,
return -EINVAL;
fmt->dataformat = stream->meta.format;
- fmt->buffersize = UVC_METADATA_BUF_SIZE;
+ fmt->buffersize = stream->meta.buffersize;
return 0;
}
@@ -63,6 +63,7 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *priv,
struct uvc_device *dev = stream->dev;
struct v4l2_meta_format *fmt = &format->fmt.meta;
u32 fmeta = V4L2_META_FMT_UVC;
+ u32 buffersize;
if (format->type != vfh->vdev->queue->type)
return -EINVAL;
@@ -74,10 +75,12 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *priv,
}
}
+ buffersize = max(UVC_METADATA_BUF_MIN_SIZE, fmt->buffersize);
+
memset(fmt, 0, sizeof(*fmt));
fmt->dataformat = fmeta;
- fmt->buffersize = UVC_METADATA_BUF_SIZE;
+ fmt->buffersize = buffersize;
return 0;
}
@@ -103,6 +106,7 @@ static int uvc_meta_v4l2_set_format(struct file *file, void *priv,
return -EBUSY;
stream->meta.format = fmt->dataformat;
+ stream->meta.buffersize = fmt->buffersize;
return 0;
}
@@ -229,6 +233,7 @@ int uvc_meta_register(struct uvc_streaming *stream)
struct uvc_video_queue *queue = &stream->meta.queue;
stream->meta.format = V4L2_META_FMT_UVC;
+ stream->meta.buffersize = UVC_METADATA_BUF_MIN_SIZE;
return uvc_register_video_device(dev, stream, queue,
V4L2_BUF_TYPE_META_CAPTURE,
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 8b8f44b4a045..3c002c8f442f 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -78,16 +78,15 @@ static int uvc_queue_setup(struct vb2_queue *vq,
unsigned int sizes[], struct device *alloc_devs[])
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
- struct uvc_streaming *stream;
+ struct uvc_streaming *stream = queue->stream;
unsigned int size;
switch (vq->type) {
case V4L2_BUF_TYPE_META_CAPTURE:
- size = UVC_METADATA_BUF_SIZE;
+ size = stream->meta.buffersize;
break;
default:
- stream = uvc_queue_to_stream(queue);
size = stream->ctrl.dwMaxVideoFrameSize;
break;
}
@@ -113,7 +112,7 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
- uvc_dbg(uvc_queue_to_stream(queue)->dev, CAPTURE,
+ uvc_dbg(queue->stream->dev, CAPTURE,
"[E] Bytes used out of bounds\n");
return -EINVAL;
}
@@ -160,17 +159,16 @@ static void uvc_buffer_finish(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
- struct uvc_streaming *stream = uvc_queue_to_stream(queue);
struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf);
if (vb->state == VB2_BUF_STATE_DONE)
- uvc_video_clock_update(stream, vbuf, buf);
+ uvc_video_clock_update(queue->stream, vbuf, buf);
}
static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
- struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ struct uvc_streaming *stream = queue->stream;
int ret;
lockdep_assert_irqs_enabled();
@@ -197,11 +195,11 @@ err_buffers:
static void uvc_stop_streaming_video(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
- struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ struct uvc_streaming *stream = queue->stream;
lockdep_assert_irqs_enabled();
- uvc_video_stop_streaming(uvc_queue_to_stream(queue));
+ uvc_video_stop_streaming(stream);
uvc_pm_put(stream->dev);
@@ -238,12 +236,14 @@ static const struct vb2_ops uvc_meta_queue_qops = {
.stop_streaming = uvc_stop_streaming_meta,
};
-int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue,
+ enum v4l2_buf_type type)
{
int ret;
+ queue->stream = stream;
queue->queue.type = type;
- queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.mem_ops = &vb2_vmalloc_memops;
@@ -256,7 +256,6 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
queue->queue.ops = &uvc_meta_queue_qops;
break;
default:
- queue->queue.io_modes |= VB2_DMABUF;
queue->queue.ops = &uvc_queue_qops;
break;
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index d5860661c115..cda1697204ea 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -230,6 +230,9 @@ static u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
case V4L2_PIX_FMT_M420:
return frame->wWidth;
+ case V4L2_PIX_FMT_P010:
+ return frame->wWidth * 2;
+
default:
return format->bpp * frame->wWidth / 8;
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 8480d65ecb85..0a0c01b2420f 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -331,6 +331,7 @@ struct uvc_buffer {
#define UVC_QUEUE_DISCONNECTED (1 << 0)
struct uvc_video_queue {
+ struct uvc_streaming *stream;
struct video_device vdev;
struct vb2_queue queue;
struct mutex mutex; /*
@@ -409,7 +410,7 @@ struct uvc_stats_stream {
unsigned int max_sof; /* Maximum STC.SOF value */
};
-#define UVC_METADATA_BUF_SIZE 10240
+#define UVC_METADATA_BUF_MIN_SIZE 10240
/**
* struct uvc_copy_op: Context structure to schedule asynchronous memcpy
@@ -482,6 +483,7 @@ struct uvc_streaming {
struct {
struct uvc_video_queue queue;
u32 format;
+ u32 buffersize;
} meta;
/* Context data used by the bulk completion handler. */
@@ -691,7 +693,8 @@ do { \
struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
-int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
+int uvc_queue_init(struct uvc_streaming *stream, struct uvc_video_queue *queue,
+ enum v4l2_buf_type type);
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
@@ -702,12 +705,6 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
return vb2_is_streaming(&queue->queue);
}
-static inline struct uvc_streaming *
-uvc_queue_to_stream(struct uvc_video_queue *queue)
-{
- return container_of(queue, struct uvc_streaming, queue);
-}
-
/* V4L2 interface */
extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
extern const struct v4l2_file_operations uvc_fops;
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 03daa8c4ff7a..77f3298821b5 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -465,8 +465,15 @@ static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
enum v4l2_mbus_type mbus_type;
int rval;
+ /*
+ * Return -EPROBE_DEFER if there's no endpoint -- in case the endpoint's
+ * origin is a software node, it may be that the endpoint has not been
+ * instantiated yet, but will be with probing of another driver. This is
+ * the case with the IPU bridge; once we have no such cases left, return
+ * another error such as -EINVAL.
+ */
if (!fwnode)
- return -EINVAL;
+ return -EPROBE_DEFER;
pr_debug("===== begin parsing endpoint %pfw\n", fwnode);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 32e6f60e26c7..831c69c958b8 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -2545,6 +2545,125 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
}
EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper);
+int __v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct media_pad *local_sink_pad;
+ struct v4l2_subdev_route *route;
+ struct device *dev = sd->dev;
+ int ret = 0;
+
+ lockdep_assert_held(state->lock);
+
+ if (WARN_ON(!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)))
+ return -EINVAL;
+
+ /* Iterate over sink pads */
+ media_entity_for_each_pad(&sd->entity, local_sink_pad) {
+ struct v4l2_mbus_frame_desc source_fd;
+ bool have_source_fd = false;
+
+ if (!(local_sink_pad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ /*
+ * Copy frame desc entries for the streams going from the sink
+ * pad to the requested pad
+ */
+ for_each_active_route(&state->routing, route) {
+ struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
+ struct media_pad *remote_source_pad;
+ struct v4l2_subdev *remote_sd;
+
+ if (route->source_pad != pad ||
+ route->sink_pad != local_sink_pad->index)
+ continue;
+
+ if (!have_source_fd) {
+ remote_source_pad = media_pad_remote_pad_unique(local_sink_pad);
+ if (IS_ERR(remote_source_pad)) {
+ dev_dbg(dev, "Failed to find remote pad for sink pad %u\n",
+ local_sink_pad->index);
+ return PTR_ERR(remote_source_pad);
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_source_pad->entity);
+ if (!remote_sd)
+ return -EINVAL;
+
+ ret = v4l2_subdev_call(remote_sd, pad,
+ get_frame_desc,
+ remote_source_pad->index,
+ &source_fd);
+ if (ret) {
+ dev_err(dev,
+ "Failed to get frame desc from remote subdev %s\n",
+ remote_sd->name);
+ return ret;
+ }
+
+ have_source_fd = true;
+
+ if (fd->num_entries == 0) {
+ fd->type = source_fd.type;
+ } else if (fd->type != source_fd.type) {
+ dev_err(dev,
+ "Frame desc type mismatch: %u != %u\n",
+ fd->type, source_fd.type);
+ return -EPIPE;
+ }
+ }
+
+ for (unsigned int i = 0; i < source_fd.num_entries; i++) {
+ if (source_fd.entry[i].stream == route->sink_stream) {
+ source_entry = &source_fd.entry[i];
+ break;
+ }
+ }
+
+ if (!source_entry) {
+ dev_dbg(dev,
+ "Failed to find stream %u from source frame desc\n",
+ route->sink_stream);
+ return -EPIPE;
+ }
+
+ if (fd->num_entries >= V4L2_FRAME_DESC_ENTRY_MAX) {
+ dev_dbg(dev, "Frame desc entry limit reached\n");
+ return -ENOSPC;
+ }
+
+ fd->entry[fd->num_entries] = *source_entry;
+
+ fd->entry[fd->num_entries].stream = route->source_stream;
+
+ fd->num_entries++;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_get_frame_desc_passthrough);
+
+int v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ ret = __v4l2_subdev_get_frame_desc_passthrough(sd, state, pad, fd);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_frame_desc_passthrough);
+
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
index a496075c0d2a..a77ed32abe55 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -197,6 +197,7 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client)
cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata;
cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data);
cells[2].name = "tps68470-gpio";
+ cells[2].swnode = board_data->tps68470_gpio_swnode;
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
diff --git a/drivers/platform/x86/intel/int3472/tps68470.h b/drivers/platform/x86/intel/int3472/tps68470.h
index 35915e701593..3bbaade96c57 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.h
+++ b/drivers/platform/x86/intel/int3472/tps68470.h
@@ -17,6 +17,7 @@ struct tps68470_regulator_platform_data;
struct int3472_tps68470_board_data {
const char *dev_name;
const struct tps68470_regulator_platform_data *tps68470_regulator_pdata;
+ const struct software_node *tps68470_gpio_swnode;
unsigned int n_gpiod_lookups;
struct gpiod_lookup_table *tps68470_gpio_lookup_tables[];
};
diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
index 71357a036292..c1ddbf9a82c0 100644
--- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c
+++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
@@ -12,6 +12,7 @@
#include <linux/dmi.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/tps68470.h>
+#include <linux/property.h>
#include <linux/regulator/machine.h>
#include "tps68470.h"
@@ -150,8 +151,6 @@ static const struct regulator_init_data dell_7212_tps68470_core_reg_init_data =
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
- .num_consumer_supplies = 0,
- .consumer_supplies = NULL,
};
static const struct regulator_init_data dell_7212_tps68470_ana_reg_init_data = {
@@ -161,8 +160,6 @@ static const struct regulator_init_data dell_7212_tps68470_ana_reg_init_data = {
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
- .num_consumer_supplies = 0,
- .consumer_supplies = NULL,
};
static const struct regulator_init_data dell_7212_tps68470_vcm_reg_init_data = {
@@ -172,8 +169,6 @@ static const struct regulator_init_data dell_7212_tps68470_vcm_reg_init_data = {
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
- .num_consumer_supplies = 0,
- .consumer_supplies = NULL,
};
static const struct regulator_init_data dell_7212_tps68470_vio_reg_init_data = {
@@ -183,8 +178,6 @@ static const struct regulator_init_data dell_7212_tps68470_vio_reg_init_data = {
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
- .num_consumer_supplies = 0,
- .consumer_supplies = NULL,
};
static const struct regulator_init_data dell_7212_tps68470_vsio_reg_init_data = {
@@ -232,6 +225,70 @@ static const struct tps68470_regulator_platform_data dell_7212_tps68470_pdata =
},
};
+/* Settings for MSI Prestige 14 AI+ Evo C2VMG laptop. */
+static struct regulator_consumer_supply ovti5675_avdd_consumer_supplies[] = {
+ REGULATOR_SUPPLY("avdd", "i2c-OVTI5675:00"),
+};
+
+static struct regulator_consumer_supply ovti5675_dovdd_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dovdd", "i2c-OVTI5675:00"),
+};
+
+static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"),
+};
+
+static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_data = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti5675_dvdd_consumer_supplies),
+ .consumer_supplies = ovti5675_dvdd_consumer_supplies,
+};
+
+static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_data = {
+ .constraints = {
+ .min_uV = 2815200,
+ .max_uV = 2815200,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti5675_avdd_consumer_supplies),
+ .consumer_supplies = ovti5675_avdd_consumer_supplies,
+};
+
+static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti5675_dovdd_consumer_supplies),
+ .consumer_supplies = ovti5675_dovdd_consumer_supplies,
+};
+
+static const struct tps68470_regulator_platform_data msi_p14_ai_evo_tps68470_pdata = {
+ .reg_init_data = {
+ [TPS68470_CORE] = &msi_p14_ai_evo_tps68470_core_reg_init_data,
+ [TPS68470_ANA] = &msi_p14_ai_evo_tps68470_ana_reg_init_data,
+ [TPS68470_VIO] = &msi_p14_ai_evo_tps68470_vio_reg_init_data,
+ [TPS68470_VSIO] = &msi_p14_ai_evo_tps68470_vsio_reg_init_data,
+ },
+};
+
static struct gpiod_lookup_table surface_go_int347a_gpios = {
.dev_id = "i2c-INT347A:00",
.table = {
@@ -258,6 +315,23 @@ static struct gpiod_lookup_table dell_7212_int3479_gpios = {
}
};
+static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = {
+ .dev_id = "i2c-OVTI5675:00",
+ .table = {
+ GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW),
+ { }
+ }
+};
+
+static const struct property_entry msi_p14_ai_evo_gpio_props[] = {
+ PROPERTY_ENTRY_BOOL("daisy-chain-enable"),
+ { }
+};
+
+static const struct software_node msi_p14_ai_evo_tps68470_gpio_swnode = {
+ .properties = msi_p14_ai_evo_gpio_props,
+};
+
static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = {
.dev_name = "i2c-INT3472:05",
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
@@ -287,6 +361,16 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data =
},
};
+static const struct int3472_tps68470_board_data msi_p14_ai_evo_tps68470_board_data = {
+ .dev_name = "i2c-INT3472:06",
+ .tps68470_regulator_pdata = &msi_p14_ai_evo_tps68470_pdata,
+ .tps68470_gpio_swnode = &msi_p14_ai_evo_tps68470_gpio_swnode,
+ .n_gpiod_lookups = 1,
+ .tps68470_gpio_lookup_tables = {
+ &msi_p14_ai_evo_ovti5675_gpios,
+ },
+};
+
static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
{
.matches = {
@@ -316,6 +400,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
},
.driver_data = (void *)&dell_7212_tps68470_board_data,
},
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"),
+ },
+ .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data,
+ },
{ }
};
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index ab250c89cd4d..1aa31bddf970 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -34,8 +34,6 @@ source "drivers/staging/media/max96712/Kconfig"
source "drivers/staging/media/meson/vdec/Kconfig"
-source "drivers/staging/media/starfive/Kconfig"
-
source "drivers/staging/media/sunxi/Kconfig"
source "drivers/staging/media/tegra-video/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 4a073938b2b2..6f78b0edde1e 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -4,7 +4,6 @@ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
obj-$(CONFIG_VIDEO_MAX96712) += max96712/
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
-obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index a7f22de1c889..50366bf10f32 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -1356,6 +1356,10 @@ static int atomisp_s_parm(struct file *file, void *fh,
static long atomisp_vidioc_default(struct file *file, void *fh,
bool valid_prio, unsigned int cmd, void *arg)
{
+ /* Disable all private IOCTLs for now! */
+ if (cmd)
+ return -EINVAL;
+
struct video_device *vdev = video_devdata(file);
struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd;
int err;
diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c
index 607992100baf..014d0c6f0a8b 100644
--- a/drivers/staging/media/av7110/av7110.c
+++ b/drivers/staging/media/av7110/av7110.c
@@ -460,7 +460,7 @@ static void gpioirq(struct tasklet_struct *t)
if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
pr_err("%s(): saa7146_wait_for_debi_done timed out\n", __func__);
- BUG(); /* maybe we should try resetting the debi? */
+ return;
}
spin_lock(&av7110->debilock);
diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
index 68b3979ba5f2..fdae467fd7ab 100644
--- a/drivers/staging/media/av7110/av7110_ir.c
+++ b/drivers/staging/media/av7110/av7110_ir.c
@@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110)
void av7110_ir_exit(struct av7110 *av7110)
{
rc_unregister_device(av7110->ir.rcdev);
+ rc_free_device(av7110->ir.rcdev);
}
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index fd7e37d803e7..ef22a083f8eb 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -97,9 +97,6 @@ struct csi_priv {
/* the mipi virtual channel number at link validate */
int vc_num;
- /* media bus config of the upstream subdevice CSI is receiving from */
- struct v4l2_mbus_config mbus_cfg;
-
spinlock_t irqlock; /* protect eof_irq handler */
struct timer_list eof_timeout_timer;
int eof_irq;
@@ -403,7 +400,8 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv,
}
/* init the SMFC IDMAC channel */
-static int csi_idmac_setup_channel(struct csi_priv *priv)
+static int csi_idmac_setup_channel(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
{
struct imx_media_video_dev *vdev = priv->vdev;
const struct imx_media_pixfmt *incc;
@@ -432,7 +430,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
image.phys0 = phys[0];
image.phys1 = phys[1];
- passthrough = requires_passthrough(&priv->mbus_cfg, infmt, incc);
+ passthrough = requires_passthrough(mbus_cfg, infmt, incc);
passthrough_cycles = 1;
/*
@@ -572,11 +570,12 @@ static void csi_idmac_unsetup(struct csi_priv *priv,
csi_idmac_unsetup_vb2_buf(priv, state);
}
-static int csi_idmac_setup(struct csi_priv *priv)
+static int csi_idmac_setup(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
{
int ret;
- ret = csi_idmac_setup_channel(priv);
+ ret = csi_idmac_setup_channel(priv, mbus_cfg);
if (ret)
return ret;
@@ -595,7 +594,8 @@ static int csi_idmac_setup(struct csi_priv *priv)
return 0;
}
-static int csi_idmac_start(struct csi_priv *priv)
+static int csi_idmac_start(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
{
struct imx_media_video_dev *vdev = priv->vdev;
int ret;
@@ -619,7 +619,7 @@ static int csi_idmac_start(struct csi_priv *priv)
priv->last_eof = false;
priv->nfb4eof = false;
- ret = csi_idmac_setup(priv);
+ ret = csi_idmac_setup(priv, mbus_cfg);
if (ret) {
v4l2_err(&priv->sd, "csi_idmac_setup failed: %d\n", ret);
goto out_free_dma_buf;
@@ -701,7 +701,8 @@ static void csi_idmac_stop(struct csi_priv *priv)
}
/* Update the CSI whole sensor and active windows */
-static int csi_setup(struct csi_priv *priv)
+static int csi_setup(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
{
struct v4l2_mbus_framefmt *infmt, *outfmt;
const struct imx_media_pixfmt *incc;
@@ -719,7 +720,7 @@ static int csi_setup(struct csi_priv *priv)
* if cycles is set, we need to handle this over multiple cycles as
* generic/bayer data
*/
- if (is_parallel_bus(&priv->mbus_cfg) && incc->cycles) {
+ if (is_parallel_bus(mbus_cfg) && incc->cycles) {
if_fmt.width *= incc->cycles;
crop.width *= incc->cycles;
}
@@ -730,7 +731,7 @@ static int csi_setup(struct csi_priv *priv)
priv->crop.width == 2 * priv->compose.width,
priv->crop.height == 2 * priv->compose.height);
- ipu_csi_init_interface(priv->csi, &priv->mbus_cfg, &if_fmt, outfmt);
+ ipu_csi_init_interface(priv->csi, mbus_cfg, &if_fmt, outfmt);
ipu_csi_set_dest(priv->csi, priv->dest);
@@ -743,14 +744,46 @@ static int csi_setup(struct csi_priv *priv)
return 0;
}
+static void csi_set_src(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
+{
+ bool is_csi2;
+
+ is_csi2 = !is_parallel_bus(mbus_cfg);
+ if (is_csi2) {
+ /*
+ * NOTE! It seems the virtual channels from the mipi csi-2
+ * receiver are used only for routing by the video mux's,
+ * or for hard-wired routing to the CSI's. Once the stream
+ * enters the CSI's however, they are treated internally
+ * in the IPU as virtual channel 0.
+ */
+ ipu_csi_set_mipi_datatype(priv->csi, 0,
+ &priv->format_mbus[CSI_SINK_PAD]);
+ }
+
+ /* select either parallel or MIPI-CSI2 as input to CSI */
+ ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
+}
+
static int csi_start(struct csi_priv *priv)
{
+ struct v4l2_mbus_config mbus_cfg = { .type = 0 };
struct v4l2_fract *input_fi, *output_fi;
int ret;
+ ret = csi_get_upstream_mbus_config(priv, &mbus_cfg);
+ if (ret) {
+ v4l2_err(&priv->sd,
+ "failed to get upstream media bus configuration\n");
+ return ret;
+ }
+
input_fi = &priv->frame_interval[CSI_SINK_PAD];
output_fi = &priv->frame_interval[priv->active_output_pad];
+ csi_set_src(priv, &mbus_cfg);
+
/* start upstream */
ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1);
ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
@@ -758,7 +791,7 @@ static int csi_start(struct csi_priv *priv)
return ret;
/* Skip first few frames from a BT.656 source */
- if (priv->mbus_cfg.type == V4L2_MBUS_BT656) {
+ if (mbus_cfg.type == V4L2_MBUS_BT656) {
u32 delay_usec, bad_frames = 20;
delay_usec = DIV_ROUND_UP_ULL((u64)USEC_PER_SEC *
@@ -769,12 +802,12 @@ static int csi_start(struct csi_priv *priv)
}
if (priv->dest == IPU_CSI_DEST_IDMAC) {
- ret = csi_idmac_start(priv);
+ ret = csi_idmac_start(priv, &mbus_cfg);
if (ret)
goto stop_upstream;
}
- ret = csi_setup(priv);
+ ret = csi_setup(priv, &mbus_cfg);
if (ret)
goto idmac_stop;
@@ -1121,7 +1154,6 @@ static int csi_link_validate(struct v4l2_subdev *sd,
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_mbus_config mbus_cfg = { .type = 0 };
- bool is_csi2;
int ret;
ret = v4l2_subdev_link_validate_default(sd, link,
@@ -1136,26 +1168,6 @@ static int csi_link_validate(struct v4l2_subdev *sd,
return ret;
}
- mutex_lock(&priv->lock);
-
- priv->mbus_cfg = mbus_cfg;
- is_csi2 = !is_parallel_bus(&mbus_cfg);
- if (is_csi2) {
- /*
- * NOTE! It seems the virtual channels from the mipi csi-2
- * receiver are used only for routing by the video mux's,
- * or for hard-wired routing to the CSI's. Once the stream
- * enters the CSI's however, they are treated internally
- * in the IPU as virtual channel 0.
- */
- ipu_csi_set_mipi_datatype(priv->csi, 0,
- &priv->format_mbus[CSI_SINK_PAD]);
- }
-
- /* select either parallel or MIPI-CSI2 as input to CSI */
- ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
-
- mutex_unlock(&priv->lock);
return ret;
}
@@ -1509,7 +1521,7 @@ static void csi_try_fmt(struct csi_priv *priv,
}
imx_media_try_colorimetry(&sdformat->format,
- priv->active_output_pad == CSI_SRC_PAD_DIRECT);
+ priv->active_output_pad == CSI_SRC_PAD_DIRECT);
}
static int csi_set_fmt(struct v4l2_subdev *sd,
diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
index bb28daa4d713..7413551052ae 100644
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -57,9 +57,8 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
of_node_put(csi_np);
if (ret) {
/* unavailable or already added is not an error */
- if (ret == -ENODEV || ret == -EEXIST) {
+ if (ret == -ENODEV || ret == -EEXIST)
continue;
- }
/* other error, can't continue */
return ret;
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 86f2b30cb06c..58f1112e28e5 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -616,7 +616,7 @@ out:
}
static int vdic_link_setup(struct media_entity *entity,
- const struct media_pad *local,
+ const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index f095d9134fee..135daca7d55b 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -231,7 +231,7 @@ int imx_media_probe_complete(struct v4l2_async_notifier *notifier);
struct imx_media_dev *imx_media_dev_init(struct device *dev,
const struct media_device_ops *ops);
int imx_media_dev_notifier_register(struct imx_media_dev *imxmd,
- const struct v4l2_async_notifier_operations *ops);
+ const struct v4l2_async_notifier_operations *ops);
/* imx-media-fim.c */
struct imx_media_fim;
diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c
index bdf5a457752b..84c4d0bf027d 100644
--- a/drivers/staging/media/ipu3/ipu3.c
+++ b/drivers/staging/media/ipu3/ipu3.c
@@ -151,7 +151,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe)
/* May be called from atomic context */
static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
- int queue, unsigned int pipe)
+ int queue, unsigned int pipe)
{
unsigned int i;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
diff --git a/drivers/staging/media/ipu7/TODO b/drivers/staging/media/ipu7/TODO
index 7fbc37059adf..dc27bb6463da 100644
--- a/drivers/staging/media/ipu7/TODO
+++ b/drivers/staging/media/ipu7/TODO
@@ -19,10 +19,8 @@ staging directory.
IPU7 driver is expected to work with the common IPU module in future.
- Register definition cleanup
- Cleanup the register definitions - remove some unnecessary definitions
- remove 'U' suffix for hexadecimal and decimal values and add IPU7 prefix
- for IPU7 specific registers.
- Some ISYS IO sub-blocks register definitions are offset values from
- specific sub-block base, but it is not clear and well suited for driver
- to use, need to update the register definitions to make it more clear
- and readable.
+ Add IPU7 prefix for IPU7 specific registers and related macros. Some
+ ISYS IO sub-blocks register definitions are offset values from specific
+ sub-block base, but it is not clear and well suited for driver to use,
+ need to update the register definitions to make it more clear and
+ readable.
diff --git a/drivers/staging/media/starfive/Kconfig b/drivers/staging/media/starfive/Kconfig
deleted file mode 100644
index 34727cf56072..000000000000
--- a/drivers/staging/media/starfive/Kconfig
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-
-comment "StarFive media platform drivers"
-
-source "drivers/staging/media/starfive/camss/Kconfig"
diff --git a/drivers/staging/media/starfive/Makefile b/drivers/staging/media/starfive/Makefile
deleted file mode 100644
index 4639fa1bca32..000000000000
--- a/drivers/staging/media/starfive/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-y += camss/
diff --git a/drivers/staging/media/starfive/camss/Kconfig b/drivers/staging/media/starfive/camss/Kconfig
deleted file mode 100644
index 9ea5708fe409..000000000000
--- a/drivers/staging/media/starfive/camss/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config VIDEO_STARFIVE_CAMSS
- tristate "Starfive Camera Subsystem driver"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV && OF
- depends on HAS_DMA
- depends on PM
- depends on ARCH_STARFIVE || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_FWNODE
- help
- Enable this to support for the Starfive Camera subsystem
- found on Starfive JH7110 SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called starfive-camss.
diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile
deleted file mode 100644
index 005790202e7b..000000000000
--- a/drivers/staging/media/starfive/camss/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for StarFive Camera Subsystem driver
-#
-
-starfive-camss-objs += \
- stf-camss.o \
- stf-capture.o \
- stf-isp.o \
- stf-isp-hw-ops.o \
- stf-video.o
-
-obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o
diff --git a/drivers/staging/media/starfive/camss/TODO.txt b/drivers/staging/media/starfive/camss/TODO.txt
deleted file mode 100644
index 7d459f4f09d0..000000000000
--- a/drivers/staging/media/starfive/camss/TODO.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Unstaging requirements:
-- Add userspace support which demonstrates the ability to receive statistics and
- adapt hardware modules configuration accordingly;
-- Add documentation for description of the statistics data structures;
diff --git a/drivers/staging/media/starfive/camss/stf-camss.c b/drivers/staging/media/starfive/camss/stf-camss.c
deleted file mode 100644
index 259aaad010d2..000000000000
--- a/drivers/staging/media/starfive/camss/stf-camss.c
+++ /dev/null
@@ -1,438 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * stf_camss.c
- *
- * Starfive Camera Subsystem driver
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- *
- * Author: Jack Zhu <jack.zhu@starfivetech.com>
- * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
- *
- */
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-mc.h>
-
-#include "stf-camss.h"
-
-static const char * const stfcamss_clocks[] = {
- "wrapper_clk_c",
- "ispcore_2x",
- "isp_axi",
-};
-
-static const char * const stfcamss_resets[] = {
- "wrapper_p",
- "wrapper_c",
- "axiwr",
- "isp_top_n",
- "isp_top_axi",
-};
-
-static const struct stf_isr_data stf_isrs[] = {
- {"wr_irq", stf_wr_irq_handler},
- {"isp_irq", stf_isp_irq_handler},
- {"line_irq", stf_line_irq_handler},
-};
-
-static int stfcamss_get_mem_res(struct stfcamss *stfcamss)
-{
- struct platform_device *pdev = to_platform_device(stfcamss->dev);
-
- stfcamss->syscon_base =
- devm_platform_ioremap_resource_byname(pdev, "syscon");
- if (IS_ERR(stfcamss->syscon_base))
- return PTR_ERR(stfcamss->syscon_base);
-
- stfcamss->isp_base = devm_platform_ioremap_resource_byname(pdev, "isp");
- if (IS_ERR(stfcamss->isp_base))
- return PTR_ERR(stfcamss->isp_base);
-
- return 0;
-}
-
-/*
- * stfcamss_of_parse_endpoint_node - Parse port endpoint node
- * @dev: Device
- * @node: Device node to be parsed
- * @csd: Parsed data from port endpoint node
- *
- * Return 0 on success or a negative error code on failure
- */
-static int stfcamss_of_parse_endpoint_node(struct stfcamss *stfcamss,
- struct device_node *node,
- struct stfcamss_async_subdev *csd)
-{
- struct v4l2_fwnode_endpoint vep = { { 0 } };
- int ret;
-
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
- if (ret) {
- dev_err(stfcamss->dev, "endpoint not defined at %pOF\n", node);
- return ret;
- }
-
- csd->port = vep.base.port;
-
- return 0;
-}
-
-/*
- * stfcamss_of_parse_ports - Parse ports node
- * @stfcamss: STFCAMSS device
- *
- * Return number of "port" nodes found in "ports" node
- */
-static int stfcamss_of_parse_ports(struct stfcamss *stfcamss)
-{
- struct device_node *node = NULL;
- int ret, num_subdevs = 0;
-
- for_each_endpoint_of_node(stfcamss->dev->of_node, node) {
- struct stfcamss_async_subdev *csd;
-
- if (!of_device_is_available(node))
- continue;
-
- csd = v4l2_async_nf_add_fwnode_remote(&stfcamss->notifier,
- of_fwnode_handle(node),
- struct stfcamss_async_subdev);
- if (IS_ERR(csd)) {
- ret = PTR_ERR(csd);
- dev_err(stfcamss->dev, "failed to add async notifier\n");
- goto err_cleanup;
- }
-
- ret = stfcamss_of_parse_endpoint_node(stfcamss, node, csd);
- if (ret)
- goto err_cleanup;
-
- num_subdevs++;
- }
-
- return num_subdevs;
-
-err_cleanup:
- of_node_put(node);
- return ret;
-}
-
-static int stfcamss_register_devs(struct stfcamss *stfcamss)
-{
- struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
- struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
- int ret;
-
- ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev);
- if (ret < 0) {
- dev_err(stfcamss->dev,
- "failed to register stf isp%d entity: %d\n", 0, ret);
- return ret;
- }
-
- ret = stf_capture_register(stfcamss, &stfcamss->v4l2_dev);
- if (ret < 0) {
- dev_err(stfcamss->dev,
- "failed to register capture: %d\n", ret);
- goto err_isp_unregister;
- }
-
- ret = media_create_pad_link(&isp_dev->subdev.entity, STF_ISP_PAD_SRC,
- &cap_yuv->video.vdev.entity, 0, 0);
- if (ret)
- goto err_cap_unregister;
-
- cap_yuv->video.source_subdev = &isp_dev->subdev;
-
- return ret;
-
-err_cap_unregister:
- stf_capture_unregister(stfcamss);
-err_isp_unregister:
- stf_isp_unregister(&stfcamss->isp_dev);
-
- return ret;
-}
-
-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);
-}
-
-static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asc)
-{
- struct stfcamss *stfcamss =
- container_of(async, struct stfcamss, notifier);
- struct stfcamss_async_subdev *csd =
- container_of(asc, struct stfcamss_async_subdev, asd);
- enum stf_port_num port = csd->port;
- struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
- struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW];
- struct media_pad *pad;
- int ret;
-
- if (port == STF_PORT_CSI2RX) {
- pad = &isp_dev->pads[STF_ISP_PAD_SINK];
- } else {
- dev_err(stfcamss->dev, "not support port %d\n", port);
- return -EPERM;
- }
-
- ret = v4l2_create_fwnode_links_to_pad(subdev, pad, 0);
- if (ret)
- return ret;
-
- ret = media_create_pad_link(&subdev->entity, 1,
- &cap_raw->video.vdev.entity, 0, 0);
- if (ret)
- return ret;
-
- isp_dev->source_subdev = subdev;
- cap_raw->video.source_subdev = subdev;
-
- return 0;
-}
-
-static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf)
-{
- struct stfcamss *stfcamss =
- container_of(ntf, struct stfcamss, notifier);
-
- return v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
-}
-
-static const struct v4l2_async_notifier_operations
-stfcamss_subdev_notifier_ops = {
- .bound = stfcamss_subdev_notifier_bound,
- .complete = stfcamss_subdev_notifier_complete,
-};
-
-static void stfcamss_mc_init(struct platform_device *pdev,
- struct stfcamss *stfcamss)
-{
- stfcamss->media_dev.dev = stfcamss->dev;
- strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem",
- sizeof(stfcamss->media_dev.model));
- media_device_init(&stfcamss->media_dev);
-
- stfcamss->v4l2_dev.mdev = &stfcamss->media_dev;
-}
-
-/*
- * stfcamss_probe - Probe STFCAMSS platform device
- * @pdev: Pointer to STFCAMSS platform device
- *
- * Return 0 on success or a negative error code on failure
- */
-static int stfcamss_probe(struct platform_device *pdev)
-{
- struct stfcamss *stfcamss;
- struct device *dev = &pdev->dev;
- int ret, num_subdevs;
- unsigned int i;
-
- stfcamss = devm_kzalloc(dev, sizeof(*stfcamss), GFP_KERNEL);
- if (!stfcamss)
- return -ENOMEM;
-
- stfcamss->dev = dev;
-
- for (i = 0; i < ARRAY_SIZE(stf_isrs); ++i) {
- int irq;
-
- irq = platform_get_irq(pdev, i);
- if (irq < 0)
- return irq;
-
- ret = devm_request_irq(stfcamss->dev, irq, stf_isrs[i].isr, 0,
- stf_isrs[i].name, stfcamss);
- if (ret) {
- dev_err(dev, "request irq failed: %d\n", ret);
- return ret;
- }
- }
-
- stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk);
- for (i = 0; i < stfcamss->nclks; ++i)
- stfcamss->sys_clk[i].id = stfcamss_clocks[i];
- ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk);
- if (ret) {
- dev_err(dev, "Failed to get clk controls\n");
- return ret;
- }
-
- stfcamss->nrsts = ARRAY_SIZE(stfcamss->sys_rst);
- for (i = 0; i < stfcamss->nrsts; ++i)
- stfcamss->sys_rst[i].id = stfcamss_resets[i];
- ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
- stfcamss->sys_rst);
- if (ret) {
- dev_err(dev, "Failed to get reset controls\n");
- return ret;
- }
-
- ret = stfcamss_get_mem_res(stfcamss);
- if (ret) {
- dev_err(dev, "Could not map registers\n");
- return ret;
- }
-
- platform_set_drvdata(pdev, stfcamss);
-
- v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev);
-
- num_subdevs = stfcamss_of_parse_ports(stfcamss);
- if (num_subdevs < 0) {
- ret = -ENODEV;
- dev_err(dev, "Failed to get sub devices: %d\n", ret);
- goto err_cleanup_notifier;
- }
-
- ret = stf_isp_init(stfcamss);
- if (ret < 0) {
- dev_err(dev, "Failed to init isp: %d\n", ret);
- goto err_cleanup_notifier;
- }
-
- stfcamss_mc_init(pdev, stfcamss);
-
- ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev);
- if (ret < 0) {
- dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
- goto err_cleanup_media_device;
- }
-
- ret = media_device_register(&stfcamss->media_dev);
- if (ret) {
- dev_err(dev, "Failed to register media device: %d\n", ret);
- goto err_unregister_device;
- }
-
- ret = stfcamss_register_devs(stfcamss);
- if (ret < 0) {
- dev_err(dev, "Failed to register subdevice: %d\n", ret);
- goto err_unregister_media_dev;
- }
-
- pm_runtime_enable(dev);
-
- stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops;
- ret = v4l2_async_nf_register(&stfcamss->notifier);
- if (ret) {
- dev_err(dev, "Failed to register async subdev nodes: %d\n",
- ret);
- pm_runtime_disable(dev);
- goto err_unregister_subdevs;
- }
-
- return 0;
-
-err_unregister_subdevs:
- stfcamss_unregister_devs(stfcamss);
-err_unregister_media_dev:
- media_device_unregister(&stfcamss->media_dev);
-err_unregister_device:
- v4l2_device_unregister(&stfcamss->v4l2_dev);
-err_cleanup_media_device:
- media_device_cleanup(&stfcamss->media_dev);
-err_cleanup_notifier:
- v4l2_async_nf_cleanup(&stfcamss->notifier);
- return ret;
-}
-
-/*
- * stfcamss_remove - Remove STFCAMSS platform device
- * @pdev: Pointer to STFCAMSS platform device
- */
-static void stfcamss_remove(struct platform_device *pdev)
-{
- struct stfcamss *stfcamss = platform_get_drvdata(pdev);
-
- stfcamss_unregister_devs(stfcamss);
- v4l2_device_unregister(&stfcamss->v4l2_dev);
- media_device_cleanup(&stfcamss->media_dev);
- v4l2_async_nf_cleanup(&stfcamss->notifier);
- pm_runtime_disable(&pdev->dev);
-}
-
-static const struct of_device_id stfcamss_of_match[] = {
- { .compatible = "starfive,jh7110-camss" },
- { /* sentinel */ },
-};
-
-MODULE_DEVICE_TABLE(of, stfcamss_of_match);
-
-static int __maybe_unused stfcamss_runtime_suspend(struct device *dev)
-{
- struct stfcamss *stfcamss = dev_get_drvdata(dev);
- int ret;
-
- ret = reset_control_bulk_assert(stfcamss->nrsts, stfcamss->sys_rst);
- if (ret) {
- dev_err(dev, "reset assert failed\n");
- return ret;
- }
-
- clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
-
- return 0;
-}
-
-static int __maybe_unused stfcamss_runtime_resume(struct device *dev)
-{
- struct stfcamss *stfcamss = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_bulk_prepare_enable(stfcamss->nclks, stfcamss->sys_clk);
- if (ret) {
- dev_err(dev, "clock prepare enable failed\n");
- return ret;
- }
-
- ret = reset_control_bulk_deassert(stfcamss->nrsts, stfcamss->sys_rst);
- if (ret < 0) {
- dev_err(dev, "cannot deassert resets\n");
- clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
- return ret;
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops stfcamss_pm_ops = {
- SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend,
- stfcamss_runtime_resume,
- NULL)
-};
-
-static struct platform_driver stfcamss_driver = {
- .probe = stfcamss_probe,
- .remove = stfcamss_remove,
- .driver = {
- .name = "starfive-camss",
- .pm = &stfcamss_pm_ops,
- .of_match_table = stfcamss_of_match,
- },
-};
-
-module_platform_driver(stfcamss_driver);
-
-MODULE_AUTHOR("Jack Zhu <jack.zhu@starfivetech.com>");
-MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
-MODULE_DESCRIPTION("StarFive Camera Subsystem driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/starfive/camss/stf-camss.h b/drivers/staging/media/starfive/camss/stf-camss.h
deleted file mode 100644
index e2b0cfb437bd..000000000000
--- a/drivers/staging/media/starfive/camss/stf-camss.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * stf_camss.h
- *
- * Starfive Camera Subsystem driver
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#ifndef STF_CAMSS_H
-#define STF_CAMSS_H
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/reset.h>
-#include <media/media-device.h>
-#include <media/media-entity.h>
-#include <media/v4l2-async.h>
-#include <media/v4l2-device.h>
-
-#include "stf-isp.h"
-#include "stf-capture.h"
-
-enum stf_port_num {
- STF_PORT_DVP = 0,
- STF_PORT_CSI2RX
-};
-
-enum stf_clk {
- STF_CLK_WRAPPER_CLK_C = 0,
- STF_CLK_ISPCORE_2X,
- STF_CLK_ISP_AXI,
- STF_CLK_NUM
-};
-
-enum stf_rst {
- STF_RST_WRAPPER_P = 0,
- STF_RST_WRAPPER_C,
- STF_RST_AXIWR,
- STF_RST_ISP_TOP_N,
- STF_RST_ISP_TOP_AXI,
- STF_RST_NUM
-};
-
-struct stf_isr_data {
- const char *name;
- irqreturn_t (*isr)(int irq, void *priv);
-};
-
-struct stfcamss {
- struct v4l2_device v4l2_dev;
- struct media_device media_dev;
- struct media_pipeline pipe;
- struct device *dev;
- struct stf_isp_dev isp_dev;
- struct stf_capture captures[STF_CAPTURE_NUM];
- struct v4l2_async_notifier notifier;
- void __iomem *syscon_base;
- void __iomem *isp_base;
- struct clk_bulk_data sys_clk[STF_CLK_NUM];
- int nclks;
- struct reset_control_bulk_data sys_rst[STF_RST_NUM];
- int nrsts;
-};
-
-struct stfcamss_async_subdev {
- struct v4l2_async_connection asd; /* must be first */
- enum stf_port_num port;
-};
-
-static inline u32 stf_isp_reg_read(struct stfcamss *stfcamss, u32 reg)
-{
- return ioread32(stfcamss->isp_base + reg);
-}
-
-static inline void stf_isp_reg_write(struct stfcamss *stfcamss,
- u32 reg, u32 val)
-{
- iowrite32(val, stfcamss->isp_base + reg);
-}
-
-static inline void stf_isp_reg_write_delay(struct stfcamss *stfcamss,
- u32 reg, u32 val, u32 delay)
-{
- iowrite32(val, stfcamss->isp_base + reg);
- usleep_range(1000 * delay, 1000 * delay + 100);
-}
-
-static inline void stf_isp_reg_set_bit(struct stfcamss *stfcamss,
- u32 reg, u32 mask, u32 val)
-{
- u32 value;
-
- value = ioread32(stfcamss->isp_base + reg) & ~mask;
- val &= mask;
- val |= value;
- iowrite32(val, stfcamss->isp_base + reg);
-}
-
-static inline void stf_isp_reg_set(struct stfcamss *stfcamss, u32 reg, u32 mask)
-{
- iowrite32(ioread32(stfcamss->isp_base + reg) | mask,
- stfcamss->isp_base + reg);
-}
-
-static inline u32 stf_syscon_reg_read(struct stfcamss *stfcamss, u32 reg)
-{
- return ioread32(stfcamss->syscon_base + reg);
-}
-
-static inline void stf_syscon_reg_write(struct stfcamss *stfcamss,
- u32 reg, u32 val)
-{
- iowrite32(val, stfcamss->syscon_base + reg);
-}
-
-static inline void stf_syscon_reg_set_bit(struct stfcamss *stfcamss,
- u32 reg, u32 bit_mask)
-{
- u32 value;
-
- value = ioread32(stfcamss->syscon_base + reg);
- iowrite32(value | bit_mask, stfcamss->syscon_base + reg);
-}
-
-static inline void stf_syscon_reg_clear_bit(struct stfcamss *stfcamss,
- u32 reg, u32 bit_mask)
-{
- u32 value;
-
- value = ioread32(stfcamss->syscon_base + reg);
- iowrite32(value & ~bit_mask, stfcamss->syscon_base + reg);
-}
-#endif /* STF_CAMSS_H */
diff --git a/drivers/staging/media/starfive/camss/stf-capture.c b/drivers/staging/media/starfive/camss/stf-capture.c
deleted file mode 100644
index e15d2e97eb0b..000000000000
--- a/drivers/staging/media/starfive/camss/stf-capture.c
+++ /dev/null
@@ -1,605 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * stf_capture.c
- *
- * StarFive Camera Subsystem - capture device
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#include "stf-camss.h"
-
-static const char * const stf_cap_names[] = {
- "capture_raw",
- "capture_yuv",
-};
-
-static const struct stfcamss_format_info stf_wr_fmts[] = {
- {
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .pixelformat = V4L2_PIX_FMT_SRGGB10,
- .planes = 1,
- .vsub = { 1 },
- .bpp = 16,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGRBG10,
- .planes = 1,
- .vsub = { 1 },
- .bpp = 16,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGBRG10,
- .planes = 1,
- .vsub = { 1 },
- .bpp = 16,
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .pixelformat = V4L2_PIX_FMT_SBGGR10,
- .planes = 1,
- .vsub = { 1 },
- .bpp = 16,
- },
-};
-
-static const struct stfcamss_format_info stf_isp_fmts[] = {
- {
- .code = MEDIA_BUS_FMT_YUYV8_1_5X8,
- .pixelformat = V4L2_PIX_FMT_NV12,
- .planes = 2,
- .vsub = { 1, 2 },
- .bpp = 8,
- },
-};
-
-static inline struct stf_capture *to_stf_capture(struct stfcamss_video *video)
-{
- return container_of(video, struct stf_capture, video);
-}
-
-static void stf_set_raw_addr(struct stfcamss *stfcamss, dma_addr_t addr)
-{
- stf_syscon_reg_write(stfcamss, VIN_START_ADDR_O, (long)addr);
- stf_syscon_reg_write(stfcamss, VIN_START_ADDR_N, (long)addr);
-}
-
-static void stf_set_yuv_addr(struct stfcamss *stfcamss,
- dma_addr_t y_addr, dma_addr_t uv_addr)
-{
- stf_isp_reg_write(stfcamss, ISP_REG_Y_PLANE_START_ADDR, y_addr);
- stf_isp_reg_write(stfcamss, ISP_REG_UV_PLANE_START_ADDR, uv_addr);
-}
-
-static void stf_init_addrs(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *output = &cap->buffers;
- dma_addr_t addr0, addr1;
-
- output->active_buf = 0;
-
- if (!output->buf[0])
- return;
-
- addr0 = output->buf[0]->addr[0];
- addr1 = output->buf[0]->addr[1];
-
- if (cap->type == STF_CAPTURE_RAW)
- stf_set_raw_addr(video->stfcamss, addr0);
- else if (cap->type == STF_CAPTURE_YUV)
- stf_set_yuv_addr(video->stfcamss, addr0, addr1);
-}
-
-static struct stfcamss_buffer *stf_buf_get_pending(struct stf_v_buf *output)
-{
- struct stfcamss_buffer *buffer = NULL;
-
- if (!list_empty(&output->pending_bufs)) {
- buffer = list_first_entry(&output->pending_bufs,
- struct stfcamss_buffer,
- queue);
- list_del(&buffer->queue);
- }
-
- return buffer;
-}
-
-static void stf_cap_s_cfg(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *output = &cap->buffers;
- unsigned long flags;
-
- spin_lock_irqsave(&output->lock, flags);
-
- output->state = STF_OUTPUT_IDLE;
- output->buf[0] = stf_buf_get_pending(output);
-
- if (!output->buf[0] && output->buf[1]) {
- output->buf[0] = output->buf[1];
- output->buf[1] = NULL;
- }
-
- if (output->buf[0])
- output->state = STF_OUTPUT_SINGLE;
-
- output->sequence = 0;
- stf_init_addrs(video);
-
- spin_unlock_irqrestore(&output->lock, flags);
-}
-
-static int stf_cap_s_cleanup(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *output = &cap->buffers;
- unsigned long flags;
-
- spin_lock_irqsave(&output->lock, flags);
-
- output->state = STF_OUTPUT_OFF;
-
- spin_unlock_irqrestore(&output->lock, flags);
-
- return 0;
-}
-
-static void stf_wr_data_en(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stfcamss *stfcamss = cap->video.stfcamss;
-
- stf_syscon_reg_set_bit(stfcamss, VIN_CHANNEL_SEL_EN, U0_VIN_AXIWR0_EN);
-}
-
-static void stf_wr_irq_enable(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stfcamss *stfcamss = cap->video.stfcamss;
-
- stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M);
-}
-
-static void stf_wr_irq_disable(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stfcamss *stfcamss = cap->video.stfcamss;
-
- stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
- stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
- stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M);
-}
-
-static void stf_channel_set(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stfcamss *stfcamss = cap->video.stfcamss;
- u32 val;
-
- if (cap->type == STF_CAPTURE_RAW) {
- const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
-
- val = stf_syscon_reg_read(stfcamss, VIN_CHANNEL_SEL_EN);
- val &= ~U0_VIN_CHANNEL_SEL_MASK;
- val |= CHANNEL(0);
- stf_syscon_reg_write(stfcamss, VIN_CHANNEL_SEL_EN, val);
-
- val = stf_syscon_reg_read(stfcamss, VIN_INRT_PIX_CFG);
- val &= ~U0_VIN_PIX_CT_MASK;
- val |= PIX_CT(1);
-
- val &= ~U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS;
- val |= PIXEL_HEIGH_BIT_SEL(0);
-
- val &= ~U0_VIN_PIX_CNT_END_MASK;
- val |= PIX_CNT_END(pix->width / 4 - 1);
-
- stf_syscon_reg_write(stfcamss, VIN_INRT_PIX_CFG, val);
- } else if (cap->type == STF_CAPTURE_YUV) {
- val = stf_syscon_reg_read(stfcamss, VIN_CFG_REG);
- val &= ~U0_VIN_MIPI_BYTE_EN_ISP0_MASK;
- val |= U0_VIN_MIPI_BYTE_EN_ISP0(0);
-
- val &= ~U0_VIN_MIPI_CHANNEL_SEL0_MASK;
- val |= U0_VIN_MIPI_CHANNEL_SEL0(0);
-
- val &= ~U0_VIN_PIX_NUM_MASK;
- val |= U0_VIN_PIX_NUM(0);
-
- val &= ~U0_VIN_P_I_MIPI_HAEDER_EN0_MASK;
- val |= U0_VIN_P_I_MIPI_HAEDER_EN0(1);
-
- stf_syscon_reg_write(stfcamss, VIN_CFG_REG, val);
- }
-}
-
-static void stf_capture_start(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
-
- stf_channel_set(video);
- if (cap->type == STF_CAPTURE_RAW) {
- stf_wr_irq_enable(video);
- stf_wr_data_en(video);
- }
-
- stf_cap_s_cfg(video);
-}
-
-static void stf_capture_stop(struct stfcamss_video *video)
-{
- struct stf_capture *cap = to_stf_capture(video);
-
- if (cap->type == STF_CAPTURE_RAW)
- stf_wr_irq_disable(video);
-
- stf_cap_s_cleanup(video);
-}
-
-static void stf_capture_init(struct stfcamss *stfcamss, struct stf_capture *cap)
-{
- cap->buffers.state = STF_OUTPUT_OFF;
- cap->buffers.buf[0] = NULL;
- cap->buffers.buf[1] = NULL;
- cap->buffers.active_buf = 0;
- atomic_set(&cap->buffers.frame_skip, 4);
- INIT_LIST_HEAD(&cap->buffers.pending_bufs);
- INIT_LIST_HEAD(&cap->buffers.ready_bufs);
- spin_lock_init(&cap->buffers.lock);
-
- cap->video.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cap->video.stfcamss = stfcamss;
- cap->video.bpl_alignment = 16 * 8;
-
- if (cap->type == STF_CAPTURE_RAW) {
- cap->video.formats = stf_wr_fmts;
- cap->video.nformats = ARRAY_SIZE(stf_wr_fmts);
- cap->video.bpl_alignment = 8;
- } else if (cap->type == STF_CAPTURE_YUV) {
- cap->video.formats = stf_isp_fmts;
- cap->video.nformats = ARRAY_SIZE(stf_isp_fmts);
- cap->video.bpl_alignment = 1;
- }
-}
-
-static void stf_buf_add_ready(struct stf_v_buf *output,
- struct stfcamss_buffer *buffer)
-{
- INIT_LIST_HEAD(&buffer->queue);
- list_add_tail(&buffer->queue, &output->ready_bufs);
-}
-
-static struct stfcamss_buffer *stf_buf_get_ready(struct stf_v_buf *output)
-{
- struct stfcamss_buffer *buffer = NULL;
-
- if (!list_empty(&output->ready_bufs)) {
- buffer = list_first_entry(&output->ready_bufs,
- struct stfcamss_buffer,
- queue);
- list_del(&buffer->queue);
- }
-
- return buffer;
-}
-
-static void stf_buf_add_pending(struct stf_v_buf *output,
- struct stfcamss_buffer *buffer)
-{
- INIT_LIST_HEAD(&buffer->queue);
- list_add_tail(&buffer->queue, &output->pending_bufs);
-}
-
-static void stf_buf_update_on_last(struct stf_v_buf *output)
-{
- switch (output->state) {
- case STF_OUTPUT_CONTINUOUS:
- output->state = STF_OUTPUT_SINGLE;
- output->active_buf = !output->active_buf;
- break;
- case STF_OUTPUT_SINGLE:
- output->state = STF_OUTPUT_STOPPING;
- break;
- default:
- break;
- }
-}
-
-static void stf_buf_update_on_next(struct stf_v_buf *output)
-{
- switch (output->state) {
- case STF_OUTPUT_CONTINUOUS:
- output->active_buf = !output->active_buf;
- break;
- case STF_OUTPUT_SINGLE:
- default:
- break;
- }
-}
-
-static void stf_buf_update_on_new(struct stfcamss_video *video,
- struct stfcamss_buffer *new_buf)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *output = &cap->buffers;
-
- switch (output->state) {
- case STF_OUTPUT_SINGLE:
- stf_buf_add_pending(output, new_buf);
- break;
- case STF_OUTPUT_IDLE:
- if (!output->buf[0]) {
- output->buf[0] = new_buf;
- stf_init_addrs(video);
- output->state = STF_OUTPUT_SINGLE;
- } else {
- stf_buf_add_pending(output, new_buf);
- }
- break;
- case STF_OUTPUT_STOPPING:
- if (output->last_buffer) {
- output->buf[output->active_buf] = output->last_buffer;
- output->last_buffer = NULL;
- }
-
- output->state = STF_OUTPUT_SINGLE;
- stf_buf_add_pending(output, new_buf);
- break;
- case STF_OUTPUT_CONTINUOUS:
- default:
- stf_buf_add_pending(output, new_buf);
- break;
- }
-}
-
-static void stf_buf_flush(struct stf_v_buf *output, enum vb2_buffer_state state)
-{
- struct stfcamss_buffer *buf;
- struct stfcamss_buffer *t;
-
- list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
- vb2_buffer_done(&buf->vb.vb2_buf, state);
- list_del(&buf->queue);
- }
- list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) {
- vb2_buffer_done(&buf->vb.vb2_buf, state);
- list_del(&buf->queue);
- }
-}
-
-static void stf_buf_done(struct stf_v_buf *output)
-{
- struct stfcamss_buffer *ready_buf;
- u64 ts = ktime_get_ns();
- unsigned long flags;
-
- if (output->state == STF_OUTPUT_OFF ||
- output->state == STF_OUTPUT_RESERVED)
- return;
-
- spin_lock_irqsave(&output->lock, flags);
-
- while ((ready_buf = stf_buf_get_ready(output))) {
- ready_buf->vb.vb2_buf.timestamp = ts;
- ready_buf->vb.sequence = output->sequence++;
-
- vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
- }
-
- spin_unlock_irqrestore(&output->lock, flags);
-}
-
-static void stf_change_buffer(struct stf_v_buf *output)
-{
- struct stf_capture *cap = container_of(output, struct stf_capture,
- buffers);
- struct stfcamss *stfcamss = cap->video.stfcamss;
- struct stfcamss_buffer *ready_buf;
- dma_addr_t *new_addr;
- unsigned long flags;
- u32 active_index;
-
- if (output->state == STF_OUTPUT_OFF ||
- output->state == STF_OUTPUT_STOPPING ||
- output->state == STF_OUTPUT_RESERVED ||
- output->state == STF_OUTPUT_IDLE)
- return;
-
- spin_lock_irqsave(&output->lock, flags);
-
- active_index = output->active_buf;
-
- ready_buf = output->buf[active_index];
- if (!ready_buf) {
- dev_dbg(stfcamss->dev, "missing ready buf %d %d.\n",
- active_index, output->state);
- active_index = !active_index;
- ready_buf = output->buf[active_index];
- if (!ready_buf) {
- dev_dbg(stfcamss->dev,
- "missing ready buf2 %d %d.\n",
- active_index, output->state);
- goto out_unlock;
- }
- }
-
- /* Get next buffer */
- output->buf[active_index] = stf_buf_get_pending(output);
- if (!output->buf[active_index]) {
- new_addr = ready_buf->addr;
- stf_buf_update_on_last(output);
- } else {
- new_addr = output->buf[active_index]->addr;
- stf_buf_update_on_next(output);
- }
-
- if (output->state == STF_OUTPUT_STOPPING) {
- output->last_buffer = ready_buf;
- } else {
- if (cap->type == STF_CAPTURE_RAW)
- stf_set_raw_addr(stfcamss, new_addr[0]);
- else if (cap->type == STF_CAPTURE_YUV)
- stf_set_yuv_addr(stfcamss, new_addr[0], new_addr[1]);
-
- stf_buf_add_ready(output, ready_buf);
- }
-
-out_unlock:
- spin_unlock_irqrestore(&output->lock, flags);
-}
-
-irqreturn_t stf_wr_irq_handler(int irq, void *priv)
-{
- struct stfcamss *stfcamss = priv;
- struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_RAW];
-
- if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) {
- stf_change_buffer(&cap->buffers);
- stf_buf_done(&cap->buffers);
- }
-
- stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
- stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
-
- return IRQ_HANDLED;
-}
-
-irqreturn_t stf_isp_irq_handler(int irq, void *priv)
-{
- struct stfcamss *stfcamss = priv;
- struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV];
- u32 status;
-
- status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0);
- if (status & ISPC_ISP) {
- if (status & ISPC_ENUO)
- stf_buf_done(&cap->buffers);
-
- stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0,
- (status & ~ISPC_INT_ALL_MASK) |
- ISPC_ISP | ISPC_CSI | ISPC_SC);
- }
-
- return IRQ_HANDLED;
-}
-
-irqreturn_t stf_line_irq_handler(int irq, void *priv)
-{
- struct stfcamss *stfcamss = priv;
- struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV];
- u32 status;
-
- status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0);
- if (status & ISPC_LINE) {
- if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) {
- if ((status & ISPC_ENUO))
- stf_change_buffer(&cap->buffers);
- }
-
- stf_isp_reg_set_bit(stfcamss, ISP_REG_CSIINTS,
- CSI_INTS_MASK, CSI_INTS(0x3));
- stf_isp_reg_set_bit(stfcamss, ISP_REG_IESHD,
- SHAD_UP_M | SHAD_UP_EN, 0x3);
-
- stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0,
- (status & ~ISPC_INT_ALL_MASK) | ISPC_LINE);
- }
-
- return IRQ_HANDLED;
-}
-
-static int stf_queue_buffer(struct stfcamss_video *video,
- struct stfcamss_buffer *buf)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *v_bufs = &cap->buffers;
- unsigned long flags;
-
- spin_lock_irqsave(&v_bufs->lock, flags);
- stf_buf_update_on_new(video, buf);
- spin_unlock_irqrestore(&v_bufs->lock, flags);
-
- return 0;
-}
-
-static int stf_flush_buffers(struct stfcamss_video *video,
- enum vb2_buffer_state state)
-{
- struct stf_capture *cap = to_stf_capture(video);
- struct stf_v_buf *v_bufs = &cap->buffers;
- unsigned long flags;
- unsigned int i;
-
- spin_lock_irqsave(&v_bufs->lock, flags);
-
- stf_buf_flush(v_bufs, state);
-
- for (i = 0; i < ARRAY_SIZE(v_bufs->buf); i++) {
- if (v_bufs->buf[i])
- vb2_buffer_done(&v_bufs->buf[i]->vb.vb2_buf, state);
-
- v_bufs->buf[i] = NULL;
- }
-
- if (v_bufs->last_buffer) {
- vb2_buffer_done(&v_bufs->last_buffer->vb.vb2_buf, state);
- v_bufs->last_buffer = NULL;
- }
-
- spin_unlock_irqrestore(&v_bufs->lock, flags);
- return 0;
-}
-
-static const struct stfcamss_video_ops stf_capture_ops = {
- .queue_buffer = stf_queue_buffer,
- .flush_buffers = stf_flush_buffers,
- .start_streaming = stf_capture_start,
- .stop_streaming = stf_capture_stop,
-};
-
-static void stf_capture_unregister_one(struct stf_capture *cap)
-{
- if (!video_is_registered(&cap->video.vdev))
- return;
-
- media_entity_cleanup(&cap->video.vdev.entity);
- vb2_video_unregister_device(&cap->video.vdev);
-}
-
-void stf_capture_unregister(struct stfcamss *stfcamss)
-{
- struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW];
- struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
-
- stf_capture_unregister_one(cap_raw);
- stf_capture_unregister_one(cap_yuv);
-}
-
-int stf_capture_register(struct stfcamss *stfcamss,
- struct v4l2_device *v4l2_dev)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < ARRAY_SIZE(stfcamss->captures); i++) {
- struct stf_capture *capture = &stfcamss->captures[i];
-
- capture->type = i;
- capture->video.ops = &stf_capture_ops;
- stf_capture_init(stfcamss, capture);
-
- ret = stf_video_register(&capture->video, v4l2_dev,
- stf_cap_names[i]);
- if (ret < 0) {
- dev_err(stfcamss->dev,
- "Failed to register video node: %d\n", ret);
- stf_capture_unregister(stfcamss);
- return ret;
- }
- }
-
- return 0;
-}
diff --git a/drivers/staging/media/starfive/camss/stf-capture.h b/drivers/staging/media/starfive/camss/stf-capture.h
deleted file mode 100644
index 2f9740b7e500..000000000000
--- a/drivers/staging/media/starfive/camss/stf-capture.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * stf_capture.h
- *
- * Starfive Camera Subsystem driver
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#ifndef STF_CAPTURE_H
-#define STF_CAPTURE_H
-
-#include "stf-video.h"
-
-#define VIN_CHANNEL_SEL_EN 0x14
-#define VIN_START_ADDR_N 0x18
-#define VIN_INRT_PIX_CFG 0x1c
-#define VIN_START_ADDR_O 0x20
-#define VIN_CFG_REG 0x24
-
-#define U0_VIN_CNFG_AXI_DVP_EN BIT(2)
-
-#define U0_VIN_CHANNEL_SEL_MASK GENMASK(3, 0)
-#define U0_VIN_AXIWR0_EN BIT(4)
-#define CHANNEL(x) ((x) << 0)
-
-#define U0_VIN_INTR_CLEAN BIT(0)
-#define U0_VIN_INTR_M BIT(1)
-#define U0_VIN_PIX_CNT_END_MASK GENMASK(12, 2)
-#define U0_VIN_PIX_CT_MASK GENMASK(14, 13)
-#define U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS GENMASK(16, 15)
-
-#define PIX_CNT_END(x) ((x) << 2)
-#define PIX_CT(x) ((x) << 13)
-#define PIXEL_HEIGH_BIT_SEL(x) ((x) << 15)
-
-#define U0_VIN_CNFG_DVP_HS_POS BIT(1)
-#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2)
-#define U0_VIN_CNFG_DVP_VS_POS BIT(3)
-#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4)
-#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5)
-#define U0_VIN_MIPI_BYTE_EN_ISP0(n) ((n) << 6)
-#define U0_VIN_MIPI_CHANNEL_SEL0(n) ((n) << 8)
-#define U0_VIN_P_I_MIPI_HAEDER_EN0(n) ((n) << 12)
-#define U0_VIN_PIX_NUM(n) ((n) << 13)
-#define U0_VIN_MIPI_BYTE_EN_ISP0_MASK GENMASK(7, 6)
-#define U0_VIN_MIPI_CHANNEL_SEL0_MASK GENMASK(11, 8)
-#define U0_VIN_P_I_MIPI_HAEDER_EN0_MASK BIT(12)
-#define U0_VIN_PIX_NUM_MASK GENMASK(16, 13)
-
-enum stf_v_state {
- STF_OUTPUT_OFF,
- STF_OUTPUT_RESERVED,
- STF_OUTPUT_SINGLE,
- STF_OUTPUT_CONTINUOUS,
- STF_OUTPUT_IDLE,
- STF_OUTPUT_STOPPING
-};
-
-struct stf_v_buf {
- int active_buf;
- struct stfcamss_buffer *buf[2];
- struct stfcamss_buffer *last_buffer;
- struct list_head pending_bufs;
- struct list_head ready_bufs;
- enum stf_v_state state;
- unsigned int sequence;
- /* protects the above member variables */
- spinlock_t lock;
- atomic_t frame_skip;
-};
-
-struct stf_capture {
- struct stfcamss_video video;
- struct stf_v_buf buffers;
- enum stf_capture_type type;
-};
-
-irqreturn_t stf_wr_irq_handler(int irq, void *priv);
-irqreturn_t stf_isp_irq_handler(int irq, void *priv);
-irqreturn_t stf_line_irq_handler(int irq, void *priv);
-int stf_capture_register(struct stfcamss *stfcamss,
- struct v4l2_device *v4l2_dev);
-void stf_capture_unregister(struct stfcamss *stfcamss);
-
-#endif
diff --git a/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c b/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c
deleted file mode 100644
index c34631ff9422..000000000000
--- a/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c
+++ /dev/null
@@ -1,445 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * stf_isp_hw_ops.c
- *
- * Register interface file for StarFive ISP driver
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- *
- */
-
-#include "stf-camss.h"
-
-static void stf_isp_config_obc(struct stfcamss *stfcamss)
-{
- u32 reg_val, reg_add;
-
- stf_isp_reg_write(stfcamss, ISP_REG_OBC_CFG, OBC_W_H(11) | OBC_W_W(11));
-
- reg_val = GAIN_D_POINT(0x40) | GAIN_C_POINT(0x40) |
- GAIN_B_POINT(0x40) | GAIN_A_POINT(0x40);
- for (reg_add = ISP_REG_OBCG_CFG_0; reg_add <= ISP_REG_OBCG_CFG_3;) {
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- }
-
- reg_val = OFFSET_D_POINT(0) | OFFSET_C_POINT(0) |
- OFFSET_B_POINT(0) | OFFSET_A_POINT(0);
- for (reg_add = ISP_REG_OBCO_CFG_0; reg_add <= ISP_REG_OBCO_CFG_3;) {
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- }
-}
-
-static void stf_isp_config_oecf(struct stfcamss *stfcamss)
-{
- u32 reg_add, par_val;
- u16 par_h, par_l;
-
- par_h = 0x10; par_l = 0;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG0; reg_add <= ISP_REG_OECF_Y3_CFG0;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x40; par_l = 0x20;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG1; reg_add <= ISP_REG_OECF_Y3_CFG1;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x80; par_l = 0x60;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG2; reg_add <= ISP_REG_OECF_Y3_CFG2;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0xc0; par_l = 0xa0;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG3; reg_add <= ISP_REG_OECF_Y3_CFG3;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x100; par_l = 0xe0;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG4; reg_add <= ISP_REG_OECF_Y3_CFG4;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x200; par_l = 0x180;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG5; reg_add <= ISP_REG_OECF_Y3_CFG5;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x300; par_l = 0x280;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG6; reg_add <= ISP_REG_OECF_Y3_CFG6;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x3fe; par_l = 0x380;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_X0_CFG7; reg_add <= ISP_REG_OECF_Y3_CFG7;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 0x20;
- }
-
- par_h = 0x80; par_l = 0x80;
- par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
- for (reg_add = ISP_REG_OECF_S0_CFG0; reg_add <= ISP_REG_OECF_S3_CFG7;) {
- stf_isp_reg_write(stfcamss, reg_add, par_val);
- reg_add += 4;
- }
-}
-
-static void stf_isp_config_lccf(struct stfcamss *stfcamss)
-{
- u32 reg_add;
-
- stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_0,
- Y_DISTANCE(0x21C) | X_DISTANCE(0x3C0));
- stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_1, LCCF_MAX_DIS(0xb));
-
- for (reg_add = ISP_REG_LCCF_CFG_2; reg_add <= ISP_REG_LCCF_CFG_5;) {
- stf_isp_reg_write(stfcamss, reg_add,
- LCCF_F2_PAR(0x0) | LCCF_F1_PAR(0x0));
- reg_add += 4;
- }
-}
-
-static void stf_isp_config_awb(struct stfcamss *stfcamss)
-{
- u32 reg_val, reg_add;
- u16 symbol_h, symbol_l;
-
- symbol_h = 0x0; symbol_l = 0x0;
- reg_val = AWB_X_SYMBOL_H(symbol_h) | AWB_X_SYMBOL_L(symbol_l);
-
- for (reg_add = ISP_REG_AWB_X0_CFG_0; reg_add <= ISP_REG_AWB_X3_CFG_1;) {
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- }
-
- symbol_h = 0x0, symbol_l = 0x0;
- reg_val = AWB_Y_SYMBOL_H(symbol_h) | AWB_Y_SYMBOL_L(symbol_l);
-
- for (reg_add = ISP_REG_AWB_Y0_CFG_0; reg_add <= ISP_REG_AWB_Y3_CFG_1;) {
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- }
-
- symbol_h = 0x80, symbol_l = 0x80;
- reg_val = AWB_S_SYMBOL_H(symbol_h) | AWB_S_SYMBOL_L(symbol_l);
-
- for (reg_add = ISP_REG_AWB_S0_CFG_0; reg_add <= ISP_REG_AWB_S3_CFG_1;) {
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- }
-}
-
-static void stf_isp_config_grgb(struct stfcamss *stfcamss)
-{
- stf_isp_reg_write(stfcamss, ISP_REG_ICTC,
- GF_MODE(1) | MAXGT(0x140) | MINGT(0x40));
- stf_isp_reg_write(stfcamss, ISP_REG_IDBC, BADGT(0x200) | BADXT(0x200));
-}
-
-static void stf_isp_config_cfa(struct stfcamss *stfcamss)
-{
- stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG,
- SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) | SMY3(2) |
- SMY2(3) | SMY1(2) | SMY0(3));
- stf_isp_reg_write(stfcamss, ISP_REG_ICFAM, CROSS_COV(3) | HV_W(2));
-}
-
-static void stf_isp_config_ccm(struct stfcamss *stfcamss)
-{
- u32 reg_add;
-
- stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_0, DNRM_F(6) | CCM_M_DAT(0));
-
- for (reg_add = ISP_REG_ICAMD_12; reg_add <= ISP_REG_ICAMD_20;) {
- stf_isp_reg_write(stfcamss, reg_add, CCM_M_DAT(0x80));
- reg_add += 0x10;
- }
-
- stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_24, CCM_M_DAT(0x700));
- stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_25, CCM_M_DAT(0x200));
-}
-
-static void stf_isp_config_gamma(struct stfcamss *stfcamss)
-{
- u32 reg_val, reg_add;
- u16 gamma_slope_v, gamma_v;
-
- gamma_slope_v = 0x2400; gamma_v = 0x0;
- reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
- stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL0, reg_val);
-
- gamma_slope_v = 0x800; gamma_v = 0x20;
- for (reg_add = ISP_REG_GAMMA_VAL1; reg_add <= ISP_REG_GAMMA_VAL7;) {
- reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- gamma_v += 0x20;
- }
-
- gamma_v = 0x100;
- for (reg_add = ISP_REG_GAMMA_VAL8; reg_add <= ISP_REG_GAMMA_VAL13;) {
- reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
- stf_isp_reg_write(stfcamss, reg_add, reg_val);
- reg_add += 4;
- gamma_v += 0x80;
- }
-
- gamma_v = 0x3fe;
- reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
- stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL14, reg_val);
-}
-
-static void stf_isp_config_r2y(struct stfcamss *stfcamss)
-{
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_0, 0x4C);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_1, 0x97);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_2, 0x1d);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_3, 0x1d5);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_4, 0x1ac);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_5, 0x80);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_6, 0x80);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_7, 0x194);
- stf_isp_reg_write(stfcamss, ISP_REG_R2Y_8, 0x1ec);
-}
-
-static void stf_isp_config_y_curve(struct stfcamss *stfcamss)
-{
- u32 reg_add;
- u16 y_curve;
-
- y_curve = 0x0;
- for (reg_add = ISP_REG_YCURVE_0; reg_add <= ISP_REG_YCURVE_63;) {
- stf_isp_reg_write(stfcamss, reg_add, y_curve);
- reg_add += 4;
- y_curve += 0x10;
- }
-}
-
-static void stf_isp_config_sharpen(struct stfcamss *sc)
-{
- u32 reg_add;
-
- stf_isp_reg_write(sc, ISP_REG_SHARPEN0, S_DELTA(0x7) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN1, S_DELTA(0x18) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN2, S_DELTA(0x80) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN3, S_DELTA(0x100) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN4, S_DELTA(0x10) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN5, S_DELTA(0x60) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN6, S_DELTA(0x100) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN7, S_DELTA(0x190) | S_WEIGHT(0xf));
- stf_isp_reg_write(sc, ISP_REG_SHARPEN8, S_DELTA(0x0) | S_WEIGHT(0xf));
-
- for (reg_add = ISP_REG_SHARPEN9; reg_add <= ISP_REG_SHARPEN14;) {
- stf_isp_reg_write(sc, reg_add, S_WEIGHT(0xf));
- reg_add += 4;
- }
-
- for (reg_add = ISP_REG_SHARPEN_FS0; reg_add <= ISP_REG_SHARPEN_FS5;) {
- stf_isp_reg_write(sc, reg_add, S_FACTOR(0x10) | S_SLOPE(0x0));
- reg_add += 4;
- }
-
- stf_isp_reg_write(sc, ISP_REG_SHARPEN_WN,
- PDIRF(0x8) | NDIRF(0x8) | WSUM(0xd7c));
- stf_isp_reg_write(sc, ISP_REG_IUVS1, UVDIFF2(0xC0) | UVDIFF1(0x40));
- stf_isp_reg_write(sc, ISP_REG_IUVS2, UVF(0xff) | UVSLOPE(0x0));
- stf_isp_reg_write(sc, ISP_REG_IUVCKS1,
- UVCKDIFF2(0xa0) | UVCKDIFF1(0x40));
-}
-
-static void stf_isp_config_dnyuv(struct stfcamss *stfcamss)
-{
- u32 reg_val;
-
- reg_val = YUVSW5(7) | YUVSW4(7) | YUVSW3(7) | YUVSW2(7) |
- YUVSW1(7) | YUVSW0(7);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR0, reg_val);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR0, reg_val);
-
- reg_val = YUVSW3(7) | YUVSW2(7) | YUVSW1(7) | YUVSW0(7);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR1, reg_val);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR1, reg_val);
-
- reg_val = CURVE_D_H(0x60) | CURVE_D_L(0x40);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR0, reg_val);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR0, reg_val);
-
- reg_val = CURVE_D_H(0xd8) | CURVE_D_L(0x90);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR1, reg_val);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR1, reg_val);
-
- reg_val = CURVE_D_H(0x1e6) | CURVE_D_L(0x144);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR2, reg_val);
- stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR2, reg_val);
-}
-
-static void stf_isp_config_sat(struct stfcamss *stfcamss)
-{
- stf_isp_reg_write(stfcamss, ISP_REG_CS_GAIN, CMAD(0x0) | CMAB(0x100));
- stf_isp_reg_write(stfcamss, ISP_REG_CS_THRESHOLD, CMD(0x1f) | CMB(0x1));
- stf_isp_reg_write(stfcamss, ISP_REG_CS_OFFSET, VOFF(0x0) | UOFF(0x0));
- stf_isp_reg_write(stfcamss, ISP_REG_CS_HUE_F, SIN(0x0) | COS(0x100));
- stf_isp_reg_write(stfcamss, ISP_REG_CS_SCALE, 0x8);
- stf_isp_reg_write(stfcamss, ISP_REG_YADJ0, YOIR(0x401) | YIMIN(0x1));
- stf_isp_reg_write(stfcamss, ISP_REG_YADJ1, YOMAX(0x3ff) | YOMIN(0x1));
-}
-
-int stf_isp_reset(struct stf_isp_dev *isp_dev)
-{
- stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0,
- ISPC_RST_MASK, ISPC_RST);
- stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0,
- ISPC_RST_MASK, 0);
-
- return 0;
-}
-
-void stf_isp_init_cfg(struct stf_isp_dev *isp_dev)
-{
- stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DC_CFG_1, DC_AXI_ID(0x0));
- stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DEC_CFG,
- DEC_V_KEEP(0x0) |
- DEC_V_PERIOD(0x0) |
- DEC_H_KEEP(0x0) |
- DEC_H_PERIOD(0x0));
-
- stf_isp_config_obc(isp_dev->stfcamss);
- stf_isp_config_oecf(isp_dev->stfcamss);
- stf_isp_config_lccf(isp_dev->stfcamss);
- stf_isp_config_awb(isp_dev->stfcamss);
- stf_isp_config_grgb(isp_dev->stfcamss);
- stf_isp_config_cfa(isp_dev->stfcamss);
- stf_isp_config_ccm(isp_dev->stfcamss);
- stf_isp_config_gamma(isp_dev->stfcamss);
- stf_isp_config_r2y(isp_dev->stfcamss);
- stf_isp_config_y_curve(isp_dev->stfcamss);
- stf_isp_config_sharpen(isp_dev->stfcamss);
- stf_isp_config_dnyuv(isp_dev->stfcamss);
- stf_isp_config_sat(isp_dev->stfcamss);
-
- stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_CSI_MODULE_CFG,
- CSI_DUMP_EN | CSI_SC_EN | CSI_AWB_EN |
- CSI_LCCF_EN | CSI_OECF_EN | CSI_OBC_EN | CSI_DEC_EN);
- stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_ISP_CTRL_1,
- CTRL_SAT(1) | CTRL_DBC | CTRL_CTC | CTRL_YHIST |
- CTRL_YCURVE | CTRL_BIYUV | CTRL_SCE | CTRL_EE |
- CTRL_CCE | CTRL_RGE | CTRL_CME | CTRL_AE | CTRL_CE);
-}
-
-static void stf_isp_config_crop(struct stfcamss *stfcamss,
- struct v4l2_rect *crop)
-{
- u32 bpp = stfcamss->isp_dev.current_fmt->bpp;
- u32 val;
-
- val = VSTART_CAP(crop->top) | HSTART_CAP(crop->left);
- stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_START_CFG, val);
-
- val = VEND_CAP(crop->height + crop->top - 1) |
- HEND_CAP(crop->width + crop->left - 1);
- stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_END_CFG, val);
-
- val = H_ACT_CAP(crop->height) | W_ACT_CAP(crop->width);
- stf_isp_reg_write(stfcamss, ISP_REG_PIPELINE_XY_SIZE, val);
-
- val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
- stf_isp_reg_write(stfcamss, ISP_REG_STRIDE, val);
-}
-
-static void stf_isp_config_raw_fmt(struct stfcamss *stfcamss, u32 mcode)
-{
- u32 val, val1;
-
- switch (mcode) {
- case MEDIA_BUS_FMT_SRGGB10_1X10:
- case MEDIA_BUS_FMT_SRGGB8_1X8:
- /* 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R */
- val = SMY13(3) | SMY12(2) | SMY11(3) | SMY10(2) |
- SMY3(1) | SMY2(0) | SMY1(1) | SMY0(0);
- val1 = CTRL_SAT(0x0);
- break;
- case MEDIA_BUS_FMT_SGRBG10_1X10:
- case MEDIA_BUS_FMT_SGRBG8_1X8:
- /* 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr */
- val = SMY13(2) | SMY12(3) | SMY11(2) | SMY10(3) |
- SMY3(0) | SMY2(1) | SMY1(0) | SMY0(1);
- val1 = CTRL_SAT(0x2);
- break;
- case MEDIA_BUS_FMT_SGBRG10_1X10:
- case MEDIA_BUS_FMT_SGBRG8_1X8:
- /* 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb */
- val = SMY13(1) | SMY12(0) | SMY11(1) | SMY10(0) |
- SMY3(3) | SMY2(2) | SMY1(3) | SMY0(2);
- val1 = CTRL_SAT(0x3);
- break;
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- case MEDIA_BUS_FMT_SBGGR8_1X8:
- /* 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B */
- val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) |
- SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3);
- val1 = CTRL_SAT(0x1);
- break;
- default:
- val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) |
- SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3);
- val1 = CTRL_SAT(0x1);
- break;
- }
- stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG, val);
- stf_isp_reg_set_bit(stfcamss, ISP_REG_ISP_CTRL_1, CTRL_SAT_MASK, val1);
-}
-
-void stf_isp_settings(struct stf_isp_dev *isp_dev,
- struct v4l2_rect *crop, u32 mcode)
-{
- struct stfcamss *stfcamss = isp_dev->stfcamss;
-
- stf_isp_config_crop(stfcamss, crop);
- stf_isp_config_raw_fmt(stfcamss, mcode);
-
- stf_isp_reg_set_bit(stfcamss, ISP_REG_DUMP_CFG_1,
- DUMP_BURST_LEN_MASK | DUMP_SD_MASK,
- DUMP_BURST_LEN(3));
-
- stf_isp_reg_write(stfcamss, ISP_REG_ITIIWSR,
- ITI_HSIZE(IMAGE_MAX_HEIGH) |
- ITI_WSIZE(IMAGE_MAX_WIDTH));
- stf_isp_reg_write(stfcamss, ISP_REG_ITIDWLSR, 0x960);
- stf_isp_reg_write(stfcamss, ISP_REG_ITIDRLSR, 0x960);
- stf_isp_reg_write(stfcamss, ISP_REG_SENSOR, IMAGER_SEL(1));
-}
-
-void stf_isp_stream_set(struct stf_isp_dev *isp_dev)
-{
- struct stfcamss *stfcamss = isp_dev->stfcamss;
-
- stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
- ISPC_ENUO | ISPC_ENLS | ISPC_RST, 10);
- stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
- ISPC_ENUO | ISPC_ENLS, 10);
- stf_isp_reg_write(stfcamss, ISP_REG_IESHD, SHAD_UP_M);
- stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
- ISPC_ENUO | ISPC_ENLS | ISPC_EN, 10);
- stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS,
- CSI_INTS(1) | CSI_SHA_M(4), 10);
- stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS,
- CSI_INTS(2) | CSI_SHA_M(4), 10);
- stf_isp_reg_write_delay(stfcamss, ISP_REG_CSI_INPUT_EN_AND_STATUS,
- CSI_EN_S, 10);
-}
diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
deleted file mode 100644
index df7a903fbb1b..000000000000
--- a/drivers/staging/media/starfive/camss/stf-isp.c
+++ /dev/null
@@ -1,379 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * stf_isp.c
- *
- * StarFive Camera Subsystem - ISP Module
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-#include <media/v4l2-rect.h>
-
-#include "stf-camss.h"
-
-static int isp_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_selection *sel);
-
-static const struct stf_isp_format isp_formats_sink[] = {
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
- { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
- { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
-};
-
-static const struct stf_isp_format isp_formats_source[] = {
- { MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
-};
-
-static const struct stf_isp_format_table isp_formats_st7110[] = {
- { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
- { isp_formats_source, ARRAY_SIZE(isp_formats_source) },
-};
-
-static const struct stf_isp_format *
-stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode)
-{
- unsigned int i;
-
- for (i = 0; i < fmt_table->nfmts; i++) {
- if (fmt_table->fmts[i].code == mcode)
- return &fmt_table->fmts[i];
- }
-
- return NULL;
-}
-
-int stf_isp_init(struct stfcamss *stfcamss)
-{
- struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
-
- isp_dev->stfcamss = stfcamss;
- isp_dev->formats = isp_formats_st7110;
- isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
- isp_dev->current_fmt = &isp_formats_source[0];
-
- return 0;
-}
-
-static int isp_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
- struct v4l2_subdev_state *sd_state;
- struct v4l2_mbus_framefmt *fmt;
- struct v4l2_rect *crop;
-
- sd_state = v4l2_subdev_lock_and_get_active_state(sd);
- fmt = v4l2_subdev_state_get_format(sd_state, STF_ISP_PAD_SINK);
- crop = v4l2_subdev_state_get_crop(sd_state, STF_ISP_PAD_SRC);
-
- if (enable) {
- stf_isp_reset(isp_dev);
- stf_isp_init_cfg(isp_dev);
- stf_isp_settings(isp_dev, crop, fmt->code);
- stf_isp_stream_set(isp_dev);
- }
-
- v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable);
-
- v4l2_subdev_unlock_state(sd_state);
- return 0;
-}
-
-static void isp_try_format(struct stf_isp_dev *isp_dev,
- struct v4l2_subdev_state *state,
- unsigned int pad,
- struct v4l2_mbus_framefmt *fmt)
-{
- const struct stf_isp_format_table *formats;
-
- if (pad >= STF_ISP_PAD_MAX) {
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- return;
- }
-
- formats = &isp_dev->formats[pad];
-
- fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
- STFCAMSS_FRAME_MAX_WIDTH);
- fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
- STFCAMSS_FRAME_MAX_HEIGHT);
- fmt->height &= ~0x1;
- fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->flags = 0;
-
- if (!stf_g_fmt_by_mcode(formats, fmt->code))
- fmt->code = formats->fmts[0].code;
-}
-
-static int isp_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
- const struct stf_isp_format_table *formats;
-
- if (code->pad == STF_ISP_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(isp_formats_sink))
- return -EINVAL;
-
- formats = &isp_dev->formats[code->pad];
- code->code = formats->fmts[code->index].code;
- } else {
- struct v4l2_mbus_framefmt *sink_fmt;
-
- if (code->index >= ARRAY_SIZE(isp_formats_source))
- return -EINVAL;
-
- sink_fmt = v4l2_subdev_state_get_format(state,
- STF_ISP_PAD_SRC);
-
- code->code = sink_fmt->code;
- if (!code->code)
- return -EINVAL;
- }
- code->flags = 0;
-
- return 0;
-}
-
-static int isp_set_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_format *fmt)
-{
- struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = v4l2_subdev_state_get_format(state, fmt->pad);
- if (!format)
- return -EINVAL;
-
- isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
- *format = fmt->format;
-
- isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
- fmt->format.code);
-
- /* Propagate to in crop */
- if (fmt->pad == STF_ISP_PAD_SINK) {
- struct v4l2_subdev_selection sel = { 0 };
-
- /* Reset sink pad compose selection */
- sel.which = fmt->which;
- sel.pad = STF_ISP_PAD_SINK;
- sel.target = V4L2_SEL_TGT_CROP;
- sel.r.width = fmt->format.width;
- sel.r.height = fmt->format.height;
- isp_set_selection(sd, state, &sel);
- }
-
- return 0;
-}
-
-static const struct v4l2_rect stf_frame_min_crop = {
- .width = STFCAMSS_FRAME_MIN_WIDTH,
- .height = STFCAMSS_FRAME_MIN_HEIGHT,
- .top = 0,
- .left = 0,
-};
-
-static void isp_try_crop(struct stf_isp_dev *isp_dev,
- struct v4l2_subdev_state *state,
- struct v4l2_rect *crop)
-{
- struct v4l2_mbus_framefmt *fmt =
- v4l2_subdev_state_get_format(state, STF_ISP_PAD_SINK);
-
- const struct v4l2_rect bounds = {
- .width = fmt->width,
- .height = fmt->height,
- .left = 0,
- .top = 0,
- };
-
- v4l2_rect_set_min_size(crop, &stf_frame_min_crop);
- v4l2_rect_map_inside(crop, &bounds);
-}
-
-static int isp_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_selection *sel)
-{
- struct v4l2_subdev_format fmt = { 0 };
- struct v4l2_rect *rect;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- if (sel->pad == STF_ISP_PAD_SINK) {
- fmt.format = *v4l2_subdev_state_get_format(state,
- sel->pad);
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = fmt.format.width;
- sel->r.height = fmt.format.height;
- } else if (sel->pad == STF_ISP_PAD_SRC) {
- rect = v4l2_subdev_state_get_crop(state, sel->pad);
- sel->r = *rect;
- }
- break;
-
- case V4L2_SEL_TGT_CROP:
- rect = v4l2_subdev_state_get_crop(state, sel->pad);
- if (!rect)
- return -EINVAL;
-
- sel->r = *rect;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int isp_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_selection *sel)
-{
- struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
- struct v4l2_rect *rect;
-
- if (sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- if (sel->target == V4L2_SEL_TGT_CROP &&
- sel->pad == STF_ISP_PAD_SINK) {
- struct v4l2_subdev_selection crop = { 0 };
-
- rect = v4l2_subdev_state_get_crop(state, sel->pad);
- if (!rect)
- return -EINVAL;
-
- isp_try_crop(isp_dev, state, &sel->r);
- *rect = sel->r;
-
- /* Reset source crop selection */
- crop.which = sel->which;
- crop.pad = STF_ISP_PAD_SRC;
- crop.target = V4L2_SEL_TGT_CROP;
- crop.r = *rect;
- isp_set_selection(sd, state, &crop);
- } else if (sel->target == V4L2_SEL_TGT_CROP &&
- sel->pad == STF_ISP_PAD_SRC) {
- struct v4l2_subdev_format fmt = { 0 };
-
- rect = v4l2_subdev_state_get_crop(state, sel->pad);
- if (!rect)
- return -EINVAL;
-
- isp_try_crop(isp_dev, state, &sel->r);
- *rect = sel->r;
-
- /* Reset source pad format width and height */
- fmt.which = sel->which;
- fmt.pad = STF_ISP_PAD_SRC;
- fmt.format.width = rect->width;
- fmt.format.height = rect->height;
- isp_set_format(sd, state, &fmt);
- }
-
- dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%ux%u\n",
- sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
-
- return 0;
-}
-
-static int isp_init_formats(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state)
-{
- struct v4l2_subdev_format format = {
- .pad = STF_ISP_PAD_SINK,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .format = {
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .width = 1920,
- .height = 1080
- }
- };
-
- return isp_set_format(sd, sd_state, &format);
-}
-
-static const struct v4l2_subdev_video_ops isp_video_ops = {
- .s_stream = isp_set_stream,
-};
-
-static const struct v4l2_subdev_pad_ops isp_pad_ops = {
- .enum_mbus_code = isp_enum_mbus_code,
- .get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = isp_set_format,
- .get_selection = isp_get_selection,
- .set_selection = isp_set_selection,
-};
-
-static const struct v4l2_subdev_ops isp_v4l2_ops = {
- .video = &isp_video_ops,
- .pad = &isp_pad_ops,
-};
-
-static const struct v4l2_subdev_internal_ops isp_internal_ops = {
- .init_state = isp_init_formats,
-};
-
-static const struct media_entity_operations isp_media_ops = {
- .link_validate = v4l2_subdev_link_validate,
-};
-
-int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
-{
- struct v4l2_subdev *sd = &isp_dev->subdev;
- struct media_pad *pads = isp_dev->pads;
- int ret;
-
- v4l2_subdev_init(sd, &isp_v4l2_ops);
- sd->internal_ops = &isp_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp");
- v4l2_set_subdevdata(sd, isp_dev);
-
- pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
-
- sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
- sd->entity.ops = &isp_media_ops;
- ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads);
- if (ret) {
- dev_err(isp_dev->stfcamss->dev,
- "Failed to init media entity: %d\n", ret);
- return ret;
- }
-
- ret = v4l2_subdev_init_finalize(sd);
- if (ret)
- goto err_entity_cleanup;
-
- ret = v4l2_device_register_subdev(v4l2_dev, sd);
- if (ret) {
- dev_err(isp_dev->stfcamss->dev,
- "Failed to register subdev: %d\n", ret);
- goto err_subdev_cleanup;
- }
-
- return 0;
-
-err_subdev_cleanup:
- v4l2_subdev_cleanup(sd);
-err_entity_cleanup:
- media_entity_cleanup(&sd->entity);
- return ret;
-}
-
-int stf_isp_unregister(struct stf_isp_dev *isp_dev)
-{
- v4l2_device_unregister_subdev(&isp_dev->subdev);
- v4l2_subdev_cleanup(&isp_dev->subdev);
- media_entity_cleanup(&isp_dev->subdev.entity);
-
- return 0;
-}
diff --git a/drivers/staging/media/starfive/camss/stf-isp.h b/drivers/staging/media/starfive/camss/stf-isp.h
deleted file mode 100644
index 955cbb048363..000000000000
--- a/drivers/staging/media/starfive/camss/stf-isp.h
+++ /dev/null
@@ -1,428 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * stf_isp.h
- *
- * StarFive Camera Subsystem - ISP Module
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#ifndef STF_ISP_H
-#define STF_ISP_H
-
-#include <media/v4l2-subdev.h>
-
-#include "stf-video.h"
-
-#define ISP_RAW_DATA_BITS 12
-#define SCALER_RATIO_MAX 1
-#define STF_ISP_REG_OFFSET_MAX 0x0fff
-#define STF_ISP_REG_DELAY_MAX 100
-
-/* isp registers */
-#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x000
-#define CSI_SCD_ERR BIT(6)
-#define CSI_ITU656_ERR BIT(4)
-#define CSI_ITU656_F BIT(3)
-#define CSI_SCD_DONE BIT(2)
-#define CSI_BUSY_S BIT(1)
-#define CSI_EN_S BIT(0)
-
-#define ISP_REG_CSIINTS 0x008
-#define CSI_INTS(n) ((n) << 16)
-#define CSI_SHA_M(n) ((n) << 0)
-#define CSI_INTS_MASK GENMASK(17, 16)
-
-#define ISP_REG_CSI_MODULE_CFG 0x010
-#define CSI_DUMP_EN BIT(19)
-#define CSI_VS_EN BIT(18)
-#define CSI_SC_EN BIT(17)
-#define CSI_OBA_EN BIT(16)
-#define CSI_AWB_EN BIT(7)
-#define CSI_LCCF_EN BIT(6)
-#define CSI_OECFHM_EN BIT(5)
-#define CSI_OECF_EN BIT(4)
-#define CSI_LCBQ_EN BIT(3)
-#define CSI_OBC_EN BIT(2)
-#define CSI_DEC_EN BIT(1)
-#define CSI_DC_EN BIT(0)
-
-#define ISP_REG_SENSOR 0x014
-#define DVP_SYNC_POL(n) ((n) << 2)
-#define ITU656_EN(n) ((n) << 1)
-#define IMAGER_SEL(n) ((n) << 0)
-
-#define ISP_REG_RAW_FORMAT_CFG 0x018
-#define SMY13(n) ((n) << 14)
-#define SMY12(n) ((n) << 12)
-#define SMY11(n) ((n) << 10)
-#define SMY10(n) ((n) << 8)
-#define SMY3(n) ((n) << 6)
-#define SMY2(n) ((n) << 4)
-#define SMY1(n) ((n) << 2)
-#define SMY0(n) ((n) << 0)
-
-#define ISP_REG_PIC_CAPTURE_START_CFG 0x01c
-#define VSTART_CAP(n) ((n) << 16)
-#define HSTART_CAP(n) ((n) << 0)
-
-#define ISP_REG_PIC_CAPTURE_END_CFG 0x020
-#define VEND_CAP(n) ((n) << 16)
-#define HEND_CAP(n) ((n) << 0)
-
-#define ISP_REG_DUMP_CFG_0 0x024
-#define ISP_REG_DUMP_CFG_1 0x028
-#define DUMP_ID(n) ((n) << 24)
-#define DUMP_SHT(n) ((n) << 20)
-#define DUMP_BURST_LEN(n) ((n) << 16)
-#define DUMP_SD(n) ((n) << 0)
-#define DUMP_BURST_LEN_MASK GENMASK(17, 16)
-#define DUMP_SD_MASK GENMASK(15, 0)
-
-#define ISP_REG_DEC_CFG 0x030
-#define DEC_V_KEEP(n) ((n) << 24)
-#define DEC_V_PERIOD(n) ((n) << 16)
-#define DEC_H_KEEP(n) ((n) << 8)
-#define DEC_H_PERIOD(n) ((n) << 0)
-
-#define ISP_REG_OBC_CFG 0x034
-#define OBC_W_H(y) ((y) << 4)
-#define OBC_W_W(x) ((x) << 0)
-
-#define ISP_REG_DC_CFG_1 0x044
-#define DC_AXI_ID(n) ((n) << 0)
-
-#define ISP_REG_LCCF_CFG_0 0x050
-#define Y_DISTANCE(y) ((y) << 16)
-#define X_DISTANCE(x) ((x) << 0)
-
-#define ISP_REG_LCCF_CFG_1 0x058
-#define LCCF_MAX_DIS(n) ((n) << 0)
-
-#define ISP_REG_LCBQ_CFG_0 0x074
-#define H_LCBQ(y) ((y) << 12)
-#define W_LCBQ(x) ((x) << 8)
-
-#define ISP_REG_LCBQ_CFG_1 0x07c
-#define Y_COOR(y) ((y) << 16)
-#define X_COOR(x) ((x) << 0)
-
-#define ISP_REG_LCCF_CFG_2 0x0e0
-#define ISP_REG_LCCF_CFG_3 0x0e4
-#define ISP_REG_LCCF_CFG_4 0x0e8
-#define ISP_REG_LCCF_CFG_5 0x0ec
-#define LCCF_F2_PAR(n) ((n) << 16)
-#define LCCF_F1_PAR(n) ((n) << 0)
-
-#define ISP_REG_OECF_X0_CFG0 0x100
-#define ISP_REG_OECF_X0_CFG1 0x104
-#define ISP_REG_OECF_X0_CFG2 0x108
-#define ISP_REG_OECF_X0_CFG3 0x10c
-#define ISP_REG_OECF_X0_CFG4 0x110
-#define ISP_REG_OECF_X0_CFG5 0x114
-#define ISP_REG_OECF_X0_CFG6 0x118
-#define ISP_REG_OECF_X0_CFG7 0x11c
-
-#define ISP_REG_OECF_Y3_CFG0 0x1e0
-#define ISP_REG_OECF_Y3_CFG1 0x1e4
-#define ISP_REG_OECF_Y3_CFG2 0x1e8
-#define ISP_REG_OECF_Y3_CFG3 0x1ec
-#define ISP_REG_OECF_Y3_CFG4 0x1f0
-#define ISP_REG_OECF_Y3_CFG5 0x1f4
-#define ISP_REG_OECF_Y3_CFG6 0x1f8
-#define ISP_REG_OECF_Y3_CFG7 0x1fc
-
-#define ISP_REG_OECF_S0_CFG0 0x200
-#define ISP_REG_OECF_S3_CFG7 0x27c
-#define OCEF_PAR_H(n) ((n) << 16)
-#define OCEF_PAR_L(n) ((n) << 0)
-
-#define ISP_REG_AWB_X0_CFG_0 0x280
-#define ISP_REG_AWB_X0_CFG_1 0x284
-#define ISP_REG_AWB_X1_CFG_0 0x288
-#define ISP_REG_AWB_X1_CFG_1 0x28c
-#define ISP_REG_AWB_X2_CFG_0 0x290
-#define ISP_REG_AWB_X2_CFG_1 0x294
-#define ISP_REG_AWB_X3_CFG_0 0x298
-#define ISP_REG_AWB_X3_CFG_1 0x29c
-#define AWB_X_SYMBOL_H(n) ((n) << 16)
-#define AWB_X_SYMBOL_L(n) ((n) << 0)
-
-#define ISP_REG_AWB_Y0_CFG_0 0x2a0
-#define ISP_REG_AWB_Y0_CFG_1 0x2a4
-#define ISP_REG_AWB_Y1_CFG_0 0x2a8
-#define ISP_REG_AWB_Y1_CFG_1 0x2ac
-#define ISP_REG_AWB_Y2_CFG_0 0x2b0
-#define ISP_REG_AWB_Y2_CFG_1 0x2b4
-#define ISP_REG_AWB_Y3_CFG_0 0x2b8
-#define ISP_REG_AWB_Y3_CFG_1 0x2bc
-#define AWB_Y_SYMBOL_H(n) ((n) << 16)
-#define AWB_Y_SYMBOL_L(n) ((n) << 0)
-
-#define ISP_REG_AWB_S0_CFG_0 0x2c0
-#define ISP_REG_AWB_S0_CFG_1 0x2c4
-#define ISP_REG_AWB_S1_CFG_0 0x2c8
-#define ISP_REG_AWB_S1_CFG_1 0x2cc
-#define ISP_REG_AWB_S2_CFG_0 0x2d0
-#define ISP_REG_AWB_S2_CFG_1 0x2d4
-#define ISP_REG_AWB_S3_CFG_0 0x2d8
-#define ISP_REG_AWB_S3_CFG_1 0x2dc
-#define AWB_S_SYMBOL_H(n) ((n) << 16)
-#define AWB_S_SYMBOL_L(n) ((n) << 0)
-
-#define ISP_REG_OBCG_CFG_0 0x2e0
-#define ISP_REG_OBCG_CFG_1 0x2e4
-#define ISP_REG_OBCG_CFG_2 0x2e8
-#define ISP_REG_OBCG_CFG_3 0x2ec
-#define ISP_REG_OBCO_CFG_0 0x2f0
-#define ISP_REG_OBCO_CFG_1 0x2f4
-#define ISP_REG_OBCO_CFG_2 0x2f8
-#define ISP_REG_OBCO_CFG_3 0x2fc
-#define GAIN_D_POINT(x) ((x) << 24)
-#define GAIN_C_POINT(x) ((x) << 16)
-#define GAIN_B_POINT(x) ((x) << 8)
-#define GAIN_A_POINT(x) ((x) << 0)
-#define OFFSET_D_POINT(x) ((x) << 24)
-#define OFFSET_C_POINT(x) ((x) << 16)
-#define OFFSET_B_POINT(x) ((x) << 8)
-#define OFFSET_A_POINT(x) ((x) << 0)
-
-#define ISP_REG_ISP_CTRL_0 0xa00
-#define ISPC_LINE BIT(27)
-#define ISPC_SC BIT(26)
-#define ISPC_CSI BIT(25)
-#define ISPC_ISP BIT(24)
-#define ISPC_ENUO BIT(20)
-#define ISPC_ENLS BIT(17)
-#define ISPC_ENSS1 BIT(12)
-#define ISPC_ENSS0 BIT(11)
-#define ISPC_RST BIT(1)
-#define ISPC_EN BIT(0)
-#define ISPC_RST_MASK BIT(1)
-#define ISPC_INT_ALL_MASK GENMASK(27, 24)
-
-#define ISP_REG_ISP_CTRL_1 0xa08
-#define CTRL_SAT(n) ((n) << 28)
-#define CTRL_DBC BIT(22)
-#define CTRL_CTC BIT(21)
-#define CTRL_YHIST BIT(20)
-#define CTRL_YCURVE BIT(19)
-#define CTRL_CTM BIT(18)
-#define CTRL_BIYUV BIT(17)
-#define CTRL_SCE BIT(8)
-#define CTRL_EE BIT(7)
-#define CTRL_CCE BIT(5)
-#define CTRL_RGE BIT(4)
-#define CTRL_CME BIT(3)
-#define CTRL_AE BIT(2)
-#define CTRL_CE BIT(1)
-#define CTRL_SAT_MASK GENMASK(31, 28)
-
-#define ISP_REG_PIPELINE_XY_SIZE 0xa0c
-#define H_ACT_CAP(n) ((n) << 16)
-#define W_ACT_CAP(n) ((n) << 0)
-
-#define ISP_REG_ICTC 0xa10
-#define GF_MODE(n) ((n) << 30)
-#define MAXGT(n) ((n) << 16)
-#define MINGT(n) ((n) << 0)
-
-#define ISP_REG_IDBC 0xa14
-#define BADGT(n) ((n) << 16)
-#define BADXT(n) ((n) << 0)
-
-#define ISP_REG_ICFAM 0xa1c
-#define CROSS_COV(n) ((n) << 4)
-#define HV_W(n) ((n) << 0)
-
-#define ISP_REG_CS_GAIN 0xa30
-#define CMAD(n) ((n) << 16)
-#define CMAB(n) ((n) << 0)
-
-#define ISP_REG_CS_THRESHOLD 0xa34
-#define CMD(n) ((n) << 16)
-#define CMB(n) ((n) << 0)
-
-#define ISP_REG_CS_OFFSET 0xa38
-#define VOFF(n) ((n) << 16)
-#define UOFF(n) ((n) << 0)
-
-#define ISP_REG_CS_HUE_F 0xa3c
-#define SIN(n) ((n) << 16)
-#define COS(n) ((n) << 0)
-
-#define ISP_REG_CS_SCALE 0xa40
-
-#define ISP_REG_IESHD 0xa50
-#define SHAD_UP_M BIT(1)
-#define SHAD_UP_EN BIT(0)
-
-#define ISP_REG_YADJ0 0xa54
-#define YOIR(n) ((n) << 16)
-#define YIMIN(n) ((n) << 0)
-
-#define ISP_REG_YADJ1 0xa58
-#define YOMAX(n) ((n) << 16)
-#define YOMIN(n) ((n) << 0)
-
-#define ISP_REG_Y_PLANE_START_ADDR 0xa80
-#define ISP_REG_UV_PLANE_START_ADDR 0xa84
-#define ISP_REG_STRIDE 0xa88
-
-#define ISP_REG_ITIIWSR 0xb20
-#define ITI_HSIZE(n) ((n) << 16)
-#define ITI_WSIZE(n) ((n) << 0)
-
-#define ISP_REG_ITIDWLSR 0xb24
-#define ISP_REG_ITIPDFR 0xb38
-#define ISP_REG_ITIDRLSR 0xb3C
-
-#define ISP_REG_DNYUV_YSWR0 0xc00
-#define ISP_REG_DNYUV_YSWR1 0xc04
-#define ISP_REG_DNYUV_CSWR0 0xc08
-#define ISP_REG_DNYUV_CSWR1 0xc0c
-#define YUVSW5(n) ((n) << 20)
-#define YUVSW4(n) ((n) << 16)
-#define YUVSW3(n) ((n) << 12)
-#define YUVSW2(n) ((n) << 8)
-#define YUVSW1(n) ((n) << 4)
-#define YUVSW0(n) ((n) << 0)
-
-#define ISP_REG_DNYUV_YDR0 0xc10
-#define ISP_REG_DNYUV_YDR1 0xc14
-#define ISP_REG_DNYUV_YDR2 0xc18
-#define ISP_REG_DNYUV_CDR0 0xc1c
-#define ISP_REG_DNYUV_CDR1 0xc20
-#define ISP_REG_DNYUV_CDR2 0xc24
-#define CURVE_D_H(n) ((n) << 16)
-#define CURVE_D_L(n) ((n) << 0)
-
-#define ISP_REG_ICAMD_0 0xc40
-#define ISP_REG_ICAMD_12 0xc70
-#define ISP_REG_ICAMD_20 0xc90
-#define ISP_REG_ICAMD_24 0xca0
-#define ISP_REG_ICAMD_25 0xca4
-#define DNRM_F(n) ((n) << 16)
-#define CCM_M_DAT(n) ((n) << 0)
-
-#define ISP_REG_GAMMA_VAL0 0xe00
-#define ISP_REG_GAMMA_VAL1 0xe04
-#define ISP_REG_GAMMA_VAL2 0xe08
-#define ISP_REG_GAMMA_VAL3 0xe0c
-#define ISP_REG_GAMMA_VAL4 0xe10
-#define ISP_REG_GAMMA_VAL5 0xe14
-#define ISP_REG_GAMMA_VAL6 0xe18
-#define ISP_REG_GAMMA_VAL7 0xe1c
-#define ISP_REG_GAMMA_VAL8 0xe20
-#define ISP_REG_GAMMA_VAL9 0xe24
-#define ISP_REG_GAMMA_VAL10 0xe28
-#define ISP_REG_GAMMA_VAL11 0xe2c
-#define ISP_REG_GAMMA_VAL12 0xe30
-#define ISP_REG_GAMMA_VAL13 0xe34
-#define ISP_REG_GAMMA_VAL14 0xe38
-#define GAMMA_S_VAL(n) ((n) << 16)
-#define GAMMA_VAL(n) ((n) << 0)
-
-#define ISP_REG_R2Y_0 0xe40
-#define ISP_REG_R2Y_1 0xe44
-#define ISP_REG_R2Y_2 0xe48
-#define ISP_REG_R2Y_3 0xe4c
-#define ISP_REG_R2Y_4 0xe50
-#define ISP_REG_R2Y_5 0xe54
-#define ISP_REG_R2Y_6 0xe58
-#define ISP_REG_R2Y_7 0xe5c
-#define ISP_REG_R2Y_8 0xe60
-
-#define ISP_REG_SHARPEN0 0xe80
-#define ISP_REG_SHARPEN1 0xe84
-#define ISP_REG_SHARPEN2 0xe88
-#define ISP_REG_SHARPEN3 0xe8c
-#define ISP_REG_SHARPEN4 0xe90
-#define ISP_REG_SHARPEN5 0xe94
-#define ISP_REG_SHARPEN6 0xe98
-#define ISP_REG_SHARPEN7 0xe9c
-#define ISP_REG_SHARPEN8 0xea0
-#define ISP_REG_SHARPEN9 0xea4
-#define ISP_REG_SHARPEN10 0xea8
-#define ISP_REG_SHARPEN11 0xeac
-#define ISP_REG_SHARPEN12 0xeb0
-#define ISP_REG_SHARPEN13 0xeb4
-#define ISP_REG_SHARPEN14 0xeb8
-#define S_DELTA(n) ((n) << 16)
-#define S_WEIGHT(n) ((n) << 8)
-
-#define ISP_REG_SHARPEN_FS0 0xebc
-#define ISP_REG_SHARPEN_FS1 0xec0
-#define ISP_REG_SHARPEN_FS2 0xec4
-#define ISP_REG_SHARPEN_FS3 0xec8
-#define ISP_REG_SHARPEN_FS4 0xecc
-#define ISP_REG_SHARPEN_FS5 0xed0
-#define S_FACTOR(n) ((n) << 24)
-#define S_SLOPE(n) ((n) << 0)
-
-#define ISP_REG_SHARPEN_WN 0xed4
-#define PDIRF(n) ((n) << 28)
-#define NDIRF(n) ((n) << 24)
-#define WSUM(n) ((n) << 0)
-
-#define ISP_REG_IUVS1 0xed8
-#define UVDIFF2(n) ((n) << 16)
-#define UVDIFF1(n) ((n) << 0)
-
-#define ISP_REG_IUVS2 0xedc
-#define UVF(n) ((n) << 24)
-#define UVSLOPE(n) ((n) << 0)
-
-#define ISP_REG_IUVCKS1 0xee0
-#define UVCKDIFF2(n) ((n) << 16)
-#define UVCKDIFF1(n) ((n) << 0)
-
-#define ISP_REG_IUVCKS2 0xee4
-
-#define ISP_REG_ISHRPET 0xee8
-#define TH(n) ((n) << 8)
-#define EN(n) ((n) << 0)
-
-#define ISP_REG_YCURVE_0 0xf00
-#define ISP_REG_YCURVE_63 0xffc
-
-#define IMAGE_MAX_WIDTH 1920
-#define IMAGE_MAX_HEIGH 1080
-
-/* pad id for media framework */
-enum stf_isp_pad_id {
- STF_ISP_PAD_SINK = 0,
- STF_ISP_PAD_SRC,
- STF_ISP_PAD_MAX
-};
-
-struct stf_isp_format {
- u32 code;
- u8 bpp;
-};
-
-struct stf_isp_format_table {
- const struct stf_isp_format *fmts;
- int nfmts;
-};
-
-struct stf_isp_dev {
- struct stfcamss *stfcamss;
- struct v4l2_subdev subdev;
- struct media_pad pads[STF_ISP_PAD_MAX];
- const struct stf_isp_format_table *formats;
- unsigned int nformats;
- struct v4l2_subdev *source_subdev;
- const struct stf_isp_format *current_fmt;
-};
-
-int stf_isp_reset(struct stf_isp_dev *isp_dev);
-void stf_isp_init_cfg(struct stf_isp_dev *isp_dev);
-void stf_isp_settings(struct stf_isp_dev *isp_dev,
- struct v4l2_rect *crop, u32 mcode);
-void stf_isp_stream_set(struct stf_isp_dev *isp_dev);
-int stf_isp_init(struct stfcamss *stfcamss);
-int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev);
-int stf_isp_unregister(struct stf_isp_dev *isp_dev);
-
-#endif /* STF_ISP_H */
diff --git a/drivers/staging/media/starfive/camss/stf-video.c b/drivers/staging/media/starfive/camss/stf-video.c
deleted file mode 100644
index a0420eb6a0aa..000000000000
--- a/drivers/staging/media/starfive/camss/stf-video.c
+++ /dev/null
@@ -1,570 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * stf_video.c
- *
- * StarFive Camera Subsystem - V4L2 device node
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#include <linux/pm_runtime.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-mc.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "stf-camss.h"
-#include "stf-video.h"
-
-/* -----------------------------------------------------------------------------
- * Helper functions
- */
-
-static inline struct stfcamss_buffer *
-to_stfcamss_buffer(struct vb2_v4l2_buffer *vbuf)
-{
- return container_of(vbuf, struct stfcamss_buffer, vb);
-}
-
-static const struct stfcamss_format_info *
-video_g_fi_by_index(struct stfcamss_video *video, int index)
-{
- if (index >= video->nformats)
- return NULL;
-
- return &video->formats[index];
-}
-
-static const struct stfcamss_format_info *
-video_g_fi_by_mcode(struct stfcamss_video *video, u32 mcode)
-{
- unsigned int i;
-
- for (i = 0; i < video->nformats; i++) {
- if (video->formats[i].code == mcode)
- return &video->formats[i];
- }
-
- return NULL;
-}
-
-static const struct stfcamss_format_info *
-video_g_fi_by_pfmt(struct stfcamss_video *video, u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < video->nformats; i++) {
- if (video->formats[i].pixelformat == pixelformat)
- return &video->formats[i];
- }
-
- return NULL;
-}
-
-static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f)
-{
- struct v4l2_pix_format *pix = &f->fmt.pix;
- const struct stfcamss_format_info *fi;
- u32 width, height;
- u32 bpl;
- unsigned int i;
-
- fi = video_g_fi_by_pfmt(video, pix->pixelformat);
- if (!fi)
- fi = &video->formats[0]; /* default format */
-
- width = pix->width;
- height = pix->height;
-
- memset(pix, 0, sizeof(*pix));
-
- pix->pixelformat = fi->pixelformat;
- pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
- STFCAMSS_FRAME_MAX_WIDTH);
- pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
- STFCAMSS_FRAME_MAX_HEIGHT);
- bpl = pix->width * fi->bpp / 8;
- bpl = ALIGN(bpl, video->bpl_alignment);
- pix->bytesperline = bpl;
-
- for (i = 0; i < fi->planes; ++i)
- pix->sizeimage += bpl * pix->height / fi->vsub[i];
-
- pix->field = V4L2_FIELD_NONE;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- pix->flags = 0;
- pix->ycbcr_enc =
- V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
- pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
- pix->colorspace,
- pix->ycbcr_enc);
- pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
-
- return 0;
-}
-
-static int stf_video_init_format(struct stfcamss_video *video)
-{
- int ret;
- struct v4l2_format format = {
- .type = video->type,
- .fmt.pix = {
- .width = 1920,
- .height = 1080,
- .pixelformat = V4L2_PIX_FMT_NV12,
- },
- };
-
- ret = __video_try_fmt(video, &format);
-
- if (ret < 0)
- return ret;
-
- video->active_fmt = format;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Video queue operations
- */
-
-static int video_queue_setup(struct vb2_queue *q,
- unsigned int *num_buffers,
- unsigned int *num_planes,
- unsigned int sizes[],
- struct device *alloc_devs[])
-{
- struct stfcamss_video *video = vb2_get_drv_priv(q);
- const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix;
-
- if (*num_planes) {
- if (*num_planes != 1)
- return -EINVAL;
-
- if (sizes[0] < format->sizeimage)
- return -EINVAL;
- } else {
- *num_planes = 1;
- sizes[0] = format->sizeimage;
- }
-
- if (!sizes[0]) {
- dev_dbg(video->stfcamss->dev,
- "%s: error size is zero.\n", __func__);
- return -EINVAL;
- }
-
- dev_dbg(video->stfcamss->dev, "planes = %d, size = %d\n",
- *num_planes, sizes[0]);
-
- return 0;
-}
-
-static int video_buf_init(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
- struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf);
- const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
- dma_addr_t *paddr;
-
- paddr = vb2_plane_cookie(vb, 0);
- buffer->addr[0] = *paddr;
-
- if (fmt->pixelformat == V4L2_PIX_FMT_NV12)
- buffer->addr[1] =
- buffer->addr[0] + fmt->bytesperline * fmt->height;
-
- return 0;
-}
-
-static int video_buf_prepare(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
- const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
-
- if (fmt->sizeimage > vb2_plane_size(vb, 0)) {
- dev_dbg(video->stfcamss->dev,
- "sizeimage = %u, plane size = %u\n",
- fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0));
- return -EINVAL;
- }
- vb2_set_plane_payload(vb, 0, fmt->sizeimage);
-
- vbuf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static void video_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
- struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf);
-
- video->ops->queue_buffer(video, buffer);
-}
-
-static int video_get_subdev_format(struct stfcamss_video *video,
- struct v4l2_subdev_format *fmt)
-{
- struct v4l2_subdev *subdev;
- struct media_pad *pad;
- struct media_entity *entity;
- int ret;
-
- entity = &video->vdev.entity;
- while (1) {
- pad = &entity->pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- pad = media_pad_remote_pad_first(pad);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
- break;
-
- entity = pad->entity;
- subdev = media_entity_to_v4l2_subdev(entity);
-
- fmt->pad = pad->index;
-
- ret = v4l2_subdev_call_state_active(subdev, pad, get_fmt, fmt);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- else if (!ret)
- break;
- }
-
- return 0;
-}
-
-static int stf_video_check_format(struct stfcamss_video *video)
-{
- struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
- const struct stfcamss_format_info *fi;
- int ret;
- struct v4l2_subdev_format sd_fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
-
- ret = video_get_subdev_format(video, &sd_fmt);
- if (ret < 0)
- return ret;
-
- fi = video_g_fi_by_mcode(video, sd_fmt.format.code);
- if (!fi)
- return -EINVAL;
-
- if (pix->pixelformat != fi->pixelformat ||
- pix->height != sd_fmt.format.height ||
- pix->width != sd_fmt.format.width ||
- pix->field != sd_fmt.format.field)
- return -EPIPE;
-
- return 0;
-}
-
-static int video_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct stfcamss_video *video = vb2_get_drv_priv(q);
- struct video_device *vdev = &video->vdev;
- int ret;
-
- ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
- if (ret < 0) {
- dev_err(video->stfcamss->dev,
- "Failed to media_pipeline_start: %d\n", ret);
- goto err_ret_buffers;
- }
-
- ret = pm_runtime_resume_and_get(video->stfcamss->dev);
- if (ret < 0) {
- dev_err(video->stfcamss->dev, "power up failed %d\n", ret);
- goto err_pipeline_stop;
- }
-
- video->ops->start_streaming(video);
-
- ret = v4l2_subdev_call(video->source_subdev, video, s_stream, true);
- if (ret) {
- dev_err(video->stfcamss->dev, "stream on failed\n");
- goto err_pm_put;
- }
-
- return 0;
-
-err_pm_put:
- pm_runtime_put(video->stfcamss->dev);
-err_pipeline_stop:
- video_device_pipeline_stop(vdev);
-err_ret_buffers:
- video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
- return ret;
-}
-
-static void video_stop_streaming(struct vb2_queue *q)
-{
- struct stfcamss_video *video = vb2_get_drv_priv(q);
- struct video_device *vdev = &video->vdev;
-
- video->ops->stop_streaming(video);
-
- v4l2_subdev_call(video->source_subdev, video, s_stream, false);
-
- pm_runtime_put(video->stfcamss->dev);
-
- video_device_pipeline_stop(vdev);
- video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
-}
-
-static const struct vb2_ops stf_video_vb2_q_ops = {
- .queue_setup = video_queue_setup,
- .buf_init = video_buf_init,
- .buf_prepare = video_buf_prepare,
- .buf_queue = video_buf_queue,
- .start_streaming = video_start_streaming,
- .stop_streaming = video_stop_streaming,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 ioctls
- */
-
-static int video_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- strscpy(cap->driver, "starfive-camss", sizeof(cap->driver));
- strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card));
-
- return 0;
-}
-
-static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
-{
- struct stfcamss_video *video = video_drvdata(file);
- const struct stfcamss_format_info *fi;
-
- if (f->index >= video->nformats)
- return -EINVAL;
-
- if (f->mbus_code) {
- /* Each entry in formats[] table has unique mbus_code */
- if (f->index > 0)
- return -EINVAL;
-
- fi = video_g_fi_by_mcode(video, f->mbus_code);
- } else {
- fi = video_g_fi_by_index(video, f->index);
- }
-
- if (!fi)
- return -EINVAL;
-
- f->pixelformat = fi->pixelformat;
-
- return 0;
-}
-
-static int video_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- struct stfcamss_video *video = video_drvdata(file);
- unsigned int i;
-
- if (fsize->index)
- return -EINVAL;
-
- for (i = 0; i < video->nformats; i++) {
- if (video->formats[i].pixelformat == fsize->pixel_format)
- break;
- }
-
- if (i == video->nformats)
- return -EINVAL;
-
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
- fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
- fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
- fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
-
- return 0;
-}
-
-static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct stfcamss_video *video = video_drvdata(file);
-
- *f = video->active_fmt;
-
- return 0;
-}
-
-static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct stfcamss_video *video = video_drvdata(file);
- int ret;
-
- if (vb2_is_busy(&video->vb2_q))
- return -EBUSY;
-
- ret = __video_try_fmt(video, f);
- if (ret < 0)
- return ret;
-
- video->active_fmt = *f;
-
- return 0;
-}
-
-static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct stfcamss_video *video = video_drvdata(file);
-
- return __video_try_fmt(video, f);
-}
-
-static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
- .vidioc_querycap = video_querycap,
- .vidioc_enum_fmt_vid_cap = video_enum_fmt,
- .vidioc_enum_framesizes = video_enum_framesizes,
- .vidioc_g_fmt_vid_cap = video_g_fmt,
- .vidioc_s_fmt_vid_cap = video_s_fmt,
- .vidioc_try_fmt_vid_cap = video_try_fmt,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 file operations
- */
-
-static const struct v4l2_file_operations stf_vid_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .poll = vb2_fop_poll,
- .mmap = vb2_fop_mmap,
- .read = vb2_fop_read,
-};
-
-/* -----------------------------------------------------------------------------
- * STFCAMSS video core
- */
-
-static int stf_link_validate(struct media_link *link)
-{
- struct video_device *vdev =
- media_entity_to_video_device(link->sink->entity);
- struct stfcamss_video *video = video_get_drvdata(vdev);
- int ret;
-
- ret = stf_video_check_format(video);
-
- return ret;
-}
-
-static const struct media_entity_operations stf_media_ops = {
- .link_validate = stf_link_validate,
-};
-
-static void stf_video_release(struct video_device *vdev)
-{
- struct stfcamss_video *video = video_get_drvdata(vdev);
-
- media_entity_cleanup(&vdev->entity);
-
- mutex_destroy(&video->q_lock);
- mutex_destroy(&video->lock);
-}
-
-int stf_video_register(struct stfcamss_video *video,
- struct v4l2_device *v4l2_dev, const char *name)
-{
- struct video_device *vdev = &video->vdev;
- struct vb2_queue *q;
- struct media_pad *pad = &video->pad;
- int ret;
-
- mutex_init(&video->q_lock);
- mutex_init(&video->lock);
-
- q = &video->vb2_q;
- q->drv_priv = video;
- q->mem_ops = &vb2_dma_contig_memops;
- q->ops = &stf_video_vb2_q_ops;
- q->type = video->type;
- q->io_modes = VB2_DMABUF | VB2_MMAP;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->buf_struct_size = sizeof(struct stfcamss_buffer);
- q->dev = video->stfcamss->dev;
- q->lock = &video->q_lock;
- q->min_queued_buffers = STFCAMSS_MIN_BUFFERS;
- ret = vb2_queue_init(q);
- if (ret < 0) {
- dev_err(video->stfcamss->dev,
- "Failed to init vb2 queue: %d\n", ret);
- goto err_mutex_destroy;
- }
-
- pad->flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vdev->entity, 1, pad);
- if (ret < 0) {
- dev_err(video->stfcamss->dev,
- "Failed to init video entity: %d\n", ret);
- goto err_mutex_destroy;
- }
-
- ret = stf_video_init_format(video);
- if (ret < 0) {
- dev_err(video->stfcamss->dev,
- "Failed to init format: %d\n", ret);
- goto err_media_cleanup;
- }
-
- vdev->fops = &stf_vid_fops;
- vdev->ioctl_ops = &stf_vid_ioctl_ops;
- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- vdev->entity.ops = &stf_media_ops;
- vdev->vfl_dir = VFL_DIR_RX;
- vdev->release = stf_video_release;
- vdev->v4l2_dev = v4l2_dev;
- vdev->queue = &video->vb2_q;
- vdev->lock = &video->lock;
- strscpy(vdev->name, name, sizeof(vdev->name));
-
- video_set_drvdata(vdev, video);
-
- ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
- if (ret < 0) {
- dev_err(video->stfcamss->dev,
- "Failed to register video device: %d\n", ret);
- goto err_media_cleanup;
- }
-
- return 0;
-
-err_media_cleanup:
- media_entity_cleanup(&vdev->entity);
-err_mutex_destroy:
- mutex_destroy(&video->lock);
- mutex_destroy(&video->q_lock);
- return ret;
-}
-
-void stf_video_unregister(struct stfcamss_video *video)
-{
- vb2_video_unregister_device(&video->vdev);
-}
diff --git a/drivers/staging/media/starfive/camss/stf-video.h b/drivers/staging/media/starfive/camss/stf-video.h
deleted file mode 100644
index 8052b77e3ad8..000000000000
--- a/drivers/staging/media/starfive/camss/stf-video.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * stf_video.h
- *
- * StarFive Camera Subsystem - V4L2 device node
- *
- * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
- */
-
-#ifndef STF_VIDEO_H
-#define STF_VIDEO_H
-
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-v4l2.h>
-
-#define STFCAMSS_FRAME_MIN_WIDTH 64
-#define STFCAMSS_FRAME_MAX_WIDTH 1920
-#define STFCAMSS_FRAME_MIN_HEIGHT 64
-#define STFCAMSS_FRAME_MAX_HEIGHT 1080
-#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8
-#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128
-#define STFCAMSS_MIN_BUFFERS 2
-
-#define STFCAMSS_MAX_ENTITY_NAME_LEN 27
-
-enum stf_v_line_id {
- STF_V_LINE_WR = 0,
- STF_V_LINE_ISP,
- STF_V_LINE_MAX,
-};
-
-enum stf_capture_type {
- STF_CAPTURE_RAW = 0,
- STF_CAPTURE_YUV,
- STF_CAPTURE_NUM,
-};
-
-struct stfcamss_buffer {
- struct vb2_v4l2_buffer vb;
- dma_addr_t addr[2];
- struct list_head queue;
-};
-
-struct fract {
- u8 numerator;
- u8 denominator;
-};
-
-/*
- * struct stfcamss_format_info - ISP media bus format information
- * @code: V4L2 media bus format code
- * @pixelformat: V4L2 pixel format FCC identifier
- * @planes: Number of planes
- * @vsub: Vertical subsampling (for each plane)
- * @bpp: Bits per pixel when stored in memory (for each plane)
- */
-struct stfcamss_format_info {
- u32 code;
- u32 pixelformat;
- u8 planes;
- u8 vsub[3];
- u8 bpp;
-};
-
-struct stfcamss_video {
- struct stfcamss *stfcamss;
- struct vb2_queue vb2_q;
- struct video_device vdev;
- struct media_pad pad;
- struct v4l2_format active_fmt;
- enum v4l2_buf_type type;
- const struct stfcamss_video_ops *ops;
- struct mutex lock; /* serialize device access */
- struct mutex q_lock; /* protects the queue */
- unsigned int bpl_alignment;
- const struct stfcamss_format_info *formats;
- unsigned int nformats;
- struct v4l2_subdev *source_subdev;
-};
-
-struct stfcamss_video_ops {
- int (*queue_buffer)(struct stfcamss_video *video,
- struct stfcamss_buffer *buf);
- int (*flush_buffers)(struct stfcamss_video *video,
- enum vb2_buffer_state state);
- void (*start_streaming)(struct stfcamss_video *video);
- void (*stop_streaming)(struct stfcamss_video *video);
-};
-
-int stf_video_register(struct stfcamss_video *video,
- struct v4l2_device *v4l2_dev, const char *name);
-
-void stf_video_unregister(struct stfcamss_video *video);
-
-#endif /* STF_VIDEO_H */
diff --git a/drivers/staging/media/tegra-video/Makefile b/drivers/staging/media/tegra-video/Makefile
index 6c7552e05109..96380b5dbd8b 100644
--- a/drivers/staging/media/tegra-video/Makefile
+++ b/drivers/staging/media/tegra-video/Makefile
@@ -6,5 +6,6 @@ tegra-video-objs := \
csi.o
tegra-video-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o
+tegra-video-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra20.o
tegra-video-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video.o
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 7842104ca933..41d57dac61a7 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -12,6 +12,7 @@
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/tegra-mipi-cal.h>
#include <media/v4l2-fwnode.h>
@@ -20,17 +21,6 @@
#define MHZ 1000000
-static inline struct tegra_csi *
-host1x_client_to_csi(struct host1x_client *client)
-{
- return container_of(client, struct tegra_csi, client);
-}
-
-static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev)
-{
- return container_of(subdev, struct tegra_csi_channel, subdev);
-}
-
/*
* CSI is a separate subdevice which has 6 source pads to generate
* test pattern. CSI subdevice pad ops are used only for TPG and
@@ -455,6 +445,22 @@ static const struct v4l2_subdev_ops tegra_csi_ops = {
.pad = &tegra_csi_pad_ops,
};
+struct v4l2_subdev *tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan)
+{
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+
+ pad = media_pad_remote_pad_first(&chan->pad);
+ if (!pad)
+ return NULL;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+ if (!subdev)
+ return NULL;
+
+ return subdev->ops == &tegra_csi_ops ? subdev : NULL;
+}
+
static int tegra_csi_channel_alloc(struct tegra_csi *csi,
struct device_node *node,
unsigned int port_num, unsigned int lanes,
@@ -704,6 +710,8 @@ static int __maybe_unused csi_runtime_suspend(struct device *dev)
clk_bulk_disable_unprepare(csi->soc->num_clks, csi->clks);
+ regulator_disable(csi->vdd);
+
return 0;
}
@@ -712,13 +720,23 @@ static int __maybe_unused csi_runtime_resume(struct device *dev)
struct tegra_csi *csi = dev_get_drvdata(dev);
int ret;
+ ret = regulator_enable(csi->vdd);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD supply: %d\n", ret);
+ return ret;
+ }
+
ret = clk_bulk_prepare_enable(csi->soc->num_clks, csi->clks);
if (ret < 0) {
dev_err(csi->dev, "failed to enable clocks: %d\n", ret);
- return ret;
+ goto disable_vdd;
}
return 0;
+
+disable_vdd:
+ regulator_disable(csi->vdd);
+ return ret;
}
static int tegra_csi_init(struct host1x_client *client)
@@ -796,6 +814,11 @@ static int tegra_csi_probe(struct platform_device *pdev)
return ret;
}
+ csi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
+ if (IS_ERR(csi->vdd))
+ return dev_err_probe(&pdev->dev, PTR_ERR(csi->vdd),
+ "failed to get VDD supply");
+
if (!pdev->dev.pm_domain) {
ret = -ENOENT;
dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret);
@@ -804,6 +827,17 @@ static int tegra_csi_probe(struct platform_device *pdev)
csi->dev = &pdev->dev;
csi->ops = csi->soc->ops;
+
+ if (csi->soc->mipi_ops) {
+ ret = devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node,
+ csi->soc->mipi_ops);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to add MIPI calibration operations\n");
+ }
+
+ mutex_init(&csi->mipi_lock);
+
platform_set_drvdata(pdev, csi);
pm_runtime_enable(&pdev->dev);
@@ -836,6 +870,12 @@ static void tegra_csi_remove(struct platform_device *pdev)
}
static const struct of_device_id tegra_csi_of_id_table[] = {
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
#endif
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 609c5952e050..f0277cb42446 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -115,6 +115,7 @@ struct tegra_csi_ops {
* struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
*
* @ops: csi hardware operations
+ * @mipi_ops: MIPI calibration operations
* @csi_max_channels: supported max streaming channels
* @clk_names: csi and cil clock names
* @num_clks: total clocks count
@@ -123,6 +124,7 @@ struct tegra_csi_ops {
*/
struct tegra_csi_soc {
const struct tegra_csi_ops *ops;
+ const struct tegra_mipi_ops *mipi_ops;
unsigned int csi_max_channels;
const char * const *clk_names;
unsigned int num_clks;
@@ -130,6 +132,12 @@ struct tegra_csi_soc {
unsigned int tpg_frmrate_table_size;
};
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern const struct tegra_csi_soc tegra20_csi_soc;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+extern const struct tegra_csi_soc tegra30_csi_soc;
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
extern const struct tegra_csi_soc tegra210_csi_soc;
#endif
@@ -141,8 +149,10 @@ extern const struct tegra_csi_soc tegra210_csi_soc;
* @client: host1x_client struct
* @iomem: register base
* @clks: clock for CSI and CIL
+ * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
* @soc: pointer to SoC data structure
* @ops: csi operations
+ * @mipi_lock: for MIPI calibration operations
* @csi_chans: list head for CSI channels
*/
struct tegra_csi {
@@ -150,11 +160,23 @@ struct tegra_csi {
struct host1x_client client;
void __iomem *iomem;
struct clk_bulk_data *clks;
+ struct regulator *vdd;
const struct tegra_csi_soc *soc;
const struct tegra_csi_ops *ops;
+ struct mutex mipi_lock; /* for register access */
struct list_head csi_chans;
};
+static inline struct tegra_csi *host1x_client_to_csi(struct host1x_client *client)
+{
+ return container_of(client, struct tegra_csi, client);
+}
+
+static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct tegra_csi_channel, subdev);
+}
+
void tegra_csi_error_recover(struct v4l2_subdev *subdev);
void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan,
u8 csi_port_num,
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index aa9ff7fec4f9..e513e6ccb776 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -4,6 +4,9 @@
*
* Copyright (C) 2023 SKIDATA GmbH
* Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ *
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
*/
/*
@@ -12,10 +15,15 @@
*/
#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
#include <linux/delay.h>
#include <linux/host1x.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/tegra-mipi-cal.h>
#include <linux/v4l2-mediabus.h>
#include "vip.h"
@@ -23,19 +31,27 @@
#define TEGRA_VI_SYNCPT_WAIT_TIMEOUT msecs_to_jiffies(200)
-/* This are just good-sense numbers. The actual min/max is not documented. */
#define TEGRA20_MIN_WIDTH 32U
+#define TEGRA20_MAX_WIDTH 8190U
#define TEGRA20_MIN_HEIGHT 32U
-#define TEGRA20_MAX_WIDTH 2048U
-#define TEGRA20_MAX_HEIGHT 2048U
+#define TEGRA20_MAX_HEIGHT 8190U
+
+/* Tegra20/Tegra30 has 2 outputs in VI */
+enum tegra_vi_out {
+ TEGRA_VI_OUT_1 = 0,
+ TEGRA_VI_OUT_2 = 1,
+};
/* --------------------------------------------------------------------------
* Registers
*/
-#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060
-#define VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT BIT(8)
-#define VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT 0
+#define TEGRA_VI_CONT_SYNCPT_OUT(n) (0x0060 + (n) * 4)
+#define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
+#define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
+
+#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
+#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
#define TEGRA_VI_VI_INPUT_CONTROL 0x0088
#define VI_INPUT_FIELD_DETECT BIT(27)
@@ -47,6 +63,7 @@
#define VI_INPUT_YUV_INPUT_FORMAT_YVYU (3 << VI_INPUT_YUV_INPUT_FORMAT_SFT)
#define VI_INPUT_INPUT_FORMAT_SFT 2 /* bits [5:2] */
#define VI_INPUT_INPUT_FORMAT_YUV422 (0 << VI_INPUT_INPUT_FORMAT_SFT)
+#define VI_INPUT_INPUT_FORMAT_BAYER (2 << VI_INPUT_INPUT_FORMAT_SFT)
#define VI_INPUT_VIP_INPUT_ENABLE BIT(1)
#define TEGRA_VI_VI_CORE_CONTROL 0x008c
@@ -67,7 +84,7 @@
#define VI_VI_CORE_CONTROL_OUTPUT_TO_EPP_SFT 2
#define VI_VI_CORE_CONTROL_OUTPUT_TO_ISP_SFT 0
-#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090
+#define TEGRA_VI_VI_OUTPUT_CONTROL(n) (0x0090 + (n) * 4)
#define VI_OUTPUT_FORMAT_EXT BIT(22)
#define VI_OUTPUT_V_DIRECTION BIT(20)
#define VI_OUTPUT_H_DIRECTION BIT(19)
@@ -81,6 +98,10 @@
#define VI_OUTPUT_OUTPUT_FORMAT_SFT 0
#define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
+/* TEGRA_VI_OUT_2 supported formats */
+#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
+#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
+#define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define TEGRA_VI_VIP_H_ACTIVE 0x00a4
#define VI_VIP_H_ACTIVE_PERIOD_SFT 16 /* active pixels/line, must be even */
@@ -90,26 +111,26 @@
#define VI_VIP_V_ACTIVE_PERIOD_SFT 16 /* active lines */
#define VI_VIP_V_ACTIVE_START_SFT 0
-#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4
-#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8
+#define TEGRA_VI_VB0_START_ADDRESS(n) (0x00c4 + (n) * 44)
+#define TEGRA_VI_VB0_BASE_ADDRESS(n) (0x00c8 + (n) * 44)
#define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc
#define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0
#define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4
#define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8
-#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0
-#define VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT 16
-#define VI_FIRST_OUTPUT_FRAME_WIDTH_SFT 0
+#define TEGRA_VI_OUTPUT_FRAME_SIZE(n) (0x00e0 + (n) * 24)
+#define VI_OUTPUT_FRAME_HEIGHT_SFT 16
+#define VI_OUTPUT_FRAME_WIDTH_SFT 0
-#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4
+#define TEGRA_VI_VB0_COUNT(n) (0x00e4 + (n) * 24)
-#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8
-#define VI_VB0_SIZE_FIRST_V_SFT 16
-#define VI_VB0_SIZE_FIRST_H_SFT 0
+#define TEGRA_VI_VB0_SIZE(n) (0x00e8 + (n) * 24)
+#define VI_VB0_SIZE_V_SFT 16
+#define VI_VB0_SIZE_H_SFT 0
-#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec
-#define VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT 30
-#define VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT 0
+#define TEGRA_VI_VB0_BUFFER_STRIDE(n) (0x00ec + (n) * 24)
+#define VI_VB0_BUFFER_STRIDE_CHROMA_SFT 30
+#define VI_VB0_BUFFER_STRIDE_LUMA_SFT 0
#define TEGRA_VI_H_LPF_CONTROL 0x0108
#define VI_H_LPF_CONTROL_CHROMA_SFT 16
@@ -137,15 +158,113 @@
#define VI_CAMERA_CONTROL_TEST_MODE BIT(1)
#define VI_CAMERA_CONTROL_VIP_ENABLE BIT(0)
-#define TEGRA_VI_VI_ENABLE 0x01a4
+#define TEGRA_VI_VI_ENABLE(n) (0x01a4 + (n) * 4)
#define VI_VI_ENABLE_SW_FLOW_CONTROL_OUT1 BIT(1)
#define VI_VI_ENABLE_FIRST_OUTPUT_TO_MEM_DISABLE BIT(0)
#define TEGRA_VI_VI_RAISE 0x01ac
#define VI_VI_RAISE_ON_EDGE BIT(0)
+#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
+#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
+#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
+#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
+
+/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
+#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
+#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
+#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
+#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
+#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
+#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
+#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
+#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
+#define CSI_PP_HEADER_EC_ENABLE BIT(27)
+#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
+#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
+#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
+#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
+#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
+#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
+#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
+#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
+#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
+#define CSI_PP_DATA_TYPE(n) ((n) << 8)
+#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
+#define CSI_PP_WORD_COUNT_HEADER BIT(6)
+#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
+#define CSI_PP_PACKET_HEADER_SENT BIT(4)
+#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
+#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
+#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
+#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
+#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
+#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
+#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
+#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
+#define CSI_PP_VSYNC_START_MARKER BIT(4)
+#define CSI_PP_SINGLE_SHOT BIT(2)
+#define CSI_PP_NOP 0
+#define CSI_PP_ENABLE 1
+#define CSI_PP_DISABLE 2
+#define CSI_PP_RESET 3
+#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
+#define CSI_A_PHY_CIL_NOP 0x0
+#define CSI_A_PHY_CIL_ENABLE 0x1
+#define CSI_A_PHY_CIL_DISABLE 0x2
+#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
+#define CSI_B_PHY_CIL_NOP (0x0 << 16)
+#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
+#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
+#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
+#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
+#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
+#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
+#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
+#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
+#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
+#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
+#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
+#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
+#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
+#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
+#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
+#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
+#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
+#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
+#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
+#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
+#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
+#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
+#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
+#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
+#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
+#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
+#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
+#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
+#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
+#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
+#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
+#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
+#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
+#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
+#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
+#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
+#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
+#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
+#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
+#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
+#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
+#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
+#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
+#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
+#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
+
/* --------------------------------------------------------------------------
- * VI
+ * Read and Write helpers
*/
static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
@@ -153,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
writel(val, chan->vi->iomem + addr);
}
+static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
+{
+ return readl(chan->vi->iomem + addr);
+}
+
+static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
+{
+ writel(val, csi_chan->csi->iomem + addr);
+}
+
+static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
+{
+ return readl(csi_chan->csi->iomem + addr);
+}
+
+static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
+{
+ writel(val, csi->iomem + addr);
+}
+
+static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
+{
+ return readl(csi->iomem + addr);
+}
+
+/* --------------------------------------------------------------------------
+ * VI
+ */
+
/*
* Get the main input format (YUV/RGB...) and the YUV variant as values to
* be written into registers for the current VI input mbus code.
@@ -179,6 +327,18 @@ static void tegra20_vi_get_input_formats(struct tegra_vi_channel *chan,
case MEDIA_BUS_FMT_YVYU8_2X8:
(*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_YVYU;
break;
+ /* RAW8 */
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ /* RAW10 */
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ (*main_input_format) = VI_INPUT_INPUT_FORMAT_BAYER;
+ break;
}
}
@@ -213,6 +373,18 @@ static void tegra20_vi_get_output_formats(struct tegra_vi_channel *chan,
case V4L2_PIX_FMT_YVU420:
(*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR;
break;
+ /* RAW8 */
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT;
+ break;
}
}
@@ -251,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
{
struct tegra_vi *vi = chan->vi;
- struct host1x_syncpt *out_sp;
+ struct host1x_syncpt *out_sp, *fs_sp;
out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
if (!out_sp)
- return -ENOMEM;
+ return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
chan->mw_ack_sp[0] = out_sp;
+ fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
+ if (!fs_sp)
+ return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
+
+ chan->frame_start_sp[0] = fs_sp;
+
return 0;
}
static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
{
host1x_syncpt_put(chan->mw_ack_sp[0]);
+ host1x_syncpt_put(chan->frame_start_sp[0]);
}
static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
@@ -272,18 +451,13 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
+ pix->bytesperline = roundup(pix->width, 8) * bpp;
+ pix->sizeimage = pix->bytesperline * pix->height;
+
switch (pix->pixelformat) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- pix->bytesperline = roundup(pix->width, 2) * 2;
- pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
- break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- pix->bytesperline = roundup(pix->width, 8);
- pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
+ pix->sizeimage = pix->sizeimage * 3 / 2;
break;
}
}
@@ -305,6 +479,16 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
+ /* RAW8 */
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SBGGR10:
if (chan->vflip)
chan->start_offset += stride * (height - 1);
if (chan->hflip)
@@ -367,48 +551,111 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
- tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, base);
- tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_FIRST, base + chan->start_offset);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_1), base);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), base + chan->start_offset);
+ break;
+ /* RAW8 */
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SBGGR10:
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_2), base);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_2), base + chan->start_offset);
break;
}
}
static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
- struct tegra_channel_buffer *buf)
+ struct tegra_channel_buffer *buf,
+ struct tegra_csi_channel *csi_chan)
{
+ u32 val;
int err;
- chan->next_out_sp_idx++;
-
tegra20_channel_vi_buffer_setup(chan, buf);
- tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
+ if (csi_chan) {
+ u32 port = csi_chan->csi_port_nums[0] & 1;
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
+
+ /*
+ * ERESTARTSYS workaround for syncpoints is used because host1x_syncpt_wait
+ * is unconditionally interruptible. This is not an issue with single shots
+ * or low resolution capture, but -ERESTARTSYS occurs quite often with high
+ * resolution or high framerate captures and if not addressed here will
+ * cause capture to fail entirely.
+ *
+ * TODO: once uninterruptible version of host1x_syncpt_wait is available,
+ * host1x_syncpt_wait should be swapped and ERESTARTSYS workaround can be
+ * removed.
+ */
+
+ val = host1x_syncpt_read(chan->frame_start_sp[0]);
+ do {
+ err = host1x_syncpt_wait(chan->frame_start_sp[0],
+ val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
+ } while (err == -ERESTARTSYS);
+
+ if (err) {
+ if (err != -ERESTARTSYS)
+ dev_err_ratelimited(&chan->video.dev,
+ "frame start syncpt timeout: %d\n", err);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
+ goto exit;
+ }
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+ } else {
+ tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
+ }
+
+ val = host1x_syncpt_read(chan->mw_ack_sp[0]);
+ do {
+ err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
+ TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
+ } while (err == -ERESTARTSYS);
- /* Wait for syncpt counter to reach frame start event threshold */
- err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
- TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
if (err) {
- host1x_syncpt_incr(chan->mw_ack_sp[0]);
- dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
- release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
- return err;
+ if (err != -ERESTARTSYS)
+ dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
+ goto exit;
}
- tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
- VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
+ if (!csi_chan)
+ tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
+ VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
+exit:
release_buffer(chan, buf, VB2_BUF_STATE_DONE);
- return 0;
+ return err;
}
static int tegra20_chan_capture_kthread_start(void *data)
{
struct tegra_vi_channel *chan = data;
struct tegra_channel_buffer *buf;
+ struct v4l2_subdev *csi_subdev = NULL;
+ struct tegra_csi_channel *csi_chan = NULL;
unsigned int retries = 0;
int err = 0;
+ csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
+ if (csi_subdev)
+ csi_chan = to_csi_chan(csi_subdev);
+
while (1) {
/*
* Source is not streaming if error is non-zero.
@@ -433,7 +680,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
list_del_init(&buf->queue);
spin_unlock(&chan->start_lock);
- err = tegra20_channel_capture_frame(chan, buf);
+ err = tegra20_channel_capture_frame(chan, buf, csi_chan);
if (!err) {
retries = 0;
continue;
@@ -451,55 +698,37 @@ static int tegra20_chan_capture_kthread_start(void *data)
static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
{
u32 output_fourcc = chan->format.pixelformat;
+ u32 data_type = chan->fmtinfo->img_dt;
int width = chan->format.width;
int height = chan->format.height;
- int stride_l = chan->format.bytesperline;
+ int stride_l = chan->format.bytesperline * height;
int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 ||
output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0;
- int main_output_format;
- int yuv_output_format;
-
- tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
-
- /*
- * Set up low pass filter. Use 0x240 for chromaticity and 0x240
- * for luminance, which is the default and means not to touch
- * anything.
- */
- tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
- 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
- 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
-
- /* Set up raise-on-edge, so we get an interrupt on end of frame. */
- tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
-
- tegra20_vi_write(chan, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL,
- (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
- (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
- yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
- main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
+ enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
+ data_type == TEGRA_IMAGE_DT_RAW10) ?
+ TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
/* Set up frame size */
- tegra20_vi_write(chan, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE,
- height << VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT |
- width << VI_FIRST_OUTPUT_FRAME_WIDTH_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
+ height << VI_OUTPUT_FRAME_HEIGHT_SFT |
+ width << VI_OUTPUT_FRAME_WIDTH_SFT);
/* First output memory enabled */
- tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0);
+ tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0);
/* Set the number of frames in the buffer */
- tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT_FIRST, 1);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT(output_channel), 1);
/* Set up buffer frame size */
- tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE_FIRST,
- height << VI_VB0_SIZE_FIRST_V_SFT |
- width << VI_VB0_SIZE_FIRST_H_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE(output_channel),
+ height << VI_VB0_SIZE_V_SFT |
+ width << VI_VB0_SIZE_H_SFT);
- tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST,
- stride_l << VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT |
- stride_c << VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE(output_channel),
+ stride_l << VI_VB0_BUFFER_STRIDE_LUMA_SFT |
+ stride_c << VI_VB0_BUFFER_STRIDE_CHROMA_SFT);
- tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0);
+ tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0);
}
static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
@@ -508,18 +737,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
struct media_pipeline *pipe = &chan->video.pipe;
int err;
- chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
-
err = video_device_pipeline_start(&chan->video, pipe);
if (err)
goto error_pipeline_start;
- tegra20_camera_capture_setup(chan);
+ /*
+ * Set up low pass filter. Use 0x240 for chromaticity and 0x240
+ * for luminance, which is the default and means not to touch
+ * anything.
+ */
+ tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
+ 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
+ 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
+
+ /* Set up raise-on-edge, so we get an interrupt on end of frame. */
+ tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
err = tegra_channel_set_stream(chan, true);
if (err)
goto error_set_stream;
+ tegra20_camera_capture_setup(chan);
+
chan->sequence = 0;
chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
@@ -567,20 +806,40 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
.vi_stop_streaming = tegra20_vi_stop_streaming,
};
-#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
-{ \
- .code = MEDIA_BUS_FMT_##MBUS_CODE, \
- .bpp = BPP, \
- .fourcc = V4L2_PIX_FMT_##FOURCC, \
+#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
+{ \
+ .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
+ .bit_width = BIT_WIDTH, \
+ .code = MEDIA_BUS_FMT_##MBUS_CODE, \
+ .bpp = BPP, \
+ .fourcc = V4L2_PIX_FMT_##FOURCC, \
}
static const struct tegra_video_format tegra20_video_formats[] = {
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
- TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
- TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
- TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
+ /* YUV422 */
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 2, UYVY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 2, VYUY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 2, YUYV),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 2, YVYU),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 2, UYVY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_1X16, 2, VYUY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_1X16, 2, YUYV),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_1X16, 2, YVYU),
+ /* YUV420P */
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YUV420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YVU420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 1, YUV420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 1, YVU420),
+ /* RAW 8 */
+ TEGRA20_VIDEO_FMT(RAW8, 8, SRGGB8_1X8, 2, SRGGB8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SGRBG8_1X8, 2, SGRBG8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SGBRG8_1X8, 2, SGBRG8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SBGGR8_1X8, 2, SBGGR8),
+ /* RAW 10 */
+ TEGRA20_VIDEO_FMT(RAW10, 10, SRGGB10_1X10, 2, SRGGB10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SGRBG10_1X10, 2, SGRBG10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SGBRG10_1X10, 2, SGBRG10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SBGGR10_1X10, 2, SBGGR10),
};
const struct tegra_vi_soc tegra20_vi_soc = {
@@ -588,12 +847,352 @@ const struct tegra_vi_soc tegra20_vi_soc = {
.nformats = ARRAY_SIZE(tegra20_video_formats),
.default_video_format = &tegra20_video_formats[0],
.ops = &tegra20_vi_ops,
- .vi_max_channels = 1, /* parallel input (VIP) */
- .vi_max_clk_hz = 150000000,
+ .hw_revision = 1,
+ .vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */
+ .vi_max_clk_hz = 450000000,
.has_h_v_flip = true,
};
/* --------------------------------------------------------------------------
+ * MIPI Calibration
+ */
+static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
+{
+ struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
+ unsigned int port = mipi->pads;
+ u32 value;
+ int ret;
+
+ guard(mutex)(&csi->mipi_lock);
+
+ ret = pm_runtime_resume_and_get(csi->dev);
+ if (ret < 0) {
+ dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
+ return ret;
+ }
+
+ tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_HSPDOS(4) |
+ CSI_CIL_MIPI_CAL_HSPUOS(3) |
+ CSI_CIL_MIPI_CAL_TERMOS(0));
+ tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
+ CSI_PAD_DRIV_DN_REF(5) |
+ CSI_PAD_DRIV_UP_REF(7) |
+ CSI_PAD_TERM_REF(0));
+
+ /* CSI B */
+ value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4);
+
+ if (port == PORT_B)
+ value |= CSI_CIL_MIPI_CAL_SEL_B;
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
+
+ /* CSI A */
+ value = CSI_CIL_MIPI_CAL_STARTCAL |
+ CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
+ CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4);
+
+ if (port == PORT_A)
+ value |= CSI_CIL_MIPI_CAL_SEL_A;
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
+
+ return 0;
+}
+
+static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
+{
+ struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
+ void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
+ unsigned int port = mipi->pads;
+ u32 value, pp = 0, cil = 0;
+ int ret;
+
+ /* This part is only for CSI */
+ if (port > PORT_B) {
+ pm_runtime_put(csi->dev);
+
+ return 0;
+ }
+
+ guard(mutex)(&csi->mipi_lock);
+
+ ret = readl_relaxed_poll_timeout(cil_status_reg, value,
+ value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
+ if (ret < 0) {
+ dev_warn(csi->dev, "MIPI calibration timeout!\n");
+ goto exit;
+ }
+
+ /* clear status */
+ tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
+ ret = readl_relaxed_poll_timeout(cil_status_reg, value,
+ !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
+ if (ret < 0) {
+ dev_warn(csi->dev, "MIPI calibration status timeout!\n");
+ goto exit;
+ }
+
+ pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
+ cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
+ if (pp | cil) {
+ dev_warn(csi->dev, "Calibration status not been cleared!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
+
+ /* un-select to avoid interference with DSI */
+ tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4));
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
+ CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4));
+
+ pm_runtime_put(csi->dev);
+
+ return ret;
+}
+
+static const struct tegra_mipi_ops tegra20_mipi_ops = {
+ .start_calibration = tegra20_start_pad_calibration,
+ .finish_calibration = tegra20_finish_pad_calibration,
+};
+
+/* --------------------------------------------------------------------------
+ * CSI
+ */
+static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
+{
+ tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
+}
+
+static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
+ u8 portno)
+{
+ struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
+ int width = vi_chan->format.width;
+ int height = vi_chan->format.height;
+ u32 data_type = vi_chan->fmtinfo->img_dt;
+ u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
+ enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
+
+ unsigned int main_output_format, yuv_output_format;
+ unsigned int port = portno & 1;
+ u32 value;
+
+ tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
+
+ switch (data_type) {
+ case TEGRA_IMAGE_DT_RAW8:
+ case TEGRA_IMAGE_DT_RAW10:
+ output_channel = TEGRA_VI_OUT_2;
+ if (port == PORT_A)
+ main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
+ else
+ main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
+ break;
+ }
+
+ tegra20_csi_capture_clean(csi_chan);
+
+ /* CSI port cleanup */
+ tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
+ tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
+ tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
+ CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
+ CSI_PP_EXP_FRAME_HEIGHT(height) |
+ CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
+ CSI_PP_LINE_TIMEOUT_ENABLE);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
+ CSI_PP_OUTPUT_FORMAT_PIXEL |
+ CSI_PP_DATA_TYPE(data_type) |
+ CSI_PP_CRC_CHECK_ENABLE |
+ CSI_PP_WORD_COUNT_HEADER |
+ CSI_PP_DATA_IDENTIFIER_ENABLE |
+ CSI_PP_PACKET_HEADER_SENT |
+ port);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
+ CSI_SKIP_PACKET_THRESHOLD(0x3f) |
+ (csi_chan->numlanes - 1));
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
+ CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
+ 0x5); /* Clock settle time */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
+ host1x_syncpt_id(vi_chan->frame_start_sp[0])
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
+ host1x_syncpt_id(vi_chan->mw_ack_sp[0])
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
+
+ value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
+ CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
+ (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
+ (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
+ yuv_output_format | main_output_format);
+
+ return 0;
+};
+
+static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
+{
+ struct tegra_csi *csi = csi_chan->csi;
+ unsigned int port = portno & 1;
+ u32 value;
+
+ value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
+ dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
+
+ value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
+ dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+
+ if (csi_chan->numlanes == 4) {
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
+ CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
+ } else {
+ value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
+ CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
+ }
+}
+
+static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
+{
+ u8 *portnos = csi_chan->csi_port_nums;
+ int ret, i;
+
+ for (i = 0; i < csi_chan->numgangports; i++) {
+ ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
+ if (ret)
+ goto stream_start_fail;
+ }
+
+ return 0;
+
+stream_start_fail:
+ for (i = i - 1; i >= 0; i--)
+ tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
+
+ return ret;
+}
+
+static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
+{
+ u8 *portnos = csi_chan->csi_port_nums;
+ int i;
+
+ for (i = 0; i < csi_chan->numgangports; i++)
+ tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
+}
+
+static const struct tegra_csi_ops tegra20_csi_ops = {
+ .csi_start_streaming = tegra20_csi_start_streaming,
+ .csi_stop_streaming = tegra20_csi_stop_streaming,
+};
+
+static const char * const tegra20_csi_clks[] = {
+ NULL,
+};
+
+const struct tegra_csi_soc tegra20_csi_soc = {
+ .ops = &tegra20_csi_ops,
+ .mipi_ops = &tegra20_mipi_ops,
+ .csi_max_channels = 2, /* CSI-A and CSI-B */
+ .clk_names = tegra20_csi_clks,
+ .num_clks = ARRAY_SIZE(tegra20_csi_clks),
+};
+
+static const char * const tegra30_csi_clks[] = {
+ "csi",
+ "csia-pad",
+ "csib-pad",
+};
+
+const struct tegra_csi_soc tegra30_csi_soc = {
+ .ops = &tegra20_csi_ops,
+ .mipi_ops = &tegra20_mipi_ops,
+ .csi_max_channels = 2, /* CSI-A and CSI-B */
+ .clk_names = tegra30_csi_clks,
+ .num_clks = ARRAY_SIZE(tegra30_csi_clks),
+};
+
+/* --------------------------------------------------------------------------
* VIP
*/
@@ -606,13 +1205,17 @@ const struct tegra_vi_soc tegra20_vi_soc = {
static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
{
struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&vip_chan->subdev);
+ u32 data_type = vi_chan->fmtinfo->img_dt;
int width = vi_chan->format.width;
int height = vi_chan->format.height;
-
- unsigned int main_input_format;
- unsigned int yuv_input_format;
+ enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
+ data_type == TEGRA_IMAGE_DT_RAW10) ?
+ TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
+ unsigned int main_input_format, yuv_input_format;
+ unsigned int main_output_format, yuv_output_format;
tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
+ tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
@@ -638,13 +1241,18 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
GENMASK(9, 2) << VI_DATA_INPUT_SFT);
tegra20_vi_write(vi_chan, TEGRA_VI_PIN_INVERSION, 0);
- tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT_1,
- VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT |
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
host1x_syncpt_id(vi_chan->mw_ack_sp[0])
- << VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT);
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
+ (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
+ (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
+ yuv_output_format | main_output_format);
+
return 0;
}
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 9c0b38585d63..f14cdc7b5211 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -160,8 +160,8 @@ static void tegra_channel_buffer_queue(struct vb2_buffer *vb)
wake_up_interruptible(&chan->start_wait);
}
-struct v4l2_subdev *
-tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan)
+static struct v4l2_subdev *
+tegra_channel_get_remote_bridge_subdev(struct tegra_vi_channel *chan)
{
struct media_pad *pad;
@@ -182,7 +182,7 @@ tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
struct media_entity *entity;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
if (!subdev)
return NULL;
@@ -204,7 +204,7 @@ static int tegra_channel_enable_stream(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
int ret;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
ret = v4l2_subdev_call(subdev, video, s_stream, true);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
@@ -217,7 +217,7 @@ static int tegra_channel_disable_stream(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
int ret;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
ret = v4l2_subdev_call(subdev, video, s_stream, false);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
@@ -476,17 +476,11 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
fse.code = fmtinfo->code;
ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse);
if (ret) {
- if (!v4l2_subdev_has_op(subdev, pad, get_selection)) {
+ if (!v4l2_subdev_has_op(subdev, pad, get_selection) ||
+ v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel)) {
try_crop->width = 0;
try_crop->height = 0;
} else {
- ret = v4l2_subdev_call(subdev, pad, get_selection,
- NULL, &sdsel);
- if (ret) {
- ret = -EINVAL;
- goto out_free;
- }
-
try_crop->width = sdsel.r.width;
try_crop->height = sdsel.r.height;
}
@@ -968,6 +962,7 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan)
}
#else
struct v4l2_subdev *subdev;
+ struct v4l2_ctrl *hflip, *vflip;
/* custom control */
v4l2_ctrl_new_custom(&chan->ctrl_handler, &syncpt_timeout_ctrl, NULL);
@@ -993,11 +988,13 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan)
return ret;
}
- if (chan->vi->soc->has_h_v_flip) {
+ hflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_HFLIP);
+ if (chan->vi->soc->has_h_v_flip && !hflip)
v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- }
+ vflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_VFLIP);
+ if (chan->vi->soc->has_h_v_flip && !vflip)
+ v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
#endif
/* setup the controls */
@@ -1421,29 +1418,19 @@ static int __maybe_unused vi_runtime_resume(struct device *dev)
struct tegra_vi *vi = dev_get_drvdata(dev);
int ret;
- ret = regulator_enable(vi->vdd);
- if (ret) {
- dev_err(dev, "failed to enable VDD supply: %d\n", ret);
- return ret;
- }
-
ret = clk_set_rate(vi->clk, vi->soc->vi_max_clk_hz);
if (ret) {
dev_err(dev, "failed to set vi clock rate: %d\n", ret);
- goto disable_vdd;
+ return ret;
}
ret = clk_prepare_enable(vi->clk);
if (ret) {
dev_err(dev, "failed to enable vi clock: %d\n", ret);
- goto disable_vdd;
+ return ret;
}
return 0;
-
-disable_vdd:
- regulator_disable(vi->vdd);
- return ret;
}
static int __maybe_unused vi_runtime_suspend(struct device *dev)
@@ -1452,8 +1439,6 @@ static int __maybe_unused vi_runtime_suspend(struct device *dev)
clk_disable_unprepare(vi->clk);
- regulator_disable(vi->vdd);
-
return 0;
}
@@ -1634,11 +1619,11 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier)
goto unregister_video;
}
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
if (!subdev) {
ret = -ENODEV;
dev_err(vi->dev,
- "failed to get remote csi subdev: %d\n", ret);
+ "failed to get remote bridge subdev: %d\n", ret);
goto unregister_video;
}
@@ -1898,13 +1883,6 @@ static int tegra_vi_probe(struct platform_device *pdev)
return ret;
}
- vi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
- if (IS_ERR(vi->vdd)) {
- ret = PTR_ERR(vi->vdd);
- dev_err(&pdev->dev, "failed to get VDD supply: %d\n", ret);
- return ret;
- }
-
if (!pdev->dev.pm_domain) {
ret = -ENOENT;
dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret);
@@ -1959,7 +1937,7 @@ static void tegra_vi_remove(struct platform_device *pdev)
}
static const struct of_device_id tegra_vi_of_id_table[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vi", .data = &tegra20_vi_soc },
#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
index 1e6a5caa7082..46328e488aa3 100644
--- a/drivers/staging/media/tegra-video/vi.h
+++ b/drivers/staging/media/tegra-video/vi.h
@@ -94,7 +94,6 @@ struct tegra_vi_soc {
* @client: host1x_client struct
* @iomem: register base
* @clk: main clock for VI block
- * @vdd: vdd regulator for VI hardware, normally it is avdd_dsi_csi
* @soc: pointer to SoC data structure
* @ops: vi operations
* @vi_chans: list head for VI channels
@@ -104,7 +103,6 @@ struct tegra_vi {
struct host1x_client client;
void __iomem *iomem;
struct clk *clk;
- struct regulator *vdd;
const struct tegra_vi_soc *soc;
const struct tegra_vi_ops *ops;
struct list_head vi_chans;
@@ -127,7 +125,6 @@ struct tegra_vi {
* frame through host1x syncpoint counters (On Tegra20 used for the
* OUT_1 syncpt)
* @sp_incr_lock: protects cpu syncpoint increment.
- * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
*
* @kthread_start_capture: kthread to start capture of single frame when
* vb buffer is available. This thread programs VI CSI hardware
@@ -190,7 +187,6 @@ struct tegra_vi_channel {
struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
/* protects the cpu syncpoint increment */
spinlock_t sp_incr_lock[GANG_PORTS_MAX];
- u32 next_out_sp_idx;
struct task_struct *kthread_start_capture;
wait_queue_head_t start_wait;
@@ -296,7 +292,7 @@ struct tegra_video_format {
u32 fourcc;
};
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
extern const struct tegra_vi_soc tegra20_vi_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
index ae1ae03fa9de..2e84385708ba 100644
--- a/drivers/staging/media/tegra-video/video.c
+++ b/drivers/staging/media/tegra-video/video.c
@@ -121,10 +121,16 @@ static void host1x_video_remove(struct host1x_device *dev)
}
static const struct of_device_id host1x_video_subdevs[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vip", },
{ .compatible = "nvidia,tegra20-vi", },
#endif
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ { .compatible = "nvidia,tegra20-csi", },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ { .compatible = "nvidia,tegra30-csi", },
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-csi", },
{ .compatible = "nvidia,tegra210-vi", },
diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c
index 80cd3b113125..9ff1f1750a15 100644
--- a/drivers/staging/media/tegra-video/vip.c
+++ b/drivers/staging/media/tegra-video/vip.c
@@ -264,7 +264,7 @@ static void tegra_vip_remove(struct platform_device *pdev)
}
static const struct of_device_id tegra_vip_of_id_table[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc },
#endif
{ }
diff --git a/drivers/staging/media/tegra-video/vip.h b/drivers/staging/media/tegra-video/vip.h
index fdded00447e4..563059cbf5b5 100644
--- a/drivers/staging/media/tegra-video/vip.h
+++ b/drivers/staging/media/tegra-video/vip.h
@@ -50,7 +50,7 @@ struct tegra_vip_soc {
const struct tegra_vip_ops *ops;
};
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
extern const struct tegra_vip_soc tegra20_vip_soc;
#endif
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 5e7a63143a4a..1f5f55917d1c 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -453,16 +453,6 @@ void host1x_client_unregister(struct host1x_client *client);
int host1x_client_suspend(struct host1x_client *client);
int host1x_client_resume(struct host1x_client *client);
-struct tegra_mipi_device;
-
-struct tegra_mipi_device *tegra_mipi_request(struct device *device,
- struct device_node *np);
-void tegra_mipi_free(struct tegra_mipi_device *device);
-int tegra_mipi_enable(struct tegra_mipi_device *device);
-int tegra_mipi_disable(struct tegra_mipi_device *device);
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
-
/* host1x memory contexts */
struct host1x_memory_context {
diff --git a/include/linux/tegra-mipi-cal.h b/include/linux/tegra-mipi-cal.h
new file mode 100644
index 000000000000..2a540b50f65d
--- /dev/null
+++ b/include/linux/tegra-mipi-cal.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TEGRA_MIPI_CAL_H_
+#define __TEGRA_MIPI_CAL_H_
+
+struct tegra_mipi_device {
+ const struct tegra_mipi_ops *ops;
+ struct platform_device *pdev;
+ unsigned long pads;
+};
+
+/**
+ * Operations for Tegra MIPI calibration device
+ */
+struct tegra_mipi_ops {
+ /**
+ * @enable:
+ *
+ * Enable MIPI calibration device
+ */
+ int (*enable)(struct tegra_mipi_device *device);
+
+ /**
+ * @disable:
+ *
+ * Disable MIPI calibration device
+ */
+ int (*disable)(struct tegra_mipi_device *device);
+
+ /**
+ * @start_calibration:
+ *
+ * Start MIPI calibration
+ */
+ int (*start_calibration)(struct tegra_mipi_device *device);
+
+ /**
+ * @finish_calibration:
+ *
+ * Finish MIPI calibration
+ */
+ int (*finish_calibration)(struct tegra_mipi_device *device);
+};
+
+int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
+ const struct tegra_mipi_ops *ops);
+
+struct tegra_mipi_device *tegra_mipi_request(struct device *device,
+ struct device_node *np);
+void tegra_mipi_free(struct tegra_mipi_device *device);
+
+int tegra_mipi_enable(struct tegra_mipi_device *device);
+int tegra_mipi_disable(struct tegra_mipi_device *device);
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
+
+#endif /* __TEGRA_MIPI_CAL_H_ */
diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
index ea92ac623a45..05bfebab42b6 100644
--- a/include/linux/usb/uvc.h
+++ b/include/linux/usb/uvc.h
@@ -138,6 +138,9 @@
#define UVC_GUID_FORMAT_M420 \
{ 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_P010 \
+ { 'P', '0', '1', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_H264 \
{ 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 35c7a0546f02..d37fffc5dc3c 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -81,7 +81,6 @@ struct lirc_fh {
/**
* struct rc_dev - represents a remote control device
* @dev: driver model's view of this device
- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev
* @registered: set to true by rc_register_device(), false by
* rc_unregister_device
* @idle: used to keep track of RX state
@@ -156,7 +155,6 @@ struct lirc_fh {
*/
struct rc_dev {
struct device dev;
- bool managed_alloc;
bool registered;
bool idle;
bool encode_wakeup;
@@ -303,7 +301,7 @@ struct ir_raw_event {
#define US_TO_NS(usec) ((usec) * 1000)
#define MS_TO_US(msec) ((msec) * 1000)
-#define IR_MAX_DURATION MS_TO_US(500)
+#define IR_MAX_DURATION MS_TO_US(1000)
#define IR_DEFAULT_TIMEOUT MS_TO_US(125)
#define IR_MAX_TIMEOUT LIRC_VALUE_MASK
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index cd82e70ccbaa..d7abbd76a421 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -218,8 +218,9 @@ enum v4l2_fwnode_bus_type {
*
* Return: %0 on success or a negative error code on failure:
* %-ENOMEM on memory allocation failure
- * %-EINVAL on parsing failure, including @fwnode == NULL
+ * %-EINVAL on parsing failure
* %-ENXIO on mismatching bus types
+ * %-EPROBE_DEFER on NULL @fwnode
*/
int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep);
@@ -276,8 +277,9 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep);
*
* Return: %0 on success or a negative error code on failure:
* %-ENOMEM on memory allocation failure
- * %-EINVAL on parsing failure, including @fwnode == NULL
+ * %-EINVAL on parsing failure
* %-ENXIO on mismatching bus types
+ * %-EPROBE_DEFER on NULL @fwnode
*/
int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index a37d9a847196..d256b7ec8f84 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -1722,6 +1722,62 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
*/
int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable);
+/**
+ * __v4l2_subdev_get_frame_desc_passthrough - Helper to implement the
+ * subdev get_frame_desc operation in simple passthrough cases
+ * @sd: The subdevice
+ * @state: The locked subdevice active state
+ * @pad: The source pad index
+ * @fd: The mbus frame desc
+ *
+ * This helper implements the get_frame_desc operation for subdevices that pass
+ * streams through without modification.
+ *
+ * The helper iterates over the subdevice's sink pads, calls get_frame_desc on
+ * the remote subdevice connected to each sink pad, and collects the frame desc
+ * entries for streams that are routed to the given source pad according to the
+ * subdevice's routing table. Each entry is copied as-is from the upstream
+ * source, with the exception of the 'stream' field which is remapped to the
+ * source stream ID from the routing table.
+ *
+ * The frame desc type is taken from the first upstream source. If multiple
+ * sink pads are involved and the upstream sources report different frame desc
+ * types, -EPIPE is returned.
+ *
+ * The caller must hold the subdevice's active state lock. This variant is
+ * intended for drivers that need to perform additional work around the
+ * passthrough frame descriptor collection. Drivers that do not need any
+ * customization should use v4l2_subdev_get_frame_desc_passthrough() instead.
+ *
+ * Return: 0 on success, or a negative error code otherwise.
+ */
+int __v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd);
+
+/**
+ * v4l2_subdev_get_frame_desc_passthrough() - Helper to implement the subdev
+ * get_frame_desc operation in simple passthrough cases
+ * @sd: The subdevice
+ * @pad: The source pad index
+ * @fd: The mbus frame desc
+ *
+ * This function locks the subdevice's active state, calls
+ * __v4l2_subdev_get_frame_desc_passthrough(), and unlocks the state.
+ *
+ * This function can be assigned directly as the .get_frame_desc callback in
+ * &v4l2_subdev_pad_ops for subdevices that pass streams through without
+ * modification. Drivers that need to perform additional work should use
+ * __v4l2_subdev_get_frame_desc_passthrough() in their custom
+ * .get_frame_desc implementation instead.
+ *
+ * Return: 0 on success, or a negative error code otherwise.
+ */
+int v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd);
+
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */