From 50ba0232fd5312410f1b65247e774244f89a628e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 20:50:36 +0200 Subject: Merging upstream version 6.8.9. Signed-off-by: Daniel Baumann --- drivers/media/cec/core/cec-adap.c | 68 +- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 2 + drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/common/videobuf2/videobuf2-core.c | 515 +-- drivers/media/common/videobuf2/videobuf2-v4l2.c | 138 +- drivers/media/dvb-core/dvb_vb2.c | 21 +- drivers/media/dvb-frontends/rtl2832_sdr.c | 5 +- drivers/media/i2c/Kconfig | 73 + drivers/media/i2c/Makefile | 6 + drivers/media/i2c/adv7180.c | 28 +- drivers/media/i2c/adv7183.c | 2 - drivers/media/i2c/adv748x/adv748x-afe.c | 6 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +- drivers/media/i2c/adv748x/adv748x-hdmi.c | 6 +- drivers/media/i2c/adv7511-v4l2.c | 4 +- drivers/media/i2c/adv7604.c | 4 +- drivers/media/i2c/adv7842.c | 4 +- drivers/media/i2c/ak7375.c | 132 +- drivers/media/i2c/alvium-csi2.c | 2558 ++++++++++++++ drivers/media/i2c/alvium-csi2.h | 475 +++ drivers/media/i2c/ar0521.c | 5 +- drivers/media/i2c/ccs/Kconfig | 1 + drivers/media/i2c/ccs/ccs-core.c | 134 +- drivers/media/i2c/ccs/ccs-reg-access.c | 213 +- drivers/media/i2c/ccs/ccs-regs.h | 906 ++--- drivers/media/i2c/ccs/ccs.h | 3 + drivers/media/i2c/ccs/smiapp-reg-defs.h | 951 ++--- drivers/media/i2c/ds90ub913.c | 13 +- drivers/media/i2c/ds90ub953.c | 13 +- drivers/media/i2c/ds90ub960.c | 23 +- drivers/media/i2c/et8ek8/et8ek8_driver.c | 23 +- drivers/media/i2c/gc0308.c | 1451 ++++++++ drivers/media/i2c/gc2145.c | 1450 ++++++++ drivers/media/i2c/hi556.c | 13 +- drivers/media/i2c/hi846.c | 21 +- drivers/media/i2c/hi847.c | 9 +- drivers/media/i2c/imx208.c | 9 +- drivers/media/i2c/imx214.c | 207 +- drivers/media/i2c/imx219.c | 21 +- drivers/media/i2c/imx258.c | 9 +- drivers/media/i2c/imx274.c | 74 +- drivers/media/i2c/imx290.c | 18 +- drivers/media/i2c/imx296.c | 28 +- drivers/media/i2c/imx319.c | 19 +- drivers/media/i2c/imx334.c | 16 +- drivers/media/i2c/imx335.c | 223 +- drivers/media/i2c/imx355.c | 7 +- drivers/media/i2c/imx412.c | 16 +- drivers/media/i2c/imx415.c | 16 +- drivers/media/i2c/isl7998x.c | 6 +- drivers/media/i2c/max9286.c | 32 +- drivers/media/i2c/mt9m001.c | 16 +- drivers/media/i2c/mt9m111.c | 44 +- drivers/media/i2c/mt9m114.c | 102 +- drivers/media/i2c/mt9p031.c | 14 +- drivers/media/i2c/mt9t112.c | 1 - drivers/media/i2c/mt9v011.c | 34 +- drivers/media/i2c/mt9v032.c | 10 +- drivers/media/i2c/mt9v111.c | 44 +- drivers/media/i2c/og01a1b.c | 10 +- drivers/media/i2c/ov01a10.c | 12 +- drivers/media/i2c/ov02a10.c | 16 +- drivers/media/i2c/ov08d10.c | 9 +- drivers/media/i2c/ov08x40.c | 7 +- drivers/media/i2c/ov13858.c | 10 +- drivers/media/i2c/ov13b10.c | 10 +- drivers/media/i2c/ov2640.c | 16 +- drivers/media/i2c/ov2659.c | 6 +- drivers/media/i2c/ov2680.c | 34 +- drivers/media/i2c/ov2685.c | 4 +- drivers/media/i2c/ov2740.c | 379 +- drivers/media/i2c/ov4689.c | 2 +- drivers/media/i2c/ov5640.c | 49 +- drivers/media/i2c/ov5645.c | 16 +- drivers/media/i2c/ov5647.c | 12 +- drivers/media/i2c/ov5648.c | 72 +- drivers/media/i2c/ov5670.c | 23 +- drivers/media/i2c/ov5675.c | 9 +- drivers/media/i2c/ov5693.c | 18 +- drivers/media/i2c/ov5695.c | 8 +- drivers/media/i2c/ov64a40.c | 3690 ++++++++++++++++++++ drivers/media/i2c/ov6650.c | 64 +- drivers/media/i2c/ov7251.c | 36 +- drivers/media/i2c/ov7670.c | 37 +- drivers/media/i2c/ov772x.c | 30 +- drivers/media/i2c/ov7740.c | 47 +- drivers/media/i2c/ov8856.c | 9 +- drivers/media/i2c/ov8858.c | 16 +- drivers/media/i2c/ov8865.c | 66 +- drivers/media/i2c/ov9282.c | 18 +- drivers/media/i2c/ov9640.c | 2 - drivers/media/i2c/ov9650.c | 35 +- drivers/media/i2c/ov9734.c | 9 +- drivers/media/i2c/rj54n1cb0c.c | 4 +- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 65 +- drivers/media/i2c/s5k5baf.c | 69 +- drivers/media/i2c/s5k6a3.c | 8 +- drivers/media/i2c/saa6752hs.c | 4 +- drivers/media/i2c/st-mipid02.c | 486 +-- drivers/media/i2c/st-vgxy61.c | 34 +- drivers/media/i2c/tc358746.c | 22 +- drivers/media/i2c/tda1997x.c | 16 +- drivers/media/i2c/thp7312.c | 2256 ++++++++++++ drivers/media/i2c/tvp514x.c | 41 +- drivers/media/i2c/tvp5150.c | 8 +- drivers/media/i2c/tvp7002.c | 6 +- drivers/media/i2c/tw9900.c | 781 +++++ drivers/media/i2c/tw9910.c | 2 - drivers/media/i2c/video-i2c.c | 7 +- drivers/media/mc/Kconfig | 7 - drivers/media/mc/mc-device.c | 4 - drivers/media/pci/bt8xx/bttv-driver.c | 2 +- drivers/media/pci/cobalt/cobalt-v4l2.c | 2 +- drivers/media/pci/cx18/cx18-streams.c | 7 +- drivers/media/pci/cx23885/cx23885-417.c | 2 +- drivers/media/pci/cx23885/cx23885-dvb.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 4 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx88/cx88-blackbird.c | 2 +- drivers/media/pci/cx88/cx88-dvb.c | 2 +- drivers/media/pci/cx88/cx88-video.c | 4 +- drivers/media/pci/dt3155/dt3155.c | 4 +- drivers/media/pci/intel/ipu-bridge.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 26 +- drivers/media/pci/intel/ivsc/mei_csi.c | 83 +- drivers/media/pci/ivtv/Kconfig | 4 +- drivers/media/pci/ivtv/ivtv-driver.h | 1 + drivers/media/pci/ivtv/ivtv-streams.c | 4 +- drivers/media/pci/ivtv/ivtvfb.c | 6 +- drivers/media/pci/mgb4/mgb4_vin.c | 2 +- drivers/media/pci/mgb4/mgb4_vout.c | 2 +- drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 5 +- drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 9 +- drivers/media/pci/tw5864/tw5864-video.c | 2 +- drivers/media/pci/tw68/tw68-video.c | 7 +- drivers/media/pci/tw686x/tw686x-video.c | 7 +- drivers/media/pci/zoran/zoran_driver.c | 6 +- drivers/media/platform/amphion/vpu_dbg.c | 30 +- drivers/media/platform/amphion/vpu_v4l2.c | 8 +- drivers/media/platform/aspeed/aspeed-video.c | 2 +- drivers/media/platform/atmel/atmel-isi.c | 15 +- drivers/media/platform/cadence/cdns-csi2rx.c | 14 +- drivers/media/platform/cadence/cdns-csi2tx.c | 3 +- drivers/media/platform/chips-media/Kconfig | 18 +- drivers/media/platform/chips-media/Makefile | 6 +- drivers/media/platform/chips-media/coda-bit.c | 2666 -------------- drivers/media/platform/chips-media/coda-common.c | 3361 ------------------ drivers/media/platform/chips-media/coda-gdi.c | 146 - drivers/media/platform/chips-media/coda-h264.c | 429 --- drivers/media/platform/chips-media/coda-jpeg.c | 1547 -------- drivers/media/platform/chips-media/coda-mpeg2.c | 87 - drivers/media/platform/chips-media/coda-mpeg4.c | 87 - drivers/media/platform/chips-media/coda.h | 403 --- drivers/media/platform/chips-media/coda/Kconfig | 18 + drivers/media/platform/chips-media/coda/Makefile | 6 + drivers/media/platform/chips-media/coda/coda-bit.c | 2666 ++++++++++++++ .../media/platform/chips-media/coda/coda-common.c | 3361 ++++++++++++++++++ drivers/media/platform/chips-media/coda/coda-gdi.c | 146 + .../media/platform/chips-media/coda/coda-h264.c | 429 +++ .../media/platform/chips-media/coda/coda-jpeg.c | 1547 ++++++++ .../media/platform/chips-media/coda/coda-mpeg2.c | 87 + .../media/platform/chips-media/coda/coda-mpeg4.c | 87 + drivers/media/platform/chips-media/coda/coda.h | 403 +++ .../media/platform/chips-media/coda/coda_regs.h | 563 +++ drivers/media/platform/chips-media/coda/imx-vdoa.c | 346 ++ drivers/media/platform/chips-media/coda/imx-vdoa.h | 50 + drivers/media/platform/chips-media/coda/trace.h | 175 + drivers/media/platform/chips-media/coda_regs.h | 563 --- drivers/media/platform/chips-media/imx-vdoa.c | 346 -- drivers/media/platform/chips-media/imx-vdoa.h | 50 - drivers/media/platform/chips-media/trace.h | 175 - drivers/media/platform/chips-media/wave5/Kconfig | 15 + drivers/media/platform/chips-media/wave5/Makefile | 10 + .../platform/chips-media/wave5/wave5-helper.c | 213 ++ .../platform/chips-media/wave5/wave5-helper.h | 31 + .../media/platform/chips-media/wave5/wave5-hw.c | 2551 ++++++++++++++ .../platform/chips-media/wave5/wave5-regdefine.h | 732 ++++ .../media/platform/chips-media/wave5/wave5-vdi.c | 205 ++ .../media/platform/chips-media/wave5/wave5-vdi.h | 35 + .../platform/chips-media/wave5/wave5-vpu-dec.c | 1932 ++++++++++ .../platform/chips-media/wave5/wave5-vpu-enc.c | 1794 ++++++++++ .../media/platform/chips-media/wave5/wave5-vpu.c | 291 ++ .../media/platform/chips-media/wave5/wave5-vpu.h | 83 + .../platform/chips-media/wave5/wave5-vpuapi.c | 960 +++++ .../platform/chips-media/wave5/wave5-vpuapi.h | 870 +++++ .../platform/chips-media/wave5/wave5-vpuconfig.h | 77 + .../platform/chips-media/wave5/wave5-vpuerror.h | 292 ++ drivers/media/platform/chips-media/wave5/wave5.h | 114 + .../media/platform/mediatek/jpeg/mtk_jpeg_core.c | 7 +- .../media/platform/mediatek/mdp3/mtk-mdp3-core.c | 16 +- drivers/media/platform/mediatek/vcodec/Kconfig | 1 - .../mediatek/vcodec/common/mtk_vcodec_fw_vpu.c | 8 +- .../mediatek/vcodec/decoder/mtk_vcodec_dec.c | 24 +- .../mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c | 31 + .../mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h | 16 + .../vcodec/decoder/mtk_vcodec_dec_stateless.c | 168 +- .../vcodec/decoder/vdec/vdec_hevc_req_multi_if.c | 2 +- .../vcodec/decoder/vdec/vdec_vp9_req_lat_if.c | 9 +- .../platform/mediatek/vcodec/decoder/vdec_vpu_if.c | 2 + .../mediatek/vcodec/encoder/mtk_vcodec_enc.c | 2 +- .../mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c | 5 + .../mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h | 2 + .../platform/mediatek/vcodec/encoder/venc_vpu_if.c | 2 + .../media/platform/microchip/microchip-csi2dc.c | 25 +- .../media/platform/microchip/microchip-isc-base.c | 41 +- .../platform/microchip/microchip-isc-scaler.c | 26 +- drivers/media/platform/nuvoton/npcm-video.c | 34 +- drivers/media/platform/nvidia/tegra-vde/Kconfig | 1 - drivers/media/platform/nvidia/tegra-vde/v4l2.c | 2 +- drivers/media/platform/nxp/imx-mipi-csis.c | 20 +- drivers/media/platform/nxp/imx7-media-csi.c | 58 +- .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 20 +- .../media/platform/nxp/imx8-isi/imx8-isi-debug.c | 27 +- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 28 +- .../media/platform/nxp/imx8-isi/imx8-isi-video.c | 4 +- drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 23 +- .../media/platform/qcom/camss/camss-csid-gen2.c | 31 +- drivers/media/platform/qcom/camss/camss-csid.c | 20 +- drivers/media/platform/qcom/camss/camss-csid.h | 7 + drivers/media/platform/qcom/camss/camss-csiphy.c | 15 +- drivers/media/platform/qcom/camss/camss-ispif.c | 17 +- drivers/media/platform/qcom/camss/camss-vfe-170.c | 36 - drivers/media/platform/qcom/camss/camss-vfe-4-1.c | 8 +- drivers/media/platform/qcom/camss/camss-vfe-4-7.c | 36 - drivers/media/platform/qcom/camss/camss-vfe-4-8.c | 31 - drivers/media/platform/qcom/camss/camss-vfe-480.c | 69 +- drivers/media/platform/qcom/camss/camss-vfe.c | 115 +- drivers/media/platform/qcom/camss/camss-vfe.h | 26 + drivers/media/platform/qcom/camss/camss.c | 122 +- drivers/media/platform/qcom/camss/camss.h | 10 +- drivers/media/platform/qcom/venus/core.c | 4 + drivers/media/platform/qcom/venus/vdec.c | 4 +- drivers/media/platform/qcom/venus/venc.c | 4 +- drivers/media/platform/renesas/rcar-isp.c | 4 +- .../media/platform/renesas/rcar-vin/rcar-csi2.c | 4 +- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 2 +- drivers/media/platform/renesas/rcar_drif.c | 5 +- drivers/media/platform/renesas/renesas-ceu.c | 2 +- .../media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 16 +- .../media/platform/renesas/rzg2l-cru/rzg2l-ip.c | 16 +- .../media/platform/renesas/rzg2l-cru/rzg2l-video.c | 2 +- drivers/media/platform/renesas/sh_vou.c | 2 +- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 43 +- drivers/media/platform/renesas/vsp1/vsp1_clu.c | 4 +- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 138 +- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 12 +- drivers/media/platform/renesas/vsp1/vsp1_hgo.c | 4 +- drivers/media/platform/renesas/vsp1/vsp1_hgt.c | 4 +- drivers/media/platform/renesas/vsp1/vsp1_histo.c | 24 +- drivers/media/platform/renesas/vsp1/vsp1_hsit.c | 12 +- drivers/media/platform/renesas/vsp1/vsp1_lif.c | 3 +- drivers/media/platform/renesas/vsp1/vsp1_lut.c | 1 - drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 8 +- drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 41 +- drivers/media/platform/renesas/vsp1/vsp1_sru.c | 37 +- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 40 +- drivers/media/platform/renesas/vsp1/vsp1_uif.c | 25 +- drivers/media/platform/renesas/vsp1/vsp1_video.c | 4 +- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 10 +- drivers/media/platform/rockchip/rga/rga-buf.c | 162 +- drivers/media/platform/rockchip/rga/rga-hw.c | 146 +- drivers/media/platform/rockchip/rga/rga.c | 174 +- drivers/media/platform/rockchip/rga/rga.h | 35 +- .../platform/rockchip/rkisp1/rkisp1-capture.c | 2 +- .../media/platform/rockchip/rkisp1/rkisp1-common.h | 1 + .../media/platform/rockchip/rkisp1/rkisp1-csi.c | 26 +- .../media/platform/rockchip/rkisp1/rkisp1-debug.c | 6 + .../media/platform/rockchip/rkisp1/rkisp1-isp.c | 115 +- .../media/platform/rockchip/rkisp1/rkisp1-regs.h | 9 +- .../platform/rockchip/rkisp1/rkisp1-resizer.c | 63 +- .../media/platform/samsung/exynos-gsc/gsc-core.h | 1 - .../platform/samsung/exynos4-is/fimc-capture.c | 12 +- .../media/platform/samsung/exynos4-is/fimc-core.c | 2 +- .../platform/samsung/exynos4-is/fimc-is-i2c.c | 1 - .../media/platform/samsung/exynos4-is/fimc-isp.c | 24 +- .../media/platform/samsung/exynos4-is/fimc-lite.c | 16 +- .../media/platform/samsung/exynos4-is/mipi-csis.c | 3 +- .../platform/samsung/s3c-camif/camif-capture.c | 8 +- .../media/platform/samsung/s5p-mfc/regs-mfc-v12.h | 52 + .../media/platform/samsung/s5p-mfc/regs-mfc-v7.h | 1 + .../media/platform/samsung/s5p-mfc/regs-mfc-v8.h | 3 + drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c | 36 +- .../platform/samsung/s5p-mfc/s5p_mfc_common.h | 33 +- .../media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c | 14 +- .../media/platform/samsung/s5p-mfc/s5p_mfc_dec.c | 60 +- .../media/platform/samsung/s5p-mfc/s5p_mfc_enc.c | 150 +- .../media/platform/samsung/s5p-mfc/s5p_mfc_opr.h | 14 +- .../platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c | 12 +- .../platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c | 299 +- .../platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h | 7 +- drivers/media/platform/st/sti/hva/hva-v4l2.c | 13 +- drivers/media/platform/st/stm32/Kconfig | 16 + drivers/media/platform/st/stm32/Makefile | 1 + drivers/media/platform/st/stm32/stm32-dcmi.c | 10 +- .../media/platform/st/stm32/stm32-dcmipp/Makefile | 4 + .../st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 956 +++++ .../st/stm32/stm32-dcmipp/dcmipp-byteproc.c | 565 +++ .../platform/st/stm32/stm32-dcmipp/dcmipp-common.c | 111 + .../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 217 ++ .../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 604 ++++ .../st/stm32/stm32-dcmipp/dcmipp-parallel.c | 440 +++ drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 1 + drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h | 1 + drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 2 +- .../media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 17 +- .../platform/sunxi/sun6i-csi/sun6i_csi_bridge.c | 18 +- .../platform/sunxi/sun6i-csi/sun6i_csi_capture.c | 2 +- .../sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c | 18 +- .../sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c | 18 +- drivers/media/platform/sunxi/sun8i-di/sun8i-di.c | 4 +- .../platform/sunxi/sun8i-rotate/sun8i_rotate.c | 4 +- drivers/media/platform/ti/am437x/am437x-vpfe.c | 7 +- drivers/media/platform/ti/cal/cal-camerarx.c | 28 +- drivers/media/platform/ti/cal/cal-video.c | 9 +- drivers/media/platform/ti/davinci/vpif_capture.c | 7 +- drivers/media/platform/ti/davinci/vpif_display.c | 7 +- .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 2 +- drivers/media/platform/ti/omap/omap_vout.c | 7 +- drivers/media/platform/ti/omap3isp/ispccdc.c | 19 +- drivers/media/platform/ti/omap3isp/ispccp2.c | 13 +- drivers/media/platform/ti/omap3isp/ispcsi2.c | 9 +- drivers/media/platform/ti/omap3isp/isppreview.c | 18 +- drivers/media/platform/ti/omap3isp/ispresizer.c | 21 +- drivers/media/platform/verisilicon/Kconfig | 1 - drivers/media/platform/verisilicon/hantro.h | 9 +- drivers/media/platform/verisilicon/hantro_drv.c | 4 +- drivers/media/platform/verisilicon/hantro_g2.c | 14 + .../platform/verisilicon/hantro_g2_hevc_dec.c | 18 +- .../media/platform/verisilicon/hantro_g2_vp9_dec.c | 28 +- drivers/media/platform/verisilicon/hantro_hw.h | 7 +- .../media/platform/verisilicon/hantro_postproc.c | 93 +- drivers/media/platform/verisilicon/hantro_v4l2.c | 26 +- drivers/media/platform/video-mux.c | 28 +- drivers/media/platform/xilinx/xilinx-csi2rxss.c | 74 +- drivers/media/platform/xilinx/xilinx-tpg.c | 9 +- drivers/media/platform/xilinx/xilinx-vip.c | 4 +- drivers/media/rc/Kconfig | 1 + drivers/media/rc/ir-hix5hd2.c | 10 +- drivers/media/rc/meson-ir-tx.c | 34 +- drivers/media/rc/pwm-ir-tx.c | 87 +- drivers/media/test-drivers/Kconfig | 1 - drivers/media/test-drivers/vicodec/Kconfig | 1 - drivers/media/test-drivers/vicodec/vicodec-core.c | 20 +- drivers/media/test-drivers/vidtv/vidtv_pes.c | 1 + drivers/media/test-drivers/vimc/vimc-capture.c | 2 +- drivers/media/test-drivers/vimc/vimc-debayer.c | 21 +- drivers/media/test-drivers/vimc/vimc-scaler.c | 20 +- drivers/media/test-drivers/vimc/vimc-sensor.c | 17 +- drivers/media/test-drivers/visl/Kconfig | 1 - drivers/media/test-drivers/visl/visl-core.c | 21 + drivers/media/test-drivers/visl/visl-dec.c | 104 +- drivers/media/test-drivers/visl/visl-dec.h | 8 + drivers/media/test-drivers/visl/visl-trace-av1.h | 314 ++ .../media/test-drivers/visl/visl-trace-points.c | 1 + drivers/media/test-drivers/visl/visl-video.c | 18 + drivers/media/test-drivers/visl/visl-video.h | 1 + drivers/media/test-drivers/visl/visl.h | 1 + drivers/media/test-drivers/vivid/Kconfig | 1 - drivers/media/test-drivers/vivid/vivid-core.c | 18 +- drivers/media/test-drivers/vivid/vivid-meta-cap.c | 3 - drivers/media/test-drivers/vivid/vivid-meta-out.c | 5 +- drivers/media/test-drivers/vivid/vivid-touch-cap.c | 5 +- drivers/media/test-drivers/vivid/vivid-vbi-cap.c | 3 - drivers/media/test-drivers/vivid/vivid-vbi-out.c | 3 - drivers/media/test-drivers/vivid/vivid-vid-cap.c | 3 - drivers/media/test-drivers/vivid/vivid-vid-out.c | 5 +- drivers/media/usb/airspy/airspy.c | 5 +- drivers/media/usb/cx231xx/cx231xx-417.c | 7 +- drivers/media/usb/cx231xx/cx231xx-video.c | 9 +- drivers/media/usb/dvb-usb/cxusb-analog.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 6 +- drivers/media/usb/gspca/gspca.c | 6 +- drivers/media/usb/hackrf/hackrf.c | 5 +- drivers/media/usb/usbtv/usbtv-video.c | 12 +- drivers/media/usb/uvc/uvc_video.c | 2 +- drivers/media/v4l2-core/v4l2-async.c | 3 - drivers/media/v4l2-core/v4l2-common.c | 11 +- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 10 +- drivers/media/v4l2-core/v4l2-dev.c | 6 +- drivers/media/v4l2-core/v4l2-fwnode.c | 4 +- drivers/media/v4l2-core/v4l2-ioctl.c | 6 +- drivers/media/v4l2-core/v4l2-mem2mem.c | 9 +- drivers/media/v4l2-core/v4l2-subdev.c | 334 +- 384 files changed, 42415 insertions(+), 14438 deletions(-) create mode 100644 drivers/media/i2c/alvium-csi2.c create mode 100644 drivers/media/i2c/alvium-csi2.h create mode 100644 drivers/media/i2c/gc0308.c create mode 100644 drivers/media/i2c/gc2145.c create mode 100644 drivers/media/i2c/ov64a40.c create mode 100644 drivers/media/i2c/thp7312.c create mode 100644 drivers/media/i2c/tw9900.c delete mode 100644 drivers/media/platform/chips-media/coda-bit.c delete mode 100644 drivers/media/platform/chips-media/coda-common.c delete mode 100644 drivers/media/platform/chips-media/coda-gdi.c delete mode 100644 drivers/media/platform/chips-media/coda-h264.c delete mode 100644 drivers/media/platform/chips-media/coda-jpeg.c delete mode 100644 drivers/media/platform/chips-media/coda-mpeg2.c delete mode 100644 drivers/media/platform/chips-media/coda-mpeg4.c delete mode 100644 drivers/media/platform/chips-media/coda.h create mode 100644 drivers/media/platform/chips-media/coda/Kconfig create mode 100644 drivers/media/platform/chips-media/coda/Makefile create mode 100644 drivers/media/platform/chips-media/coda/coda-bit.c create mode 100644 drivers/media/platform/chips-media/coda/coda-common.c create mode 100644 drivers/media/platform/chips-media/coda/coda-gdi.c create mode 100644 drivers/media/platform/chips-media/coda/coda-h264.c create mode 100644 drivers/media/platform/chips-media/coda/coda-jpeg.c create mode 100644 drivers/media/platform/chips-media/coda/coda-mpeg2.c create mode 100644 drivers/media/platform/chips-media/coda/coda-mpeg4.c create mode 100644 drivers/media/platform/chips-media/coda/coda.h create mode 100644 drivers/media/platform/chips-media/coda/coda_regs.h create mode 100644 drivers/media/platform/chips-media/coda/imx-vdoa.c create mode 100644 drivers/media/platform/chips-media/coda/imx-vdoa.h create mode 100644 drivers/media/platform/chips-media/coda/trace.h delete mode 100644 drivers/media/platform/chips-media/coda_regs.h delete mode 100644 drivers/media/platform/chips-media/imx-vdoa.c delete mode 100644 drivers/media/platform/chips-media/imx-vdoa.h delete mode 100644 drivers/media/platform/chips-media/trace.h create mode 100644 drivers/media/platform/chips-media/wave5/Kconfig create mode 100644 drivers/media/platform/chips-media/wave5/Makefile create mode 100644 drivers/media/platform/chips-media/wave5/wave5-helper.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-helper.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-hw.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-regdefine.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vdi.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vdi.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpu.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpu.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpuapi.c create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpuapi.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5-vpuerror.h create mode 100644 drivers/media/platform/chips-media/wave5/wave5.h create mode 100644 drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/Makefile create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c create mode 100644 drivers/media/test-drivers/visl/visl-trace-av1.h (limited to 'drivers/media') diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 6bb49bb3f..559a172eb 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -511,7 +511,7 @@ int cec_thread_func(void *_adap) pr_warn("cec-%s: transmit timed out\n", adap->name); } adap->transmit_in_progress = false; - adap->tx_timeouts++; + adap->tx_timeout_cnt++; goto unlock; } @@ -625,6 +625,33 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, msg->tx_low_drive_cnt += low_drive_cnt; msg->tx_error_cnt += error_cnt; + adap->tx_arb_lost_cnt += arb_lost_cnt; + adap->tx_low_drive_cnt += low_drive_cnt; + adap->tx_error_cnt += error_cnt; + + /* + * Low Drive transmission errors should really not happen for + * well-behaved CEC devices and proper HDMI cables. + * + * Ditto for the 'Error' status. + * + * For the first few times that this happens, log this. + * Stop logging after that, since that will not add any more + * useful information and instead it will just flood the kernel log. + */ + if (done && adap->tx_low_drive_log_cnt < 8 && msg->tx_low_drive_cnt) { + adap->tx_low_drive_log_cnt++; + dprintk(0, "low drive counter: %u (seq %u: %*ph)\n", + msg->tx_low_drive_cnt, msg->sequence, + msg->len, msg->msg); + } + if (done && adap->tx_error_log_cnt < 8 && msg->tx_error_cnt) { + adap->tx_error_log_cnt++; + dprintk(0, "error counter: %u (seq %u: %*ph)\n", + msg->tx_error_cnt, msg->sequence, + msg->len, msg->msg); + } + /* Mark that we're done with this transmit */ adap->transmitting = NULL; @@ -1124,20 +1151,6 @@ void cec_received_msg_ts(struct cec_adapter *adap, if (valid_la && min_len) { /* These messages have special length requirements */ switch (cmd) { - case CEC_MSG_TIMER_STATUS: - if (msg->msg[2] & 0x10) { - switch (msg->msg[2] & 0xf) { - case CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE: - case CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE: - if (msg->len < 5) - valid_la = false; - break; - } - } else if ((msg->msg[2] & 0xf) == CEC_OP_PROG_ERROR_DUPLICATE) { - if (msg->len < 5) - valid_la = false; - } - break; case CEC_MSG_RECORD_ON: switch (msg->msg[2]) { case CEC_OP_RECORD_SRC_OWN: @@ -1607,6 +1620,8 @@ int cec_adap_enable(struct cec_adapter *adap) if (enable) { adap->last_initiator = 0xff; adap->transmit_in_progress = false; + adap->tx_low_drive_log_cnt = 0; + adap->tx_error_log_cnt = 0; ret = adap->ops->adap_enable(adap, true); if (!ret) { /* @@ -2265,10 +2280,25 @@ int cec_adap_status(struct seq_file *file, void *priv) if (adap->monitor_pin_cnt) seq_printf(file, "file handles in Monitor Pin mode: %u\n", adap->monitor_pin_cnt); - if (adap->tx_timeouts) { - seq_printf(file, "transmit timeouts: %u\n", - adap->tx_timeouts); - adap->tx_timeouts = 0; + if (adap->tx_timeout_cnt) { + seq_printf(file, "transmit timeout count: %u\n", + adap->tx_timeout_cnt); + adap->tx_timeout_cnt = 0; + } + if (adap->tx_low_drive_cnt) { + seq_printf(file, "transmit low drive count: %u\n", + adap->tx_low_drive_cnt); + adap->tx_low_drive_cnt = 0; + } + if (adap->tx_arb_lost_cnt) { + seq_printf(file, "transmit arbitration lost count: %u\n", + adap->tx_arb_lost_cnt); + adap->tx_arb_lost_cnt = 0; + } + if (adap->tx_error_cnt) { + seq_printf(file, "transmit error count: %u\n", + adap->tx_error_cnt); + adap->tx_error_cnt = 0; } data = adap->transmitting; if (data) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 42dde3f0d..52ec0ba4b 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -324,6 +324,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Boxy", "0000:00:02.0", port_d_conns }, /* Google Taranza */ { "Google", "Taranza", "0000:00:02.0", port_db_conns }, + /* Google Dexi */ + { "Google", "Dexi", "0000:00:02.0", port_db_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index 792144593..a7047e548 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -387,7 +387,7 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, q->gfp_flags = __GFP_DMA32; q->buf_struct_size = sizeof(struct saa7146_buf); q->lock = &dev->v4l2_lock; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 19f80ff49..b6bf8f232 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -31,6 +31,16 @@ #include +#define PLANE_INDEX_BITS 3 +#define PLANE_INDEX_SHIFT (PAGE_SHIFT + PLANE_INDEX_BITS) +#define PLANE_INDEX_MASK (BIT_MASK(PLANE_INDEX_BITS) - 1) +#define MAX_BUFFER_INDEX BIT_MASK(30 - PLANE_INDEX_SHIFT) +#define BUFFER_INDEX_MASK (MAX_BUFFER_INDEX - 1) + +#if BIT(PLANE_INDEX_BITS) != VIDEO_MAX_PLANES +#error PLANE_INDEX_BITS order must be equal to VIDEO_MAX_PLANES +#endif + static int debug; module_param(debug, int, 0644); @@ -356,23 +366,29 @@ static void __setup_offsets(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; unsigned int plane; - unsigned long off = 0; - - if (vb->index) { - struct vb2_buffer *prev = q->bufs[vb->index - 1]; - struct vb2_plane *p = &prev->planes[prev->num_planes - 1]; + unsigned long offset = 0; - off = PAGE_ALIGN(p->m.offset + p->length); - } + /* + * The offset "cookie" value has the following constraints: + * - a buffer can have up to 8 planes. + * - v4l2 mem2mem uses bit 30 to distinguish between + * OUTPUT (aka "source", bit 30 is 0) and + * CAPTURE (aka "destination", bit 30 is 1) buffers. + * - must be page aligned + * That led to this bit mapping when PAGE_SHIFT = 12: + * |30 |29 15|14 12|11 0| + * |DST_QUEUE_OFF_BASE|buffer index|plane index| 0 | + * where there are 15 bits to store the buffer index. + * Depending on PAGE_SHIFT value we can have fewer bits + * to store the buffer index. + */ + offset = vb->index << PLANE_INDEX_SHIFT; for (plane = 0; plane < vb->num_planes; ++plane) { - vb->planes[plane].m.offset = off; + vb->planes[plane].m.offset = offset + (plane << PAGE_SHIFT); dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n", - vb->index, plane, off); - - off += vb->planes[plane].length; - off = PAGE_ALIGN(off); + vb->index, plane, offset); } } @@ -397,6 +413,31 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb) vb->skip_cache_sync_on_finish = 1; } +/** + * vb2_queue_add_buffer() - add a buffer to a queue + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @vb: pointer to &struct vb2_buffer to be added to the queue. + * @index: index where add vb2_buffer in the queue + */ +static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index) +{ + WARN_ON(index >= q->max_num_buffers || q->bufs[index] || vb->vb2_queue); + + q->bufs[index] = vb; + vb->index = index; + vb->vb2_queue = q; +} + +/** + * vb2_queue_remove_buffer() - remove a buffer from a queue + * @vb: pointer to &struct vb2_buffer to be removed from the queue. + */ +static void vb2_queue_remove_buffer(struct vb2_buffer *vb) +{ + vb->vb2_queue->bufs[vb->index] = NULL; + vb->vb2_queue = NULL; +} + /* * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type) * video buffer memory for all buffers/planes on the queue and initializes the @@ -408,13 +449,17 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, unsigned int num_buffers, unsigned int num_planes, const unsigned plane_sizes[VB2_MAX_PLANES]) { + unsigned int q_num_buffers = vb2_get_num_buffers(q); unsigned int buffer, plane; struct vb2_buffer *vb; int ret; - /* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */ + /* + * Ensure that the number of already queue + the number of buffers already + * in the queue is below q->max_num_buffers + */ num_buffers = min_t(unsigned int, num_buffers, - VB2_MAX_FRAME - q->num_buffers); + q->max_num_buffers - q_num_buffers); for (buffer = 0; buffer < num_buffers; ++buffer) { /* Allocate vb2 buffer structures */ @@ -425,9 +470,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, } vb->state = VB2_BUF_STATE_DEQUEUED; - vb->vb2_queue = q; vb->num_planes = num_planes; - vb->index = q->num_buffers + buffer; vb->type = q->type; vb->memory = memory; init_buffer_cache_hints(q, vb); @@ -435,9 +478,9 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, vb->planes[plane].length = plane_sizes[plane]; vb->planes[plane].min_length = plane_sizes[plane]; } - call_void_bufop(q, init_buffer, vb); - q->bufs[vb->index] = vb; + vb2_queue_add_buffer(q, vb, q_num_buffers + buffer); + call_void_bufop(q, init_buffer, vb); /* Allocate video buffer memory for the MMAP type */ if (memory == VB2_MEMORY_MMAP) { @@ -445,7 +488,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, if (ret) { dprintk(q, 1, "failed allocating memory for buffer %d\n", buffer); - q->bufs[vb->index] = NULL; + vb2_queue_remove_buffer(vb); kfree(vb); break; } @@ -460,7 +503,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, dprintk(q, 1, "buffer %d %p initialization failed\n", buffer, vb); __vb2_buf_mem_free(vb); - q->bufs[vb->index] = NULL; + vb2_queue_remove_buffer(vb); kfree(vb); break; } @@ -480,10 +523,11 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; struct vb2_buffer *vb; + unsigned int q_num_buffers = vb2_get_num_buffers(q); - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; ++buffer) { - vb = q->bufs[buffer]; + vb = vb2_get_buffer(q, buffer); if (!vb) continue; @@ -505,13 +549,14 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; + unsigned int q_num_buffers = vb2_get_num_buffers(q); lockdep_assert_held(&q->mmap_lock); /* Call driver-provided cleanup function for each buffer, if provided */ - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; ++buffer) { - struct vb2_buffer *vb = q->bufs[buffer]; + struct vb2_buffer *vb = vb2_get_buffer(q, buffer); if (vb && vb->planes[0].mem_priv) call_void_vb_qop(vb, buf_cleanup, vb); @@ -522,25 +567,26 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) #ifdef CONFIG_VIDEO_ADV_DEBUG /* - * Check that all the calls were balances during the life-time of this - * queue. If not (or if the debug level is 1 or up), then dump the - * counters to the kernel log. + * Check that all the calls were balanced during the life-time of this + * queue. If not then dump the counters to the kernel log. */ - if (q->num_buffers) { + if (q_num_buffers) { bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || q->cnt_prepare_streaming != q->cnt_unprepare_streaming || q->cnt_wait_prepare != q->cnt_wait_finish; - if (unbalanced || debug) { - pr_info("counters for queue %p:%s\n", q, - unbalanced ? " UNBALANCED!" : ""); - pr_info(" setup: %u start_streaming: %u stop_streaming: %u\n", - q->cnt_queue_setup, q->cnt_start_streaming, - q->cnt_stop_streaming); - pr_info(" prepare_streaming: %u unprepare_streaming: %u\n", - q->cnt_prepare_streaming, q->cnt_unprepare_streaming); - pr_info(" wait_prepare: %u wait_finish: %u\n", - q->cnt_wait_prepare, q->cnt_wait_finish); + if (unbalanced) { + pr_info("unbalanced counters for queue %p:\n", q); + if (q->cnt_start_streaming != q->cnt_stop_streaming) + pr_info(" setup: %u start_streaming: %u stop_streaming: %u\n", + q->cnt_queue_setup, q->cnt_start_streaming, + q->cnt_stop_streaming); + if (q->cnt_prepare_streaming != q->cnt_unprepare_streaming) + pr_info(" prepare_streaming: %u unprepare_streaming: %u\n", + q->cnt_prepare_streaming, q->cnt_unprepare_streaming); + if (q->cnt_wait_prepare != q->cnt_wait_finish) + pr_info(" wait_prepare: %u wait_finish: %u\n", + q->cnt_wait_prepare, q->cnt_wait_finish); } q->cnt_queue_setup = 0; q->cnt_wait_prepare = 0; @@ -550,53 +596,71 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) q->cnt_stop_streaming = 0; q->cnt_unprepare_streaming = 0; } - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - struct vb2_buffer *vb = q->bufs[buffer]; - bool unbalanced = vb->cnt_mem_alloc != vb->cnt_mem_put || - vb->cnt_mem_prepare != vb->cnt_mem_finish || - vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr || - vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf || - vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf || - vb->cnt_buf_queue != vb->cnt_buf_done || - vb->cnt_buf_prepare != vb->cnt_buf_finish || - vb->cnt_buf_init != vb->cnt_buf_cleanup; - - if (unbalanced || debug) { - pr_info(" counters for queue %p, buffer %d:%s\n", - q, buffer, unbalanced ? " UNBALANCED!" : ""); - pr_info(" buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n", - vb->cnt_buf_init, vb->cnt_buf_cleanup, - vb->cnt_buf_prepare, vb->cnt_buf_finish); - pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n", - vb->cnt_buf_out_validate, vb->cnt_buf_queue, - vb->cnt_buf_done, vb->cnt_buf_request_complete); - pr_info(" alloc: %u put: %u prepare: %u finish: %u mmap: %u\n", - vb->cnt_mem_alloc, vb->cnt_mem_put, - vb->cnt_mem_prepare, vb->cnt_mem_finish, - vb->cnt_mem_mmap); - pr_info(" get_userptr: %u put_userptr: %u\n", - vb->cnt_mem_get_userptr, vb->cnt_mem_put_userptr); - pr_info(" attach_dmabuf: %u detach_dmabuf: %u map_dmabuf: %u unmap_dmabuf: %u\n", - vb->cnt_mem_attach_dmabuf, vb->cnt_mem_detach_dmabuf, - vb->cnt_mem_map_dmabuf, vb->cnt_mem_unmap_dmabuf); - pr_info(" get_dmabuf: %u num_users: %u vaddr: %u cookie: %u\n", + for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) { + struct vb2_buffer *vb = vb2_get_buffer(q, buffer); + bool unbalanced; + + if (!vb) + continue; + + unbalanced = vb->cnt_mem_alloc != vb->cnt_mem_put || + vb->cnt_mem_prepare != vb->cnt_mem_finish || + vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr || + vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf || + vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf || + vb->cnt_buf_queue != vb->cnt_buf_done || + vb->cnt_buf_prepare != vb->cnt_buf_finish || + vb->cnt_buf_init != vb->cnt_buf_cleanup; + + if (unbalanced) { + pr_info("unbalanced counters for queue %p, buffer %d:\n", + q, buffer); + if (vb->cnt_buf_init != vb->cnt_buf_cleanup) + pr_info(" buf_init: %u buf_cleanup: %u\n", + vb->cnt_buf_init, vb->cnt_buf_cleanup); + if (vb->cnt_buf_prepare != vb->cnt_buf_finish) + pr_info(" buf_prepare: %u buf_finish: %u\n", + vb->cnt_buf_prepare, vb->cnt_buf_finish); + if (vb->cnt_buf_queue != vb->cnt_buf_done) + pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n", + vb->cnt_buf_out_validate, vb->cnt_buf_queue, + vb->cnt_buf_done, vb->cnt_buf_request_complete); + if (vb->cnt_mem_alloc != vb->cnt_mem_put) + pr_info(" alloc: %u put: %u\n", + vb->cnt_mem_alloc, vb->cnt_mem_put); + if (vb->cnt_mem_prepare != vb->cnt_mem_finish) + pr_info(" prepare: %u finish: %u\n", + vb->cnt_mem_prepare, vb->cnt_mem_finish); + if (vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr) + pr_info(" get_userptr: %u put_userptr: %u\n", + vb->cnt_mem_get_userptr, vb->cnt_mem_put_userptr); + if (vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf) + pr_info(" attach_dmabuf: %u detach_dmabuf: %u\n", + vb->cnt_mem_attach_dmabuf, vb->cnt_mem_detach_dmabuf); + if (vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf) + pr_info(" map_dmabuf: %u unmap_dmabuf: %u\n", + vb->cnt_mem_map_dmabuf, vb->cnt_mem_unmap_dmabuf); + pr_info(" get_dmabuf: %u num_users: %u\n", vb->cnt_mem_get_dmabuf, - vb->cnt_mem_num_users, - vb->cnt_mem_vaddr, - vb->cnt_mem_cookie); + vb->cnt_mem_num_users); } } #endif /* Free vb2 buffers */ - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + for (buffer = q_num_buffers - buffers; buffer < q_num_buffers; ++buffer) { - kfree(q->bufs[buffer]); - q->bufs[buffer] = NULL; + struct vb2_buffer *vb = vb2_get_buffer(q, buffer); + + if (!vb) + continue; + + vb2_queue_remove_buffer(vb); + kfree(vb); } q->num_buffers -= buffers; - if (!q->num_buffers) { + if (!vb2_get_num_buffers(q)) { q->memory = VB2_MEMORY_UNKNOWN; INIT_LIST_HEAD(&q->queued_list); } @@ -627,16 +691,21 @@ EXPORT_SYMBOL(vb2_buffer_in_use); static bool __buffers_in_use(struct vb2_queue *q) { unsigned int buffer; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - if (vb2_buffer_in_use(q, q->bufs[buffer])) + for (buffer = 0; buffer < vb2_get_num_buffers(q); ++buffer) { + struct vb2_buffer *vb = vb2_get_buffer(q, buffer); + + if (!vb) + continue; + + if (vb2_buffer_in_use(q, vb)) return true; } return false; } -void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb) +void vb2_core_querybuf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb) { - call_void_bufop(q, fill_user_buffer, q->bufs[index], pb); + call_void_bufop(q, fill_user_buffer, vb, pb); } EXPORT_SYMBOL_GPL(vb2_core_querybuf); @@ -748,10 +817,11 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int flags, unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; + unsigned int q_num_bufs = vb2_get_num_buffers(q); unsigned plane_sizes[VB2_MAX_PLANES] = { }; bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT; unsigned int i; - int ret; + int ret = 0; if (q->streaming) { dprintk(q, 1, "streaming active\n"); @@ -763,7 +833,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, return -EBUSY; } - if (*count == 0 || q->num_buffers != 0 || + if (*count == 0 || q_num_bufs != 0 || (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || !verify_coherency_flags(q, non_coherent_mem)) { /* @@ -781,7 +851,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, * queued without ever calling STREAMON. */ __vb2_queue_cancel(q); - __vb2_queue_free(q, q->num_buffers); + __vb2_queue_free(q, q_num_bufs); mutex_unlock(&q->mmap_lock); /* @@ -795,17 +865,22 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, /* * Make sure the requested values and current defaults are sane. */ - WARN_ON(q->min_buffers_needed > VB2_MAX_FRAME); - num_buffers = max_t(unsigned int, *count, q->min_buffers_needed); - num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); + num_buffers = max_t(unsigned int, *count, q->min_queued_buffers); + num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers); memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); /* * Set this now to ensure that drivers see the correct q->memory value * in the queue_setup op. */ mutex_lock(&q->mmap_lock); + if (!q->bufs) + q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); + if (!q->bufs) + ret = -ENOMEM; q->memory = memory; mutex_unlock(&q->mmap_lock); + if (ret) + return ret; set_queue_coherency(q, non_coherent_mem); /* @@ -842,7 +917,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, * There is no point in continuing if we can't allocate the minimum * number of buffers needed by this vb2_queue. */ - if (allocated_buffers < q->min_buffers_needed) + if (allocated_buffers < q->min_queued_buffers) ret = -ENOMEM; /* @@ -876,7 +951,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, if (ret < 0) { /* * Note: __vb2_queue_free() will subtract 'allocated_buffers' - * from q->num_buffers and it will reset q->memory to + * from already queued buffers and it will reset q->memory to * VB2_MEMORY_UNKNOWN. */ __vb2_queue_free(q, allocated_buffers); @@ -910,10 +985,11 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int num_planes = 0, num_buffers, allocated_buffers; unsigned plane_sizes[VB2_MAX_PLANES] = { }; bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT; - bool no_previous_buffers = !q->num_buffers; - int ret; + unsigned int q_num_bufs = vb2_get_num_buffers(q); + bool no_previous_buffers = !q_num_bufs; + int ret = 0; - if (q->num_buffers == VB2_MAX_FRAME) { + if (q_num_bufs == q->max_num_buffers) { dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } @@ -930,7 +1006,13 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, */ mutex_lock(&q->mmap_lock); q->memory = memory; + if (!q->bufs) + q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL); + if (!q->bufs) + ret = -ENOMEM; mutex_unlock(&q->mmap_lock); + if (ret) + return ret; q->waiting_for_buffers = !q->is_output; set_queue_coherency(q, non_coherent_mem); } else { @@ -942,7 +1024,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, return -EINVAL; } - num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); + num_buffers = min(*count, q->max_num_buffers - q_num_bufs); if (requested_planes && requested_sizes) { num_planes = requested_planes; @@ -974,7 +1056,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, num_buffers = allocated_buffers; /* - * q->num_buffers contains the total number of buffers, that the + * num_buffers contains the total number of buffers, that the * queue driver has set up */ ret = call_qop(q, queue_setup, q, &num_buffers, @@ -995,7 +1077,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, if (ret < 0) { /* * Note: __vb2_queue_free() will subtract 'allocated_buffers' - * from q->num_buffers and it will reset q->memory to + * from already queued buffers and it will reset q->memory to * VB2_MEMORY_UNKNOWN. */ __vb2_queue_free(q, allocated_buffers); @@ -1470,9 +1552,6 @@ static void vb2_req_unprepare(struct media_request_object *obj) WARN_ON(!vb->req_obj.req); } -int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, - struct media_request *req); - static void vb2_req_queue(struct media_request_object *obj) { struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj); @@ -1487,7 +1566,7 @@ static void vb2_req_queue(struct media_request_object *obj) * set. We just ignore that, and expect this will be caught the * next time vb2_req_prepare() is called. */ - err = vb2_core_qbuf(vb->vb2_queue, vb->index, NULL, NULL); + err = vb2_core_qbuf(vb->vb2_queue, vb, NULL, NULL); WARN_ON_ONCE(err && err != -EIO); mutex_unlock(vb->vb2_queue->lock); } @@ -1542,12 +1621,10 @@ unsigned int vb2_request_buffer_cnt(struct media_request *req) } EXPORT_SYMBOL_GPL(vb2_request_buffer_cnt); -int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) +int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb) { - struct vb2_buffer *vb; int ret; - vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(q, 1, "invalid buffer state %s\n", vb2_state_name(vb->state)); @@ -1576,7 +1653,7 @@ EXPORT_SYMBOL_GPL(vb2_core_prepare_buf); * @q: videobuf2 queue * * Attempt to start streaming. When this function is called there must be - * at least q->min_buffers_needed buffers queued up (i.e. the minimum + * at least q->min_queued_buffers queued up (i.e. the minimum * number of buffers required for the DMA engine to function). If the * @start_streaming op fails it is supposed to return all the driver-owned * buffers back to vb2 in state QUEUED. Check if that happened and if @@ -1617,8 +1694,12 @@ static int vb2_start_streaming(struct vb2_queue *q) * Forcefully reclaim buffers if the driver did not * correctly return them to vb2. */ - for (i = 0; i < q->num_buffers; ++i) { - vb = q->bufs[i]; + for (i = 0; i < vb2_get_num_buffers(q); ++i) { + vb = vb2_get_buffer(q, i); + + if (!vb) + continue; + if (vb->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED); } @@ -1634,10 +1715,9 @@ static int vb2_start_streaming(struct vb2_queue *q) return ret; } -int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, +int vb2_core_qbuf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb, struct media_request *req) { - struct vb2_buffer *vb; enum vb2_buffer_state orig_state; int ret; @@ -1646,8 +1726,6 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return -EIO; } - vb = q->bufs[index]; - if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->requires_requests) { dprintk(q, 1, "qbuf requires a request\n"); @@ -1768,7 +1846,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, * then we can finally call start_streaming(). */ if (q->streaming && !q->start_streaming_called && - q->queued_count >= q->min_buffers_needed) { + q->queued_count >= q->min_queued_buffers) { ret = vb2_start_streaming(q); if (ret) { /* @@ -2022,12 +2100,18 @@ static void __vb2_queue_cancel(struct vb2_queue *q) * to vb2 in stop_streaming(). */ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { - for (i = 0; i < q->num_buffers; ++i) - if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { - pr_warn("driver bug: stop_streaming operation is leaving buf %p in active state\n", - q->bufs[i]); - vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR); + for (i = 0; i < vb2_get_num_buffers(q); i++) { + struct vb2_buffer *vb = vb2_get_buffer(q, i); + + if (!vb) + continue; + + if (vb->state == VB2_BUF_STATE_ACTIVE) { + pr_warn("driver bug: stop_streaming operation is leaving buffer %u in active state\n", + vb->index); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } + } /* Must be zero now */ WARN_ON(atomic_read(&q->owned_by_drv_count)); } @@ -2060,10 +2144,15 @@ static void __vb2_queue_cancel(struct vb2_queue *q) * call to __fill_user_buffer() after buf_finish(). That order can't * be changed, so we can't move the buf_finish() to __vb2_dqbuf(). */ - for (i = 0; i < q->num_buffers; ++i) { - struct vb2_buffer *vb = q->bufs[i]; - struct media_request *req = vb->req_obj.req; + for (i = 0; i < vb2_get_num_buffers(q); i++) { + struct vb2_buffer *vb; + struct media_request *req; + + vb = vb2_get_buffer(q, i); + if (!vb) + continue; + req = vb->req_obj.req; /* * If a request is associated with this buffer, then * call buf_request_cancel() to give the driver to complete() @@ -2103,6 +2192,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) int vb2_core_streamon(struct vb2_queue *q, unsigned int type) { + unsigned int q_num_bufs = vb2_get_num_buffers(q); int ret; if (type != q->type) { @@ -2115,14 +2205,14 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) return 0; } - if (!q->num_buffers) { + if (!q_num_bufs) { dprintk(q, 1, "no buffers have been allocated\n"); return -EINVAL; } - if (q->num_buffers < q->min_buffers_needed) { - dprintk(q, 1, "need at least %u allocated buffers\n", - q->min_buffers_needed); + if (q_num_bufs < q->min_queued_buffers) { + dprintk(q, 1, "need at least %u queued buffers\n", + q->min_queued_buffers); return -EINVAL; } @@ -2134,7 +2224,7 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) * Tell driver to start streaming provided sufficient buffers * are available. */ - if (q->queued_count >= q->min_buffers_needed) { + if (q->queued_count >= q->min_queued_buffers) { ret = vb2_start_streaming(q); if (ret) goto unprepare; @@ -2185,13 +2275,12 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) EXPORT_SYMBOL_GPL(vb2_core_streamoff); /* - * __find_plane_by_offset() - find plane associated with the given offset off + * __find_plane_by_offset() - find plane associated with the given offset */ -static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, - unsigned int *_buffer, unsigned int *_plane) +static int __find_plane_by_offset(struct vb2_queue *q, unsigned long offset, + struct vb2_buffer **vb, unsigned int *plane) { - struct vb2_buffer *vb; - unsigned int buffer, plane; + unsigned int buffer; /* * Sanity checks to ensure the lock is held, MEMORY_MMAP is @@ -2209,30 +2298,22 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, return -EBUSY; } - /* - * Go over all buffers and their planes, comparing the given offset - * with an offset assigned to each plane. If a match is found, - * return its buffer and plane numbers. - */ - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - vb = q->bufs[buffer]; + /* Get buffer and plane from the offset */ + buffer = (offset >> PLANE_INDEX_SHIFT) & BUFFER_INDEX_MASK; + *plane = (offset >> PAGE_SHIFT) & PLANE_INDEX_MASK; - for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->planes[plane].m.offset == off) { - *_buffer = buffer; - *_plane = plane; - return 0; - } - } - } + *vb = vb2_get_buffer(q, buffer); + if (!*vb) + return -EINVAL; + if (*plane >= (*vb)->num_planes) + return -EINVAL; - return -EINVAL; + return 0; } int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, - unsigned int index, unsigned int plane, unsigned int flags) + struct vb2_buffer *vb, unsigned int plane, unsigned int flags) { - struct vb2_buffer *vb = NULL; struct vb2_plane *vb_plane; int ret; struct dma_buf *dbuf; @@ -2257,13 +2338,6 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, return -EINVAL; } - if (index >= q->num_buffers) { - dprintk(q, 1, "buffer index out of range\n"); - return -EINVAL; - } - - vb = q->bufs[index]; - if (plane >= vb->num_planes) { dprintk(q, 1, "buffer plane out of range\n"); return -EINVAL; @@ -2282,20 +2356,20 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { dprintk(q, 1, "failed to export buffer %d, plane %d\n", - index, plane); + vb->index, plane); return -EINVAL; } ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n", - index, plane, ret); + vb->index, plane, ret); dma_buf_put(dbuf); return ret; } dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n", - index, plane, ret); + vb->index, plane, ret); *fd = ret; return 0; @@ -2304,9 +2378,9 @@ EXPORT_SYMBOL_GPL(vb2_core_expbuf); int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) { - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; struct vb2_buffer *vb; - unsigned int buffer = 0, plane = 0; + unsigned int plane = 0; int ret; unsigned long length; @@ -2335,12 +2409,10 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * Find the plane corresponding to the offset passed by userspace. This * will return an error if not MEMORY_MMAP or file I/O is in progress. */ - ret = __find_plane_by_offset(q, off, &buffer, &plane); + ret = __find_plane_by_offset(q, offset, &vb, &plane); if (ret) goto unlock; - vb = q->bufs[buffer]; - /* * MMAP requires page_aligned buffers. * The buffer length was page_aligned at __vb2_buf_mem_alloc(), @@ -2368,7 +2440,7 @@ unlock: if (ret) return ret; - dprintk(q, 3, "buffer %d, plane %d successfully mapped\n", buffer, plane); + dprintk(q, 3, "buffer %u, plane %d successfully mapped\n", vb->index, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); @@ -2380,9 +2452,9 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, unsigned long pgoff, unsigned long flags) { - unsigned long off = pgoff << PAGE_SHIFT; + unsigned long offset = pgoff << PAGE_SHIFT; struct vb2_buffer *vb; - unsigned int buffer, plane; + unsigned int plane; void *vaddr; int ret; @@ -2392,12 +2464,10 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, * Find the plane corresponding to the offset passed by userspace. This * will return an error if not MEMORY_MMAP or file I/O is in progress. */ - ret = __find_plane_by_offset(q, off, &buffer, &plane); + ret = __find_plane_by_offset(q, offset, &vb, &plane); if (ret) goto unlock; - vb = q->bufs[buffer]; - vaddr = vb2_plane_vaddr(vb, plane); mutex_unlock(&q->mmap_lock); return vaddr ? (unsigned long)vaddr : -EINVAL; @@ -2414,6 +2484,16 @@ int vb2_core_queue_init(struct vb2_queue *q) /* * Sanity check */ + /* + * For drivers who don't support max_num_buffers ensure + * a backward compatibility. + */ + if (!q->max_num_buffers) + q->max_num_buffers = VB2_MAX_FRAME; + + /* The maximum is limited by offset cookie encoding pattern */ + q->max_num_buffers = min_t(unsigned int, q->max_num_buffers, MAX_BUFFER_INDEX); + if (WARN_ON(!q) || WARN_ON(!q->ops) || WARN_ON(!q->mem_ops) || @@ -2423,18 +2503,22 @@ int vb2_core_queue_init(struct vb2_queue *q) WARN_ON(!q->ops->buf_queue)) return -EINVAL; + if (WARN_ON(q->max_num_buffers > MAX_BUFFER_INDEX) || + WARN_ON(q->min_queued_buffers > q->max_num_buffers)) + return -EINVAL; + if (WARN_ON(q->requires_requests && !q->supports_requests)) return -EINVAL; /* * This combination is not allowed since a non-zero value of - * q->min_buffers_needed can cause vb2_core_qbuf() to fail if + * q->min_queued_buffers can cause vb2_core_qbuf() to fail if * it has to call start_streaming(), and the Request API expects * that queueing a request (and thus queueing a buffer contained * in that request) will always succeed. There is no method of * propagating an error back to userspace. */ - if (WARN_ON(q->supports_requests && q->min_buffers_needed)) + if (WARN_ON(q->supports_requests && q->min_queued_buffers)) return -EINVAL; INIT_LIST_HEAD(&q->queued_list); @@ -2468,7 +2552,9 @@ void vb2_core_queue_release(struct vb2_queue *q) __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); mutex_lock(&q->mmap_lock); - __vb2_queue_free(q, q->num_buffers); + __vb2_queue_free(q, vb2_get_num_buffers(q)); + kfree(q->bufs); + q->bufs = NULL; mutex_unlock(&q->mmap_lock); } EXPORT_SYMBOL_GPL(vb2_core_queue_release); @@ -2497,7 +2583,7 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, /* * Start file I/O emulator only if streaming API has not been used yet. */ - if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { + if (vb2_get_num_buffers(q) == 0 && !vb2_fileio_is_active(q)) { if (!q->is_output && (q->io_modes & VB2_READ) && (req_events & (EPOLLIN | EPOLLRDNORM))) { if (__vb2_init_fileio(q, 1)) @@ -2535,7 +2621,7 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, * For output streams you can call write() as long as there are fewer * buffers queued than there are buffers available. */ - if (q->is_output && q->fileio && q->queued_count < q->num_buffers) + if (q->is_output && q->fileio && q->queued_count < vb2_get_num_buffers(q)) return EPOLLOUT | EPOLLWRNORM; if (list_empty(&q->done_list)) { @@ -2584,8 +2670,8 @@ struct vb2_fileio_buf { * struct vb2_fileio_data - queue context used by file io emulator * * @cur_index: the index of the buffer currently being read from or - * written to. If equal to q->num_buffers then a new buffer - * must be dequeued. + * written to. If equal to number of buffers in the vb2_queue + * then a new buffer must be dequeued. * @initial_index: in the read() case all buffers are queued up immediately * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles * buffers. However, in the write() case no buffers are initially @@ -2595,9 +2681,9 @@ struct vb2_fileio_buf { * buffers. This means that initially __vb2_perform_fileio() * needs to know what buffer index to use when it is queuing up * the buffers for the first time. That initial index is stored - * in this field. Once it is equal to q->num_buffers all - * available buffers have been queued and __vb2_perform_fileio() - * should start the normal dequeue/queue cycle. + * in this field. Once it is equal to number of buffers in the + * vb2_queue all available buffers have been queued and + * __vb2_perform_fileio() should start the normal dequeue/queue cycle. * * vb2 provides a compatibility layer and emulator of file io (read and * write) calls on top of streaming API. For proper operation it required @@ -2625,6 +2711,7 @@ struct vb2_fileio_data { static int __vb2_init_fileio(struct vb2_queue *q, int read) { struct vb2_fileio_data *fileio; + struct vb2_buffer *vb; int i, ret; unsigned int count = 0; @@ -2644,18 +2731,18 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) /* * Check if streaming api has not been already activated. */ - if (q->streaming || q->num_buffers > 0) + if (q->streaming || vb2_get_num_buffers(q) > 0) return -EBUSY; /* - * Start with q->min_buffers_needed + 1, driver can increase it in + * Start with q->min_queued_buffers + 1, driver can increase it in * queue_setup() * - * 'min_buffers_needed' buffers need to be queued up before you + * 'min_queued_buffers' buffers need to be queued up before you * can start streaming, plus 1 for userspace (or in this case, * kernelspace) processing. */ - count = max(2, q->min_buffers_needed + 1); + count = max(2, q->min_queued_buffers + 1); dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", (read) ? "read" : "write", count, q->fileio_read_once, @@ -2680,11 +2767,18 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) if (ret) goto err_kfree; + /* + * Userspace can never add or delete buffers later, so there + * will never be holes. It is safe to assume that vb2_get_buffer(q, 0) + * will always return a valid vb pointer + */ + vb = vb2_get_buffer(q, 0); + /* * Check if plane_count is correct * (multiplane buffers are not supported). */ - if (q->bufs[0]->num_planes != 1) { + if (vb->num_planes != 1) { ret = -EBUSY; goto err_reqbufs; } @@ -2692,13 +2786,16 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) /* * Get kernel address of each buffer. */ - for (i = 0; i < q->num_buffers; i++) { - fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0); + for (i = 0; i < vb2_get_num_buffers(q); i++) { + /* vb can never be NULL when using fileio. */ + vb = vb2_get_buffer(q, i); + + fileio->bufs[i].vaddr = vb2_plane_vaddr(vb, 0); if (fileio->bufs[i].vaddr == NULL) { ret = -EINVAL; goto err_reqbufs; } - fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0); + fileio->bufs[i].size = vb2_plane_size(vb, 0); } /* @@ -2708,18 +2805,23 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) /* * Queue all buffers. */ - for (i = 0; i < q->num_buffers; i++) { - ret = vb2_core_qbuf(q, i, NULL, NULL); + for (i = 0; i < vb2_get_num_buffers(q); i++) { + struct vb2_buffer *vb2 = vb2_get_buffer(q, i); + + if (!vb2) + continue; + + ret = vb2_core_qbuf(q, vb2, NULL, NULL); if (ret) goto err_reqbufs; fileio->bufs[i].queued = 1; } /* * All buffers have been queued, so mark that by setting - * initial_index to q->num_buffers + * initial_index to the number of buffers in the vb2_queue */ - fileio->initial_index = q->num_buffers; - fileio->cur_index = q->num_buffers; + fileio->initial_index = vb2_get_num_buffers(q); + fileio->cur_index = fileio->initial_index; } /* @@ -2812,7 +2914,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Check if we need to dequeue the buffer. */ index = fileio->cur_index; - if (index >= q->num_buffers) { + if (index >= vb2_get_num_buffers(q)) { struct vb2_buffer *b; /* @@ -2826,15 +2928,17 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ fileio->cur_index = index; buf = &fileio->bufs[index]; - b = q->bufs[index]; + + /* b can never be NULL when using fileio. */ + b = vb2_get_buffer(q, index); /* * Get number of bytes filled by the driver */ buf->pos = 0; buf->queued = 0; - buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0) - : vb2_plane_size(q->bufs[index], 0); + buf->size = read ? vb2_get_plane_payload(b, 0) + : vb2_plane_size(b, 0); /* Compensate for data_offset on read in the multiplanar case. */ if (is_multiplanar && read && b->planes[0].data_offset < buf->size) { @@ -2877,7 +2981,8 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Queue next buffer if required. */ if (buf->pos == buf->size || (!read && fileio->write_immediately)) { - struct vb2_buffer *b = q->bufs[index]; + /* b can never be NULL when using fileio. */ + struct vb2_buffer *b = vb2_get_buffer(q, index); /* * Check if this is the last buffer to read. @@ -2894,7 +2999,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ if (copy_timestamp) b->timestamp = ktime_get_ns(); - ret = vb2_core_qbuf(q, index, NULL, NULL); + ret = vb2_core_qbuf(q, b, NULL, NULL); dprintk(q, 5, "vb2_qbuf result: %d\n", ret); if (ret) return ret; @@ -2904,20 +3009,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ buf->pos = 0; buf->queued = 1; - buf->size = vb2_plane_size(q->bufs[index], 0); + buf->size = vb2_plane_size(b, 0); fileio->q_count += 1; /* * If we are queuing up buffers for the first time, then * increase initial_index by one. */ - if (fileio->initial_index < q->num_buffers) + if (fileio->initial_index < vb2_get_num_buffers(q)) fileio->initial_index++; /* * The next buffer to use is either a buffer that's going to be - * queued for the first time (initial_index < q->num_buffers) - * or it is equal to q->num_buffers, meaning that the next - * time we need to dequeue a buffer since we've now queued up - * all the 'first time' buffers. + * queued for the first time (initial_index < number of buffers in the vb2_queue) + * or it is equal to the number of buffers in the vb2_queue, + * meaning that the next time we need to dequeue a buffer since + * we've now queued up all the 'first time' buffers. */ fileio->cur_index = fileio->initial_index; } @@ -2962,7 +3067,7 @@ static int vb2_thread(void *data) int ret = 0; if (q->is_output) { - prequeue = q->num_buffers; + prequeue = vb2_get_num_buffers(q); copy_timestamp = q->copy_timestamp; } @@ -2975,7 +3080,9 @@ static int vb2_thread(void *data) * Call vb2_dqbuf to get buffer back. */ if (prequeue) { - vb = q->bufs[index++]; + vb = vb2_get_buffer(q, index++); + if (!vb) + continue; prequeue--; } else { call_void_qop(q, wait_finish, q); @@ -2984,7 +3091,7 @@ static int vb2_thread(void *data) call_void_qop(q, wait_prepare, q); dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); if (!ret) - vb = q->bufs[index]; + vb = vb2_get_buffer(q, index); } if (ret || threadio->stop) break; @@ -2997,7 +3104,7 @@ static int vb2_thread(void *data) if (copy_timestamp) vb->timestamp = ktime_get_ns(); if (!threadio->stop) - ret = vb2_core_qbuf(q, vb->index, NULL, NULL); + ret = vb2_core_qbuf(q, vb, NULL, NULL); call_void_qop(q, wait_prepare, q); if (ret || threadio->stop) break; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index c7a54d82a..c575198e8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -364,13 +364,12 @@ static void set_buffer_cache_hints(struct vb2_queue *q, } static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev, - struct v4l2_buffer *b, bool is_prepare, - struct media_request **p_req) + struct vb2_buffer *vb, struct v4l2_buffer *b, + bool is_prepare, struct media_request **p_req) { const char *opname = is_prepare ? "prepare_buf" : "qbuf"; struct media_request *req; struct vb2_v4l2_buffer *vbuf; - struct vb2_buffer *vb; int ret; if (b->type != q->type) { @@ -378,23 +377,11 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md return -EINVAL; } - if (b->index >= q->num_buffers) { - dprintk(q, 1, "%s: buffer index out of range\n", opname); - return -EINVAL; - } - - if (q->bufs[b->index] == NULL) { - /* Should never happen */ - dprintk(q, 1, "%s: buffer is NULL\n", opname); - return -EINVAL; - } - if (b->memory != q->memory) { dprintk(q, 1, "%s: invalid memory type\n", opname); return -EINVAL; } - vb = q->bufs[b->index]; vbuf = to_vb2_v4l2_buffer(vb); ret = __verify_planes_array(vb, b); if (ret) @@ -628,11 +615,22 @@ static const struct vb2_buf_ops v4l2_buf_ops = { struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp) { unsigned int i; + struct vb2_buffer *vb2; - for (i = 0; i < q->num_buffers; i++) - if (q->bufs[i]->copied_timestamp && - q->bufs[i]->timestamp == timestamp) - return vb2_get_buffer(q, i); + /* + * This loop doesn't scale if there is a really large number of buffers. + * Maybe something more efficient will be needed in this case. + */ + for (i = 0; i < q->max_num_buffers; i++) { + vb2 = vb2_get_buffer(q, i); + + if (!vb2) + continue; + + if (vb2->copied_timestamp && + vb2->timestamp == timestamp) + return vb2; + } return NULL; } EXPORT_SYMBOL_GPL(vb2_find_buffer); @@ -660,20 +658,33 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) return -EINVAL; } - if (b->index >= q->num_buffers) { - dprintk(q, 1, "buffer index out of range\n"); + vb = vb2_get_buffer(q, b->index); + if (!vb) { + dprintk(q, 1, "can't find the requested buffer %u\n", b->index); return -EINVAL; } - vb = q->bufs[b->index]; + ret = __verify_planes_array(vb, b); if (!ret) - vb2_core_querybuf(q, b->index, b); + vb2_core_querybuf(q, vb, b); return ret; } EXPORT_SYMBOL(vb2_querybuf); -static void fill_buf_caps(struct vb2_queue *q, u32 *caps) +static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory, + u32 *flags, u32 *caps, u32 *max_num_bufs) { + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { + /* + * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, + * but in order to avoid bugs we zero out all bits. + */ + *flags = 0; + } else { + /* Clear all unknown flags. */ + *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; + } + *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; if (q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; @@ -685,25 +696,11 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; if (q->allow_cache_hints && q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; -#ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; -#endif -} - -static void validate_memory_flags(struct vb2_queue *q, - int memory, - u32 *flags) -{ - if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { - /* - * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, - * but in order to avoid bugs we zero out all bits. - */ - *flags = 0; - } else { - /* Clear all unknown flags. */ - *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; + if (max_num_bufs) { + *max_num_bufs = q->max_num_buffers; + *caps |= V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS; } } @@ -712,8 +709,8 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) int ret = vb2_verify_memory_type(q, req->memory, req->type); u32 flags = req->flags; - fill_buf_caps(q, &req->capabilities); - validate_memory_flags(q, req->memory, &flags); + vb2_set_flags_and_caps(q, req->memory, &flags, + &req->capabilities, NULL); req->flags = flags; return ret ? ret : vb2_core_reqbufs(q, req->memory, req->flags, &req->count); @@ -723,6 +720,7 @@ EXPORT_SYMBOL_GPL(vb2_reqbufs); int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b) { + struct vb2_buffer *vb; int ret; if (vb2_fileio_is_active(q)) { @@ -733,9 +731,15 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, if (b->flags & V4L2_BUF_FLAG_REQUEST_FD) return -EINVAL; - ret = vb2_queue_or_prepare_buf(q, mdev, b, true, NULL); + vb = vb2_get_buffer(q, b->index); + if (!vb) { + dprintk(q, 1, "can't find the requested buffer %u\n", b->index); + return -EINVAL; + } + + ret = vb2_queue_or_prepare_buf(q, mdev, vb, b, true, NULL); - return ret ? ret : vb2_core_prepare_buf(q, b->index, b); + return ret ? ret : vb2_core_prepare_buf(q, vb, b); } EXPORT_SYMBOL_GPL(vb2_prepare_buf); @@ -747,9 +751,9 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) int ret = vb2_verify_memory_type(q, create->memory, f->type); unsigned i; - fill_buf_caps(q, &create->capabilities); - validate_memory_flags(q, create->memory, &create->flags); - create->index = q->num_buffers; + create->index = vb2_get_num_buffers(q); + vb2_set_flags_and_caps(q, create->memory, &create->flags, + &create->capabilities, &create->max_num_buffers); if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -803,6 +807,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b) { struct media_request *req = NULL; + struct vb2_buffer *vb; int ret; if (vb2_fileio_is_active(q)) { @@ -810,10 +815,16 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, return -EBUSY; } - ret = vb2_queue_or_prepare_buf(q, mdev, b, false, &req); + vb = vb2_get_buffer(q, b->index); + if (!vb) { + dprintk(q, 1, "can't find the requested buffer %u\n", b->index); + return -EINVAL; + } + + ret = vb2_queue_or_prepare_buf(q, mdev, vb, b, false, &req); if (ret) return ret; - ret = vb2_core_qbuf(q, b->index, b, req); + ret = vb2_core_qbuf(q, vb, b, req); if (req) media_request_put(req); return ret; @@ -873,7 +884,15 @@ EXPORT_SYMBOL_GPL(vb2_streamoff); int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) { - return vb2_core_expbuf(q, &eb->fd, eb->type, eb->index, + struct vb2_buffer *vb; + + vb = vb2_get_buffer(q, eb->index); + if (!vb) { + dprintk(q, 1, "can't find the requested buffer %u\n", eb->index); + return -EINVAL; + } + + return vb2_core_expbuf(q, &eb->fd, eb->type, vb, eb->plane, eb->flags); } EXPORT_SYMBOL_GPL(vb2_expbuf); @@ -985,8 +1004,8 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); u32 flags = p->flags; - fill_buf_caps(vdev->queue, &p->capabilities); - validate_memory_flags(vdev->queue, p->memory, &flags); + vb2_set_flags_and_caps(vdev->queue, p->memory, &flags, + &p->capabilities, NULL); p->flags = flags; if (res) return res; @@ -1005,12 +1024,11 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, struct v4l2_create_buffers *p) { struct video_device *vdev = video_devdata(file); - int res = vb2_verify_memory_type(vdev->queue, p->memory, - p->format.type); + int res = vb2_verify_memory_type(vdev->queue, p->memory, p->format.type); - p->index = vdev->queue->num_buffers; - fill_buf_caps(vdev->queue, &p->capabilities); - validate_memory_flags(vdev->queue, p->memory, &p->flags); + p->index = vb2_get_num_buffers(vdev->queue); + vb2_set_flags_and_caps(vdev->queue, p->memory, &p->flags, + &p->capabilities, &p->max_num_buffers); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. @@ -1115,7 +1133,7 @@ int _vb2_fop_release(struct file *file, struct mutex *lock) if (lock) mutex_lock(lock); - if (file->private_data == vdev->queue->owner) { + if (!vdev->queue->owner || file->private_data == vdev->queue->owner) { vb2_queue_release(vdev->queue); vdev->queue->owner = NULL; } @@ -1243,7 +1261,7 @@ void vb2_video_unregister_device(struct video_device *vdev) */ get_device(&vdev->dev); video_unregister_device(vdev); - if (vdev->queue && vdev->queue->owner) { + if (vdev->queue) { struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 909df82fe..192a8230c 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -167,17 +167,14 @@ int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking) memset(ctx, 0, sizeof(struct dvb_vb2_ctx)); q->type = DVB_BUF_TYPE_CAPTURE; - /**capture type*/ - q->is_output = 0; /**only mmap is supported currently*/ q->io_modes = VB2_MMAP; q->drv_priv = ctx; q->buf_struct_size = sizeof(struct dvb_buffer); - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->ops = &dvb_vb2_qops; q->mem_ops = &vb2_vmalloc_memops; q->buf_ops = &dvb_vb2_buf_ops; - q->num_buffers = 0; ret = vb2_core_queue_init(q); if (ret) { ctx->state = DVB_VB2_STATE_NONE; @@ -355,12 +352,13 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) int dvb_vb2_querybuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b) { struct vb2_queue *q = &ctx->vb_q; + struct vb2_buffer *vb2 = vb2_get_buffer(q, b->index); - if (b->index >= q->num_buffers) { - dprintk(1, "[%s] buffer index out of range\n", ctx->name); + if (!vb2) { + dprintk(1, "[%s] invalid buffer index\n", ctx->name); return -EINVAL; } - vb2_core_querybuf(&ctx->vb_q, b->index, b); + vb2_core_querybuf(&ctx->vb_q, vb2, b); dprintk(3, "[%s] index=%d\n", ctx->name, b->index); return 0; } @@ -370,7 +368,7 @@ int dvb_vb2_expbuf(struct dvb_vb2_ctx *ctx, struct dmx_exportbuffer *exp) struct vb2_queue *q = &ctx->vb_q; int ret; - ret = vb2_core_expbuf(&ctx->vb_q, &exp->fd, q->type, exp->index, + ret = vb2_core_expbuf(&ctx->vb_q, &exp->fd, q->type, q->bufs[exp->index], 0, exp->flags); if (ret) { dprintk(1, "[%s] index=%d errno=%d\n", ctx->name, @@ -385,13 +383,14 @@ int dvb_vb2_expbuf(struct dvb_vb2_ctx *ctx, struct dmx_exportbuffer *exp) int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b) { struct vb2_queue *q = &ctx->vb_q; + struct vb2_buffer *vb2 = vb2_get_buffer(q, b->index); int ret; - if (b->index >= q->num_buffers) { - dprintk(1, "[%s] buffer index out of range\n", ctx->name); + if (!vb2) { + dprintk(1, "[%s] invalid buffer index\n", ctx->name); return -EINVAL; } - ret = vb2_core_qbuf(&ctx->vb_q, b->index, b, NULL); + ret = vb2_core_qbuf(&ctx->vb_q, vb2, b, NULL); if (ret) { dprintk(1, "[%s] index=%d errno=%d\n", ctx->name, b->index, ret); diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 02c619e51..023db6e79 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -439,12 +439,13 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, { struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); struct platform_device *pdev = dev->pdev; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev_dbg(&pdev->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ - if (vq->num_buffers + *nbuffers < 8) - *nbuffers = 8 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 8) + *nbuffers = 8 - q_num_bufs; *nplanes = 1; sizes[0] = PAGE_ALIGN(dev->buffersize); dev_dbg(&pdev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 59ee0ca2c..4c3435921 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -41,6 +41,16 @@ config VIDEO_APTINA_PLL config VIDEO_CCS_PLL tristate +config VIDEO_ALVIUM_CSI2 + tristate "Allied Vision ALVIUM MIPI CSI-2 camera support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor-level driver for the Allied Vision + ALVIUM camera connected via MIPI CSI-2 interface. + + To compile this driver as a module, choose M here: the + module will be called alvium-csi2. + config VIDEO_AR0521 tristate "ON Semiconductor AR0521 sensor support" help @@ -50,6 +60,26 @@ config VIDEO_AR0521 To compile this driver as a module, choose M here: the module will be called ar0521. +config VIDEO_GC0308 + tristate "GalaxyCore GC0308 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the GalaxyCore + GC0308 camera. + + To compile this driver as a module, choose M here: the + module will be called gc0308. + +config VIDEO_GC2145 + select V4L2_CCI_I2C + tristate "GalaxyCore GC2145 sensor support" + help + This is a V4L2 sensor-level driver for GalaxyCore GC2145 + 2 Mpixel camera. + + To compile this driver as a module, choose M here: the + module will be called gc2145. + config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" help @@ -455,6 +485,16 @@ config VIDEO_OV5695 To compile this driver as a module, choose M here: the module will be called ov5695. +config VIDEO_OV64A40 + tristate "OmniVision OV64A40 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV64A40 camera. + + To compile this driver as a module, choose M here: the + module will be called ov64a40. + config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" help @@ -628,6 +668,23 @@ source "drivers/media/i2c/et8ek8/Kconfig" endif +menu "Camera ISPs" + visible if MEDIA_CAMERA_SUPPORT + +config VIDEO_THP7312 + tristate "THine THP7312 support" + depends on I2C + select FW_LOADER + select MEDIA_CONTROLLER + select V4L2_CCI_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor-level driver for the THine + THP7312 ISP. + +endmenu + menu "Lens drivers" visible if MEDIA_CAMERA_SUPPORT @@ -1186,6 +1243,21 @@ config VIDEO_TW2804 To compile this driver as a module, choose M here: the module will be called tw2804. +config VIDEO_TW9900 + tristate "Techwell TW9900 video decoder" + depends on GPIOLIB + depends on VIDEO_DEV && I2C + depends on PM + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_ASYNC + help + Support for the Techwell TW9900 multi-standard video decoder. + It supports NTSC, PAL standards with auto-detection features. + + To compile this driver as a module, choose M here: the + module will be called tw9900. + config VIDEO_TW9903 tristate "Techwell TW9903 video decoder" depends on VIDEO_DEV && I2C @@ -1432,6 +1504,7 @@ config VIDEO_ST_MIPID02 depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API + select V4L2_CCI_I2C select V4L2_FWNODE help Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index f5010f80a..dfbe6448b 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o +obj-$(CONFIG_VIDEO_ALVIUM_CSI2) += alvium-csi2.o obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o obj-$(CONFIG_VIDEO_AR0521) += ar0521.o obj-$(CONFIG_VIDEO_BT819) += bt819.o @@ -36,6 +37,8 @@ obj-$(CONFIG_VIDEO_DW9719) += dw9719.o obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ +obj-$(CONFIG_VIDEO_GC0308) += gc0308.o +obj-$(CONFIG_VIDEO_GC2145) += gc2145.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o obj-$(CONFIG_VIDEO_HI847) += hi847.o @@ -92,6 +95,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5675) += ov5675.o obj-$(CONFIG_VIDEO_OV5693) += ov5693.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o +obj-$(CONFIG_VIDEO_OV64A40) += ov64a40.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o obj-$(CONFIG_VIDEO_OV7251) += ov7251.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o @@ -128,6 +132,7 @@ obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +obj-$(CONFIG_VIDEO_THP7312) += thp7312.o obj-$(CONFIG_VIDEO_THS7303) += ths7303.o obj-$(CONFIG_VIDEO_THS8200) += ths8200.o obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o @@ -136,6 +141,7 @@ obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o obj-$(CONFIG_VIDEO_TW2804) += tw2804.o +obj-$(CONFIG_VIDEO_TW9900) += tw9900.o obj-$(CONFIG_VIDEO_TW9903) += tw9903.o obj-$(CONFIG_VIDEO_TW9906) += tw9906.o obj-$(CONFIG_VIDEO_TW9910) += tw9910.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 541344731..409b9a37f 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -463,11 +463,19 @@ static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } -static int adv7180_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int adv7180_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct adv7180_state *state = to_state(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (state->curr_norm & V4L2_STD_525_60) { fi->interval.numerator = 1001; fi->interval.denominator = 30000; @@ -769,7 +777,7 @@ static int adv7180_get_pad_format(struct v4l2_subdev *sd, struct adv7180_state *state = to_state(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); } else { adv7180_mbus_fmt(sd, &format->format); format->format.field = state->field; @@ -806,15 +814,15 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, adv7180_set_power(state, true); } } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } return ret; } -static int adv7180_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int adv7180_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -913,7 +921,6 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, - .g_frame_interval = adv7180_g_frame_interval, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, @@ -929,10 +936,10 @@ static const struct v4l2_subdev_core_ops adv7180_core_ops = { }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { - .init_cfg = adv7180_init_cfg, .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, + .get_frame_interval = adv7180_get_frame_interval, .get_mbus_config = adv7180_get_mbus_config, }; @@ -947,6 +954,10 @@ static const struct v4l2_subdev_ops adv7180_ops = { .sensor = &adv7180_sensor_ops, }; +static const struct v4l2_subdev_internal_ops adv7180_internal_ops = { + .init_state = adv7180_init_state, +}; + static irqreturn_t adv7180_irq(int irq, void *devid) { struct adv7180_state *state = devid; @@ -1458,6 +1469,7 @@ static int adv7180_probe(struct i2c_client *client) state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + sd->internal_ops = &adv7180_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ret = adv7180_init_controls(state); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 3659feafa..2a2cace4a 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -442,8 +442,6 @@ static int adv7183_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) decoder->fmt = *fmt; - else - sd_state->pads->try_fmt = *fmt; return 0; } diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 00095c776..50d9fbadb 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -354,8 +354,8 @@ static int adv748x_afe_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_afe_fill_format(afe, &sdformat->format); @@ -378,7 +378,7 @@ static int adv748x_afe_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_afe_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index a5a7cb228..5b265b722 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -147,7 +147,7 @@ adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &tx->format; } diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 400d71c27..ec151dc69 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -441,8 +441,8 @@ static int adv748x_hdmi_get_format(struct v4l2_subdev *sd, return -EINVAL; if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, - sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, + sdformat->pad); sdformat->format = *mbusformat; } else { adv748x_hdmi_fill_format(hdmi, &sdformat->format); @@ -464,7 +464,7 @@ static int adv748x_hdmi_set_format(struct v4l2_subdev *sd, if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) return adv748x_hdmi_get_format(sd, sd_state, sdformat); - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; return 0; diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index a9183d928..0f780eb6e 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1238,7 +1238,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; @@ -1293,7 +1293,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index b202a85fb..810fa8826 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1929,7 +1929,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -1978,7 +1978,7 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index c1664a362..2ad0f9f55 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2087,7 +2087,7 @@ static int adv7842_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else { format->format.code = state->format->code; @@ -2119,7 +2119,7 @@ static int adv7842_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); fmt->code = format->format.code; } else { state->format = info; diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c index 463b51d46..9a2432cea 100644 --- a/drivers/media/i2c/ak7375.c +++ b/drivers/media/i2c/ak7375.c @@ -10,30 +10,60 @@ #include #include -#define AK7375_MAX_FOCUS_POS 4095 -/* - * This sets the minimum granularity for the focus positions. - * A value of 1 gives maximum accuracy for a desired focus position - */ -#define AK7375_FOCUS_STEPS 1 -/* - * This acts as the minimum granularity of lens movement. - * Keep this value power of 2, so the control steps can be - * uniformly adjusted for gradual lens movement, with desired - * number of control steps. - */ -#define AK7375_CTRL_STEPS 64 -#define AK7375_CTRL_DELAY_US 1000 -/* - * The vcm may take up 10 ms (tDELAY) to power on and start taking - * I2C messages. Based on AK7371 datasheet. - */ -#define AK7375_POWER_DELAY_US 10000 +struct ak73xx_chipdef { + u8 reg_position; + u8 reg_cont; + u8 shift_pos; + u8 mode_active; + u8 mode_standby; + bool has_standby; /* Some chips may not have standby mode */ + u16 focus_pos_max; + /* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ + u16 focus_steps; + /* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ + u16 ctrl_steps; + u16 ctrl_delay_us; + /* + * The vcm may take time (tDELAY) to power on and start taking + * I2C messages. + */ + u16 power_delay_us; +}; -#define AK7375_REG_POSITION 0x0 -#define AK7375_REG_CONT 0x2 -#define AK7375_MODE_ACTIVE 0x0 -#define AK7375_MODE_STANDBY 0x40 +static const struct ak73xx_chipdef ak7345_cdef = { + .reg_position = 0x0, + .reg_cont = 0x2, + .shift_pos = 7, /* 9 bits position values, need to << 7 */ + .mode_active = 0x0, + .has_standby = false, + .focus_pos_max = 511, + .focus_steps = 1, + .ctrl_steps = 16, + .ctrl_delay_us = 1000, + .power_delay_us = 20000, +}; + +static const struct ak73xx_chipdef ak7375_cdef = { + .reg_position = 0x0, + .reg_cont = 0x2, + .shift_pos = 4, /* 12 bits position values, need to << 4 */ + .mode_active = 0x0, + .mode_standby = 0x40, + .has_standby = true, + .focus_pos_max = 4095, + .focus_steps = 1, + .ctrl_steps = 64, + .ctrl_delay_us = 1000, + .power_delay_us = 10000, +}; static const char * const ak7375_supply_names[] = { "vdd", @@ -42,6 +72,7 @@ static const char * const ak7375_supply_names[] = { /* ak7375 device structure */ struct ak7375_device { + const struct ak73xx_chipdef *cdef; struct v4l2_ctrl_handler ctrls_vcm; struct v4l2_subdev sd; struct v4l2_ctrl *focus; @@ -86,10 +117,11 @@ static int ak7375_i2c_write(struct ak7375_device *ak7375, static int ak7375_set_ctrl(struct v4l2_ctrl *ctrl) { struct ak7375_device *dev_vcm = to_ak7375_vcm(ctrl); + const struct ak73xx_chipdef *cdef = dev_vcm->cdef; if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) - return ak7375_i2c_write(dev_vcm, AK7375_REG_POSITION, - ctrl->val << 4, 2); + return ak7375_i2c_write(dev_vcm, cdef->reg_position, + ctrl->val << cdef->shift_pos, 2); return -EINVAL; } @@ -128,11 +160,12 @@ static int ak7375_init_controls(struct ak7375_device *dev_vcm) { struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; const struct v4l2_ctrl_ops *ops = &ak7375_vcm_ctrl_ops; + const struct ak73xx_chipdef *cdef = dev_vcm->cdef; v4l2_ctrl_handler_init(hdl, 1); dev_vcm->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, - 0, AK7375_MAX_FOCUS_POS, AK7375_FOCUS_STEPS, 0); + 0, cdef->focus_pos_max, cdef->focus_steps, 0); if (hdl->error) dev_err(dev_vcm->sd.dev, "%s fail error: 0x%x\n", @@ -153,6 +186,8 @@ static int ak7375_probe(struct i2c_client *client) if (!ak7375_dev) return -ENOMEM; + ak7375_dev->cdef = device_get_match_data(&client->dev); + for (i = 0; i < ARRAY_SIZE(ak7375_supply_names); i++) ak7375_dev->supplies[i].supply = ak7375_supply_names[i]; @@ -206,32 +241,35 @@ static void ak7375_remove(struct i2c_client *client) /* * This function sets the vcm position, so it consumes least current - * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * The lens position is gradually moved in units of ctrl_steps, * to make the movements smoothly. */ static int __maybe_unused ak7375_vcm_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + const struct ak73xx_chipdef *cdef = ak7375_dev->cdef; int ret, val; if (!ak7375_dev->active) return 0; - for (val = ak7375_dev->focus->val & ~(AK7375_CTRL_STEPS - 1); - val >= 0; val -= AK7375_CTRL_STEPS) { - ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, - val << 4, 2); + for (val = ak7375_dev->focus->val & ~(cdef->ctrl_steps - 1); + val >= 0; val -= cdef->ctrl_steps) { + ret = ak7375_i2c_write(ak7375_dev, cdef->reg_position, + val << cdef->shift_pos, 2); if (ret) dev_err_once(dev, "%s I2C failure: %d\n", __func__, ret); - usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + usleep_range(cdef->ctrl_delay_us, cdef->ctrl_delay_us + 10); } - ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, - AK7375_MODE_STANDBY, 1); - if (ret) - dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + if (cdef->has_standby) { + ret = ak7375_i2c_write(ak7375_dev, cdef->reg_cont, + cdef->mode_standby, 1); + if (ret) + dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + } ret = regulator_bulk_disable(ARRAY_SIZE(ak7375_supply_names), ak7375_dev->supplies); @@ -246,13 +284,14 @@ static int __maybe_unused ak7375_vcm_suspend(struct device *dev) /* * This function sets the vcm position to the value set by the user * through v4l2_ctrl_ops s_ctrl handler - * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * The lens position is gradually moved in units of ctrl_steps, * to make the movements smoothly. */ static int __maybe_unused ak7375_vcm_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + const struct ak73xx_chipdef *cdef = ak7375_dev->cdef; int ret, val; if (ak7375_dev->active) @@ -264,24 +303,24 @@ static int __maybe_unused ak7375_vcm_resume(struct device *dev) return ret; /* Wait for vcm to become ready */ - usleep_range(AK7375_POWER_DELAY_US, AK7375_POWER_DELAY_US + 500); + usleep_range(cdef->power_delay_us, cdef->power_delay_us + 500); - ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, - AK7375_MODE_ACTIVE, 1); + ret = ak7375_i2c_write(ak7375_dev, cdef->reg_cont, + cdef->mode_active, 1); if (ret) { dev_err(dev, "%s I2C failure: %d\n", __func__, ret); return ret; } - for (val = ak7375_dev->focus->val % AK7375_CTRL_STEPS; + for (val = ak7375_dev->focus->val % cdef->ctrl_steps; val <= ak7375_dev->focus->val; - val += AK7375_CTRL_STEPS) { - ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, - val << 4, 2); + val += cdef->ctrl_steps) { + ret = ak7375_i2c_write(ak7375_dev, cdef->reg_position, + val << cdef->shift_pos, 2); if (ret) dev_err_ratelimited(dev, "%s I2C failure: %d\n", __func__, ret); - usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + usleep_range(cdef->ctrl_delay_us, cdef->ctrl_delay_us + 10); } ak7375_dev->active = true; @@ -290,7 +329,8 @@ static int __maybe_unused ak7375_vcm_resume(struct device *dev) } static const struct of_device_id ak7375_of_table[] = { - { .compatible = "asahi-kasei,ak7375" }, + { .compatible = "asahi-kasei,ak7345", .data = &ak7345_cdef, }, + { .compatible = "asahi-kasei,ak7375", .data = &ak7375_cdef, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ak7375_of_table); diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c new file mode 100644 index 000000000..34ff7fad3 --- /dev/null +++ b/drivers/media/i2c/alvium-csi2.c @@ -0,0 +1,2558 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allied Vision Technologies GmbH Alvium camera driver + * + * Copyright (C) 2023 Tommaso Merciai + * Copyright (C) 2023 Martin Hecht + * Copyright (C) 2023 Avnet EMG GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alvium-csi2.h" + +static const struct v4l2_mbus_framefmt alvium_csi2_default_fmt = { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .width = 640, + .height = 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB), + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB), + .field = V4L2_FIELD_NONE, +}; + +static const struct alvium_pixfmt alvium_csi2_fmts[] = { + { + /* UYVY8_2X8 */ + .id = ALVIUM_FMT_UYVY8_2X8, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_YUV422_8, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_YUV422_8B, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* UYVY8_1X16 */ + .id = ALVIUM_FMT_UYVY8_1X16, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_YUV422_8, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_YUV422_8B, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* YUYV8_1X16 */ + .id = ALVIUM_FMT_YUYV8_1X16, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_YUV422_8, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_YUV422_8B, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* YUYV8_2X8 */ + .id = ALVIUM_FMT_YUYV8_2X8, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_YUV422_8, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_YUV422_8B, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* YUYV10_1X20 */ + .id = ALVIUM_FMT_YUYV10_1X20, + .code = MEDIA_BUS_FMT_YUYV10_1X20, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_YUV422_10, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_YUV422_10B, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* RGB888_1X24 */ + .id = ALVIUM_FMT_RGB888_1X24, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_RGB888, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_RGB888, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* RBG888_1X24 */ + .id = ALVIUM_FMT_RBG888_1X24, + .code = MEDIA_BUS_FMT_RBG888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_RGB888, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_RGB888, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* BGR888_1X24 */ + .id = ALVIUM_FMT_BGR888_1X24, + .code = MEDIA_BUS_FMT_BGR888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_RGB888, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_RGB888, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* RGB888_3X8 */ + .id = ALVIUM_FMT_RGB888_3X8, + .code = MEDIA_BUS_FMT_RGB888_3X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt_av_bit = ALVIUM_BIT_RGB888, + .bay_av_bit = ALVIUM_BIT_BAY_NONE, + .mipi_fmt_regval = MIPI_CSI2_DT_RGB888, + .bay_fmt_regval = -1, + .is_raw = 0, + }, { + /* Y8_1X8 */ + .id = ALVIUM_FMT_Y8_1X8, + .code = MEDIA_BUS_FMT_Y8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW8, + .bay_av_bit = ALVIUM_BIT_BAY_MONO, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW8, + .bay_fmt_regval = 0x00, + .is_raw = 1, + }, { + /* SGRBG8_1X8 */ + .id = ALVIUM_FMT_SGRBG8_1X8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW8, + .bay_av_bit = ALVIUM_BIT_BAY_GR, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW8, + .bay_fmt_regval = 0x01, + .is_raw = 1, + }, { + /* SRGGB8_1X8 */ + .id = ALVIUM_FMT_SRGGB8_1X8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW8, + .bay_av_bit = ALVIUM_BIT_BAY_RG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW8, + .bay_fmt_regval = 0x02, + .is_raw = 1, + }, { + /* SGBRG8_1X8 */ + .id = ALVIUM_FMT_SGBRG8_1X8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW8, + .bay_av_bit = ALVIUM_BIT_BAY_GB, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW8, + .bay_fmt_regval = 0x03, + .is_raw = 1, + }, { + /* SBGGR8_1X8 */ + .id = ALVIUM_FMT_SBGGR8_1X8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW8, + .bay_av_bit = ALVIUM_BIT_BAY_BG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW8, + .bay_fmt_regval = 0x04, + .is_raw = 1, + }, { + /* Y10_1X10 */ + .id = ALVIUM_FMT_Y10_1X10, + .code = MEDIA_BUS_FMT_Y10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW10, + .bay_av_bit = ALVIUM_BIT_BAY_MONO, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW10, + .bay_fmt_regval = 0x00, + .is_raw = 1, + }, { + /* SGRBG10_1X10 */ + .id = ALVIUM_FMT_SGRBG10_1X10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW10, + .bay_av_bit = ALVIUM_BIT_BAY_GR, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW10, + .bay_fmt_regval = 0x01, + .is_raw = 1, + }, { + /* SRGGB10_1X10 */ + .id = ALVIUM_FMT_SRGGB10_1X10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW10, + .bay_av_bit = ALVIUM_BIT_BAY_RG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW10, + .bay_fmt_regval = 0x02, + .is_raw = 1, + }, { + /* SGBRG10_1X10 */ + .id = ALVIUM_FMT_SGBRG10_1X10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW10, + .bay_av_bit = ALVIUM_BIT_BAY_GB, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW10, + .bay_fmt_regval = 0x03, + .is_raw = 1, + }, { + /* SBGGR10_1X10 */ + .id = ALVIUM_FMT_SBGGR10_1X10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW10, + .bay_av_bit = ALVIUM_BIT_BAY_BG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW10, + .bay_fmt_regval = 0x04, + .is_raw = 1, + }, { + /* Y12_1X12 */ + .id = ALVIUM_FMT_Y12_1X12, + .code = MEDIA_BUS_FMT_Y12_1X12, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW12, + .bay_av_bit = ALVIUM_BIT_BAY_MONO, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW12, + .bay_fmt_regval = 0x00, + .is_raw = 1, + }, { + /* SGRBG12_1X12 */ + .id = ALVIUM_FMT_SGRBG12_1X12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW12, + .bay_av_bit = ALVIUM_BIT_BAY_GR, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW12, + .bay_fmt_regval = 0x01, + .is_raw = 1, + }, { + /* SRGGB12_1X12 */ + .id = ALVIUM_FMT_SRGGB12_1X12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW12, + .bay_av_bit = ALVIUM_BIT_BAY_RG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW12, + .bay_fmt_regval = 0x02, + .is_raw = 1, + }, { + /* SGBRG12_1X12 */ + .id = ALVIUM_FMT_SGBRG12_1X12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW12, + .bay_av_bit = ALVIUM_BIT_BAY_GB, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW12, + .bay_fmt_regval = 0x03, + .is_raw = 1, + }, { + /* SBGGR12_1X12 */ + .id = ALVIUM_FMT_SBGGR12_1X12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW12, + .bay_av_bit = ALVIUM_BIT_BAY_BG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW12, + .bay_fmt_regval = 0x04, + .is_raw = 1, + }, { + /* SBGGR14_1X14 */ + .id = ALVIUM_FMT_SBGGR14_1X14, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW14, + .bay_av_bit = ALVIUM_BIT_BAY_GR, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW14, + .bay_fmt_regval = 0x01, + .is_raw = 1, + }, { + /* SGBRG14_1X14 */ + .id = ALVIUM_FMT_SGBRG14_1X14, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW14, + .bay_av_bit = ALVIUM_BIT_BAY_RG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW14, + .bay_fmt_regval = 0x02, + .is_raw = 1, + }, { + /* SRGGB14_1X14 */ + .id = ALVIUM_FMT_SRGGB14_1X14, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW14, + .bay_av_bit = ALVIUM_BIT_BAY_GB, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW14, + .bay_fmt_regval = 0x03, + .is_raw = 1, + }, { + /* SGRBG14_1X14 */ + .id = ALVIUM_FMT_SGRBG14_1X14, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .colorspace = V4L2_COLORSPACE_RAW, + .fmt_av_bit = ALVIUM_BIT_RAW14, + .bay_av_bit = ALVIUM_BIT_BAY_BG, + .mipi_fmt_regval = MIPI_CSI2_DT_RAW14, + .bay_fmt_regval = 0x04, + .is_raw = 1, + }, + { /* sentinel */ } +}; + +static int alvium_read(struct alvium_dev *alvium, u32 reg, u64 *val, int *err) +{ + if (reg & REG_BCRM_V4L2) { + reg &= ~REG_BCRM_V4L2; + reg += alvium->bcrm_addr; + } + + return cci_read(alvium->regmap, reg, val, err); +} + +static int alvium_write(struct alvium_dev *alvium, u32 reg, u64 val, int *err) +{ + if (reg & REG_BCRM_V4L2) { + reg &= ~REG_BCRM_V4L2; + reg += alvium->bcrm_addr; + } + + return cci_write(alvium->regmap, reg, val, err); +} + +static int alvium_write_hshake(struct alvium_dev *alvium, u32 reg, u64 val) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 hshake_bit; + int ret = 0; + + /* reset handshake bit and write alvium reg */ + alvium_write(alvium, REG_BCRM_WRITE_HANDSHAKE_RW, 0, &ret); + alvium_write(alvium, reg, val, &ret); + if (ret) { + dev_err(dev, "Fail to write reg\n"); + return ret; + } + + /* poll handshake bit since bit0 = 1 */ + read_poll_timeout(alvium_read, hshake_bit, + ((hshake_bit & BCRM_HANDSHAKE_W_DONE_EN_BIT) == 1), + 15000, 45000, true, + alvium, REG_BCRM_WRITE_HANDSHAKE_RW, + &hshake_bit, &ret); + if (ret) { + dev_err(dev, "poll bit[0] = 1, hshake reg fail\n"); + return ret; + } + + /* reset handshake bit, write 0 to bit0 */ + alvium_write(alvium, REG_BCRM_WRITE_HANDSHAKE_RW, 0, &ret); + if (ret) { + dev_err(dev, "Fail to reset hshake reg\n"); + return ret; + } + + /* poll handshake bit since bit0 = 0 */ + read_poll_timeout(alvium_read, hshake_bit, + ((hshake_bit & BCRM_HANDSHAKE_W_DONE_EN_BIT) == 0), + 15000, 45000, true, + alvium, REG_BCRM_WRITE_HANDSHAKE_RW, + &hshake_bit, &ret); + if (ret) { + dev_err(dev, "poll bit[0] = 0, hshake reg fail\n"); + return ret; + } + + return 0; +} + +static int alvium_get_bcrm_vers(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 min, maj; + int ret = 0; + + ret = alvium_read(alvium, REG_BCRM_MINOR_VERSION_R, &min, &ret); + ret = alvium_read(alvium, REG_BCRM_MAJOR_VERSION_R, &maj, &ret); + if (ret) + return ret; + + dev_info(dev, "bcrm version: %llu.%llu\n", min, maj); + + return 0; +} + +static int alvium_get_fw_version(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 spec, maj, min, pat; + int ret = 0; + + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_SPEC_VERSION_R, + &spec, &ret); + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MAJOR_VERSION_R, + &maj, &ret); + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MINOR_VERSION_R, + &min, &ret); + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_PATCH_VERSION_R, + &pat, &ret); + if (ret) + return ret; + + dev_info(dev, "fw version: %llu.%llu.%llu.%llu\n", spec, maj, min, pat); + + return 0; +} + +static int alvium_get_bcrm_addr(struct alvium_dev *alvium) +{ + u64 val; + int ret; + + ret = alvium_read(alvium, REG_BCRM_REG_ADDR_R, &val, NULL); + if (ret) + return ret; + + alvium->bcrm_addr = val; + + return 0; +} + +static int alvium_is_alive(struct alvium_dev *alvium) +{ + u64 bcrm, hbeat; + int ret = 0; + + alvium_read(alvium, REG_BCRM_MINOR_VERSION_R, &bcrm, &ret); + alvium_read(alvium, REG_BCRM_HEARTBEAT_RW, &hbeat, &ret); + if (ret) + return ret; + + return hbeat; +} + +static void alvium_print_avail_mipi_fmt(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + + dev_dbg(dev, "avail mipi_fmt yuv420_8_leg: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8_LEG]); + dev_dbg(dev, "avail mipi_fmt yuv420_8: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8]); + dev_dbg(dev, "avail mipi_fmt yuv420_10: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_10]); + dev_dbg(dev, "avail mipi_fmt yuv420_8_csps: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8_CSPS]); + dev_dbg(dev, "avail mipi_fmt yuv420_10_csps: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_10_CSPS]); + dev_dbg(dev, "avail mipi_fmt yuv422_8: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV422_8]); + dev_dbg(dev, "avail mipi_fmt yuv422_10: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV422_10]); + dev_dbg(dev, "avail mipi_fmt rgb888: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB888]); + dev_dbg(dev, "avail mipi_fmt rgb666: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB666]); + dev_dbg(dev, "avail mipi_fmt rgb565: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB565]); + dev_dbg(dev, "avail mipi_fmt rgb555: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB555]); + dev_dbg(dev, "avail mipi_fmt rgb444: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB444]); + dev_dbg(dev, "avail mipi_fmt raw6: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW6]); + dev_dbg(dev, "avail mipi_fmt raw7: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW7]); + dev_dbg(dev, "avail mipi_fmt raw8: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW8]); + dev_dbg(dev, "avail mipi_fmt raw10: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW10]); + dev_dbg(dev, "avail mipi_fmt raw12: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW12]); + dev_dbg(dev, "avail mipi_fmt raw14: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW14]); + dev_dbg(dev, "avail mipi_fmt jpeg: %u\n", + alvium->is_mipi_fmt_avail[ALVIUM_BIT_JPEG]); +} + +static void alvium_print_avail_feat(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + + dev_dbg(dev, "feature rev_x: %u\n", alvium->avail_ft.rev_x); + dev_dbg(dev, "feature rev_y: %u\n", alvium->avail_ft.rev_y); + dev_dbg(dev, "feature int_autop: %u\n", alvium->avail_ft.int_autop); + dev_dbg(dev, "feature black_lvl: %u\n", alvium->avail_ft.black_lvl); + dev_dbg(dev, "feature gain: %u\n", alvium->avail_ft.gain); + dev_dbg(dev, "feature gamma: %u\n", alvium->avail_ft.gamma); + dev_dbg(dev, "feature contrast: %u\n", alvium->avail_ft.contrast); + dev_dbg(dev, "feature sat: %u\n", alvium->avail_ft.sat); + dev_dbg(dev, "feature hue: %u\n", alvium->avail_ft.hue); + dev_dbg(dev, "feature whiteb: %u\n", alvium->avail_ft.whiteb); + dev_dbg(dev, "feature sharp: %u\n", alvium->avail_ft.sharp); + dev_dbg(dev, "feature auto_exp: %u\n", alvium->avail_ft.auto_exp); + dev_dbg(dev, "feature auto_gain: %u\n", alvium->avail_ft.auto_gain); + dev_dbg(dev, "feature auto_whiteb: %u\n", alvium->avail_ft.auto_whiteb); + dev_dbg(dev, "feature dev_temp: %u\n", alvium->avail_ft.dev_temp); + dev_dbg(dev, "feature acq_abort: %u\n", alvium->avail_ft.acq_abort); + dev_dbg(dev, "feature acq_fr: %u\n", alvium->avail_ft.acq_fr); + dev_dbg(dev, "feature fr_trigger: %u\n", alvium->avail_ft.fr_trigger); + dev_dbg(dev, "feature exp_acq_line: %u\n", + alvium->avail_ft.exp_acq_line); +} + +static void alvium_print_avail_bayer(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + + dev_dbg(dev, "avail bayer mono: %u\n", + alvium->is_bay_avail[ALVIUM_BIT_BAY_MONO]); + dev_dbg(dev, "avail bayer gr: %u\n", + alvium->is_bay_avail[ALVIUM_BIT_BAY_GR]); + dev_dbg(dev, "avail bayer rg: %u\n", + alvium->is_bay_avail[ALVIUM_BIT_BAY_RG]); + dev_dbg(dev, "avail bayer gb: %u\n", + alvium->is_bay_avail[ALVIUM_BIT_BAY_GB]); + dev_dbg(dev, "avail bayer bg: %u\n", + alvium->is_bay_avail[ALVIUM_BIT_BAY_BG]); +} + +static int alvium_get_feat_inq(struct alvium_dev *alvium) +{ + struct alvium_avail_feat *f; + u64 val; + int ret; + + ret = alvium_read(alvium, REG_BCRM_FEATURE_INQUIRY_R, &val, NULL); + if (ret) + return ret; + + f = (struct alvium_avail_feat *)&val; + alvium->avail_ft = *f; + alvium_print_avail_feat(alvium); + + return 0; +} + +static int alvium_get_host_supp_csi_lanes(struct alvium_dev *alvium) +{ + u64 val; + int ret; + + ret = alvium_read(alvium, REG_BCRM_CSI2_LANE_COUNT_RW, &val, NULL); + if (ret) + return ret; + + alvium->h_sup_csi_lanes = val; + + return 0; +} + +static int alvium_set_csi_lanes(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 num_lanes; + int ret; + + num_lanes = alvium->ep.bus.mipi_csi2.num_data_lanes; + + if (num_lanes > alvium->h_sup_csi_lanes) + return -EINVAL; + + ret = alvium_write_hshake(alvium, REG_BCRM_CSI2_LANE_COUNT_RW, + num_lanes); + if (ret) { + dev_err(dev, "Fail to set csi lanes reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_lp2hs_delay(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret = 0; + + /* + * The purpose of this reg is force a DPhy reset + * for the period described by the millisecond on + * the reg, before it starts streaming. + * + * To be clear, with that value bigger than 0 the + * Alvium forces a dphy-reset on all lanes for that period. + * That means all lanes go up into low power state. + * + */ + alvium_write(alvium, REG_BCRM_LP2HS_DELAY_RW, + ALVIUM_LP2HS_DELAY_MS, &ret); + if (ret) { + dev_err(dev, "Fail to set lp2hs delay reg\n"); + return ret; + } + + return 0; +} + +static int alvium_get_csi_clk_params(struct alvium_dev *alvium) +{ + u64 min_csi_clk, max_csi_clk; + int ret = 0; + + alvium_read(alvium, REG_BCRM_CSI2_CLOCK_MIN_R, &min_csi_clk, &ret); + alvium_read(alvium, REG_BCRM_CSI2_CLOCK_MAX_R, &max_csi_clk, &ret); + if (ret) + return ret; + + alvium->min_csi_clk = min_csi_clk; + alvium->max_csi_clk = max_csi_clk; + + return 0; +} + +static int alvium_set_csi_clk(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 csi_clk; + int ret; + + csi_clk = clamp(alvium->ep.link_frequencies[0], + (u64)alvium->min_csi_clk, (u64)alvium->max_csi_clk); + + if (alvium->ep.link_frequencies[0] != (u64)csi_clk) { + dev_warn(dev, + "requested csi clock (%llu MHz) out of range [%u, %u] Adjusted to %llu\n", + alvium->ep.link_frequencies[0], + alvium->min_csi_clk, alvium->max_csi_clk, csi_clk); + } + + ret = alvium_write_hshake(alvium, REG_BCRM_CSI2_CLOCK_RW, csi_clk); + if (ret) { + dev_err(dev, "Fail to set csi clock reg\n"); + return ret; + } + + alvium->link_freq = csi_clk; + + return 0; +} + +static int alvium_get_img_width_params(struct alvium_dev *alvium) +{ + u64 imgw, imgw_min, imgw_max, imgw_inc; + int ret = 0; + + alvium_read(alvium, REG_BCRM_IMG_WIDTH_RW, &imgw, &ret); + alvium_read(alvium, REG_BCRM_IMG_WIDTH_MIN_R, &imgw_min, &ret); + alvium_read(alvium, REG_BCRM_IMG_WIDTH_MAX_R, &imgw_max, &ret); + alvium_read(alvium, REG_BCRM_IMG_WIDTH_INC_R, &imgw_inc, &ret); + if (ret) + return ret; + + alvium->dft_img_width = imgw; + alvium->img_min_width = imgw_min; + alvium->img_max_width = imgw_max; + alvium->img_inc_width = imgw_inc; + + return 0; +} + +static int alvium_get_img_height_params(struct alvium_dev *alvium) +{ + u64 imgh, imgh_min, imgh_max, imgh_inc; + int ret = 0; + + alvium_read(alvium, REG_BCRM_IMG_HEIGHT_RW, &imgh, &ret); + alvium_read(alvium, REG_BCRM_IMG_HEIGHT_MIN_R, &imgh_min, &ret); + alvium_read(alvium, REG_BCRM_IMG_HEIGHT_MAX_R, &imgh_max, &ret); + alvium_read(alvium, REG_BCRM_IMG_HEIGHT_INC_R, &imgh_inc, &ret); + if (ret) + return ret; + + alvium->dft_img_height = imgh; + alvium->img_min_height = imgh_min; + alvium->img_max_height = imgh_max; + alvium->img_inc_height = imgh_inc; + + return 0; +} + +static int alvium_set_img_width(struct alvium_dev *alvium, u32 width) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_WIDTH_RW, width); + if (ret) { + dev_err(dev, "Fail to set img width\n"); + return ret; + } + + return 0; +} + +static int alvium_set_img_height(struct alvium_dev *alvium, u32 height) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_HEIGHT_RW, height); + if (ret) { + dev_err(dev, "Fail to set img height\n"); + return ret; + } + + return 0; +} + +static int alvium_set_img_offx(struct alvium_dev *alvium, u32 offx) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_OFFSET_X_RW, offx); + if (ret) { + dev_err(dev, "Fail to set img offx\n"); + return ret; + } + + return 0; +} + +static int alvium_set_img_offy(struct alvium_dev *alvium, u32 offy) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_OFFSET_Y_RW, offy); + if (ret) { + dev_err(dev, "Fail to set img offy\n"); + return ret; + } + + return 0; +} + +static int alvium_get_offx_params(struct alvium_dev *alvium) +{ + u64 min_offx, max_offx, inc_offx; + int ret = 0; + + alvium_read(alvium, REG_BCRM_IMG_OFFSET_X_MIN_R, &min_offx, &ret); + alvium_read(alvium, REG_BCRM_IMG_OFFSET_X_MAX_R, &max_offx, &ret); + alvium_read(alvium, REG_BCRM_IMG_OFFSET_X_INC_R, &inc_offx, &ret); + if (ret) + return ret; + + alvium->min_offx = min_offx; + alvium->max_offx = max_offx; + alvium->inc_offx = inc_offx; + + return 0; +} + +static int alvium_get_offy_params(struct alvium_dev *alvium) +{ + u64 min_offy, max_offy, inc_offy; + int ret = 0; + + alvium_read(alvium, REG_BCRM_IMG_OFFSET_Y_MIN_R, &min_offy, &ret); + alvium_read(alvium, REG_BCRM_IMG_OFFSET_Y_MAX_R, &max_offy, &ret); + alvium_read(alvium, REG_BCRM_IMG_OFFSET_Y_INC_R, &inc_offy, &ret); + if (ret) + return ret; + + alvium->min_offy = min_offy; + alvium->max_offy = max_offy; + alvium->inc_offy = inc_offy; + + return 0; +} + +static int alvium_get_gain_params(struct alvium_dev *alvium) +{ + u64 dft_gain, min_gain, max_gain, inc_gain; + int ret = 0; + + alvium_read(alvium, REG_BCRM_GAIN_RW, &dft_gain, &ret); + alvium_read(alvium, REG_BCRM_GAIN_MIN_R, &min_gain, &ret); + alvium_read(alvium, REG_BCRM_GAIN_MAX_R, &max_gain, &ret); + alvium_read(alvium, REG_BCRM_GAIN_INC_R, &inc_gain, &ret); + if (ret) + return ret; + + alvium->dft_gain = dft_gain; + alvium->min_gain = min_gain; + alvium->max_gain = max_gain; + alvium->inc_gain = inc_gain; + + return 0; +} + +static int alvium_get_exposure_params(struct alvium_dev *alvium) +{ + u64 dft_exp, min_exp, max_exp, inc_exp; + int ret = 0; + + alvium_read(alvium, REG_BCRM_EXPOSURE_TIME_RW, &dft_exp, &ret); + alvium_read(alvium, REG_BCRM_EXPOSURE_TIME_MIN_R, &min_exp, &ret); + alvium_read(alvium, REG_BCRM_EXPOSURE_TIME_MAX_R, &max_exp, &ret); + alvium_read(alvium, REG_BCRM_EXPOSURE_TIME_INC_R, &inc_exp, &ret); + if (ret) + return ret; + + alvium->dft_exp = dft_exp; + alvium->min_exp = min_exp; + alvium->max_exp = max_exp; + alvium->inc_exp = inc_exp; + + return 0; +} + +static int alvium_get_red_balance_ratio_params(struct alvium_dev *alvium) +{ + u64 dft_rb, min_rb, max_rb, inc_rb; + int ret = 0; + + alvium_read(alvium, REG_BCRM_RED_BALANCE_RATIO_RW, &dft_rb, &ret); + alvium_read(alvium, REG_BCRM_RED_BALANCE_RATIO_MIN_R, &min_rb, &ret); + alvium_read(alvium, REG_BCRM_RED_BALANCE_RATIO_MAX_R, &max_rb, &ret); + alvium_read(alvium, REG_BCRM_RED_BALANCE_RATIO_INC_R, &inc_rb, &ret); + if (ret) + return ret; + + alvium->dft_rbalance = dft_rb; + alvium->min_rbalance = min_rb; + alvium->max_rbalance = max_rb; + alvium->inc_rbalance = inc_rb; + + return 0; +} + +static int alvium_get_blue_balance_ratio_params(struct alvium_dev *alvium) +{ + u64 dft_bb, min_bb, max_bb, inc_bb; + int ret = 0; + + alvium_read(alvium, REG_BCRM_BLUE_BALANCE_RATIO_RW, &dft_bb, &ret); + alvium_read(alvium, REG_BCRM_BLUE_BALANCE_RATIO_MIN_R, &min_bb, &ret); + alvium_read(alvium, REG_BCRM_BLUE_BALANCE_RATIO_MAX_R, &max_bb, &ret); + alvium_read(alvium, REG_BCRM_BLUE_BALANCE_RATIO_INC_R, &inc_bb, &ret); + if (ret) + return ret; + + alvium->dft_bbalance = dft_bb; + alvium->min_bbalance = min_bb; + alvium->max_bbalance = max_bb; + alvium->inc_bbalance = inc_bb; + + return 0; +} + +static int alvium_get_hue_params(struct alvium_dev *alvium) +{ + u64 dft_hue, min_hue, max_hue, inc_hue; + int ret = 0; + + alvium_read(alvium, REG_BCRM_HUE_RW, &dft_hue, &ret); + alvium_read(alvium, REG_BCRM_HUE_MIN_R, &min_hue, &ret); + alvium_read(alvium, REG_BCRM_HUE_MAX_R, &max_hue, &ret); + alvium_read(alvium, REG_BCRM_HUE_INC_R, &inc_hue, &ret); + if (ret) + return ret; + + alvium->dft_hue = (s32)dft_hue; + alvium->min_hue = (s32)min_hue; + alvium->max_hue = (s32)max_hue; + alvium->inc_hue = (s32)inc_hue; + + return 0; +} + +static int alvium_get_black_lvl_params(struct alvium_dev *alvium) +{ + u64 dft_blvl, min_blvl, max_blvl, inc_blvl; + int ret = 0; + + alvium_read(alvium, REG_BCRM_BLACK_LEVEL_RW, &dft_blvl, &ret); + alvium_read(alvium, REG_BCRM_BLACK_LEVEL_MIN_R, &min_blvl, &ret); + alvium_read(alvium, REG_BCRM_BLACK_LEVEL_MAX_R, &max_blvl, &ret); + alvium_read(alvium, REG_BCRM_BLACK_LEVEL_INC_R, &inc_blvl, &ret); + if (ret) + return ret; + + alvium->dft_black_lvl = (s32)dft_blvl; + alvium->min_black_lvl = (s32)min_blvl; + alvium->max_black_lvl = (s32)max_blvl; + alvium->inc_black_lvl = (s32)inc_blvl; + + return 0; +} + +static int alvium_get_gamma_params(struct alvium_dev *alvium) +{ + u64 dft_g, min_g, max_g, inc_g; + int ret = 0; + + alvium_read(alvium, REG_BCRM_GAMMA_RW, &dft_g, &ret); + alvium_read(alvium, REG_BCRM_GAMMA_MIN_R, &min_g, &ret); + alvium_read(alvium, REG_BCRM_GAMMA_MAX_R, &max_g, &ret); + alvium_read(alvium, REG_BCRM_GAMMA_INC_R, &inc_g, &ret); + if (ret) + return ret; + + alvium->dft_gamma = dft_g; + alvium->min_gamma = min_g; + alvium->max_gamma = max_g; + alvium->inc_gamma = inc_g; + + return 0; +} + +static int alvium_get_sharpness_params(struct alvium_dev *alvium) +{ + u64 dft_sh, min_sh, max_sh, inc_sh; + int ret = 0; + + alvium_read(alvium, REG_BCRM_SHARPNESS_RW, &dft_sh, &ret); + alvium_read(alvium, REG_BCRM_SHARPNESS_MIN_R, &min_sh, &ret); + alvium_read(alvium, REG_BCRM_BLACK_LEVEL_MAX_R, &max_sh, &ret); + alvium_read(alvium, REG_BCRM_SHARPNESS_INC_R, &inc_sh, &ret); + if (ret) + return ret; + + alvium->dft_sharp = (s32)dft_sh; + alvium->min_sharp = (s32)min_sh; + alvium->max_sharp = (s32)max_sh; + alvium->inc_sharp = (s32)inc_sh; + + return 0; +} + +static int alvium_get_contrast_params(struct alvium_dev *alvium) +{ + u64 dft_c, min_c, max_c, inc_c; + int ret = 0; + + alvium_read(alvium, REG_BCRM_CONTRAST_VALUE_RW, &dft_c, &ret); + alvium_read(alvium, REG_BCRM_CONTRAST_VALUE_MIN_R, &min_c, &ret); + alvium_read(alvium, REG_BCRM_CONTRAST_VALUE_MAX_R, &max_c, &ret); + alvium_read(alvium, REG_BCRM_CONTRAST_VALUE_INC_R, &inc_c, &ret); + if (ret) + return ret; + + alvium->dft_contrast = dft_c; + alvium->min_contrast = min_c; + alvium->max_contrast = max_c; + alvium->inc_contrast = inc_c; + + return 0; +} + +static int alvium_get_saturation_params(struct alvium_dev *alvium) +{ + u64 dft_sat, min_sat, max_sat, inc_sat; + int ret = 0; + + alvium_read(alvium, REG_BCRM_SATURATION_RW, &dft_sat, &ret); + alvium_read(alvium, REG_BCRM_SATURATION_MIN_R, &min_sat, &ret); + alvium_read(alvium, REG_BCRM_SATURATION_MAX_R, &max_sat, &ret); + alvium_read(alvium, REG_BCRM_SATURATION_INC_R, &inc_sat, &ret); + if (ret) + return ret; + + alvium->dft_sat = dft_sat; + alvium->min_sat = min_sat; + alvium->max_sat = max_sat; + alvium->inc_sat = inc_sat; + + return 0; +} + +static int alvium_set_bcm_mode(struct alvium_dev *alvium) +{ + int ret = 0; + + alvium_write(alvium, REG_GENCP_CHANGEMODE_W, ALVIUM_BCM_MODE, &ret); + alvium->bcrm_mode = ALVIUM_BCM_MODE; + + return ret; +} + +static int alvium_get_mode(struct alvium_dev *alvium) +{ + u64 bcrm_mode; + int ret; + + ret = alvium_read(alvium, REG_GENCP_CURRENTMODE_R, &bcrm_mode, NULL); + if (ret) + return ret; + + switch (bcrm_mode) { + case ALVIUM_BCM_MODE: + alvium->bcrm_mode = ALVIUM_BCM_MODE; + break; + case ALVIUM_GENCP_MODE: + alvium->bcrm_mode = ALVIUM_GENCP_MODE; + break; + } + + return 0; +} + +static int alvium_get_avail_mipi_data_format(struct alvium_dev *alvium) +{ + struct alvium_avail_mipi_fmt *avail_fmt; + u64 val; + int ret; + + ret = alvium_read(alvium, REG_BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_R, + &val, NULL); + if (ret) + return ret; + + avail_fmt = (struct alvium_avail_mipi_fmt *)&val; + + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8_LEG] = + avail_fmt->yuv420_8_leg; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8] = + avail_fmt->yuv420_8; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_10] = + avail_fmt->yuv420_10; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_8_CSPS] = + avail_fmt->yuv420_8_csps; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV420_10_CSPS] = + avail_fmt->yuv420_10_csps; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV422_8] = + avail_fmt->yuv422_8; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_YUV422_10] = + avail_fmt->yuv422_10; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB888] = + avail_fmt->rgb888; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB666] = + avail_fmt->rgb666; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB565] = + avail_fmt->rgb565; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB555] = + avail_fmt->rgb555; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RGB444] = + avail_fmt->rgb444; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW6] = + avail_fmt->raw6; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW7] = + avail_fmt->raw7; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW8] = + avail_fmt->raw8; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW10] = + avail_fmt->raw10; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW12] = + avail_fmt->raw12; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_RAW14] = + avail_fmt->raw14; + alvium->is_mipi_fmt_avail[ALVIUM_BIT_JPEG] = + avail_fmt->jpeg; + + alvium_print_avail_mipi_fmt(alvium); + + return 0; +} + +static int alvium_setup_mipi_fmt(struct alvium_dev *alvium) +{ + unsigned int avail_fmt_cnt = 0; + unsigned int fmt = 0; + size_t sz = 0; + + /* calculate fmt array size */ + for (fmt = 0; fmt < ALVIUM_NUM_SUPP_MIPI_DATA_FMT; fmt++) { + if (!alvium->is_mipi_fmt_avail[alvium_csi2_fmts[fmt].fmt_av_bit]) + continue; + + if (!alvium_csi2_fmts[fmt].is_raw || + alvium->is_bay_avail[alvium_csi2_fmts[fmt].bay_av_bit]) + sz++; + } + + /* init alvium_csi2_fmt array */ + alvium->alvium_csi2_fmt_n = sz; + alvium->alvium_csi2_fmt = + kmalloc_array(sz, sizeof(struct alvium_pixfmt), GFP_KERNEL); + if (!alvium->alvium_csi2_fmt) + return -ENOMEM; + + /* Create the alvium_csi2 fmt array from formats available */ + for (fmt = 0; fmt < ALVIUM_NUM_SUPP_MIPI_DATA_FMT; fmt++) { + if (!alvium->is_mipi_fmt_avail[alvium_csi2_fmts[fmt].fmt_av_bit]) + continue; + + if (!alvium_csi2_fmts[fmt].is_raw || + alvium->is_bay_avail[alvium_csi2_fmts[fmt].bay_av_bit]) { + alvium->alvium_csi2_fmt[avail_fmt_cnt] = + alvium_csi2_fmts[fmt]; + avail_fmt_cnt++; + } + } + + return 0; +} + +static int alvium_set_mipi_fmt(struct alvium_dev *alvium, + const struct alvium_pixfmt *pixfmt) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_MIPI_DATA_FORMAT_RW, + pixfmt->mipi_fmt_regval); + if (ret) { + dev_err(dev, "Fail to set mipi fmt\n"); + return ret; + } + + return 0; +} + +static int alvium_get_avail_bayer(struct alvium_dev *alvium) +{ + struct alvium_avail_bayer *avail_bay; + u64 val; + int ret; + + ret = alvium_read(alvium, REG_BCRM_IMG_BAYER_PATTERN_INQUIRY_R, + &val, NULL); + if (ret) + return ret; + + avail_bay = (struct alvium_avail_bayer *)&val; + + alvium->is_bay_avail[ALVIUM_BIT_BAY_MONO] = avail_bay->mono; + alvium->is_bay_avail[ALVIUM_BIT_BAY_GR] = avail_bay->gr; + alvium->is_bay_avail[ALVIUM_BIT_BAY_RG] = avail_bay->rg; + alvium->is_bay_avail[ALVIUM_BIT_BAY_GB] = avail_bay->gb; + alvium->is_bay_avail[ALVIUM_BIT_BAY_BG] = avail_bay->bg; + + alvium_print_avail_bayer(alvium); + + return 0; +} + +static int alvium_set_bayer_pattern(struct alvium_dev *alvium, + const struct alvium_pixfmt *pixfmt) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_BAYER_PATTERN_RW, + pixfmt->bay_fmt_regval); + if (ret) { + dev_err(dev, "Fail to set bayer pattern\n"); + return ret; + } + + return 0; +} + +static int alvium_get_frame_interval(struct alvium_dev *alvium) +{ + u64 dft_fr, min_fr, max_fr; + int ret = 0; + + alvium_read(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_RW, + &dft_fr, &ret); + alvium_read(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_MIN_R, + &min_fr, &ret); + alvium_read(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_MAX_R, + &max_fr, &ret); + if (ret) + return ret; + + alvium->dft_fr = dft_fr; + alvium->min_fr = min_fr; + alvium->max_fr = max_fr; + + return 0; +} + +static int alvium_set_frame_rate(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_RW, + alvium->fr); + if (ret) { + dev_err(dev, "Fail to set frame rate lanes reg\n"); + return ret; + } + + dev_dbg(dev, "set frame rate: %llu us\n", alvium->fr); + + return 0; +} + +static int alvium_set_stream_mipi(struct alvium_dev *alvium, bool on) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, on ? REG_BCRM_ACQUISITION_START_RW : + REG_BCRM_ACQUISITION_STOP_RW, 0x01); + if (ret) { + dev_err(dev, "Fail set_stream_mipi\n"); + return ret; + } + + return 0; +} + +static int alvium_get_gain(struct alvium_dev *alvium) +{ + u64 gain; + int ret; + + /* The unit is millibel (1 mB = 0.01 dB) */ + ret = alvium_read(alvium, REG_BCRM_GAIN_RW, &gain, NULL); + if (ret) + return ret; + + return gain; +} + +static int alvium_set_ctrl_gain(struct alvium_dev *alvium, int gain) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + /* The unit is millibel (1 mB = 0.01 dB) */ + ret = alvium_write_hshake(alvium, REG_BCRM_GAIN_RW, (u64)gain); + if (ret) { + dev_err(dev, "Fail to set gain value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_auto_gain(struct alvium_dev *alvium, bool on) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_GAIN_AUTO_RW, + on ? 0x02 : 0x00); + if (ret) { + dev_err(dev, "Fail to set autogain reg\n"); + return ret; + } + + return 0; +} + +static int alvium_get_exposure(struct alvium_dev *alvium) +{ + u64 exp; + int ret; + + /* Exposure time in ns */ + ret = alvium_read(alvium, REG_BCRM_EXPOSURE_TIME_RW, &exp, NULL); + if (ret) + return ret; + + return exp; +} + +static int alvium_set_ctrl_auto_exposure(struct alvium_dev *alvium, bool on) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW, + on ? 0x02 : 0x00); + if (ret) { + dev_err(dev, "Fail to set autoexposure reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_exposure(struct alvium_dev *alvium, int exposure_ns) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_TIME_RW, + (u64)exposure_ns); + if (ret) { + dev_err(dev, "Fail to set exposure value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_blue_balance_ratio(struct alvium_dev *alvium, + int blue) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_BLUE_BALANCE_RATIO_RW, + (u64)blue); + if (ret) { + dev_err(dev, "Fail to set blue ratio value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_red_balance_ratio(struct alvium_dev *alvium, int red) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_RED_BALANCE_RATIO_RW, + (u64)red); + if (ret) { + dev_err(dev, "Fail to set red ratio value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_awb(struct alvium_dev *alvium, bool on) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW, + on ? 0x02 : 0x00); + if (ret) { + dev_err(dev, "Fail to set awb reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_hue(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_HUE_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set hue value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_contrast(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_CONTRAST_VALUE_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set contrast value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_saturation(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_SATURATION_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set contrast value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_gamma(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_GAMMA_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set gamma value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_sharpness(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_SHARPNESS_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set sharpness value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_hflip(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_REVERSE_X_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set reverse_x value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_set_ctrl_vflip(struct alvium_dev *alvium, int val) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_write_hshake(alvium, REG_BCRM_IMG_REVERSE_Y_RW, (u64)val); + if (ret) { + dev_err(dev, "Fail to set reverse_y value reg\n"); + return ret; + } + + return 0; +} + +static int alvium_get_hw_features_params(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_get_csi_clk_params(alvium); + if (ret) { + dev_err(dev, "Fail to read min/max csi clock regs\n"); + return ret; + } + + ret = alvium_get_img_width_params(alvium); + if (ret) { + dev_err(dev, "Fail to read img width regs\n"); + return ret; + } + + ret = alvium_get_img_height_params(alvium); + if (ret) { + dev_err(dev, "Fail to read img heigth regs\n"); + return ret; + } + + ret = alvium_get_offx_params(alvium); + if (ret) { + dev_err(dev, "Fail to read offx regs\n"); + return ret; + } + + ret = alvium_get_offy_params(alvium); + if (ret) { + dev_err(dev, "Fail to read offy regs\n"); + return ret; + } + + ret = alvium_get_gain_params(alvium); + if (ret) { + dev_err(dev, "Fail to read gain regs\n"); + return ret; + } + + ret = alvium_get_exposure_params(alvium); + if (ret) { + dev_err(dev, "Fail to read min/max exp regs\n"); + return ret; + } + + ret = alvium_get_red_balance_ratio_params(alvium); + if (ret) { + dev_err(dev, "Fail to read red balance ratio regs\n"); + return ret; + } + + ret = alvium_get_blue_balance_ratio_params(alvium); + if (ret) { + dev_err(dev, "Fail to read blue balance ratio regs\n"); + return ret; + } + + ret = alvium_get_hue_params(alvium); + if (ret) { + dev_err(dev, "Fail to read hue regs\n"); + return ret; + } + + ret = alvium_get_contrast_params(alvium); + if (ret) { + dev_err(dev, "Fail to read contrast regs\n"); + return ret; + } + + ret = alvium_get_saturation_params(alvium); + if (ret) { + dev_err(dev, "Fail to read saturation regs\n"); + return ret; + } + + ret = alvium_get_black_lvl_params(alvium); + if (ret) { + dev_err(dev, "Fail to read black lvl regs\n"); + return ret; + } + + ret = alvium_get_gamma_params(alvium); + if (ret) { + dev_err(dev, "Fail to read gamma regs\n"); + return ret; + } + + ret = alvium_get_sharpness_params(alvium); + if (ret) { + dev_err(dev, "Fail to read sharpness regs\n"); + return ret; + } + + return 0; +} + +static int alvium_get_hw_info(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + ret = alvium_get_bcrm_vers(alvium); + if (ret) { + dev_err(dev, "Fail to read bcrm version reg\n"); + return ret; + } + + ret = alvium_get_bcrm_addr(alvium); + if (ret) { + dev_err(dev, "Fail to bcrm address reg\n"); + return ret; + } + + ret = alvium_get_fw_version(alvium); + if (ret) { + dev_err(dev, "Fail to read fw version reg\n"); + return ret; + } + + ret = alvium_get_host_supp_csi_lanes(alvium); + if (ret) { + dev_err(dev, "Fail to read host supported csi lanes reg\n"); + return ret; + } + + ret = alvium_get_feat_inq(alvium); + if (ret) { + dev_err(dev, "Fail to read bcrm feature inquiry reg\n"); + return ret; + } + + ret = alvium_get_hw_features_params(alvium); + if (ret) { + dev_err(dev, "Fail to read features params regs\n"); + return ret; + } + + ret = alvium_get_avail_mipi_data_format(alvium); + if (ret) { + dev_err(dev, "Fail to read available mipi data formats reg\n"); + return ret; + } + + ret = alvium_get_avail_bayer(alvium); + if (ret) { + dev_err(dev, "Fail to read available Bayer patterns reg\n"); + return ret; + } + + ret = alvium_get_mode(alvium); + if (ret) { + dev_err(dev, "Fail to get current mode reg\n"); + return ret; + } + + return 0; +} + +static int alvium_hw_init(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + int ret; + + /* Set Alvium BCM mode*/ + ret = alvium_set_bcm_mode(alvium); + if (ret) { + dev_err(dev, "Fail to set BCM mode\n"); + return ret; + } + + ret = alvium_set_csi_lanes(alvium); + if (ret) { + dev_err(dev, "Fail to set csi lanes\n"); + return ret; + } + + ret = alvium_set_csi_clk(alvium); + if (ret) { + dev_err(dev, "Fail to set csi clk\n"); + return ret; + } + + ret = alvium_set_lp2hs_delay(alvium); + if (ret) { + dev_err(dev, "Fail to set lp2hs reg\n"); + return ret; + } + + return 0; +} + +/* --------------- Subdev Operations --------------- */ + +static int alvium_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + fi->interval = alvium->frame_interval; + + return 0; +} + +static int alvium_set_frame_interval(struct alvium_dev *alvium, + struct v4l2_subdev_frame_interval *fi) +{ + struct device *dev = &alvium->i2c_client->dev; + u64 req_fr, min_fr, max_fr; + int ret; + + if (fi->interval.denominator == 0) + return -EINVAL; + + ret = alvium_get_frame_interval(alvium); + if (ret) { + dev_err(dev, "Fail to get frame interval\n"); + return ret; + } + + min_fr = alvium->min_fr; + max_fr = alvium->max_fr; + + dev_dbg(dev, "fi->interval.numerator = %d\n", + fi->interval.numerator); + dev_dbg(dev, "fi->interval.denominator = %d\n", + fi->interval.denominator); + + req_fr = (u64)((fi->interval.denominator * USEC_PER_SEC) / + fi->interval.numerator); + + if (req_fr >= max_fr && req_fr <= min_fr) + req_fr = alvium->dft_fr; + + alvium->fr = req_fr; + alvium->frame_interval.numerator = fi->interval.numerator; + alvium->frame_interval.denominator = fi->interval.denominator; + + return 0; +} + +static int alvium_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + int ret; + + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (alvium->streaming) + return -EBUSY; + + ret = alvium_set_frame_interval(alvium, fi); + if (!ret) + ret = alvium_set_frame_rate(alvium); + + return ret; +} + +static int alvium_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + + if (code->index >= alvium->alvium_csi2_fmt_n) + return -EINVAL; + + code->code = alvium->alvium_csi2_fmt[code->index].code; + + return 0; +} + +static const struct alvium_pixfmt * +alvium_code_to_pixfmt(struct alvium_dev *alvium, u32 code) +{ + unsigned int i; + + for (i = 0; alvium->alvium_csi2_fmt[i].code; ++i) + if (alvium->alvium_csi2_fmt[i].code == code) + return &alvium->alvium_csi2_fmt[i]; + + return &alvium->alvium_csi2_fmt[0]; +} + +static int alvium_set_mode(struct alvium_dev *alvium, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + int ret; + + crop = v4l2_subdev_state_get_crop(state, 0); + fmt = v4l2_subdev_state_get_format(state, 0); + + v4l_bound_align_image(&fmt->width, alvium->img_min_width, + alvium->img_max_width, 0, + &fmt->height, alvium->img_min_height, + alvium->img_max_height, 0, 0); + + /* alvium don't accept negative crop left/top */ + crop->left = clamp((u32)max(0, crop->left), alvium->min_offx, + (u32)(alvium->img_max_width - fmt->width)); + crop->top = clamp((u32)max(0, crop->top), alvium->min_offy, + (u32)(alvium->img_max_height - fmt->height)); + + ret = alvium_set_img_width(alvium, fmt->width); + if (ret) + return ret; + + ret = alvium_set_img_height(alvium, fmt->height); + if (ret) + return ret; + + ret = alvium_set_img_offx(alvium, crop->left); + if (ret) + return ret; + + ret = alvium_set_img_offy(alvium, crop->top); + if (ret) + return ret; + + return 0; +} + +static int alvium_set_framefmt(struct alvium_dev *alvium, + struct v4l2_mbus_framefmt *format) +{ + struct device *dev = &alvium->i2c_client->dev; + const struct alvium_pixfmt *alvium_csi2_fmt; + int ret = 0; + + alvium_csi2_fmt = alvium_code_to_pixfmt(alvium, format->code); + + ret = alvium_set_mipi_fmt(alvium, alvium_csi2_fmt); + if (ret) + return ret; + + if (alvium_csi2_fmt->is_raw) { + ret = alvium_set_bayer_pattern(alvium, alvium_csi2_fmt); + if (ret) + return ret; + } + + dev_dbg(dev, "start: %s, mipi_fmt_regval regval = 0x%llx", + __func__, alvium_csi2_fmt->mipi_fmt_regval); + + return ret; +} + +static int alvium_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + struct i2c_client *client = v4l2_get_subdevdata(&alvium->sd); + struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + goto out; + + ret = __v4l2_ctrl_handler_setup(&alvium->ctrls.handler); + if (ret) + goto out; + + ret = alvium_set_mode(alvium, state); + if (ret) + goto out; + + fmt = v4l2_subdev_state_get_format(state, 0); + ret = alvium_set_framefmt(alvium, fmt); + if (ret) + goto out; + + ret = alvium_set_stream_mipi(alvium, enable); + if (ret) + goto out; + + } else { + alvium_set_stream_mipi(alvium, enable); + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + alvium->streaming = !!enable; + v4l2_subdev_unlock_state(state); + + return 0; + +out: + pm_runtime_put(&client->dev); + v4l2_subdev_unlock_state(state); + return ret; +} + +static int alvium_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + struct alvium_mode *mode = &alvium->mode; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = alvium_csi2_default_fmt, + }; + struct v4l2_subdev_crop sd_crop = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .rect = { + .left = mode->crop.left, + .top = mode->crop.top, + .width = mode->crop.width, + .height = mode->crop.height, + }, + }; + + *v4l2_subdev_state_get_crop(state, 0) = sd_crop.rect; + *v4l2_subdev_state_get_format(state, 0) = sd_fmt.format; + + return 0; +} + +static int alvium_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + const struct alvium_pixfmt *alvium_csi2_fmt; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + crop = v4l2_subdev_state_get_crop(sd_state, 0); + + v4l_bound_align_image(&format->format.width, alvium->img_min_width, + alvium->img_max_width, 0, + &format->format.height, alvium->img_min_height, + alvium->img_max_height, 0, 0); + + /* Adjust left and top to prevent roll over sensor area */ + crop->left = clamp((u32)crop->left, (u32)0, + (alvium->img_max_width - fmt->width)); + crop->top = clamp((u32)crop->top, (u32)0, + (alvium->img_max_height - fmt->height)); + + /* Set also the crop width and height when set a new fmt */ + crop->width = fmt->width; + crop->height = fmt->height; + + alvium_csi2_fmt = alvium_code_to_pixfmt(alvium, format->format.code); + fmt->code = alvium_csi2_fmt->code; + + *fmt = format->format; + + return 0; +} + +static int alvium_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, 0); + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + /* + * Alvium can only shift the origin of the img + * then we accept only value with the same value of the actual fmt + */ + if (sel->r.width != fmt->width) + sel->r.width = fmt->width; + + if (sel->r.height != fmt->height) + sel->r.height = fmt->height; + + /* alvium don't accept negative crop left/top */ + crop->left = clamp((u32)max(0, sel->r.left), alvium->min_offx, + alvium->img_max_width - sel->r.width); + crop->top = clamp((u32)max(0, sel->r.top), alvium->min_offy, + alvium->img_max_height - sel->r.height); + + sel->r = *crop; + + return 0; +} + +static int alvium_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + + switch (sel->target) { + /* Current cropping area */ + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + break; + /* Cropping bounds */ + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = alvium->img_max_width; + sel->r.height = alvium->img_max_height; + break; + /* Default cropping area */ + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = alvium->min_offy; + sel->r.left = alvium->min_offx; + sel->r.width = alvium->img_max_width; + sel->r.height = alvium->img_max_height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int alvium_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct alvium_dev *alvium = sd_to_alvium(sd); + int val; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + val = alvium_get_gain(alvium); + if (val < 0) + return val; + alvium->ctrls.gain->val = val; + break; + case V4L2_CID_EXPOSURE: + val = alvium_get_exposure(alvium); + if (val < 0) + return val; + alvium->ctrls.exposure->val = val; + break; + } + + return 0; +} + +static int alvium_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct alvium_dev *alvium = sd_to_alvium(sd); + struct i2c_client *client = v4l2_get_subdevdata(&alvium->sd); + int ret; + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ret = alvium_set_ctrl_gain(alvium, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + ret = alvium_set_ctrl_auto_gain(alvium, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = alvium_set_ctrl_exposure(alvium, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = alvium_set_ctrl_auto_exposure(alvium, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + ret = alvium_set_ctrl_red_balance_ratio(alvium, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + ret = alvium_set_ctrl_blue_balance_ratio(alvium, ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = alvium_set_ctrl_awb(alvium, ctrl->val); + break; + case V4L2_CID_HUE: + ret = alvium_set_ctrl_hue(alvium, ctrl->val); + break; + case V4L2_CID_CONTRAST: + ret = alvium_set_ctrl_contrast(alvium, ctrl->val); + break; + case V4L2_CID_SATURATION: + ret = alvium_set_ctrl_saturation(alvium, ctrl->val); + break; + case V4L2_CID_GAMMA: + ret = alvium_set_ctrl_gamma(alvium, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + ret = alvium_set_ctrl_sharpness(alvium, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = alvium_set_ctrl_hflip(alvium, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = alvium_set_ctrl_vflip(alvium, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops alvium_ctrl_ops = { + .g_volatile_ctrl = alvium_g_volatile_ctrl, + .s_ctrl = alvium_s_ctrl, +}; + +static int alvium_ctrl_init(struct alvium_dev *alvium) +{ + const struct v4l2_ctrl_ops *ops = &alvium_ctrl_ops; + struct alvium_ctrls *ctrls = &alvium->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + int ret; + + v4l2_ctrl_handler_init(hdl, 32); + + /* Pixel rate is fixed */ + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_PIXEL_RATE, 0, + ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1, + ALVIUM_DEFAULT_PIXEL_RATE_MHZ); + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* Link freq is fixed */ + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_LINK_FREQ, + 0, 0, &alvium->link_freq); + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* Auto/manual white balance */ + if (alvium->avail_ft.auto_whiteb) { + ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + } + + ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_BLUE_BALANCE, + alvium->min_bbalance, + alvium->max_bbalance, + alvium->inc_bbalance, + alvium->dft_bbalance); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_RED_BALANCE, + alvium->min_rbalance, + alvium->max_rbalance, + alvium->inc_rbalance, + alvium->dft_rbalance); + + /* Auto/manual exposure */ + if (alvium->avail_ft.auto_exp) { + ctrls->auto_exp = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + } + + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_EXPOSURE, + alvium->min_exp, + alvium->max_exp, + alvium->inc_exp, + alvium->dft_exp); + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + /* Auto/manual gain */ + if (alvium->avail_ft.auto_gain) { + ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTOGAIN, + 0, 1, 1, 1); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + } + + if (alvium->avail_ft.gain) { + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_GAIN, + alvium->min_gain, + alvium->max_gain, + alvium->inc_gain, + alvium->dft_gain); + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + } + + if (alvium->avail_ft.sat) + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SATURATION, + alvium->min_sat, + alvium->max_sat, + alvium->inc_sat, + alvium->dft_sat); + + if (alvium->avail_ft.hue) + ctrls->hue = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_HUE, + alvium->min_hue, + alvium->max_hue, + alvium->inc_hue, + alvium->dft_hue); + + if (alvium->avail_ft.contrast) + ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_CONTRAST, + alvium->min_contrast, + alvium->max_contrast, + alvium->inc_contrast, + alvium->dft_contrast); + + if (alvium->avail_ft.gamma) + ctrls->gamma = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_GAMMA, + alvium->min_gamma, + alvium->max_gamma, + alvium->inc_gamma, + alvium->dft_gamma); + + if (alvium->avail_ft.sharp) + ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SHARPNESS, + alvium->min_sharp, + alvium->max_sharp, + alvium->inc_sharp, + alvium->dft_sharp); + + if (alvium->avail_ft.rev_x) + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + + if (alvium->avail_ft.rev_y) + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + ret = v4l2_fwnode_device_parse(&alvium->i2c_client->dev, &props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + if (ret) + goto free_ctrls; + + alvium->sd.ctrl_handler = hdl; + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +static const struct v4l2_subdev_core_ops alvium_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops alvium_video_ops = { + .s_stream = alvium_s_stream, +}; + +static const struct v4l2_subdev_pad_ops alvium_pad_ops = { + .enum_mbus_code = alvium_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = alvium_set_fmt, + .get_selection = alvium_get_selection, + .set_selection = alvium_set_selection, + .get_frame_interval = alvium_g_frame_interval, + .set_frame_interval = alvium_s_frame_interval, +}; + +static const struct v4l2_subdev_internal_ops alvium_internal_ops = { + .init_state = alvium_init_state, +}; + +static const struct v4l2_subdev_ops alvium_subdev_ops = { + .core = &alvium_core_ops, + .pad = &alvium_pad_ops, + .video = &alvium_video_ops, +}; + +static int alvium_subdev_init(struct alvium_dev *alvium) +{ + struct i2c_client *client = alvium->i2c_client; + struct device *dev = &alvium->i2c_client->dev; + struct v4l2_subdev *sd = &alvium->sd; + int ret; + + /* Setup initial frame interval*/ + alvium->frame_interval.numerator = 1; + alvium->frame_interval.denominator = ALVIUM_DEFAULT_FR_HZ; + alvium->fr = ALVIUM_DEFAULT_FR_HZ; + + /* Setup the initial mode */ + alvium->mode.fmt = alvium_csi2_default_fmt; + alvium->mode.width = alvium_csi2_default_fmt.width; + alvium->mode.height = alvium_csi2_default_fmt.height; + alvium->mode.crop.left = alvium->min_offx; + alvium->mode.crop.top = alvium->min_offy; + alvium->mode.crop.width = alvium_csi2_default_fmt.width; + alvium->mode.crop.height = alvium_csi2_default_fmt.height; + + /* init alvium sd */ + v4l2_i2c_subdev_init(sd, client, &alvium_subdev_ops); + + sd->internal_ops = &alvium_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + alvium->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sd->entity, 1, &alvium->pad); + if (ret) { + dev_err(dev, "Could not register media entity\n"); + return ret; + } + + ret = alvium_ctrl_init(alvium); + if (ret) { + dev_err(dev, "Control initialization error %d\n", ret); + goto entity_cleanup; + } + + alvium->sd.state_lock = alvium->ctrls.handler.lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) { + dev_err(dev, "subdev initialization error %d\n", ret); + goto err_ctrls; + } + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(&alvium->ctrls.handler); +entity_cleanup: + media_entity_cleanup(&alvium->sd.entity); + return ret; +} + +static void alvium_subdev_cleanup(struct alvium_dev *alvium) +{ + v4l2_fwnode_endpoint_free(&alvium->ep); + v4l2_subdev_cleanup(&alvium->sd); + media_entity_cleanup(&alvium->sd.entity); + v4l2_ctrl_handler_free(&alvium->ctrls.handler); +} + +static int alvium_get_dt_data(struct alvium_dev *alvium) +{ + struct device *dev = &alvium->i2c_client->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_handle *endpoint; + + if (!fwnode) + return -EINVAL; + + /* Only CSI2 is supported for now: */ + alvium->ep.bus_type = V4L2_MBUS_CSI2_DPHY; + + endpoint = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &alvium->ep)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + if (!alvium->ep.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + goto error_out; + } + + return 0; + +error_out: + v4l2_fwnode_endpoint_free(&alvium->ep); + fwnode_handle_put(endpoint); + + return -EINVAL; +} + +static int alvium_set_power(struct alvium_dev *alvium, bool on) +{ + int ret; + + if (!on) + return regulator_disable(alvium->reg_vcc); + + ret = regulator_enable(alvium->reg_vcc); + if (ret) + return ret; + + /* alvium boot time 7s */ + msleep(7000); + return 0; +} + +static int alvium_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct alvium_dev *alvium = sd_to_alvium(sd); + int ret; + + ret = alvium_set_power(alvium, true); + if (ret) + return ret; + + ret = alvium_hw_init(alvium); + if (ret) { + alvium_set_power(alvium, false); + return ret; + } + + return 0; +} + +static int alvium_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct alvium_dev *alvium = sd_to_alvium(sd); + + alvium_set_power(alvium, false); + + return 0; +} + +static const struct dev_pm_ops alvium_pm_ops = { + RUNTIME_PM_OPS(alvium_runtime_suspend, alvium_runtime_resume, NULL) +}; + +static int alvium_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct alvium_dev *alvium; + int ret; + + alvium = devm_kzalloc(dev, sizeof(*alvium), GFP_KERNEL); + if (!alvium) + return -ENOMEM; + + alvium->i2c_client = client; + + alvium->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(alvium->regmap)) + return PTR_ERR(alvium->regmap); + + ret = alvium_get_dt_data(alvium); + if (ret) + return ret; + + alvium->reg_vcc = devm_regulator_get_optional(dev, "vcc-ext-in"); + if (IS_ERR(alvium->reg_vcc)) + return dev_err_probe(dev, PTR_ERR(alvium->reg_vcc), + "no vcc-ext-in regulator provided\n"); + + ret = alvium_set_power(alvium, true); + if (ret) + goto err_powerdown; + + if (!alvium_is_alive(alvium)) { + ret = -ENODEV; + dev_err_probe(dev, ret, "Device detection failed\n"); + goto err_powerdown; + } + + ret = alvium_get_hw_info(alvium); + if (ret) { + dev_err_probe(dev, ret, "get_hw_info fail\n"); + goto err_powerdown; + } + + ret = alvium_hw_init(alvium); + if (ret) { + dev_err_probe(dev, ret, "hw_init fail\n"); + goto err_powerdown; + } + + ret = alvium_setup_mipi_fmt(alvium); + if (ret) { + dev_err_probe(dev, ret, "setup_mipi_fmt fail\n"); + goto err_powerdown; + } + + /* + * Enable runtime PM without autosuspend: + * + * Don't use pm autosuspend (alvium have ~7s boot time). + * Alvium has been powered manually: + * - mark it as active + * - increase the usage count without resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + + /* Initialize the V4L2 subdev. */ + ret = alvium_subdev_init(alvium); + if (ret) + goto err_pm; + + ret = v4l2_async_register_subdev(&alvium->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "Could not register v4l2 device\n"); + goto err_subdev; + } + + return 0; + +err_subdev: + alvium_subdev_cleanup(alvium); +err_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + kfree(alvium->alvium_csi2_fmt); +err_powerdown: + alvium_set_power(alvium, false); + + return ret; +} + +static void alvium_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct alvium_dev *alvium = sd_to_alvium(sd); + struct device *dev = &alvium->i2c_client->dev; + + v4l2_async_unregister_subdev(sd); + alvium_subdev_cleanup(alvium); + kfree(alvium->alvium_csi2_fmt); + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + alvium_set_power(alvium, false); + pm_runtime_set_suspended(dev); +} + +static const struct of_device_id alvium_of_ids[] = { + { .compatible = "alliedvision,alvium-csi2", }, + { } +}; +MODULE_DEVICE_TABLE(of, alvium_of_ids); + +static struct i2c_driver alvium_i2c_driver = { + .driver = { + .name = "alvium-csi2", + .pm = pm_ptr(&alvium_pm_ops), + .of_match_table = alvium_of_ids, + }, + .probe = alvium_probe, + .remove = alvium_remove, +}; + +module_i2c_driver(alvium_i2c_driver); + +MODULE_DESCRIPTION("Allied Vision's Alvium Camera Driver"); +MODULE_AUTHOR("Tommaso Merciai "); +MODULE_AUTHOR("Martin Hecht "); +MODULE_AUTHOR("Avnet Silica Software & Services EMEA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/alvium-csi2.h b/drivers/media/i2c/alvium-csi2.h new file mode 100644 index 000000000..b85a25169 --- /dev/null +++ b/drivers/media/i2c/alvium-csi2.h @@ -0,0 +1,475 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allied Vision Technologies GmbH Alvium camera driver + * + * Copyright (C) 2023 Tommaso Merciai + * Copyright (C) 2023 Martin Hecht + * Copyright (C) 2023 Avnet EMG GmbH + */ + +#ifndef ALVIUM_CSI2_H_ +#define ALVIUM_CSI2_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define REG_BCRM_V4L2 BIT(31) + +#define REG_BCRM_V4L2_8BIT(n) (REG_BCRM_V4L2 | CCI_REG8(n)) +#define REG_BCRM_V4L2_16BIT(n) (REG_BCRM_V4L2 | CCI_REG16(n)) +#define REG_BCRM_V4L2_32BIT(n) (REG_BCRM_V4L2 | CCI_REG32(n)) +#define REG_BCRM_V4L2_64BIT(n) (REG_BCRM_V4L2 | CCI_REG64(n)) + +/* Basic Control Register Map register offsets (BCRM) */ +#define REG_BCRM_MINOR_VERSION_R CCI_REG16(0x0000) +#define REG_BCRM_MAJOR_VERSION_R CCI_REG16(0x0002) +#define REG_BCRM_REG_ADDR_R CCI_REG16(0x0014) + +#define REG_BCRM_FEATURE_INQUIRY_R REG_BCRM_V4L2_64BIT(0x0008) +#define REG_BCRM_DEVICE_FW_SPEC_VERSION_R REG_BCRM_V4L2_8BIT(0x0010) +#define REG_BCRM_DEVICE_FW_MAJOR_VERSION_R REG_BCRM_V4L2_8BIT(0x0011) +#define REG_BCRM_DEVICE_FW_MINOR_VERSION_R REG_BCRM_V4L2_16BIT(0x0012) +#define REG_BCRM_DEVICE_FW_PATCH_VERSION_R REG_BCRM_V4L2_32BIT(0x0014) +#define REG_BCRM_WRITE_HANDSHAKE_RW REG_BCRM_V4L2_8BIT(0x0018) + +/* Streaming Control Registers */ +#define REG_BCRM_SUPPORTED_CSI2_LANE_COUNTS_R REG_BCRM_V4L2_8BIT(0x0040) +#define REG_BCRM_CSI2_LANE_COUNT_RW REG_BCRM_V4L2_8BIT(0x0044) +#define REG_BCRM_CSI2_CLOCK_MIN_R REG_BCRM_V4L2_32BIT(0x0048) +#define REG_BCRM_CSI2_CLOCK_MAX_R REG_BCRM_V4L2_32BIT(0x004c) +#define REG_BCRM_CSI2_CLOCK_RW REG_BCRM_V4L2_32BIT(0x0050) +#define REG_BCRM_BUFFER_SIZE_R REG_BCRM_V4L2_32BIT(0x0054) + +#define REG_BCRM_IPU_X_MIN_W REG_BCRM_V4L2_32BIT(0x0058) +#define REG_BCRM_IPU_X_MAX_W REG_BCRM_V4L2_32BIT(0x005c) +#define REG_BCRM_IPU_X_INC_W REG_BCRM_V4L2_32BIT(0x0060) +#define REG_BCRM_IPU_Y_MIN_W REG_BCRM_V4L2_32BIT(0x0064) +#define REG_BCRM_IPU_Y_MAX_W REG_BCRM_V4L2_32BIT(0x0068) +#define REG_BCRM_IPU_Y_INC_W REG_BCRM_V4L2_32BIT(0x006c) +#define REG_BCRM_IPU_X_R REG_BCRM_V4L2_32BIT(0x0070) +#define REG_BCRM_IPU_Y_R REG_BCRM_V4L2_32BIT(0x0074) + +#define REG_BCRM_PHY_RESET_RW REG_BCRM_V4L2_8BIT(0x0078) +#define REG_BCRM_LP2HS_DELAY_RW REG_BCRM_V4L2_32BIT(0x007c) + +/* Acquisition Control Registers */ +#define REG_BCRM_ACQUISITION_START_RW REG_BCRM_V4L2_8BIT(0x0080) +#define REG_BCRM_ACQUISITION_STOP_RW REG_BCRM_V4L2_8BIT(0x0084) +#define REG_BCRM_ACQUISITION_ABORT_RW REG_BCRM_V4L2_8BIT(0x0088) +#define REG_BCRM_ACQUISITION_STATUS_R REG_BCRM_V4L2_8BIT(0x008c) +#define REG_BCRM_ACQUISITION_FRAME_RATE_RW REG_BCRM_V4L2_64BIT(0x0090) +#define REG_BCRM_ACQUISITION_FRAME_RATE_MIN_R REG_BCRM_V4L2_64BIT(0x0098) +#define REG_BCRM_ACQUISITION_FRAME_RATE_MAX_R REG_BCRM_V4L2_64BIT(0x00a0) +#define REG_BCRM_ACQUISITION_FRAME_RATE_INC_R REG_BCRM_V4L2_64BIT(0x00a8) +#define REG_BCRM_ACQUISITION_FRAME_RATE_ENABLE_RW REG_BCRM_V4L2_8BIT(0x00b0) + +#define REG_BCRM_FRAME_START_TRIGGER_MODE_RW REG_BCRM_V4L2_8BIT(0x00b4) +#define REG_BCRM_FRAME_START_TRIGGER_SOURCE_RW REG_BCRM_V4L2_8BIT(0x00b8) +#define REG_BCRM_FRAME_START_TRIGGER_ACTIVATION_RW REG_BCRM_V4L2_8BIT(0x00bc) +#define REG_BCRM_FRAME_START_TRIGGER_SOFTWARE_W REG_BCRM_V4L2_8BIT(0x00c0) +#define REG_BCRM_FRAME_START_TRIGGER_DELAY_RW REG_BCRM_V4L2_32BIT(0x00c4) +#define REG_BCRM_EXPOSURE_ACTIVE_LINE_MODE_RW REG_BCRM_V4L2_8BIT(0x00c8) +#define REG_BCRM_EXPOSURE_ACTIVE_LINE_SELECTOR_RW REG_BCRM_V4L2_8BIT(0x00cc) +#define REG_BCRM_LINE_CONFIGURATION_RW REG_BCRM_V4L2_32BIT(0x00d0) + +#define REG_BCRM_IMG_WIDTH_RW REG_BCRM_V4L2_32BIT(0x0100) +#define REG_BCRM_IMG_WIDTH_MIN_R REG_BCRM_V4L2_32BIT(0x0104) +#define REG_BCRM_IMG_WIDTH_MAX_R REG_BCRM_V4L2_32BIT(0x0108) +#define REG_BCRM_IMG_WIDTH_INC_R REG_BCRM_V4L2_32BIT(0x010c) + +#define REG_BCRM_IMG_HEIGHT_RW REG_BCRM_V4L2_32BIT(0x0110) +#define REG_BCRM_IMG_HEIGHT_MIN_R REG_BCRM_V4L2_32BIT(0x0114) +#define REG_BCRM_IMG_HEIGHT_MAX_R REG_BCRM_V4L2_32BIT(0x0118) +#define REG_BCRM_IMG_HEIGHT_INC_R REG_BCRM_V4L2_32BIT(0x011c) + +#define REG_BCRM_IMG_OFFSET_X_RW REG_BCRM_V4L2_32BIT(0x0120) +#define REG_BCRM_IMG_OFFSET_X_MIN_R REG_BCRM_V4L2_32BIT(0x0124) +#define REG_BCRM_IMG_OFFSET_X_MAX_R REG_BCRM_V4L2_32BIT(0x0128) +#define REG_BCRM_IMG_OFFSET_X_INC_R REG_BCRM_V4L2_32BIT(0x012c) + +#define REG_BCRM_IMG_OFFSET_Y_RW REG_BCRM_V4L2_32BIT(0x0130) +#define REG_BCRM_IMG_OFFSET_Y_MIN_R REG_BCRM_V4L2_32BIT(0x0134) +#define REG_BCRM_IMG_OFFSET_Y_MAX_R REG_BCRM_V4L2_32BIT(0x0138) +#define REG_BCRM_IMG_OFFSET_Y_INC_R REG_BCRM_V4L2_32BIT(0x013c) + +#define REG_BCRM_IMG_MIPI_DATA_FORMAT_RW REG_BCRM_V4L2_32BIT(0x0140) +#define REG_BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_R REG_BCRM_V4L2_64BIT(0x0148) +#define REG_BCRM_IMG_BAYER_PATTERN_INQUIRY_R REG_BCRM_V4L2_8BIT(0x0150) +#define REG_BCRM_IMG_BAYER_PATTERN_RW REG_BCRM_V4L2_8BIT(0x0154) +#define REG_BCRM_IMG_REVERSE_X_RW REG_BCRM_V4L2_8BIT(0x0158) +#define REG_BCRM_IMG_REVERSE_Y_RW REG_BCRM_V4L2_8BIT(0x015c) + +#define REG_BCRM_SENSOR_WIDTH_R REG_BCRM_V4L2_32BIT(0x0160) +#define REG_BCRM_SENSOR_HEIGHT_R REG_BCRM_V4L2_32BIT(0x0164) +#define REG_BCRM_WIDTH_MAX_R REG_BCRM_V4L2_32BIT(0x0168) +#define REG_BCRM_HEIGHT_MAX_R REG_BCRM_V4L2_32BIT(0x016c) + +#define REG_BCRM_EXPOSURE_TIME_RW REG_BCRM_V4L2_64BIT(0x0180) +#define REG_BCRM_EXPOSURE_TIME_MIN_R REG_BCRM_V4L2_64BIT(0x0188) +#define REG_BCRM_EXPOSURE_TIME_MAX_R REG_BCRM_V4L2_64BIT(0x0190) +#define REG_BCRM_EXPOSURE_TIME_INC_R REG_BCRM_V4L2_64BIT(0x0198) +#define REG_BCRM_EXPOSURE_AUTO_RW REG_BCRM_V4L2_8BIT(0x01a0) + +#define REG_BCRM_INTENSITY_AUTO_PRECEDENCE_RW REG_BCRM_V4L2_8BIT(0x01a4) +#define REG_BCRM_INTENSITY_AUTO_PRECEDENCE_VALUE_RW REG_BCRM_V4L2_32BIT(0x01a8) +#define REG_BCRM_INTENSITY_AUTO_PRECEDENCE_MIN_R REG_BCRM_V4L2_32BIT(0x01ac) +#define REG_BCRM_INTENSITY_AUTO_PRECEDENCE_MAX_R REG_BCRM_V4L2_32BIT(0x01b0) +#define REG_BCRM_INTENSITY_AUTO_PRECEDENCE_INC_R REG_BCRM_V4L2_32BIT(0x01b4) + +#define REG_BCRM_BLACK_LEVEL_RW REG_BCRM_V4L2_32BIT(0x01b8) +#define REG_BCRM_BLACK_LEVEL_MIN_R REG_BCRM_V4L2_32BIT(0x01bc) +#define REG_BCRM_BLACK_LEVEL_MAX_R REG_BCRM_V4L2_32BIT(0x01c0) +#define REG_BCRM_BLACK_LEVEL_INC_R REG_BCRM_V4L2_32BIT(0x01c4) + +#define REG_BCRM_GAIN_RW REG_BCRM_V4L2_64BIT(0x01c8) +#define REG_BCRM_GAIN_MIN_R REG_BCRM_V4L2_64BIT(0x01d0) +#define REG_BCRM_GAIN_MAX_R REG_BCRM_V4L2_64BIT(0x01d8) +#define REG_BCRM_GAIN_INC_R REG_BCRM_V4L2_64BIT(0x01e0) +#define REG_BCRM_GAIN_AUTO_RW REG_BCRM_V4L2_8BIT(0x01e8) + +#define REG_BCRM_GAMMA_RW REG_BCRM_V4L2_64BIT(0x01f0) +#define REG_BCRM_GAMMA_MIN_R REG_BCRM_V4L2_64BIT(0x01f8) +#define REG_BCRM_GAMMA_MAX_R REG_BCRM_V4L2_64BIT(0x0200) +#define REG_BCRM_GAMMA_INC_R REG_BCRM_V4L2_64BIT(0x0208) + +#define REG_BCRM_CONTRAST_VALUE_RW REG_BCRM_V4L2_32BIT(0x0214) +#define REG_BCRM_CONTRAST_VALUE_MIN_R REG_BCRM_V4L2_32BIT(0x0218) +#define REG_BCRM_CONTRAST_VALUE_MAX_R REG_BCRM_V4L2_32BIT(0x021c) +#define REG_BCRM_CONTRAST_VALUE_INC_R REG_BCRM_V4L2_32BIT(0x0220) + +#define REG_BCRM_SATURATION_RW REG_BCRM_V4L2_32BIT(0x0240) +#define REG_BCRM_SATURATION_MIN_R REG_BCRM_V4L2_32BIT(0x0244) +#define REG_BCRM_SATURATION_MAX_R REG_BCRM_V4L2_32BIT(0x0248) +#define REG_BCRM_SATURATION_INC_R REG_BCRM_V4L2_32BIT(0x024c) + +#define REG_BCRM_HUE_RW REG_BCRM_V4L2_32BIT(0x0250) +#define REG_BCRM_HUE_MIN_R REG_BCRM_V4L2_32BIT(0x0254) +#define REG_BCRM_HUE_MAX_R REG_BCRM_V4L2_32BIT(0x0258) +#define REG_BCRM_HUE_INC_R REG_BCRM_V4L2_32BIT(0x025c) + +#define REG_BCRM_ALL_BALANCE_RATIO_RW REG_BCRM_V4L2_64BIT(0x0260) +#define REG_BCRM_ALL_BALANCE_RATIO_MIN_R REG_BCRM_V4L2_64BIT(0x0268) +#define REG_BCRM_ALL_BALANCE_RATIO_MAX_R REG_BCRM_V4L2_64BIT(0x0270) +#define REG_BCRM_ALL_BALANCE_RATIO_INC_R REG_BCRM_V4L2_64BIT(0x0278) + +#define REG_BCRM_RED_BALANCE_RATIO_RW REG_BCRM_V4L2_64BIT(0x0280) +#define REG_BCRM_RED_BALANCE_RATIO_MIN_R REG_BCRM_V4L2_64BIT(0x0288) +#define REG_BCRM_RED_BALANCE_RATIO_MAX_R REG_BCRM_V4L2_64BIT(0x0290) +#define REG_BCRM_RED_BALANCE_RATIO_INC_R REG_BCRM_V4L2_64BIT(0x0298) + +#define REG_BCRM_GREEN_BALANCE_RATIO_RW REG_BCRM_V4L2_64BIT(0x02a0) +#define REG_BCRM_GREEN_BALANCE_RATIO_MIN_R REG_BCRM_V4L2_64BIT(0x02a8) +#define REG_BCRM_GREEN_BALANCE_RATIO_MAX_R REG_BCRM_V4L2_64BIT(0x02b0) +#define REG_BCRM_GREEN_BALANCE_RATIO_INC_R REG_BCRM_V4L2_64BIT(0x02b8) + +#define REG_BCRM_BLUE_BALANCE_RATIO_RW REG_BCRM_V4L2_64BIT(0x02c0) +#define REG_BCRM_BLUE_BALANCE_RATIO_MIN_R REG_BCRM_V4L2_64BIT(0x02c8) +#define REG_BCRM_BLUE_BALANCE_RATIO_MAX_R REG_BCRM_V4L2_64BIT(0x02d0) +#define REG_BCRM_BLUE_BALANCE_RATIO_INC_R REG_BCRM_V4L2_64BIT(0x02d8) + +#define REG_BCRM_WHITE_BALANCE_AUTO_RW REG_BCRM_V4L2_8BIT(0x02e0) +#define REG_BCRM_SHARPNESS_RW REG_BCRM_V4L2_32BIT(0x0300) +#define REG_BCRM_SHARPNESS_MIN_R REG_BCRM_V4L2_32BIT(0x0304) +#define REG_BCRM_SHARPNESS_MAX_R REG_BCRM_V4L2_32BIT(0x0308) +#define REG_BCRM_SHARPNESS_INC_R REG_BCRM_V4L2_32BIT(0x030c) + +#define REG_BCRM_DEVICE_TEMPERATURE_R REG_BCRM_V4L2_32BIT(0x0310) +#define REG_BCRM_EXPOSURE_AUTO_MIN_RW REG_BCRM_V4L2_64BIT(0x0330) +#define REG_BCRM_EXPOSURE_AUTO_MAX_RW REG_BCRM_V4L2_64BIT(0x0338) +#define REG_BCRM_GAIN_AUTO_MIN_RW REG_BCRM_V4L2_64BIT(0x0340) +#define REG_BCRM_GAIN_AUTO_MAX_RW REG_BCRM_V4L2_64BIT(0x0348) + +/* Heartbeat reg*/ +#define REG_BCRM_HEARTBEAT_RW CCI_REG8(0x021f) + +/* GenCP Registers */ +#define REG_GENCP_CHANGEMODE_W CCI_REG8(0x021c) +#define REG_GENCP_CURRENTMODE_R CCI_REG8(0x021d) +#define REG_GENCP_IN_HANDSHAKE_RW CCI_REG8(0x001c) +#define REG_GENCP_OUT_SIZE_W CCI_REG16(0x0020) +#define REG_GENCP_IN_SIZE_R CCI_REG16(0x0024) + +/* defines */ +#define REG_BCRM_HANDSHAKE_STATUS_MASK 0x01 +#define REG_BCRM_HANDSHAKE_AVAILABLE_MASK 0x80 + +#define BCRM_HANDSHAKE_W_DONE_EN_BIT BIT(0) + +#define ALVIUM_DEFAULT_FR_HZ 10 +#define ALVIUM_DEFAULT_PIXEL_RATE_MHZ 148000000 + +#define ALVIUM_LP2HS_DELAY_MS 100 + +enum alvium_bcrm_mode { + ALVIUM_BCM_MODE, + ALVIUM_GENCP_MODE, + ALVIUM_NUM_MODE +}; + +enum alvium_mipi_fmt { + ALVIUM_FMT_UYVY8_2X8 = 0, + ALVIUM_FMT_UYVY8_1X16, + ALVIUM_FMT_YUYV8_1X16, + ALVIUM_FMT_YUYV8_2X8, + ALVIUM_FMT_YUYV10_1X20, + ALVIUM_FMT_RGB888_1X24, + ALVIUM_FMT_RBG888_1X24, + ALVIUM_FMT_BGR888_1X24, + ALVIUM_FMT_RGB888_3X8, + ALVIUM_FMT_Y8_1X8, + ALVIUM_FMT_SGRBG8_1X8, + ALVIUM_FMT_SRGGB8_1X8, + ALVIUM_FMT_SGBRG8_1X8, + ALVIUM_FMT_SBGGR8_1X8, + ALVIUM_FMT_Y10_1X10, + ALVIUM_FMT_SGRBG10_1X10, + ALVIUM_FMT_SRGGB10_1X10, + ALVIUM_FMT_SGBRG10_1X10, + ALVIUM_FMT_SBGGR10_1X10, + ALVIUM_FMT_Y12_1X12, + ALVIUM_FMT_SGRBG12_1X12, + ALVIUM_FMT_SRGGB12_1X12, + ALVIUM_FMT_SGBRG12_1X12, + ALVIUM_FMT_SBGGR12_1X12, + ALVIUM_FMT_SBGGR14_1X14, + ALVIUM_FMT_SGBRG14_1X14, + ALVIUM_FMT_SRGGB14_1X14, + ALVIUM_FMT_SGRBG14_1X14, + ALVIUM_NUM_SUPP_MIPI_DATA_FMT +}; + +enum alvium_av_bayer_bit { + ALVIUM_BIT_BAY_NONE = -1, + ALVIUM_BIT_BAY_MONO = 0, + ALVIUM_BIT_BAY_GR, + ALVIUM_BIT_BAY_RG, + ALVIUM_BIT_BAY_GB, + ALVIUM_BIT_BAY_BG, + ALVIUM_NUM_BAY_AV_BIT +}; + +enum alvium_av_mipi_bit { + ALVIUM_BIT_YUV420_8_LEG = 0, + ALVIUM_BIT_YUV420_8, + ALVIUM_BIT_YUV420_10, + ALVIUM_BIT_YUV420_8_CSPS, + ALVIUM_BIT_YUV420_10_CSPS, + ALVIUM_BIT_YUV422_8, + ALVIUM_BIT_YUV422_10, + ALVIUM_BIT_RGB888, + ALVIUM_BIT_RGB666, + ALVIUM_BIT_RGB565, + ALVIUM_BIT_RGB555, + ALVIUM_BIT_RGB444, + ALVIUM_BIT_RAW6, + ALVIUM_BIT_RAW7, + ALVIUM_BIT_RAW8, + ALVIUM_BIT_RAW10, + ALVIUM_BIT_RAW12, + ALVIUM_BIT_RAW14, + ALVIUM_BIT_JPEG, + ALVIUM_NUM_SUPP_MIPI_DATA_BIT +}; + +struct alvium_avail_feat { + u64 rev_x:1; + u64 rev_y:1; + u64 int_autop:1; + u64 black_lvl:1; + u64 gain:1; + u64 gamma:1; + u64 contrast:1; + u64 sat:1; + u64 hue:1; + u64 whiteb:1; + u64 sharp:1; + u64 auto_exp:1; + u64 auto_gain:1; + u64 auto_whiteb:1; + u64 dev_temp:1; + u64 acq_abort:1; + u64 acq_fr:1; + u64 fr_trigger:1; + u64 exp_acq_line:1; + u64 reserved:45; +}; + +struct alvium_avail_mipi_fmt { + u64 yuv420_8_leg:1; + u64 yuv420_8:1; + u64 yuv420_10:1; + u64 yuv420_8_csps:1; + u64 yuv420_10_csps:1; + u64 yuv422_8:1; + u64 yuv422_10:1; + u64 rgb888:1; + u64 rgb666:1; + u64 rgb565:1; + u64 rgb555:1; + u64 rgb444:1; + u64 raw6:1; + u64 raw7:1; + u64 raw8:1; + u64 raw10:1; + u64 raw12:1; + u64 raw14:1; + u64 jpeg:1; + u64 reserved:45; +}; + +struct alvium_avail_bayer { + u8 mono:1; + u8 gr:1; + u8 rg:1; + u8 gb:1; + u8 bg:1; + u8 reserved:3; +}; + +struct alvium_mode { + struct v4l2_rect crop; + struct v4l2_mbus_framefmt fmt; + u32 width; + u32 height; +}; + +struct alvium_pixfmt { + u32 code; + u32 colorspace; + u64 mipi_fmt_regval; + u64 bay_fmt_regval; + u8 id; + u8 is_raw; + u8 fmt_av_bit; + u8 bay_av_bit; +}; + +struct alvium_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *blue_balance; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; +}; + +struct alvium_dev { + struct i2c_client *i2c_client; + struct v4l2_subdev sd; + struct v4l2_fwnode_endpoint ep; + struct media_pad pad; + struct regmap *regmap; + + struct regulator *reg_vcc; + + u16 bcrm_addr; + + struct alvium_avail_feat avail_ft; + u8 is_mipi_fmt_avail[ALVIUM_NUM_SUPP_MIPI_DATA_BIT]; + u8 is_bay_avail[ALVIUM_NUM_BAY_AV_BIT]; + + u32 min_csi_clk; + u32 max_csi_clk; + u32 dft_img_width; + u32 img_min_width; + u32 img_max_width; + u32 img_inc_width; + u32 dft_img_height; + u32 img_min_height; + u32 img_max_height; + u32 img_inc_height; + u32 min_offx; + u32 max_offx; + u32 inc_offx; + u32 min_offy; + u32 max_offy; + u32 inc_offy; + u64 dft_gain; + u64 min_gain; + u64 max_gain; + u64 inc_gain; + u64 dft_exp; + u64 min_exp; + u64 max_exp; + u64 inc_exp; + u64 dft_rbalance; + u64 min_rbalance; + u64 max_rbalance; + u64 inc_rbalance; + u64 dft_bbalance; + u64 min_bbalance; + u64 max_bbalance; + u64 inc_bbalance; + s32 dft_hue; + s32 min_hue; + s32 max_hue; + s32 inc_hue; + u32 dft_contrast; + u32 min_contrast; + u32 max_contrast; + u32 inc_contrast; + u32 dft_sat; + u32 min_sat; + u32 max_sat; + u32 inc_sat; + s32 dft_black_lvl; + s32 min_black_lvl; + s32 max_black_lvl; + s32 inc_black_lvl; + u64 dft_gamma; + u64 min_gamma; + u64 max_gamma; + u64 inc_gamma; + s32 dft_sharp; + s32 min_sharp; + s32 max_sharp; + s32 inc_sharp; + + struct alvium_mode mode; + struct v4l2_fract frame_interval; + u64 dft_fr; + u64 min_fr; + u64 max_fr; + u64 fr; + + u8 h_sup_csi_lanes; + u64 link_freq; + + struct alvium_ctrls ctrls; + + u8 bcrm_mode; + + struct alvium_pixfmt *alvium_csi2_fmt; + u8 alvium_csi2_fmt_n; + + u8 streaming; + u8 apply_fiv; +}; + +static inline struct alvium_dev *sd_to_alvium(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct alvium_dev, sd); +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of_const(ctrl->handler, struct alvium_dev, + ctrls.handler)->sd; +} +#endif /* ALVIUM_CSI2_H_ */ diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 701f36345..c7d5fa532 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -446,8 +446,7 @@ static int ar0521_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, 0 - /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); else fmt = &sensor->fmt; @@ -472,7 +471,7 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0 /* pad */); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; mutex_unlock(&sensor->lock); diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig index b55c93a2e..710a729ae 100644 --- a/drivers/media/i2c/ccs/Kconfig +++ b/drivers/media/i2c/ccs/Kconfig @@ -2,6 +2,7 @@ config VIDEO_CCS tristate "MIPI CCS/SMIA++/SMIA sensor support" depends on HAVE_CLK + select V4L2_CCI_I2C select VIDEO_CCS_PLL help This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 12e6f0a26..e21287d50 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -25,8 +25,9 @@ #include #include #include -#include +#include #include +#include #include #include "ccs.h" @@ -98,7 +99,7 @@ static int ccs_limit_ptr(struct ccs_sensor *sensor, unsigned int limit, linfo = &ccs_limits[ccs_limit_offsets[limit].info]; if (WARN_ON(!sensor->ccs_limits) || - WARN_ON(offset + ccs_reg_width(linfo->reg) > + WARN_ON(offset + CCI_REG_WIDTH_BYTES(linfo->reg) > ccs_limit_offsets[limit + 1].lim)) return -EINVAL; @@ -124,7 +125,7 @@ void ccs_replace_limit(struct ccs_sensor *sensor, dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" %u = %u, 0x%x\n", linfo->reg, linfo->name, offset, val, val); - ccs_assign_limit(ptr, ccs_reg_width(linfo->reg), val); + ccs_assign_limit(ptr, CCI_REG_WIDTH_BYTES(linfo->reg), val); } u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit, @@ -138,7 +139,7 @@ u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit, if (ret) return 0; - switch (ccs_reg_width(ccs_limits[ccs_limit_offsets[limit].info].reg)) { + switch (CCI_REG_WIDTH_BYTES(ccs_limits[ccs_limit_offsets[limit].info].reg)) { case sizeof(u8): val = *(u8 *)ptr; break; @@ -172,9 +173,11 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor) end = alloc + ccs_limit_offsets[CCS_L_LAST].lim; + sensor->ccs_limits = alloc; + for (i = 0, l = 0, ptr = alloc; ccs_limits[i].size; i++) { u32 reg = ccs_limits[i].reg; - unsigned int width = ccs_reg_width(reg); + unsigned int width = CCI_REG_WIDTH_BYTES(reg); unsigned int j; if (l == CCS_L_LAST) { @@ -186,6 +189,7 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor) for (j = 0; j < ccs_limits[i].size / width; j++, reg += width, ptr += width) { + char str[16] = ""; u32 val; ret = ccs_read_addr_noconv(sensor, reg, &val); @@ -204,8 +208,15 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor) ccs_assign_limit(ptr, width, val); - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", - reg, ccs_limits[i].name, val, val); +#ifdef CONFIG_DYNAMIC_DEBUG + if (reg & (CCS_FL_FLOAT_IREAL | CCS_FL_IREAL)) + snprintf(str, sizeof(str), ", %u", + ccs_reg_conv(sensor, reg, val)); +#endif + + dev_dbg(&client->dev, + "0x%8.8x \"%s\" = %u, 0x%x%s\n", + reg, ccs_limits[i].name, val, val, str); } if (ccs_limits[i].flags & CCS_L_FL_SAME_REG) @@ -222,14 +233,13 @@ static int ccs_read_all_limits(struct ccs_sensor *sensor) goto out_err; } - sensor->ccs_limits = alloc; - if (CCS_LIM(sensor, SCALER_N_MIN) < 16) ccs_replace_limit(sensor, CCS_L_SCALER_N_MIN, 0, 16); return 0; out_err: + sensor->ccs_limits = NULL; kfree(alloc); return ret; @@ -1878,9 +1888,11 @@ static int ccs_pm_get_init(struct ccs_sensor *sensor) goto error; /* Device was already active, so don't set controls */ - if (rval == 1) + if (rval == 1 && !sensor->handler_setup_needed) return 0; + sensor->handler_setup_needed = false; + /* Restore V4L2 controls to the previously suspended device */ rval = v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler); if (rval) @@ -2030,7 +2042,7 @@ static int __ccs_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - fmt->format = *v4l2_subdev_get_pad_format(subdev, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); return 0; @@ -2061,10 +2073,10 @@ static void ccs_get_crop_compose(struct v4l2_subdev *subdev, if (crops) for (i = 0; i < subdev->entity.num_pads; i++) crops[i] = - v4l2_subdev_get_pad_crop(subdev, sd_state, i); + v4l2_subdev_state_get_crop(sd_state, i); if (comps) - *comps = v4l2_subdev_get_pad_compose(subdev, sd_state, - ssd->sink_pad); + *comps = v4l2_subdev_state_get_compose(sd_state, + ssd->sink_pad); } /* Changes require propagation only on sink pad. */ @@ -2097,7 +2109,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev, fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[CCS_PAD_SRC] = *comp; - fmt = v4l2_subdev_get_pad_format(subdev, sd_state, CCS_PAD_SRC); + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); fmt->width = comp->width; fmt->height = comp->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) @@ -2507,7 +2519,7 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, if (sel->pad == ssd->sink_pad) { struct v4l2_mbus_framefmt *mfmt = - v4l2_subdev_get_pad_format(subdev, sd_state, sel->pad); + v4l2_subdev_state_get_format(sd_state, sel->pad); src_size.width = mfmt->width; src_size.height = mfmt->height; @@ -2567,8 +2579,8 @@ static int ccs_get_selection(struct v4l2_subdev *subdev, ccs_get_native_size(ssd, &sel->r); } else if (sel->pad == ssd->sink_pad) { struct v4l2_mbus_framefmt *sink_fmt = - v4l2_subdev_get_pad_format(subdev, sd_state, - ssd->sink_pad); + v4l2_subdev_state_get_format(sd_state, + ssd->sink_pad); sel->r.top = sel->r.left = 0; sel->r.width = sink_fmt->width; sel->r.height = sink_fmt->height; @@ -2714,66 +2726,54 @@ static int ccs_identify_module(struct ccs_sensor *sensor) rval = ccs_read(sensor, MODULE_MANUFACTURER_ID, &minfo->mipi_manufacturer_id); if (!rval && !minfo->mipi_manufacturer_id) - rval = ccs_read_addr_8only(sensor, - SMIAPP_REG_U8_MANUFACTURER_ID, - &minfo->smia_manufacturer_id); + rval = ccs_read_addr(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->smia_manufacturer_id); if (!rval) - rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_MODEL_ID, - &minfo->model_id); + rval = ccs_read(sensor, MODULE_MODEL_ID, &minfo->model_id); if (!rval) - rval = ccs_read_addr_8only(sensor, - CCS_R_MODULE_REVISION_NUMBER_MAJOR, - &rev); + rval = ccs_read(sensor, MODULE_REVISION_NUMBER_MAJOR, &rev); if (!rval) { - rval = ccs_read_addr_8only(sensor, - CCS_R_MODULE_REVISION_NUMBER_MINOR, - &minfo->revision_number); + rval = ccs_read(sensor, MODULE_REVISION_NUMBER_MINOR, + &minfo->revision_number); minfo->revision_number |= rev << 8; } if (!rval) - rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_YEAR, - &minfo->module_year); + rval = ccs_read(sensor, MODULE_DATE_YEAR, &minfo->module_year); if (!rval) - rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_MONTH, - &minfo->module_month); + rval = ccs_read(sensor, MODULE_DATE_MONTH, + &minfo->module_month); if (!rval) - rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_DAY, - &minfo->module_day); + rval = ccs_read(sensor, MODULE_DATE_DAY, &minfo->module_day); /* Sensor info */ if (!rval) rval = ccs_read(sensor, SENSOR_MANUFACTURER_ID, &minfo->sensor_mipi_manufacturer_id); if (!rval && !minfo->sensor_mipi_manufacturer_id) - rval = ccs_read_addr_8only(sensor, - CCS_R_SENSOR_MANUFACTURER_ID, - &minfo->sensor_smia_manufacturer_id); + rval = ccs_read(sensor, SENSOR_MANUFACTURER_ID, + &minfo->sensor_smia_manufacturer_id); if (!rval) - rval = ccs_read_addr_8only(sensor, - CCS_R_SENSOR_MODEL_ID, - &minfo->sensor_model_id); + rval = ccs_read(sensor, SENSOR_MODEL_ID, + &minfo->sensor_model_id); if (!rval) - rval = ccs_read_addr_8only(sensor, - CCS_R_SENSOR_REVISION_NUMBER, - &minfo->sensor_revision_number); + rval = ccs_read(sensor, SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); if (!rval && !minfo->sensor_revision_number) - rval = ccs_read_addr_8only(sensor, - CCS_R_SENSOR_REVISION_NUMBER_16, - &minfo->sensor_revision_number); + rval = ccs_read(sensor, SENSOR_REVISION_NUMBER_16, + &minfo->sensor_revision_number); if (!rval) - rval = ccs_read_addr_8only(sensor, - CCS_R_SENSOR_FIRMWARE_VERSION, - &minfo->sensor_firmware_version); + rval = ccs_read(sensor, SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); /* SMIA */ if (!rval) rval = ccs_read(sensor, MIPI_CCS_VERSION, &minfo->ccs_version); if (!rval && !minfo->ccs_version) - rval = ccs_read_addr_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, - &minfo->smia_version); + rval = ccs_read_addr(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); if (!rval && !minfo->ccs_version) - rval = ccs_read_addr_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, - &minfo->smiapp_version); + rval = ccs_read_addr(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); if (rval) { dev_err(&client->dev, "sensor detection failed\n"); @@ -3004,17 +3004,17 @@ static int ccs_init_subdev(struct ccs_sensor *sensor, return 0; } -static int ccs_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ccs_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ccs_subdev *ssd = to_ccs_subdev(sd); struct ccs_sensor *sensor = ssd->sensor; unsigned int pad = ssd == sensor->pixel_array ? CCS_PA_PAD_SRC : CCS_PAD_SINK; struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_pad_format(sd, sd_state, pad); + v4l2_subdev_state_get_format(sd_state, pad); struct v4l2_rect *crop = - v4l2_subdev_get_pad_crop(sd, sd_state, pad); + v4l2_subdev_state_get_crop(sd_state, pad); bool is_active = !sd->active_state || sd->active_state == sd_state; mutex_lock(&sensor->mutex); @@ -3034,7 +3034,7 @@ static int ccs_init_cfg(struct v4l2_subdev *sd, return 0; } - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CCS_PAD_SRC); + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); fmt->code = ssd == sensor->src ? sensor->csi_format->code : sensor->internal_csi_format->code; fmt->field = V4L2_FIELD_NONE; @@ -3053,7 +3053,6 @@ static const struct v4l2_subdev_video_ops ccs_video_ops = { }; static const struct v4l2_subdev_pad_ops ccs_pad_ops = { - .init_cfg = ccs_init_cfg, .enum_mbus_code = ccs_enum_mbus_code, .get_fmt = ccs_get_format, .set_fmt = ccs_set_format, @@ -3077,6 +3076,7 @@ static const struct media_entity_operations ccs_entity_ops = { }; static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { + .init_state = ccs_init_state, .registered = ccs_registered, .unregistered = ccs_unregistered, }; @@ -3307,6 +3307,13 @@ static int ccs_probe(struct i2c_client *client) if (IS_ERR(sensor->xshutdown)) return PTR_ERR(sensor->xshutdown); + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) { + dev_err(&client->dev, "can't initialise CCI (%ld)\n", + PTR_ERR(sensor->regmap)); + return PTR_ERR(sensor->regmap); + } + rval = ccs_power_on(&client->dev); if (rval < 0) return rval; @@ -3532,6 +3539,7 @@ static int ccs_probe(struct i2c_client *client) sensor->streaming = false; sensor->dev_init_done = true; + sensor->handler_setup_needed = true; rval = ccs_write_msr_regs(sensor); if (rval) @@ -3636,12 +3644,16 @@ static int ccs_module_init(void) { unsigned int i, l; + CCS_BUILD_BUG; + for (i = 0, l = 0; ccs_limits[i].size && l < CCS_L_LAST; i++) { if (!(ccs_limits[i].flags & CCS_L_FL_SAME_REG)) { ccs_limit_offsets[l + 1].lim = ALIGN(ccs_limit_offsets[l].lim + ccs_limits[i].size, - ccs_reg_width(ccs_limits[i + 1].reg)); + ccs_limits[i + 1].reg ? + CCI_REG_WIDTH_BYTES(ccs_limits[i + 1].reg) : + 1U); ccs_limit_offsets[l].info = i; l++; } else { diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c index 25993445f..ed7907550 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.c +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -62,87 +62,6 @@ static u32 float_to_u32_mul_1000000(struct i2c_client *client, u32 phloat) } -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int ____ccs_read_addr(struct ccs_sensor *sensor, u16 reg, u16 len, - u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data_buf[sizeof(u32)] = { 0 }; - unsigned char offset_buf[sizeof(u16)]; - int r; - - if (len > sizeof(data_buf)) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = sizeof(offset_buf); - msg.buf = offset_buf; - put_unaligned_be16(reg, offset_buf); - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - msg.len = len; - msg.flags = I2C_M_RD; - msg.buf = &data_buf[sizeof(data_buf) - len]; - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - *val = get_unaligned_be32(data_buf); - - return 0; - -err: - dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); - - return r; -} - -/* Read a register using 8-bit access only. */ -static int ____ccs_read_addr_8only(struct ccs_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - unsigned int i; - int rval; - - *val = 0; - - for (i = 0; i < len; i++) { - u32 val8; - - rval = ____ccs_read_addr(sensor, reg + i, 1, &val8); - if (rval < 0) - return rval; - *val |= val8 << ((len - i - 1) << 3); - } - - return 0; -} - -unsigned int ccs_reg_width(u32 reg) -{ - if (reg & CCS_FL_16BIT) - return sizeof(u16); - if (reg & CCS_FL_32BIT) - return sizeof(u32); - - return sizeof(u8); -} - static u32 ireal32_to_u32_mul_1000000(struct i2c_client *client, u32 val) { if (val >> 10 > U32_MAX / 15625) { @@ -178,29 +97,22 @@ u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val) static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val, bool only8, bool conv) { - unsigned int len = ccs_reg_width(reg); + u64 __val; int rval; - if (!only8) - rval = ____ccs_read_addr(sensor, CCS_REG_ADDR(reg), len, val); - else - rval = ____ccs_read_addr_8only(sensor, CCS_REG_ADDR(reg), len, - val); + rval = cci_read(sensor->regmap, reg, &__val, NULL); if (rval < 0) return rval; - if (!conv) - return 0; - - *val = ccs_reg_conv(sensor, reg, *val); + *val = conv ? ccs_reg_conv(sensor, reg, __val) : __val; return 0; } -static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs, - u32 reg, u32 *val) +static int __ccs_static_data_read_ro_reg(struct ccs_reg *regs, size_t num_regs, + u32 reg, u32 *val) { - unsigned int width = ccs_reg_width(reg); + unsigned int width = CCI_REG_WIDTH_BYTES(reg); size_t i; for (i = 0; i < num_regs; i++, regs++) { @@ -235,16 +147,17 @@ static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs, return -ENOENT; } -static int ccs_read_data(struct ccs_sensor *sensor, u32 reg, u32 *val) +static int +ccs_static_data_read_ro_reg(struct ccs_sensor *sensor, u32 reg, u32 *val) { - if (!__ccs_read_data(sensor->sdata.sensor_read_only_regs, - sensor->sdata.num_sensor_read_only_regs, - reg, val)) + if (!__ccs_static_data_read_ro_reg(sensor->sdata.sensor_read_only_regs, + sensor->sdata.num_sensor_read_only_regs, + reg, val)) return 0; - return __ccs_read_data(sensor->mdata.module_read_only_regs, - sensor->mdata.num_module_read_only_regs, - reg, val); + return __ccs_static_data_read_ro_reg(sensor->mdata.module_read_only_regs, + sensor->mdata.num_module_read_only_regs, + reg, val); } static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val, @@ -253,7 +166,7 @@ static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val, int rval; if (data) { - rval = ccs_read_data(sensor, reg, val); + rval = ccs_static_data_read_ro_reg(sensor, reg, val); if (!rval) return 0; } @@ -291,71 +204,13 @@ int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val) return ccs_read_addr_raw(sensor, reg, val, false, true, false, true); } -static int ccs_write_retry(struct i2c_client *client, struct i2c_msg *msg) -{ - unsigned int retries; - int r; - - for (retries = 0; retries < 10; retries++) { - /* - * Due to unknown reason sensor stops responding. This - * loop is a temporaty solution until the root cause - * is found. - */ - r = i2c_transfer(client->adapter, msg, 1); - if (r != 1) { - usleep_range(1000, 2000); - continue; - } - - if (retries) - dev_err(&client->dev, - "sensor i2c stall encountered. retries: %d\n", - retries); - return 0; - } - - return r; -} - -int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[6]; - unsigned int len = ccs_reg_width(reg); - int r; - - if (len > sizeof(data) - 2) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; /* Write */ - msg.len = 2 + len; - msg.buf = data; - - put_unaligned_be16(CCS_REG_ADDR(reg), data); - put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2); - - dev_dbg(&client->dev, "writing reg 0x%4.4x value 0x%*.*x (%u)\n", - CCS_REG_ADDR(reg), ccs_reg_width(reg) << 1, - ccs_reg_width(reg) << 1, val, val); - - r = ccs_write_retry(client, &msg); - if (r) - dev_err(&client->dev, - "wrote 0x%x to offset 0x%x error %d\n", val, - CCS_REG_ADDR(reg), r); - - return r; -} - /* * Write to a 8/16-bit register. * Returns zero if successful, or non-zero otherwise. */ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) { + unsigned int retries = 10; int rval; rval = ccs_call_quirk(sensor, reg_access, true, ®, &val); @@ -364,7 +219,13 @@ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) if (rval < 0) return rval; - return ccs_write_addr_no_quirk(sensor, reg, val); + rval = 0; + do { + if (cci_write(sensor->regmap, reg, val, &rval)) + fsleep(1000); + } while (rval && --retries); + + return rval; } #define MAX_WRITE_LEN 32U @@ -373,40 +234,38 @@ int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs, size_t num_regs) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned char buf[2 + MAX_WRITE_LEN]; - struct i2c_msg msg = { - .addr = client->addr, - .buf = buf, - }; size_t i; for (i = 0; i < num_regs; i++, regs++) { unsigned char *regdata = regs->value; unsigned int j; + int len; - for (j = 0; j < regs->len; - j += msg.len - 2, regdata += msg.len - 2) { + for (j = 0; j < regs->len; j += len, regdata += len) { char printbuf[(MAX_WRITE_LEN << 1) + 1 /* \0 */] = { 0 }; + unsigned int retries = 10; int rval; - msg.len = min(regs->len - j, MAX_WRITE_LEN); + len = min(regs->len - j, MAX_WRITE_LEN); - bin2hex(printbuf, regdata, msg.len); + bin2hex(printbuf, regdata, len); dev_dbg(&client->dev, "writing msr reg 0x%4.4x value 0x%s\n", regs->addr + j, printbuf); - put_unaligned_be16(regs->addr + j, buf); - memcpy(buf + 2, regdata, msg.len); - - msg.len += 2; + do { + rval = regmap_bulk_write(sensor->regmap, + regs->addr + j, + regdata, len); + if (rval) + fsleep(1000); + } while (rval && --retries); - rval = ccs_write_retry(client, &msg); if (rval) { dev_err(&client->dev, "error writing %u octets to address 0x%4.4x\n", - msg.len, regs->addr + j); + len, regs->addr + j); return rval; } } diff --git a/drivers/media/i2c/ccs/ccs-regs.h b/drivers/media/i2c/ccs/ccs-regs.h index 6ce84c5ec..7b5dbc86e 100644 --- a/drivers/media/i2c/ccs/ccs-regs.h +++ b/drivers/media/i2c/ccs/ccs-regs.h @@ -10,59 +10,59 @@ #include -#define CCS_FL_BASE 16 -#define CCS_FL_16BIT BIT(CCS_FL_BASE) -#define CCS_FL_32BIT BIT(CCS_FL_BASE + 1) -#define CCS_FL_FLOAT_IREAL BIT(CCS_FL_BASE + 2) -#define CCS_FL_IREAL BIT(CCS_FL_BASE + 3) -#define CCS_R_ADDR(r) ((r) & 0xffff) +#include -#define CCS_R_MODULE_MODEL_ID (0x0000 | CCS_FL_16BIT) -#define CCS_R_MODULE_REVISION_NUMBER_MAJOR 0x0002 -#define CCS_R_FRAME_COUNT 0x0005 -#define CCS_R_PIXEL_ORDER 0x0006 +#define CCS_FL_BASE CCI_REG_PRIVATE_SHIFT +#define CCS_FL_FLOAT_IREAL BIT(CCS_FL_BASE) +#define CCS_FL_IREAL BIT(CCS_FL_BASE + 1) +#define CCS_BUILD_BUG \ + BUILD_BUG_ON(~CCI_REG_PRIVATE_MASK & (BIT(CCS_FL_BASE) | BIT(CCS_FL_BASE + 1))) +#define CCS_R_MODULE_MODEL_ID CCI_REG16(0x0000) +#define CCS_R_MODULE_REVISION_NUMBER_MAJOR CCI_REG8(0x0002) +#define CCS_R_FRAME_COUNT CCI_REG8(0x0005) +#define CCS_R_PIXEL_ORDER CCI_REG8(0x0006) #define CCS_PIXEL_ORDER_GRBG 0U #define CCS_PIXEL_ORDER_RGGB 1U #define CCS_PIXEL_ORDER_BGGR 2U #define CCS_PIXEL_ORDER_GBRG 3U -#define CCS_R_MIPI_CCS_VERSION 0x0007 +#define CCS_R_MIPI_CCS_VERSION CCI_REG8(0x0007) #define CCS_MIPI_CCS_VERSION_V1_0 0x10 #define CCS_MIPI_CCS_VERSION_V1_1 0x11 #define CCS_MIPI_CCS_VERSION_MAJOR_SHIFT 4U #define CCS_MIPI_CCS_VERSION_MAJOR_MASK 0xf0 #define CCS_MIPI_CCS_VERSION_MINOR_SHIFT 0U #define CCS_MIPI_CCS_VERSION_MINOR_MASK 0xf -#define CCS_R_DATA_PEDESTAL (0x0008 | CCS_FL_16BIT) -#define CCS_R_MODULE_MANUFACTURER_ID (0x000e | CCS_FL_16BIT) -#define CCS_R_MODULE_REVISION_NUMBER_MINOR 0x0010 -#define CCS_R_MODULE_DATE_YEAR 0x0012 -#define CCS_R_MODULE_DATE_MONTH 0x0013 -#define CCS_R_MODULE_DATE_DAY 0x0014 -#define CCS_R_MODULE_DATE_PHASE 0x0015 +#define CCS_R_DATA_PEDESTAL CCI_REG16(0x0008) +#define CCS_R_MODULE_MANUFACTURER_ID CCI_REG16(0x000e) +#define CCS_R_MODULE_REVISION_NUMBER_MINOR CCI_REG8(0x0010) +#define CCS_R_MODULE_DATE_YEAR CCI_REG8(0x0012) +#define CCS_R_MODULE_DATE_MONTH CCI_REG8(0x0013) +#define CCS_R_MODULE_DATE_DAY CCI_REG8(0x0014) +#define CCS_R_MODULE_DATE_PHASE CCI_REG8(0x0015) #define CCS_MODULE_DATE_PHASE_SHIFT 0U #define CCS_MODULE_DATE_PHASE_MASK 0x7 #define CCS_MODULE_DATE_PHASE_TS 0U #define CCS_MODULE_DATE_PHASE_ES 1U #define CCS_MODULE_DATE_PHASE_CS 2U #define CCS_MODULE_DATE_PHASE_MP 3U -#define CCS_R_SENSOR_MODEL_ID (0x0016 | CCS_FL_16BIT) -#define CCS_R_SENSOR_REVISION_NUMBER 0x0018 -#define CCS_R_SENSOR_FIRMWARE_VERSION 0x001a -#define CCS_R_SERIAL_NUMBER (0x001c | CCS_FL_32BIT) -#define CCS_R_SENSOR_MANUFACTURER_ID (0x0020 | CCS_FL_16BIT) -#define CCS_R_SENSOR_REVISION_NUMBER_16 (0x0022 | CCS_FL_16BIT) -#define CCS_R_FRAME_FORMAT_MODEL_TYPE 0x0040 +#define CCS_R_SENSOR_MODEL_ID CCI_REG16(0x0016) +#define CCS_R_SENSOR_REVISION_NUMBER CCI_REG8(0x0018) +#define CCS_R_SENSOR_FIRMWARE_VERSION CCI_REG8(0x001a) +#define CCS_R_SERIAL_NUMBER CCI_REG32(0x001c) +#define CCS_R_SENSOR_MANUFACTURER_ID CCI_REG16(0x0020) +#define CCS_R_SENSOR_REVISION_NUMBER_16 CCI_REG16(0x0022) +#define CCS_R_FRAME_FORMAT_MODEL_TYPE CCI_REG8(0x0040) #define CCS_FRAME_FORMAT_MODEL_TYPE_2_BYTE 1U #define CCS_FRAME_FORMAT_MODEL_TYPE_4_BYTE 2U -#define CCS_R_FRAME_FORMAT_MODEL_SUBTYPE 0x0041 +#define CCS_R_FRAME_FORMAT_MODEL_SUBTYPE CCI_REG8(0x0041) #define CCS_FRAME_FORMAT_MODEL_SUBTYPE_ROWS_SHIFT 0U #define CCS_FRAME_FORMAT_MODEL_SUBTYPE_ROWS_MASK 0xf #define CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_SHIFT 4U #define CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_MASK 0xf0 -#define CCS_R_FRAME_FORMAT_DESCRIPTOR(n) ((0x0042 | CCS_FL_16BIT) + (n) * 2) +#define CCS_R_FRAME_FORMAT_DESCRIPTOR(n) CCI_REG16(0x0042 + (n) * 2) #define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_MIN_N 0U #define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_MAX_N 14U -#define CCS_R_FRAME_FORMAT_DESCRIPTOR_4(n) ((0x0060 | CCS_FL_32BIT) + (n) * 4) +#define CCS_R_FRAME_FORMAT_DESCRIPTOR_4(n) CCI_REG32(0x0060 + (n) * 4) #define CCS_FRAME_FORMAT_DESCRIPTOR_PIXELS_SHIFT 0U #define CCS_FRAME_FORMAT_DESCRIPTOR_PIXELS_MASK 0xfff #define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_SHIFT 12U @@ -97,91 +97,91 @@ #define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_4 12U #define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_5 13U #define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_6 14U -#define CCS_R_ANALOG_GAIN_CAPABILITY (0x0080 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_CAPABILITY CCI_REG16(0x0080) #define CCS_ANALOG_GAIN_CAPABILITY_GLOBAL 0U #define CCS_ANALOG_GAIN_CAPABILITY_ALTERNATE_GLOBAL 2U -#define CCS_R_ANALOG_GAIN_CODE_MIN (0x0084 | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_CODE_MAX (0x0086 | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_CODE_STEP (0x0088 | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_TYPE (0x008a | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_M0 (0x008c | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_C0 (0x008e | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_M1 (0x0090 | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_C1 (0x0092 | CCS_FL_16BIT) -#define CCS_R_ANALOG_LINEAR_GAIN_MIN (0x0094 | CCS_FL_16BIT) -#define CCS_R_ANALOG_LINEAR_GAIN_MAX (0x0096 | CCS_FL_16BIT) -#define CCS_R_ANALOG_LINEAR_GAIN_STEP_SIZE (0x0098 | CCS_FL_16BIT) -#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MIN (0x009a | CCS_FL_16BIT) -#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MAX (0x009c | CCS_FL_16BIT) -#define CCS_R_ANALOG_EXPONENTIAL_GAIN_STEP_SIZE (0x009e | CCS_FL_16BIT) -#define CCS_R_DATA_FORMAT_MODEL_TYPE 0x00c0 +#define CCS_R_ANALOG_GAIN_CODE_MIN CCI_REG16(0x0084) +#define CCS_R_ANALOG_GAIN_CODE_MAX CCI_REG16(0x0086) +#define CCS_R_ANALOG_GAIN_CODE_STEP CCI_REG16(0x0088) +#define CCS_R_ANALOG_GAIN_TYPE CCI_REG16(0x008a) +#define CCS_R_ANALOG_GAIN_M0 CCI_REG16(0x008c) +#define CCS_R_ANALOG_GAIN_C0 CCI_REG16(0x008e) +#define CCS_R_ANALOG_GAIN_M1 CCI_REG16(0x0090) +#define CCS_R_ANALOG_GAIN_C1 CCI_REG16(0x0092) +#define CCS_R_ANALOG_LINEAR_GAIN_MIN CCI_REG16(0x0094) +#define CCS_R_ANALOG_LINEAR_GAIN_MAX CCI_REG16(0x0096) +#define CCS_R_ANALOG_LINEAR_GAIN_STEP_SIZE CCI_REG16(0x0098) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MIN CCI_REG16(0x009a) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MAX CCI_REG16(0x009c) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_STEP_SIZE CCI_REG16(0x009e) +#define CCS_R_DATA_FORMAT_MODEL_TYPE CCI_REG8(0x00c0) #define CCS_DATA_FORMAT_MODEL_TYPE_NORMAL 1U #define CCS_DATA_FORMAT_MODEL_TYPE_EXTENDED 2U -#define CCS_R_DATA_FORMAT_MODEL_SUBTYPE 0x00c1 +#define CCS_R_DATA_FORMAT_MODEL_SUBTYPE CCI_REG8(0x00c1) #define CCS_DATA_FORMAT_MODEL_SUBTYPE_ROWS_SHIFT 0U #define CCS_DATA_FORMAT_MODEL_SUBTYPE_ROWS_MASK 0xf #define CCS_DATA_FORMAT_MODEL_SUBTYPE_COLUMNS_SHIFT 4U #define CCS_DATA_FORMAT_MODEL_SUBTYPE_COLUMNS_MASK 0xf0 -#define CCS_R_DATA_FORMAT_DESCRIPTOR(n) ((0x00c2 | CCS_FL_16BIT) + (n) * 2) +#define CCS_R_DATA_FORMAT_DESCRIPTOR(n) CCI_REG16(0x00c2 + (n) * 2) #define CCS_LIM_DATA_FORMAT_DESCRIPTOR_MIN_N 0U #define CCS_LIM_DATA_FORMAT_DESCRIPTOR_MAX_N 15U #define CCS_DATA_FORMAT_DESCRIPTOR_COMPRESSED_SHIFT 0U #define CCS_DATA_FORMAT_DESCRIPTOR_COMPRESSED_MASK 0xff #define CCS_DATA_FORMAT_DESCRIPTOR_UNCOMPRESSED_SHIFT 8U #define CCS_DATA_FORMAT_DESCRIPTOR_UNCOMPRESSED_MASK 0xff00 -#define CCS_R_MODE_SELECT 0x0100 +#define CCS_R_MODE_SELECT CCI_REG8(0x0100) #define CCS_MODE_SELECT_SOFTWARE_STANDBY 0U #define CCS_MODE_SELECT_STREAMING 1U -#define CCS_R_IMAGE_ORIENTATION 0x0101 +#define CCS_R_IMAGE_ORIENTATION CCI_REG8(0x0101) #define CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR BIT(0) #define CCS_IMAGE_ORIENTATION_VERTICAL_FLIP BIT(1) -#define CCS_R_SOFTWARE_RESET 0x0103 +#define CCS_R_SOFTWARE_RESET CCI_REG8(0x0103) #define CCS_SOFTWARE_RESET_OFF 0U #define CCS_SOFTWARE_RESET_ON 1U -#define CCS_R_GROUPED_PARAMETER_HOLD 0x0104 -#define CCS_R_MASK_CORRUPTED_FRAMES 0x0105 +#define CCS_R_GROUPED_PARAMETER_HOLD CCI_REG8(0x0104) +#define CCS_R_MASK_CORRUPTED_FRAMES CCI_REG8(0x0105) #define CCS_MASK_CORRUPTED_FRAMES_ALLOW 0U #define CCS_MASK_CORRUPTED_FRAMES_MASK 1U -#define CCS_R_FAST_STANDBY_CTRL 0x0106 +#define CCS_R_FAST_STANDBY_CTRL CCI_REG8(0x0106) #define CCS_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0U #define CCS_FAST_STANDBY_CTRL_FRAME_TRUNCATION 1U -#define CCS_R_CCI_ADDRESS_CTRL 0x0107 -#define CCS_R_2ND_CCI_IF_CTRL 0x0108 +#define CCS_R_CCI_ADDRESS_CTRL CCI_REG8(0x0107) +#define CCS_R_2ND_CCI_IF_CTRL CCI_REG8(0x0108) #define CCS_2ND_CCI_IF_CTRL_ENABLE BIT(0) #define CCS_2ND_CCI_IF_CTRL_ACK BIT(1) -#define CCS_R_2ND_CCI_ADDRESS_CTRL 0x0109 -#define CCS_R_CSI_CHANNEL_IDENTIFIER 0x0110 -#define CCS_R_CSI_SIGNALING_MODE 0x0111 +#define CCS_R_2ND_CCI_ADDRESS_CTRL CCI_REG8(0x0109) +#define CCS_R_CSI_CHANNEL_IDENTIFIER CCI_REG8(0x0110) +#define CCS_R_CSI_SIGNALING_MODE CCI_REG8(0x0111) #define CCS_CSI_SIGNALING_MODE_CSI_2_DPHY 2U #define CCS_CSI_SIGNALING_MODE_CSI_2_CPHY 3U -#define CCS_R_CSI_DATA_FORMAT (0x0112 | CCS_FL_16BIT) -#define CCS_R_CSI_LANE_MODE 0x0114 -#define CCS_R_DPCM_FRAME_DT 0x011d -#define CCS_R_BOTTOM_EMBEDDED_DATA_DT 0x011e -#define CCS_R_BOTTOM_EMBEDDED_DATA_VC 0x011f -#define CCS_R_GAIN_MODE 0x0120 +#define CCS_R_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define CCS_R_CSI_LANE_MODE CCI_REG8(0x0114) +#define CCS_R_DPCM_FRAME_DT CCI_REG8(0x011d) +#define CCS_R_BOTTOM_EMBEDDED_DATA_DT CCI_REG8(0x011e) +#define CCS_R_BOTTOM_EMBEDDED_DATA_VC CCI_REG8(0x011f) +#define CCS_R_GAIN_MODE CCI_REG8(0x0120) #define CCS_GAIN_MODE_GLOBAL 0U #define CCS_GAIN_MODE_ALTERNATE 1U -#define CCS_R_ADC_BIT_DEPTH 0x0121 -#define CCS_R_EMB_DATA_CTRL 0x0122 +#define CCS_R_ADC_BIT_DEPTH CCI_REG8(0x0121) +#define CCS_R_EMB_DATA_CTRL CCI_REG8(0x0122) #define CCS_EMB_DATA_CTRL_RAW8_PACKING_FOR_RAW16 BIT(0) #define CCS_EMB_DATA_CTRL_RAW10_PACKING_FOR_RAW20 BIT(1) #define CCS_EMB_DATA_CTRL_RAW12_PACKING_FOR_RAW24 BIT(2) -#define CCS_R_GPIO_TRIG_MODE 0x0130 -#define CCS_R_EXTCLK_FREQUENCY_MHZ (0x0136 | (CCS_FL_16BIT | CCS_FL_IREAL)) -#define CCS_R_TEMP_SENSOR_CTRL 0x0138 +#define CCS_R_GPIO_TRIG_MODE CCI_REG8(0x0130) +#define CCS_R_EXTCLK_FREQUENCY_MHZ (CCI_REG16(0x0136) | CCS_FL_IREAL) +#define CCS_R_TEMP_SENSOR_CTRL CCI_REG8(0x0138) #define CCS_TEMP_SENSOR_CTRL_ENABLE BIT(0) -#define CCS_R_TEMP_SENSOR_MODE 0x0139 -#define CCS_R_TEMP_SENSOR_OUTPUT 0x013a -#define CCS_R_FINE_INTEGRATION_TIME (0x0200 | CCS_FL_16BIT) -#define CCS_R_COARSE_INTEGRATION_TIME (0x0202 | CCS_FL_16BIT) -#define CCS_R_ANALOG_GAIN_CODE_GLOBAL (0x0204 | CCS_FL_16BIT) -#define CCS_R_ANALOG_LINEAR_GAIN_GLOBAL (0x0206 | CCS_FL_16BIT) -#define CCS_R_ANALOG_EXPONENTIAL_GAIN_GLOBAL (0x0208 | CCS_FL_16BIT) -#define CCS_R_DIGITAL_GAIN_GLOBAL (0x020e | CCS_FL_16BIT) -#define CCS_R_SHORT_ANALOG_GAIN_GLOBAL (0x0216 | CCS_FL_16BIT) -#define CCS_R_SHORT_DIGITAL_GAIN_GLOBAL (0x0218 | CCS_FL_16BIT) -#define CCS_R_HDR_MODE 0x0220 +#define CCS_R_TEMP_SENSOR_MODE CCI_REG8(0x0139) +#define CCS_R_TEMP_SENSOR_OUTPUT CCI_REG8(0x013a) +#define CCS_R_FINE_INTEGRATION_TIME CCI_REG16(0x0200) +#define CCS_R_COARSE_INTEGRATION_TIME CCI_REG16(0x0202) +#define CCS_R_ANALOG_GAIN_CODE_GLOBAL CCI_REG16(0x0204) +#define CCS_R_ANALOG_LINEAR_GAIN_GLOBAL CCI_REG16(0x0206) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_GLOBAL CCI_REG16(0x0208) +#define CCS_R_DIGITAL_GAIN_GLOBAL CCI_REG16(0x020e) +#define CCS_R_SHORT_ANALOG_GAIN_GLOBAL CCI_REG16(0x0216) +#define CCS_R_SHORT_DIGITAL_GAIN_GLOBAL CCI_REG16(0x0218) +#define CCS_R_HDR_MODE CCI_REG8(0x0220) #define CCS_HDR_MODE_ENABLED BIT(0) #define CCS_HDR_MODE_SEPARATE_ANALOG_GAIN BIT(1) #define CCS_HDR_MODE_UPSCALING BIT(2) @@ -189,421 +189,421 @@ #define CCS_HDR_MODE_TIMING_MODE BIT(4) #define CCS_HDR_MODE_EXPOSURE_CTRL_DIRECT BIT(5) #define CCS_HDR_MODE_SEPARATE_DIGITAL_GAIN BIT(6) -#define CCS_R_HDR_RESOLUTION_REDUCTION 0x0221 +#define CCS_R_HDR_RESOLUTION_REDUCTION CCI_REG8(0x0221) #define CCS_HDR_RESOLUTION_REDUCTION_ROW_SHIFT 0U #define CCS_HDR_RESOLUTION_REDUCTION_ROW_MASK 0xf #define CCS_HDR_RESOLUTION_REDUCTION_COLUMN_SHIFT 4U #define CCS_HDR_RESOLUTION_REDUCTION_COLUMN_MASK 0xf0 -#define CCS_R_EXPOSURE_RATIO 0x0222 -#define CCS_R_HDR_INTERNAL_BIT_DEPTH 0x0223 -#define CCS_R_DIRECT_SHORT_INTEGRATION_TIME (0x0224 | CCS_FL_16BIT) -#define CCS_R_SHORT_ANALOG_LINEAR_GAIN_GLOBAL (0x0226 | CCS_FL_16BIT) -#define CCS_R_SHORT_ANALOG_EXPONENTIAL_GAIN_GLOBAL (0x0228 | CCS_FL_16BIT) -#define CCS_R_VT_PIX_CLK_DIV (0x0300 | CCS_FL_16BIT) -#define CCS_R_VT_SYS_CLK_DIV (0x0302 | CCS_FL_16BIT) -#define CCS_R_PRE_PLL_CLK_DIV (0x0304 | CCS_FL_16BIT) -#define CCS_R_PLL_MULTIPLIER (0x0306 | CCS_FL_16BIT) -#define CCS_R_OP_PIX_CLK_DIV (0x0308 | CCS_FL_16BIT) -#define CCS_R_OP_SYS_CLK_DIV (0x030a | CCS_FL_16BIT) -#define CCS_R_OP_PRE_PLL_CLK_DIV (0x030c | CCS_FL_16BIT) -#define CCS_R_OP_PLL_MULTIPLIER (0x030e | CCS_FL_16BIT) -#define CCS_R_PLL_MODE 0x0310 +#define CCS_R_EXPOSURE_RATIO CCI_REG8(0x0222) +#define CCS_R_HDR_INTERNAL_BIT_DEPTH CCI_REG8(0x0223) +#define CCS_R_DIRECT_SHORT_INTEGRATION_TIME CCI_REG16(0x0224) +#define CCS_R_SHORT_ANALOG_LINEAR_GAIN_GLOBAL CCI_REG16(0x0226) +#define CCS_R_SHORT_ANALOG_EXPONENTIAL_GAIN_GLOBAL CCI_REG16(0x0228) +#define CCS_R_VT_PIX_CLK_DIV CCI_REG16(0x0300) +#define CCS_R_VT_SYS_CLK_DIV CCI_REG16(0x0302) +#define CCS_R_PRE_PLL_CLK_DIV CCI_REG16(0x0304) +#define CCS_R_PLL_MULTIPLIER CCI_REG16(0x0306) +#define CCS_R_OP_PIX_CLK_DIV CCI_REG16(0x0308) +#define CCS_R_OP_SYS_CLK_DIV CCI_REG16(0x030a) +#define CCS_R_OP_PRE_PLL_CLK_DIV CCI_REG16(0x030c) +#define CCS_R_OP_PLL_MULTIPLIER CCI_REG16(0x030e) +#define CCS_R_PLL_MODE CCI_REG8(0x0310) #define CCS_PLL_MODE_SHIFT 0U #define CCS_PLL_MODE_MASK 0x1 #define CCS_PLL_MODE_SINGLE 0U #define CCS_PLL_MODE_DUAL 1U -#define CCS_R_OP_PIX_CLK_DIV_REV (0x0312 | CCS_FL_16BIT) -#define CCS_R_OP_SYS_CLK_DIV_REV (0x0314 | CCS_FL_16BIT) -#define CCS_R_FRAME_LENGTH_LINES (0x0340 | CCS_FL_16BIT) -#define CCS_R_LINE_LENGTH_PCK (0x0342 | CCS_FL_16BIT) -#define CCS_R_X_ADDR_START (0x0344 | CCS_FL_16BIT) -#define CCS_R_Y_ADDR_START (0x0346 | CCS_FL_16BIT) -#define CCS_R_X_ADDR_END (0x0348 | CCS_FL_16BIT) -#define CCS_R_Y_ADDR_END (0x034a | CCS_FL_16BIT) -#define CCS_R_X_OUTPUT_SIZE (0x034c | CCS_FL_16BIT) -#define CCS_R_Y_OUTPUT_SIZE (0x034e | CCS_FL_16BIT) -#define CCS_R_FRAME_LENGTH_CTRL 0x0350 +#define CCS_R_OP_PIX_CLK_DIV_REV CCI_REG16(0x0312) +#define CCS_R_OP_SYS_CLK_DIV_REV CCI_REG16(0x0314) +#define CCS_R_FRAME_LENGTH_LINES CCI_REG16(0x0340) +#define CCS_R_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define CCS_R_X_ADDR_START CCI_REG16(0x0344) +#define CCS_R_Y_ADDR_START CCI_REG16(0x0346) +#define CCS_R_X_ADDR_END CCI_REG16(0x0348) +#define CCS_R_Y_ADDR_END CCI_REG16(0x034a) +#define CCS_R_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define CCS_R_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define CCS_R_FRAME_LENGTH_CTRL CCI_REG8(0x0350) #define CCS_FRAME_LENGTH_CTRL_AUTOMATIC BIT(0) -#define CCS_R_TIMING_MODE_CTRL 0x0352 +#define CCS_R_TIMING_MODE_CTRL CCI_REG8(0x0352) #define CCS_TIMING_MODE_CTRL_MANUAL_READOUT BIT(0) #define CCS_TIMING_MODE_CTRL_DELAYED_EXPOSURE BIT(1) -#define CCS_R_START_READOUT_RS 0x0353 +#define CCS_R_START_READOUT_RS CCI_REG8(0x0353) #define CCS_START_READOUT_RS_MANUAL_READOUT_START BIT(0) -#define CCS_R_FRAME_MARGIN (0x0354 | CCS_FL_16BIT) -#define CCS_R_X_EVEN_INC (0x0380 | CCS_FL_16BIT) -#define CCS_R_X_ODD_INC (0x0382 | CCS_FL_16BIT) -#define CCS_R_Y_EVEN_INC (0x0384 | CCS_FL_16BIT) -#define CCS_R_Y_ODD_INC (0x0386 | CCS_FL_16BIT) -#define CCS_R_MONOCHROME_EN 0x0390 +#define CCS_R_FRAME_MARGIN CCI_REG16(0x0354) +#define CCS_R_X_EVEN_INC CCI_REG16(0x0380) +#define CCS_R_X_ODD_INC CCI_REG16(0x0382) +#define CCS_R_Y_EVEN_INC CCI_REG16(0x0384) +#define CCS_R_Y_ODD_INC CCI_REG16(0x0386) +#define CCS_R_MONOCHROME_EN CCI_REG8(0x0390) #define CCS_MONOCHROME_EN_ENABLED 0U -#define CCS_R_SCALING_MODE (0x0400 | CCS_FL_16BIT) +#define CCS_R_SCALING_MODE CCI_REG16(0x0400) #define CCS_SCALING_MODE_NO_SCALING 0U #define CCS_SCALING_MODE_HORIZONTAL 1U -#define CCS_R_SCALE_M (0x0404 | CCS_FL_16BIT) -#define CCS_R_SCALE_N (0x0406 | CCS_FL_16BIT) -#define CCS_R_DIGITAL_CROP_X_OFFSET (0x0408 | CCS_FL_16BIT) -#define CCS_R_DIGITAL_CROP_Y_OFFSET (0x040a | CCS_FL_16BIT) -#define CCS_R_DIGITAL_CROP_IMAGE_WIDTH (0x040c | CCS_FL_16BIT) -#define CCS_R_DIGITAL_CROP_IMAGE_HEIGHT (0x040e | CCS_FL_16BIT) -#define CCS_R_COMPRESSION_MODE (0x0500 | CCS_FL_16BIT) +#define CCS_R_SCALE_M CCI_REG16(0x0404) +#define CCS_R_SCALE_N CCI_REG16(0x0406) +#define CCS_R_DIGITAL_CROP_X_OFFSET CCI_REG16(0x0408) +#define CCS_R_DIGITAL_CROP_Y_OFFSET CCI_REG16(0x040a) +#define CCS_R_DIGITAL_CROP_IMAGE_WIDTH CCI_REG16(0x040c) +#define CCS_R_DIGITAL_CROP_IMAGE_HEIGHT CCI_REG16(0x040e) +#define CCS_R_COMPRESSION_MODE CCI_REG16(0x0500) #define CCS_COMPRESSION_MODE_NONE 0U #define CCS_COMPRESSION_MODE_DPCM_PCM_SIMPLE 1U -#define CCS_R_TEST_PATTERN_MODE (0x0600 | CCS_FL_16BIT) +#define CCS_R_TEST_PATTERN_MODE CCI_REG16(0x0600) #define CCS_TEST_PATTERN_MODE_NONE 0U #define CCS_TEST_PATTERN_MODE_SOLID_COLOR 1U #define CCS_TEST_PATTERN_MODE_COLOR_BARS 2U #define CCS_TEST_PATTERN_MODE_FADE_TO_GREY 3U #define CCS_TEST_PATTERN_MODE_PN9 4U #define CCS_TEST_PATTERN_MODE_COLOR_TILE 5U -#define CCS_R_TEST_DATA_RED (0x0602 | CCS_FL_16BIT) -#define CCS_R_TEST_DATA_GREENR (0x0604 | CCS_FL_16BIT) -#define CCS_R_TEST_DATA_BLUE (0x0606 | CCS_FL_16BIT) -#define CCS_R_TEST_DATA_GREENB (0x0608 | CCS_FL_16BIT) -#define CCS_R_VALUE_STEP_SIZE_SMOOTH 0x060a -#define CCS_R_VALUE_STEP_SIZE_QUANTISED 0x060b -#define CCS_R_TCLK_POST 0x0800 -#define CCS_R_THS_PREPARE 0x0801 -#define CCS_R_THS_ZERO_MIN 0x0802 -#define CCS_R_THS_TRAIL 0x0803 -#define CCS_R_TCLK_TRAIL_MIN 0x0804 -#define CCS_R_TCLK_PREPARE 0x0805 -#define CCS_R_TCLK_ZERO 0x0806 -#define CCS_R_TLPX 0x0807 -#define CCS_R_PHY_CTRL 0x0808 +#define CCS_R_TEST_DATA_RED CCI_REG16(0x0602) +#define CCS_R_TEST_DATA_GREENR CCI_REG16(0x0604) +#define CCS_R_TEST_DATA_BLUE CCI_REG16(0x0606) +#define CCS_R_TEST_DATA_GREENB CCI_REG16(0x0608) +#define CCS_R_VALUE_STEP_SIZE_SMOOTH CCI_REG8(0x060a) +#define CCS_R_VALUE_STEP_SIZE_QUANTISED CCI_REG8(0x060b) +#define CCS_R_TCLK_POST CCI_REG8(0x0800) +#define CCS_R_THS_PREPARE CCI_REG8(0x0801) +#define CCS_R_THS_ZERO_MIN CCI_REG8(0x0802) +#define CCS_R_THS_TRAIL CCI_REG8(0x0803) +#define CCS_R_TCLK_TRAIL_MIN CCI_REG8(0x0804) +#define CCS_R_TCLK_PREPARE CCI_REG8(0x0805) +#define CCS_R_TCLK_ZERO CCI_REG8(0x0806) +#define CCS_R_TLPX CCI_REG8(0x0807) +#define CCS_R_PHY_CTRL CCI_REG8(0x0808) #define CCS_PHY_CTRL_AUTO 0U #define CCS_PHY_CTRL_UI 1U #define CCS_PHY_CTRL_MANUAL 2U -#define CCS_R_TCLK_POST_EX (0x080a | CCS_FL_16BIT) -#define CCS_R_THS_PREPARE_EX (0x080c | CCS_FL_16BIT) -#define CCS_R_THS_ZERO_MIN_EX (0x080e | CCS_FL_16BIT) -#define CCS_R_THS_TRAIL_EX (0x0810 | CCS_FL_16BIT) -#define CCS_R_TCLK_TRAIL_MIN_EX (0x0812 | CCS_FL_16BIT) -#define CCS_R_TCLK_PREPARE_EX (0x0814 | CCS_FL_16BIT) -#define CCS_R_TCLK_ZERO_EX (0x0816 | CCS_FL_16BIT) -#define CCS_R_TLPX_EX (0x0818 | CCS_FL_16BIT) -#define CCS_R_REQUESTED_LINK_RATE (0x0820 | CCS_FL_32BIT) -#define CCS_R_DPHY_EQUALIZATION_MODE 0x0824 +#define CCS_R_TCLK_POST_EX CCI_REG16(0x080a) +#define CCS_R_THS_PREPARE_EX CCI_REG16(0x080c) +#define CCS_R_THS_ZERO_MIN_EX CCI_REG16(0x080e) +#define CCS_R_THS_TRAIL_EX CCI_REG16(0x0810) +#define CCS_R_TCLK_TRAIL_MIN_EX CCI_REG16(0x0812) +#define CCS_R_TCLK_PREPARE_EX CCI_REG16(0x0814) +#define CCS_R_TCLK_ZERO_EX CCI_REG16(0x0816) +#define CCS_R_TLPX_EX CCI_REG16(0x0818) +#define CCS_R_REQUESTED_LINK_RATE CCI_REG32(0x0820) +#define CCS_R_DPHY_EQUALIZATION_MODE CCI_REG8(0x0824) #define CCS_DPHY_EQUALIZATION_MODE_EQ2 BIT(0) -#define CCS_R_PHY_EQUALIZATION_CTRL 0x0825 +#define CCS_R_PHY_EQUALIZATION_CTRL CCI_REG8(0x0825) #define CCS_PHY_EQUALIZATION_CTRL_ENABLE BIT(0) -#define CCS_R_DPHY_PREAMBLE_CTRL 0x0826 +#define CCS_R_DPHY_PREAMBLE_CTRL CCI_REG8(0x0826) #define CCS_DPHY_PREAMBLE_CTRL_ENABLE BIT(0) -#define CCS_R_DPHY_PREAMBLE_LENGTH 0x0826 -#define CCS_R_PHY_SSC_CTRL 0x0828 +#define CCS_R_DPHY_PREAMBLE_LENGTH CCI_REG8(0x0826) +#define CCS_R_PHY_SSC_CTRL CCI_REG8(0x0828) #define CCS_PHY_SSC_CTRL_ENABLE BIT(0) -#define CCS_R_MANUAL_LP_CTRL 0x0829 +#define CCS_R_MANUAL_LP_CTRL CCI_REG8(0x0829) #define CCS_MANUAL_LP_CTRL_ENABLE BIT(0) -#define CCS_R_TWAKEUP 0x082a -#define CCS_R_TINIT 0x082b -#define CCS_R_THS_EXIT 0x082c -#define CCS_R_THS_EXIT_EX (0x082e | CCS_FL_16BIT) -#define CCS_R_PHY_PERIODIC_CALIBRATION_CTRL 0x0830 +#define CCS_R_TWAKEUP CCI_REG8(0x082a) +#define CCS_R_TINIT CCI_REG8(0x082b) +#define CCS_R_THS_EXIT CCI_REG8(0x082c) +#define CCS_R_THS_EXIT_EX CCI_REG16(0x082e) +#define CCS_R_PHY_PERIODIC_CALIBRATION_CTRL CCI_REG8(0x0830) #define CCS_PHY_PERIODIC_CALIBRATION_CTRL_FRAME_BLANKING BIT(0) -#define CCS_R_PHY_PERIODIC_CALIBRATION_INTERVAL 0x0831 -#define CCS_R_PHY_INIT_CALIBRATION_CTRL 0x0832 +#define CCS_R_PHY_PERIODIC_CALIBRATION_INTERVAL CCI_REG8(0x0831) +#define CCS_R_PHY_INIT_CALIBRATION_CTRL CCI_REG8(0x0832) #define CCS_PHY_INIT_CALIBRATION_CTRL_STREAM_START BIT(0) -#define CCS_R_DPHY_CALIBRATION_MODE 0x0833 +#define CCS_R_DPHY_CALIBRATION_MODE CCI_REG8(0x0833) #define CCS_DPHY_CALIBRATION_MODE_ALSO_ALTERNATE BIT(0) -#define CCS_R_CPHY_CALIBRATION_MODE 0x0834 +#define CCS_R_CPHY_CALIBRATION_MODE CCI_REG8(0x0834) #define CCS_CPHY_CALIBRATION_MODE_FORMAT_1 0U #define CCS_CPHY_CALIBRATION_MODE_FORMAT_2 1U #define CCS_CPHY_CALIBRATION_MODE_FORMAT_3 2U -#define CCS_R_T3_CALPREAMBLE_LENGTH 0x0835 -#define CCS_R_T3_CALPREAMBLE_LENGTH_PER 0x0836 -#define CCS_R_T3_CALALTSEQ_LENGTH 0x0837 -#define CCS_R_T3_CALALTSEQ_LENGTH_PER 0x0838 -#define CCS_R_FM2_INIT_SEED (0x083a | CCS_FL_16BIT) -#define CCS_R_T3_CALUDEFSEQ_LENGTH (0x083c | CCS_FL_16BIT) -#define CCS_R_T3_CALUDEFSEQ_LENGTH_PER (0x083e | CCS_FL_16BIT) -#define CCS_R_TGR_PREAMBLE_LENGTH 0x0841 +#define CCS_R_T3_CALPREAMBLE_LENGTH CCI_REG8(0x0835) +#define CCS_R_T3_CALPREAMBLE_LENGTH_PER CCI_REG8(0x0836) +#define CCS_R_T3_CALALTSEQ_LENGTH CCI_REG8(0x0837) +#define CCS_R_T3_CALALTSEQ_LENGTH_PER CCI_REG8(0x0838) +#define CCS_R_FM2_INIT_SEED CCI_REG16(0x083a) +#define CCS_R_T3_CALUDEFSEQ_LENGTH CCI_REG16(0x083c) +#define CCS_R_T3_CALUDEFSEQ_LENGTH_PER CCI_REG16(0x083e) +#define CCS_R_TGR_PREAMBLE_LENGTH CCI_REG8(0x0841) #define CCS_TGR_PREAMBLE_LENGTH_PREAMABLE_PROG_SEQ BIT(7) #define CCS_TGR_PREAMBLE_LENGTH_BEGIN_PREAMBLE_LENGTH_SHIFT 0U #define CCS_TGR_PREAMBLE_LENGTH_BEGIN_PREAMBLE_LENGTH_MASK 0x3f -#define CCS_R_TGR_POST_LENGTH 0x0842 +#define CCS_R_TGR_POST_LENGTH CCI_REG8(0x0842) #define CCS_TGR_POST_LENGTH_POST_LENGTH_SHIFT 0U #define CCS_TGR_POST_LENGTH_POST_LENGTH_MASK 0x1f -#define CCS_R_TGR_PREAMBLE_PROG_SEQUENCE(n2) (0x0843 + (n2)) +#define CCS_R_TGR_PREAMBLE_PROG_SEQUENCE(n2) CCI_REG8(0x0843 + (n2)) #define CCS_LIM_TGR_PREAMBLE_PROG_SEQUENCE_MIN_N2 0U #define CCS_LIM_TGR_PREAMBLE_PROG_SEQUENCE_MAX_N2 6U #define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_1_SHIFT 3U #define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_1_MASK 0x38 #define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_SHIFT 0U #define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_MASK 0x7 -#define CCS_R_T3_PREPARE (0x084e | CCS_FL_16BIT) -#define CCS_R_T3_LPX (0x0850 | CCS_FL_16BIT) -#define CCS_R_ALPS_CTRL 0x085a +#define CCS_R_T3_PREPARE CCI_REG16(0x084e) +#define CCS_R_T3_LPX CCI_REG16(0x0850) +#define CCS_R_ALPS_CTRL CCI_REG8(0x085a) #define CCS_ALPS_CTRL_LVLP_DPHY BIT(0) #define CCS_ALPS_CTRL_LVLP_CPHY BIT(1) #define CCS_ALPS_CTRL_ALP_CPHY BIT(2) -#define CCS_R_TX_REG_CSI_EPD_EN_SSP_CPHY (0x0860 | CCS_FL_16BIT) -#define CCS_R_TX_REG_CSI_EPD_OP_SLP_CPHY (0x0862 | CCS_FL_16BIT) -#define CCS_R_TX_REG_CSI_EPD_EN_SSP_DPHY (0x0864 | CCS_FL_16BIT) -#define CCS_R_TX_REG_CSI_EPD_OP_SLP_DPHY (0x0866 | CCS_FL_16BIT) -#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_CPHY 0x0868 -#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_DPHY 0x0869 -#define CCS_R_SCRAMBLING_CTRL 0x0870 +#define CCS_R_TX_REG_CSI_EPD_EN_SSP_CPHY CCI_REG16(0x0860) +#define CCS_R_TX_REG_CSI_EPD_OP_SLP_CPHY CCI_REG16(0x0862) +#define CCS_R_TX_REG_CSI_EPD_EN_SSP_DPHY CCI_REG16(0x0864) +#define CCS_R_TX_REG_CSI_EPD_OP_SLP_DPHY CCI_REG16(0x0866) +#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_CPHY CCI_REG8(0x0868) +#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_DPHY CCI_REG8(0x0869) +#define CCS_R_SCRAMBLING_CTRL CCI_REG8(0x0870) #define CCS_SCRAMBLING_CTRL_ENABLED BIT(0) #define CCS_SCRAMBLING_CTRL_SHIFT 2U #define CCS_SCRAMBLING_CTRL_MASK 0xc #define CCS_SCRAMBLING_CTRL_1_SEED_CPHY 0U #define CCS_SCRAMBLING_CTRL_4_SEED_CPHY 3U -#define CCS_R_LANE_SEED_VALUE(seed, lane) ((0x0872 | CCS_FL_16BIT) + (seed) * 16 + (lane) * 2) +#define CCS_R_LANE_SEED_VALUE(seed, lane) CCI_REG16(0x0872 + (seed) * 16 + (lane) * 2) #define CCS_LIM_LANE_SEED_VALUE_MIN_SEED 0U #define CCS_LIM_LANE_SEED_VALUE_MAX_SEED 3U #define CCS_LIM_LANE_SEED_VALUE_MIN_LANE 0U #define CCS_LIM_LANE_SEED_VALUE_MAX_LANE 7U -#define CCS_R_TX_USL_REV_ENTRY (0x08c0 | CCS_FL_16BIT) -#define CCS_R_TX_USL_REV_CLOCK_COUNTER (0x08c2 | CCS_FL_16BIT) -#define CCS_R_TX_USL_REV_LP_COUNTER (0x08c4 | CCS_FL_16BIT) -#define CCS_R_TX_USL_REV_FRAME_COUNTER (0x08c6 | CCS_FL_16BIT) -#define CCS_R_TX_USL_REV_CHRONOLOGICAL_TIMER (0x08c8 | CCS_FL_16BIT) -#define CCS_R_TX_USL_FWD_ENTRY (0x08ca | CCS_FL_16BIT) -#define CCS_R_TX_USL_GPIO (0x08cc | CCS_FL_16BIT) -#define CCS_R_TX_USL_OPERATION (0x08ce | CCS_FL_16BIT) +#define CCS_R_TX_USL_REV_ENTRY CCI_REG16(0x08c0) +#define CCS_R_TX_USL_REV_CLOCK_COUNTER CCI_REG16(0x08c2) +#define CCS_R_TX_USL_REV_LP_COUNTER CCI_REG16(0x08c4) +#define CCS_R_TX_USL_REV_FRAME_COUNTER CCI_REG16(0x08c6) +#define CCS_R_TX_USL_REV_CHRONOLOGICAL_TIMER CCI_REG16(0x08c8) +#define CCS_R_TX_USL_FWD_ENTRY CCI_REG16(0x08ca) +#define CCS_R_TX_USL_GPIO CCI_REG16(0x08cc) +#define CCS_R_TX_USL_OPERATION CCI_REG16(0x08ce) #define CCS_TX_USL_OPERATION_RESET BIT(0) -#define CCS_R_TX_USL_ALP_CTRL (0x08d0 | CCS_FL_16BIT) +#define CCS_R_TX_USL_ALP_CTRL CCI_REG16(0x08d0) #define CCS_TX_USL_ALP_CTRL_CLOCK_PAUSE BIT(0) -#define CCS_R_TX_USL_APP_BTA_ACK_TIMEOUT (0x08d2 | CCS_FL_16BIT) -#define CCS_R_TX_USL_SNS_BTA_ACK_TIMEOUT (0x08d2 | CCS_FL_16BIT) -#define CCS_R_USL_CLOCK_MODE_D_CTRL 0x08d2 +#define CCS_R_TX_USL_APP_BTA_ACK_TIMEOUT CCI_REG16(0x08d2) +#define CCS_R_TX_USL_SNS_BTA_ACK_TIMEOUT CCI_REG16(0x08d2) +#define CCS_R_USL_CLOCK_MODE_D_CTRL CCI_REG8(0x08d2) #define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_STANDBY BIT(0) #define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_VBLANK BIT(1) #define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_HBLANK BIT(2) -#define CCS_R_BINNING_MODE 0x0900 -#define CCS_R_BINNING_TYPE 0x0901 -#define CCS_R_BINNING_WEIGHTING 0x0902 -#define CCS_R_DATA_TRANSFER_IF_1_CTRL 0x0a00 +#define CCS_R_BINNING_MODE CCI_REG8(0x0900) +#define CCS_R_BINNING_TYPE CCI_REG8(0x0901) +#define CCS_R_BINNING_WEIGHTING CCI_REG8(0x0902) +#define CCS_R_DATA_TRANSFER_IF_1_CTRL CCI_REG8(0x0a00) #define CCS_DATA_TRANSFER_IF_1_CTRL_ENABLE BIT(0) #define CCS_DATA_TRANSFER_IF_1_CTRL_WRITE BIT(1) #define CCS_DATA_TRANSFER_IF_1_CTRL_CLEAR_ERROR BIT(2) -#define CCS_R_DATA_TRANSFER_IF_1_STATUS 0x0a01 +#define CCS_R_DATA_TRANSFER_IF_1_STATUS CCI_REG8(0x0a01) #define CCS_DATA_TRANSFER_IF_1_STATUS_READ_IF_READY BIT(0) #define CCS_DATA_TRANSFER_IF_1_STATUS_WRITE_IF_READY BIT(1) #define CCS_DATA_TRANSFER_IF_1_STATUS_DATA_CORRUPTED BIT(2) #define CCS_DATA_TRANSFER_IF_1_STATUS_IMPROPER_IF_USAGE BIT(3) -#define CCS_R_DATA_TRANSFER_IF_1_PAGE_SELECT 0x0a02 -#define CCS_R_DATA_TRANSFER_IF_1_DATA(p) (0x0a04 + (p)) +#define CCS_R_DATA_TRANSFER_IF_1_PAGE_SELECT CCI_REG8(0x0a02) +#define CCS_R_DATA_TRANSFER_IF_1_DATA(p) CCI_REG8(0x0a04 + (p)) #define CCS_LIM_DATA_TRANSFER_IF_1_DATA_MIN_P 0U #define CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P 63U -#define CCS_R_SHADING_CORRECTION_EN 0x0b00 +#define CCS_R_SHADING_CORRECTION_EN CCI_REG8(0x0b00) #define CCS_SHADING_CORRECTION_EN_ENABLE BIT(0) -#define CCS_R_LUMINANCE_CORRECTION_LEVEL 0x0b01 -#define CCS_R_GREEN_IMBALANCE_FILTER_EN 0x0b02 +#define CCS_R_LUMINANCE_CORRECTION_LEVEL CCI_REG8(0x0b01) +#define CCS_R_GREEN_IMBALANCE_FILTER_EN CCI_REG8(0x0b02) #define CCS_GREEN_IMBALANCE_FILTER_EN_ENABLE BIT(0) -#define CCS_R_MAPPED_DEFECT_CORRECT_EN 0x0b05 +#define CCS_R_MAPPED_DEFECT_CORRECT_EN CCI_REG8(0x0b05) #define CCS_MAPPED_DEFECT_CORRECT_EN_ENABLE BIT(0) -#define CCS_R_SINGLE_DEFECT_CORRECT_EN 0x0b06 +#define CCS_R_SINGLE_DEFECT_CORRECT_EN CCI_REG8(0x0b06) #define CCS_SINGLE_DEFECT_CORRECT_EN_ENABLE BIT(0) -#define CCS_R_DYNAMIC_COUPLET_CORRECT_EN 0x0b08 +#define CCS_R_DYNAMIC_COUPLET_CORRECT_EN CCI_REG8(0x0b08) #define CCS_DYNAMIC_COUPLET_CORRECT_EN_ENABLE BIT(0) -#define CCS_R_COMBINED_DEFECT_CORRECT_EN 0x0b0a +#define CCS_R_COMBINED_DEFECT_CORRECT_EN CCI_REG8(0x0b0a) #define CCS_COMBINED_DEFECT_CORRECT_EN_ENABLE BIT(0) -#define CCS_R_MODULE_SPECIFIC_CORRECTION_EN 0x0b0c +#define CCS_R_MODULE_SPECIFIC_CORRECTION_EN CCI_REG8(0x0b0c) #define CCS_MODULE_SPECIFIC_CORRECTION_EN_ENABLE BIT(0) -#define CCS_R_DYNAMIC_TRIPLET_DEFECT_CORRECT_EN 0x0b13 +#define CCS_R_DYNAMIC_TRIPLET_DEFECT_CORRECT_EN CCI_REG8(0x0b13) #define CCS_DYNAMIC_TRIPLET_DEFECT_CORRECT_EN_ENABLE BIT(0) -#define CCS_R_NF_CTRL 0x0b15 +#define CCS_R_NF_CTRL CCI_REG8(0x0b15) #define CCS_NF_CTRL_LUMA BIT(0) #define CCS_NF_CTRL_CHROMA BIT(1) #define CCS_NF_CTRL_COMBINED BIT(2) -#define CCS_R_OB_READOUT_CONTROL 0x0b30 +#define CCS_R_OB_READOUT_CONTROL CCI_REG8(0x0b30) #define CCS_OB_READOUT_CONTROL_ENABLE BIT(0) #define CCS_OB_READOUT_CONTROL_INTERLEAVING BIT(1) -#define CCS_R_OB_VIRTUAL_CHANNEL 0x0b31 -#define CCS_R_OB_DT 0x0b32 -#define CCS_R_OB_DATA_FORMAT 0x0b33 -#define CCS_R_COLOR_TEMPERATURE (0x0b8c | CCS_FL_16BIT) -#define CCS_R_ABSOLUTE_GAIN_GREENR (0x0b8e | CCS_FL_16BIT) -#define CCS_R_ABSOLUTE_GAIN_RED (0x0b90 | CCS_FL_16BIT) -#define CCS_R_ABSOLUTE_GAIN_BLUE (0x0b92 | CCS_FL_16BIT) -#define CCS_R_ABSOLUTE_GAIN_GREENB (0x0b94 | CCS_FL_16BIT) -#define CCS_R_CFA_CONVERSION_CTRL 0x0ba0 +#define CCS_R_OB_VIRTUAL_CHANNEL CCI_REG8(0x0b31) +#define CCS_R_OB_DT CCI_REG8(0x0b32) +#define CCS_R_OB_DATA_FORMAT CCI_REG8(0x0b33) +#define CCS_R_COLOR_TEMPERATURE CCI_REG16(0x0b8c) +#define CCS_R_ABSOLUTE_GAIN_GREENR CCI_REG16(0x0b8e) +#define CCS_R_ABSOLUTE_GAIN_RED CCI_REG16(0x0b90) +#define CCS_R_ABSOLUTE_GAIN_BLUE CCI_REG16(0x0b92) +#define CCS_R_ABSOLUTE_GAIN_GREENB CCI_REG16(0x0b94) +#define CCS_R_CFA_CONVERSION_CTRL CCI_REG8(0x0ba0) #define CCS_CFA_CONVERSION_CTRL_BAYER_CONVERSION_ENABLE BIT(0) -#define CCS_R_FLASH_STROBE_ADJUSTMENT 0x0c12 -#define CCS_R_FLASH_STROBE_START_POINT (0x0c14 | CCS_FL_16BIT) -#define CCS_R_TFLASH_STROBE_DELAY_RS_CTRL (0x0c16 | CCS_FL_16BIT) -#define CCS_R_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL (0x0c18 | CCS_FL_16BIT) -#define CCS_R_FLASH_MODE_RS 0x0c1a +#define CCS_R_FLASH_STROBE_ADJUSTMENT CCI_REG8(0x0c12) +#define CCS_R_FLASH_STROBE_START_POINT CCI_REG16(0x0c14) +#define CCS_R_TFLASH_STROBE_DELAY_RS_CTRL CCI_REG16(0x0c16) +#define CCS_R_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL CCI_REG16(0x0c18) +#define CCS_R_FLASH_MODE_RS CCI_REG8(0x0c1a) #define CCS_FLASH_MODE_RS_CONTINUOUS BIT(0) #define CCS_FLASH_MODE_RS_TRUNCATE BIT(1) #define CCS_FLASH_MODE_RS_ASYNC BIT(3) -#define CCS_R_FLASH_TRIGGER_RS 0x0c1b -#define CCS_R_FLASH_STATUS 0x0c1c +#define CCS_R_FLASH_TRIGGER_RS CCI_REG8(0x0c1b) +#define CCS_R_FLASH_STATUS CCI_REG8(0x0c1c) #define CCS_FLASH_STATUS_RETIMED BIT(0) -#define CCS_R_SA_STROBE_MODE 0x0c1d +#define CCS_R_SA_STROBE_MODE CCI_REG8(0x0c1d) #define CCS_SA_STROBE_MODE_CONTINUOUS BIT(0) #define CCS_SA_STROBE_MODE_TRUNCATE BIT(1) #define CCS_SA_STROBE_MODE_ASYNC BIT(3) #define CCS_SA_STROBE_MODE_ADJUST_EDGE BIT(4) -#define CCS_R_SA_STROBE_START_POINT (0x0c1e | CCS_FL_16BIT) -#define CCS_R_TSA_STROBE_DELAY_CTRL (0x0c20 | CCS_FL_16BIT) -#define CCS_R_TSA_STROBE_WIDTH_CTRL (0x0c22 | CCS_FL_16BIT) -#define CCS_R_SA_STROBE_TRIGGER 0x0c24 -#define CCS_R_SA_STROBE_STATUS 0x0c25 +#define CCS_R_SA_STROBE_START_POINT CCI_REG16(0x0c1e) +#define CCS_R_TSA_STROBE_DELAY_CTRL CCI_REG16(0x0c20) +#define CCS_R_TSA_STROBE_WIDTH_CTRL CCI_REG16(0x0c22) +#define CCS_R_SA_STROBE_TRIGGER CCI_REG8(0x0c24) +#define CCS_R_SA_STROBE_STATUS CCI_REG8(0x0c25) #define CCS_SA_STROBE_STATUS_RETIMED BIT(0) -#define CCS_R_TSA_STROBE_RE_DELAY_CTRL (0x0c30 | CCS_FL_16BIT) -#define CCS_R_TSA_STROBE_FE_DELAY_CTRL (0x0c32 | CCS_FL_16BIT) -#define CCS_R_PDAF_CTRL (0x0d00 | CCS_FL_16BIT) +#define CCS_R_TSA_STROBE_RE_DELAY_CTRL CCI_REG16(0x0c30) +#define CCS_R_TSA_STROBE_FE_DELAY_CTRL CCI_REG16(0x0c32) +#define CCS_R_PDAF_CTRL CCI_REG16(0x0d00) #define CCS_PDAF_CTRL_ENABLE BIT(0) #define CCS_PDAF_CTRL_PROCESSED BIT(1) #define CCS_PDAF_CTRL_INTERLEAVED BIT(2) #define CCS_PDAF_CTRL_VISIBLE_PDAF_CORRECTION BIT(3) -#define CCS_R_PDAF_VC 0x0d02 -#define CCS_R_PDAF_DT 0x0d03 -#define CCS_R_PD_X_ADDR_START (0x0d04 | CCS_FL_16BIT) -#define CCS_R_PD_Y_ADDR_START (0x0d06 | CCS_FL_16BIT) -#define CCS_R_PD_X_ADDR_END (0x0d08 | CCS_FL_16BIT) -#define CCS_R_PD_Y_ADDR_END (0x0d0a | CCS_FL_16BIT) -#define CCS_R_BRACKETING_LUT_CTRL 0x0e00 -#define CCS_R_BRACKETING_LUT_MODE 0x0e01 +#define CCS_R_PDAF_VC CCI_REG8(0x0d02) +#define CCS_R_PDAF_DT CCI_REG8(0x0d03) +#define CCS_R_PD_X_ADDR_START CCI_REG16(0x0d04) +#define CCS_R_PD_Y_ADDR_START CCI_REG16(0x0d06) +#define CCS_R_PD_X_ADDR_END CCI_REG16(0x0d08) +#define CCS_R_PD_Y_ADDR_END CCI_REG16(0x0d0a) +#define CCS_R_BRACKETING_LUT_CTRL CCI_REG8(0x0e00) +#define CCS_R_BRACKETING_LUT_MODE CCI_REG8(0x0e01) #define CCS_BRACKETING_LUT_MODE_CONTINUE_STREAMING BIT(0) #define CCS_BRACKETING_LUT_MODE_LOOP_MODE BIT(1) -#define CCS_R_BRACKETING_LUT_ENTRY_CTRL 0x0e02 -#define CCS_R_BRACKETING_LUT_FRAME(n) (0x0e10 + (n)) +#define CCS_R_BRACKETING_LUT_ENTRY_CTRL CCI_REG8(0x0e02) +#define CCS_R_BRACKETING_LUT_FRAME(n) CCI_REG8(0x0e10 + (n)) #define CCS_LIM_BRACKETING_LUT_FRAME_MIN_N 0U #define CCS_LIM_BRACKETING_LUT_FRAME_MAX_N 239U -#define CCS_R_INTEGRATION_TIME_CAPABILITY (0x1000 | CCS_FL_16BIT) +#define CCS_R_INTEGRATION_TIME_CAPABILITY CCI_REG16(0x1000) #define CCS_INTEGRATION_TIME_CAPABILITY_FINE BIT(0) -#define CCS_R_COARSE_INTEGRATION_TIME_MIN (0x1004 | CCS_FL_16BIT) -#define CCS_R_COARSE_INTEGRATION_TIME_MAX_MARGIN (0x1006 | CCS_FL_16BIT) -#define CCS_R_FINE_INTEGRATION_TIME_MIN (0x1008 | CCS_FL_16BIT) -#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN (0x100a | CCS_FL_16BIT) -#define CCS_R_DIGITAL_GAIN_CAPABILITY 0x1081 +#define CCS_R_COARSE_INTEGRATION_TIME_MIN CCI_REG16(0x1004) +#define CCS_R_COARSE_INTEGRATION_TIME_MAX_MARGIN CCI_REG16(0x1006) +#define CCS_R_FINE_INTEGRATION_TIME_MIN CCI_REG16(0x1008) +#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN CCI_REG16(0x100a) +#define CCS_R_DIGITAL_GAIN_CAPABILITY CCI_REG8(0x1081) #define CCS_DIGITAL_GAIN_CAPABILITY_NONE 0U #define CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL 2U -#define CCS_R_DIGITAL_GAIN_MIN (0x1084 | CCS_FL_16BIT) -#define CCS_R_DIGITAL_GAIN_MAX (0x1086 | CCS_FL_16BIT) -#define CCS_R_DIGITAL_GAIN_STEP_SIZE (0x1088 | CCS_FL_16BIT) -#define CCS_R_PEDESTAL_CAPABILITY 0x10e0 -#define CCS_R_ADC_CAPABILITY 0x10f0 +#define CCS_R_DIGITAL_GAIN_MIN CCI_REG16(0x1084) +#define CCS_R_DIGITAL_GAIN_MAX CCI_REG16(0x1086) +#define CCS_R_DIGITAL_GAIN_STEP_SIZE CCI_REG16(0x1088) +#define CCS_R_PEDESTAL_CAPABILITY CCI_REG8(0x10e0) +#define CCS_R_ADC_CAPABILITY CCI_REG8(0x10f0) #define CCS_ADC_CAPABILITY_BIT_DEPTH_CTRL BIT(0) -#define CCS_R_ADC_BIT_DEPTH_CAPABILITY (0x10f4 | CCS_FL_32BIT) -#define CCS_R_MIN_EXT_CLK_FREQ_MHZ (0x1100 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_EXT_CLK_FREQ_MHZ (0x1104 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_PRE_PLL_CLK_DIV (0x1108 | CCS_FL_16BIT) -#define CCS_R_MAX_PRE_PLL_CLK_DIV (0x110a | CCS_FL_16BIT) -#define CCS_R_MIN_PLL_IP_CLK_FREQ_MHZ (0x110c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_PLL_IP_CLK_FREQ_MHZ (0x1110 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_PLL_MULTIPLIER (0x1114 | CCS_FL_16BIT) -#define CCS_R_MAX_PLL_MULTIPLIER (0x1116 | CCS_FL_16BIT) -#define CCS_R_MIN_PLL_OP_CLK_FREQ_MHZ (0x1118 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_PLL_OP_CLK_FREQ_MHZ (0x111c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_VT_SYS_CLK_DIV (0x1120 | CCS_FL_16BIT) -#define CCS_R_MAX_VT_SYS_CLK_DIV (0x1122 | CCS_FL_16BIT) -#define CCS_R_MIN_VT_SYS_CLK_FREQ_MHZ (0x1124 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_VT_SYS_CLK_FREQ_MHZ (0x1128 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_VT_PIX_CLK_FREQ_MHZ (0x112c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_VT_PIX_CLK_FREQ_MHZ (0x1130 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_VT_PIX_CLK_DIV (0x1134 | CCS_FL_16BIT) -#define CCS_R_MAX_VT_PIX_CLK_DIV (0x1136 | CCS_FL_16BIT) -#define CCS_R_CLOCK_CALCULATION 0x1138 +#define CCS_R_ADC_BIT_DEPTH_CAPABILITY CCI_REG32(0x10f4) +#define CCS_R_MIN_EXT_CLK_FREQ_MHZ (CCI_REG32(0x1100) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_EXT_CLK_FREQ_MHZ (CCI_REG32(0x1104) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_PRE_PLL_CLK_DIV CCI_REG16(0x1108) +#define CCS_R_MAX_PRE_PLL_CLK_DIV CCI_REG16(0x110a) +#define CCS_R_MIN_PLL_IP_CLK_FREQ_MHZ (CCI_REG32(0x110c) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_PLL_IP_CLK_FREQ_MHZ (CCI_REG32(0x1110) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_PLL_MULTIPLIER CCI_REG16(0x1114) +#define CCS_R_MAX_PLL_MULTIPLIER CCI_REG16(0x1116) +#define CCS_R_MIN_PLL_OP_CLK_FREQ_MHZ (CCI_REG32(0x1118) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_PLL_OP_CLK_FREQ_MHZ (CCI_REG32(0x111c) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_VT_SYS_CLK_DIV CCI_REG16(0x1120) +#define CCS_R_MAX_VT_SYS_CLK_DIV CCI_REG16(0x1122) +#define CCS_R_MIN_VT_SYS_CLK_FREQ_MHZ (CCI_REG32(0x1124) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_VT_SYS_CLK_FREQ_MHZ (CCI_REG32(0x1128) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_VT_PIX_CLK_FREQ_MHZ (CCI_REG32(0x112c) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_VT_PIX_CLK_FREQ_MHZ (CCI_REG32(0x1130) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_VT_PIX_CLK_DIV CCI_REG16(0x1134) +#define CCS_R_MAX_VT_PIX_CLK_DIV CCI_REG16(0x1136) +#define CCS_R_CLOCK_CALCULATION CCI_REG8(0x1138) #define CCS_CLOCK_CALCULATION_LANE_SPEED BIT(0) #define CCS_CLOCK_CALCULATION_LINK_DECOUPLED BIT(1) #define CCS_CLOCK_CALCULATION_DUAL_PLL_OP_SYS_DDR BIT(2) #define CCS_CLOCK_CALCULATION_DUAL_PLL_OP_PIX_DDR BIT(3) -#define CCS_R_NUM_OF_VT_LANES 0x1139 -#define CCS_R_NUM_OF_OP_LANES 0x113a -#define CCS_R_OP_BITS_PER_LANE 0x113b -#define CCS_R_MIN_FRAME_LENGTH_LINES (0x1140 | CCS_FL_16BIT) -#define CCS_R_MAX_FRAME_LENGTH_LINES (0x1142 | CCS_FL_16BIT) -#define CCS_R_MIN_LINE_LENGTH_PCK (0x1144 | CCS_FL_16BIT) -#define CCS_R_MAX_LINE_LENGTH_PCK (0x1146 | CCS_FL_16BIT) -#define CCS_R_MIN_LINE_BLANKING_PCK (0x1148 | CCS_FL_16BIT) -#define CCS_R_MIN_FRAME_BLANKING_LINES (0x114a | CCS_FL_16BIT) -#define CCS_R_MIN_LINE_LENGTH_PCK_STEP_SIZE 0x114c -#define CCS_R_TIMING_MODE_CAPABILITY 0x114d +#define CCS_R_NUM_OF_VT_LANES CCI_REG8(0x1139) +#define CCS_R_NUM_OF_OP_LANES CCI_REG8(0x113a) +#define CCS_R_OP_BITS_PER_LANE CCI_REG8(0x113b) +#define CCS_R_MIN_FRAME_LENGTH_LINES CCI_REG16(0x1140) +#define CCS_R_MAX_FRAME_LENGTH_LINES CCI_REG16(0x1142) +#define CCS_R_MIN_LINE_LENGTH_PCK CCI_REG16(0x1144) +#define CCS_R_MAX_LINE_LENGTH_PCK CCI_REG16(0x1146) +#define CCS_R_MIN_LINE_BLANKING_PCK CCI_REG16(0x1148) +#define CCS_R_MIN_FRAME_BLANKING_LINES CCI_REG16(0x114a) +#define CCS_R_MIN_LINE_LENGTH_PCK_STEP_SIZE CCI_REG8(0x114c) +#define CCS_R_TIMING_MODE_CAPABILITY CCI_REG8(0x114d) #define CCS_TIMING_MODE_CAPABILITY_AUTO_FRAME_LENGTH BIT(0) #define CCS_TIMING_MODE_CAPABILITY_ROLLING_SHUTTER_MANUAL_READOUT BIT(2) #define CCS_TIMING_MODE_CAPABILITY_DELAYED_EXPOSURE_START BIT(3) #define CCS_TIMING_MODE_CAPABILITY_MANUAL_EXPOSURE_EMBEDDED_DATA BIT(4) -#define CCS_R_FRAME_MARGIN_MAX_VALUE (0x114e | CCS_FL_16BIT) -#define CCS_R_FRAME_MARGIN_MIN_VALUE 0x1150 -#define CCS_R_GAIN_DELAY_TYPE 0x1151 +#define CCS_R_FRAME_MARGIN_MAX_VALUE CCI_REG16(0x114e) +#define CCS_R_FRAME_MARGIN_MIN_VALUE CCI_REG8(0x1150) +#define CCS_R_GAIN_DELAY_TYPE CCI_REG8(0x1151) #define CCS_GAIN_DELAY_TYPE_FIXED 0U #define CCS_GAIN_DELAY_TYPE_VARIABLE 1U -#define CCS_R_MIN_OP_SYS_CLK_DIV (0x1160 | CCS_FL_16BIT) -#define CCS_R_MAX_OP_SYS_CLK_DIV (0x1162 | CCS_FL_16BIT) -#define CCS_R_MIN_OP_SYS_CLK_FREQ_MHZ (0x1164 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_SYS_CLK_FREQ_MHZ (0x1168 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_OP_PIX_CLK_DIV (0x116c | CCS_FL_16BIT) -#define CCS_R_MAX_OP_PIX_CLK_DIV (0x116e | CCS_FL_16BIT) -#define CCS_R_MIN_OP_PIX_CLK_FREQ_MHZ (0x1170 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_PIX_CLK_FREQ_MHZ (0x1174 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_X_ADDR_MIN (0x1180 | CCS_FL_16BIT) -#define CCS_R_Y_ADDR_MIN (0x1182 | CCS_FL_16BIT) -#define CCS_R_X_ADDR_MAX (0x1184 | CCS_FL_16BIT) -#define CCS_R_Y_ADDR_MAX (0x1186 | CCS_FL_16BIT) -#define CCS_R_MIN_X_OUTPUT_SIZE (0x1188 | CCS_FL_16BIT) -#define CCS_R_MIN_Y_OUTPUT_SIZE (0x118a | CCS_FL_16BIT) -#define CCS_R_MAX_X_OUTPUT_SIZE (0x118c | CCS_FL_16BIT) -#define CCS_R_MAX_Y_OUTPUT_SIZE (0x118e | CCS_FL_16BIT) -#define CCS_R_X_ADDR_START_DIV_CONSTANT 0x1190 -#define CCS_R_Y_ADDR_START_DIV_CONSTANT 0x1191 -#define CCS_R_X_ADDR_END_DIV_CONSTANT 0x1192 -#define CCS_R_Y_ADDR_END_DIV_CONSTANT 0x1193 -#define CCS_R_X_SIZE_DIV 0x1194 -#define CCS_R_Y_SIZE_DIV 0x1195 -#define CCS_R_X_OUTPUT_DIV 0x1196 -#define CCS_R_Y_OUTPUT_DIV 0x1197 -#define CCS_R_NON_FLEXIBLE_RESOLUTION_SUPPORT 0x1198 +#define CCS_R_MIN_OP_SYS_CLK_DIV CCI_REG16(0x1160) +#define CCS_R_MAX_OP_SYS_CLK_DIV CCI_REG16(0x1162) +#define CCS_R_MIN_OP_SYS_CLK_FREQ_MHZ (CCI_REG32(0x1164) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_SYS_CLK_FREQ_MHZ (CCI_REG32(0x1168) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_OP_PIX_CLK_DIV CCI_REG16(0x116c) +#define CCS_R_MAX_OP_PIX_CLK_DIV CCI_REG16(0x116e) +#define CCS_R_MIN_OP_PIX_CLK_FREQ_MHZ (CCI_REG32(0x1170) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_PIX_CLK_FREQ_MHZ (CCI_REG32(0x1174) | CCS_FL_FLOAT_IREAL) +#define CCS_R_X_ADDR_MIN CCI_REG16(0x1180) +#define CCS_R_Y_ADDR_MIN CCI_REG16(0x1182) +#define CCS_R_X_ADDR_MAX CCI_REG16(0x1184) +#define CCS_R_Y_ADDR_MAX CCI_REG16(0x1186) +#define CCS_R_MIN_X_OUTPUT_SIZE CCI_REG16(0x1188) +#define CCS_R_MIN_Y_OUTPUT_SIZE CCI_REG16(0x118a) +#define CCS_R_MAX_X_OUTPUT_SIZE CCI_REG16(0x118c) +#define CCS_R_MAX_Y_OUTPUT_SIZE CCI_REG16(0x118e) +#define CCS_R_X_ADDR_START_DIV_CONSTANT CCI_REG8(0x1190) +#define CCS_R_Y_ADDR_START_DIV_CONSTANT CCI_REG8(0x1191) +#define CCS_R_X_ADDR_END_DIV_CONSTANT CCI_REG8(0x1192) +#define CCS_R_Y_ADDR_END_DIV_CONSTANT CCI_REG8(0x1193) +#define CCS_R_X_SIZE_DIV CCI_REG8(0x1194) +#define CCS_R_Y_SIZE_DIV CCI_REG8(0x1195) +#define CCS_R_X_OUTPUT_DIV CCI_REG8(0x1196) +#define CCS_R_Y_OUTPUT_DIV CCI_REG8(0x1197) +#define CCS_R_NON_FLEXIBLE_RESOLUTION_SUPPORT CCI_REG8(0x1198) #define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_NEW_PIX_ADDR BIT(0) #define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_NEW_OUTPUT_RES BIT(1) #define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_OUTPUT_CROP_NO_PAD BIT(2) #define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_OUTPUT_SIZE_LANE_DEP BIT(3) -#define CCS_R_MIN_OP_PRE_PLL_CLK_DIV (0x11a0 | CCS_FL_16BIT) -#define CCS_R_MAX_OP_PRE_PLL_CLK_DIV (0x11a2 | CCS_FL_16BIT) -#define CCS_R_MIN_OP_PLL_IP_CLK_FREQ_MHZ (0x11a4 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_PLL_IP_CLK_FREQ_MHZ (0x11a8 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_OP_PLL_MULTIPLIER (0x11ac | CCS_FL_16BIT) -#define CCS_R_MAX_OP_PLL_MULTIPLIER (0x11ae | CCS_FL_16BIT) -#define CCS_R_MIN_OP_PLL_OP_CLK_FREQ_MHZ (0x11b0 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_PLL_OP_CLK_FREQ_MHZ (0x11b4 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_CLOCK_TREE_PLL_CAPABILITY 0x11b8 +#define CCS_R_MIN_OP_PRE_PLL_CLK_DIV CCI_REG16(0x11a0) +#define CCS_R_MAX_OP_PRE_PLL_CLK_DIV CCI_REG16(0x11a2) +#define CCS_R_MIN_OP_PLL_IP_CLK_FREQ_MHZ (CCI_REG32(0x11a4) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_PLL_IP_CLK_FREQ_MHZ (CCI_REG32(0x11a8) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_OP_PLL_MULTIPLIER CCI_REG16(0x11ac) +#define CCS_R_MAX_OP_PLL_MULTIPLIER CCI_REG16(0x11ae) +#define CCS_R_MIN_OP_PLL_OP_CLK_FREQ_MHZ (CCI_REG32(0x11b0) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_PLL_OP_CLK_FREQ_MHZ (CCI_REG32(0x11b4) | CCS_FL_FLOAT_IREAL) +#define CCS_R_CLOCK_TREE_PLL_CAPABILITY CCI_REG8(0x11b8) #define CCS_CLOCK_TREE_PLL_CAPABILITY_DUAL_PLL BIT(0) #define CCS_CLOCK_TREE_PLL_CAPABILITY_SINGLE_PLL BIT(1) #define CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER BIT(2) #define CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV BIT(3) -#define CCS_R_CLOCK_CAPA_TYPE_CAPABILITY 0x11b9 +#define CCS_R_CLOCK_CAPA_TYPE_CAPABILITY CCI_REG8(0x11b9) #define CCS_CLOCK_CAPA_TYPE_CAPABILITY_IREAL BIT(0) -#define CCS_R_MIN_EVEN_INC (0x11c0 | CCS_FL_16BIT) -#define CCS_R_MIN_ODD_INC (0x11c2 | CCS_FL_16BIT) -#define CCS_R_MAX_EVEN_INC (0x11c4 | CCS_FL_16BIT) -#define CCS_R_MAX_ODD_INC (0x11c6 | CCS_FL_16BIT) -#define CCS_R_AUX_SUBSAMP_CAPABILITY 0x11c8 +#define CCS_R_MIN_EVEN_INC CCI_REG16(0x11c0) +#define CCS_R_MIN_ODD_INC CCI_REG16(0x11c2) +#define CCS_R_MAX_EVEN_INC CCI_REG16(0x11c4) +#define CCS_R_MAX_ODD_INC CCI_REG16(0x11c6) +#define CCS_R_AUX_SUBSAMP_CAPABILITY CCI_REG8(0x11c8) #define CCS_AUX_SUBSAMP_CAPABILITY_FACTOR_POWER_OF_2 BIT(1) -#define CCS_R_AUX_SUBSAMP_MONO_CAPABILITY 0x11c9 +#define CCS_R_AUX_SUBSAMP_MONO_CAPABILITY CCI_REG8(0x11c9) #define CCS_AUX_SUBSAMP_MONO_CAPABILITY_FACTOR_POWER_OF_2 BIT(1) -#define CCS_R_MONOCHROME_CAPABILITY 0x11ca +#define CCS_R_MONOCHROME_CAPABILITY CCI_REG8(0x11ca) #define CCS_MONOCHROME_CAPABILITY_INC_ODD 0U #define CCS_MONOCHROME_CAPABILITY_INC_EVEN 1U -#define CCS_R_PIXEL_READOUT_CAPABILITY 0x11cb +#define CCS_R_PIXEL_READOUT_CAPABILITY CCI_REG8(0x11cb) #define CCS_PIXEL_READOUT_CAPABILITY_BAYER 0U #define CCS_PIXEL_READOUT_CAPABILITY_MONOCHROME 1U #define CCS_PIXEL_READOUT_CAPABILITY_BAYER_AND_MONO 2U -#define CCS_R_MIN_EVEN_INC_MONO (0x11cc | CCS_FL_16BIT) -#define CCS_R_MAX_EVEN_INC_MONO (0x11ce | CCS_FL_16BIT) -#define CCS_R_MIN_ODD_INC_MONO (0x11d0 | CCS_FL_16BIT) -#define CCS_R_MAX_ODD_INC_MONO (0x11d2 | CCS_FL_16BIT) -#define CCS_R_MIN_EVEN_INC_BC2 (0x11d4 | CCS_FL_16BIT) -#define CCS_R_MAX_EVEN_INC_BC2 (0x11d6 | CCS_FL_16BIT) -#define CCS_R_MIN_ODD_INC_BC2 (0x11d8 | CCS_FL_16BIT) -#define CCS_R_MAX_ODD_INC_BC2 (0x11da | CCS_FL_16BIT) -#define CCS_R_MIN_EVEN_INC_MONO_BC2 (0x11dc | CCS_FL_16BIT) -#define CCS_R_MAX_EVEN_INC_MONO_BC2 (0x11de | CCS_FL_16BIT) -#define CCS_R_MIN_ODD_INC_MONO_BC2 (0x11f0 | CCS_FL_16BIT) -#define CCS_R_MAX_ODD_INC_MONO_BC2 (0x11f2 | CCS_FL_16BIT) -#define CCS_R_SCALING_CAPABILITY (0x1200 | CCS_FL_16BIT) +#define CCS_R_MIN_EVEN_INC_MONO CCI_REG16(0x11cc) +#define CCS_R_MAX_EVEN_INC_MONO CCI_REG16(0x11ce) +#define CCS_R_MIN_ODD_INC_MONO CCI_REG16(0x11d0) +#define CCS_R_MAX_ODD_INC_MONO CCI_REG16(0x11d2) +#define CCS_R_MIN_EVEN_INC_BC2 CCI_REG16(0x11d4) +#define CCS_R_MAX_EVEN_INC_BC2 CCI_REG16(0x11d6) +#define CCS_R_MIN_ODD_INC_BC2 CCI_REG16(0x11d8) +#define CCS_R_MAX_ODD_INC_BC2 CCI_REG16(0x11da) +#define CCS_R_MIN_EVEN_INC_MONO_BC2 CCI_REG16(0x11dc) +#define CCS_R_MAX_EVEN_INC_MONO_BC2 CCI_REG16(0x11de) +#define CCS_R_MIN_ODD_INC_MONO_BC2 CCI_REG16(0x11f0) +#define CCS_R_MAX_ODD_INC_MONO_BC2 CCI_REG16(0x11f2) +#define CCS_R_SCALING_CAPABILITY CCI_REG16(0x1200) #define CCS_SCALING_CAPABILITY_NONE 0U #define CCS_SCALING_CAPABILITY_HORIZONTAL 1U #define CCS_SCALING_CAPABILITY_RESERVED 2U -#define CCS_R_SCALER_M_MIN (0x1204 | CCS_FL_16BIT) -#define CCS_R_SCALER_M_MAX (0x1206 | CCS_FL_16BIT) -#define CCS_R_SCALER_N_MIN (0x1208 | CCS_FL_16BIT) -#define CCS_R_SCALER_N_MAX (0x120a | CCS_FL_16BIT) -#define CCS_R_DIGITAL_CROP_CAPABILITY 0x120e +#define CCS_R_SCALER_M_MIN CCI_REG16(0x1204) +#define CCS_R_SCALER_M_MAX CCI_REG16(0x1206) +#define CCS_R_SCALER_N_MIN CCI_REG16(0x1208) +#define CCS_R_SCALER_N_MAX CCI_REG16(0x120a) +#define CCS_R_DIGITAL_CROP_CAPABILITY CCI_REG8(0x120e) #define CCS_DIGITAL_CROP_CAPABILITY_NONE 0U #define CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1U -#define CCS_R_HDR_CAPABILITY_1 0x1210 +#define CCS_R_HDR_CAPABILITY_1 CCI_REG8(0x1210) #define CCS_HDR_CAPABILITY_1_2X2_BINNING BIT(0) #define CCS_HDR_CAPABILITY_1_COMBINED_ANALOG_GAIN BIT(1) #define CCS_HDR_CAPABILITY_1_SEPARATE_ANALOG_GAIN BIT(2) @@ -611,66 +611,66 @@ #define CCS_HDR_CAPABILITY_1_RESET_SYNC BIT(4) #define CCS_HDR_CAPABILITY_1_DIRECT_SHORT_EXP_TIMING BIT(5) #define CCS_HDR_CAPABILITY_1_DIRECT_SHORT_EXP_SYNTHESIS BIT(6) -#define CCS_R_MIN_HDR_BIT_DEPTH 0x1211 -#define CCS_R_HDR_RESOLUTION_SUB_TYPES 0x1212 -#define CCS_R_HDR_RESOLUTION_SUB_TYPE(n) (0x1213 + (n)) +#define CCS_R_MIN_HDR_BIT_DEPTH CCI_REG8(0x1211) +#define CCS_R_HDR_RESOLUTION_SUB_TYPES CCI_REG8(0x1212) +#define CCS_R_HDR_RESOLUTION_SUB_TYPE(n) CCI_REG8(0x1213 + (n)) #define CCS_LIM_HDR_RESOLUTION_SUB_TYPE_MIN_N 0U #define CCS_LIM_HDR_RESOLUTION_SUB_TYPE_MAX_N 1U #define CCS_HDR_RESOLUTION_SUB_TYPE_ROW_SHIFT 0U #define CCS_HDR_RESOLUTION_SUB_TYPE_ROW_MASK 0xf #define CCS_HDR_RESOLUTION_SUB_TYPE_COLUMN_SHIFT 4U #define CCS_HDR_RESOLUTION_SUB_TYPE_COLUMN_MASK 0xf0 -#define CCS_R_HDR_CAPABILITY_2 0x121b +#define CCS_R_HDR_CAPABILITY_2 CCI_REG8(0x121b) #define CCS_HDR_CAPABILITY_2_COMBINED_DIGITAL_GAIN BIT(0) #define CCS_HDR_CAPABILITY_2_SEPARATE_DIGITAL_GAIN BIT(1) #define CCS_HDR_CAPABILITY_2_TIMING_MODE BIT(3) #define CCS_HDR_CAPABILITY_2_SYNTHESIS_MODE BIT(4) -#define CCS_R_MAX_HDR_BIT_DEPTH 0x121c -#define CCS_R_USL_SUPPORT_CAPABILITY 0x1230 +#define CCS_R_MAX_HDR_BIT_DEPTH CCI_REG8(0x121c) +#define CCS_R_USL_SUPPORT_CAPABILITY CCI_REG8(0x1230) #define CCS_USL_SUPPORT_CAPABILITY_CLOCK_TREE BIT(0) #define CCS_USL_SUPPORT_CAPABILITY_REV_CLOCK_TREE BIT(1) #define CCS_USL_SUPPORT_CAPABILITY_REV_CLOCK_CALC BIT(2) -#define CCS_R_USL_CLOCK_MODE_D_CAPABILITY 0x1231 +#define CCS_R_USL_CLOCK_MODE_D_CAPABILITY CCI_REG8(0x1231) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_STANDBY BIT(0) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_VBLANK BIT(1) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_HBLANK BIT(2) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_STANDBY BIT(3) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_VBLANK BIT(4) #define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_HBLANK BIT(5) -#define CCS_R_MIN_OP_SYS_CLK_DIV_REV 0x1234 -#define CCS_R_MAX_OP_SYS_CLK_DIV_REV 0x1236 -#define CCS_R_MIN_OP_PIX_CLK_DIV_REV 0x1238 -#define CCS_R_MAX_OP_PIX_CLK_DIV_REV 0x123a -#define CCS_R_MIN_OP_SYS_CLK_FREQ_REV_MHZ (0x123c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_SYS_CLK_FREQ_REV_MHZ (0x1240 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MIN_OP_PIX_CLK_FREQ_REV_MHZ (0x1244 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_OP_PIX_CLK_FREQ_REV_MHZ (0x1248 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) -#define CCS_R_MAX_BITRATE_REV_D_MODE_MBPS (0x124c | (CCS_FL_32BIT | CCS_FL_IREAL)) -#define CCS_R_MAX_SYMRATE_REV_C_MODE_MSPS (0x1250 | (CCS_FL_32BIT | CCS_FL_IREAL)) -#define CCS_R_COMPRESSION_CAPABILITY 0x1300 +#define CCS_R_MIN_OP_SYS_CLK_DIV_REV CCI_REG8(0x1234) +#define CCS_R_MAX_OP_SYS_CLK_DIV_REV CCI_REG8(0x1236) +#define CCS_R_MIN_OP_PIX_CLK_DIV_REV CCI_REG8(0x1238) +#define CCS_R_MAX_OP_PIX_CLK_DIV_REV CCI_REG8(0x123a) +#define CCS_R_MIN_OP_SYS_CLK_FREQ_REV_MHZ (CCI_REG32(0x123c) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_SYS_CLK_FREQ_REV_MHZ (CCI_REG32(0x1240) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MIN_OP_PIX_CLK_FREQ_REV_MHZ (CCI_REG32(0x1244) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_OP_PIX_CLK_FREQ_REV_MHZ (CCI_REG32(0x1248) | CCS_FL_FLOAT_IREAL) +#define CCS_R_MAX_BITRATE_REV_D_MODE_MBPS (CCI_REG32(0x124c) | CCS_FL_IREAL) +#define CCS_R_MAX_SYMRATE_REV_C_MODE_MSPS (CCI_REG32(0x1250) | CCS_FL_IREAL) +#define CCS_R_COMPRESSION_CAPABILITY CCI_REG8(0x1300) #define CCS_COMPRESSION_CAPABILITY_DPCM_PCM_SIMPLE BIT(0) -#define CCS_R_TEST_MODE_CAPABILITY (0x1310 | CCS_FL_16BIT) +#define CCS_R_TEST_MODE_CAPABILITY CCI_REG16(0x1310) #define CCS_TEST_MODE_CAPABILITY_SOLID_COLOR BIT(0) #define CCS_TEST_MODE_CAPABILITY_COLOR_BARS BIT(1) #define CCS_TEST_MODE_CAPABILITY_FADE_TO_GREY BIT(2) #define CCS_TEST_MODE_CAPABILITY_PN9 BIT(3) #define CCS_TEST_MODE_CAPABILITY_COLOR_TILE BIT(5) -#define CCS_R_PN9_DATA_FORMAT1 0x1312 -#define CCS_R_PN9_DATA_FORMAT2 0x1313 -#define CCS_R_PN9_DATA_FORMAT3 0x1314 -#define CCS_R_PN9_DATA_FORMAT4 0x1315 -#define CCS_R_PN9_MISC_CAPABILITY 0x1316 +#define CCS_R_PN9_DATA_FORMAT1 CCI_REG8(0x1312) +#define CCS_R_PN9_DATA_FORMAT2 CCI_REG8(0x1313) +#define CCS_R_PN9_DATA_FORMAT3 CCI_REG8(0x1314) +#define CCS_R_PN9_DATA_FORMAT4 CCI_REG8(0x1315) +#define CCS_R_PN9_MISC_CAPABILITY CCI_REG8(0x1316) #define CCS_PN9_MISC_CAPABILITY_NUM_PIXELS_SHIFT 0U #define CCS_PN9_MISC_CAPABILITY_NUM_PIXELS_MASK 0x7 #define CCS_PN9_MISC_CAPABILITY_COMPRESSION BIT(3) -#define CCS_R_TEST_PATTERN_CAPABILITY 0x1317 +#define CCS_R_TEST_PATTERN_CAPABILITY CCI_REG8(0x1317) #define CCS_TEST_PATTERN_CAPABILITY_NO_REPEAT BIT(1) -#define CCS_R_PATTERN_SIZE_DIV_M1 0x1318 -#define CCS_R_FIFO_SUPPORT_CAPABILITY 0x1502 +#define CCS_R_PATTERN_SIZE_DIV_M1 CCI_REG8(0x1318) +#define CCS_R_FIFO_SUPPORT_CAPABILITY CCI_REG8(0x1502) #define CCS_FIFO_SUPPORT_CAPABILITY_NONE 0U #define CCS_FIFO_SUPPORT_CAPABILITY_DERATING 1U #define CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING 2U -#define CCS_R_PHY_CTRL_CAPABILITY 0x1600 +#define CCS_R_PHY_CTRL_CAPABILITY CCI_REG8(0x1600) #define CCS_PHY_CTRL_CAPABILITY_AUTO_PHY_CTL BIT(0) #define CCS_PHY_CTRL_CAPABILITY_UI_PHY_CTL BIT(1) #define CCS_PHY_CTRL_CAPABILITY_DPHY_TIME_UI_REG_1_CTL BIT(2) @@ -679,7 +679,7 @@ #define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_UI_REG_1_CTL BIT(5) #define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_UI_REG_2_CTL BIT(6) #define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_CTL BIT(7) -#define CCS_R_CSI_DPHY_LANE_MODE_CAPABILITY 0x1601 +#define CCS_R_CSI_DPHY_LANE_MODE_CAPABILITY CCI_REG8(0x1601) #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_1_LANE BIT(0) #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_2_LANE BIT(1) #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_3_LANE BIT(2) @@ -688,22 +688,22 @@ #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_6_LANE BIT(5) #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_7_LANE BIT(6) #define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_8_LANE BIT(7) -#define CCS_R_CSI_SIGNALING_MODE_CAPABILITY 0x1602 +#define CCS_R_CSI_SIGNALING_MODE_CAPABILITY CCI_REG8(0x1602) #define CCS_CSI_SIGNALING_MODE_CAPABILITY_CSI_DPHY BIT(2) #define CCS_CSI_SIGNALING_MODE_CAPABILITY_CSI_CPHY BIT(3) -#define CCS_R_FAST_STANDBY_CAPABILITY 0x1603 +#define CCS_R_FAST_STANDBY_CAPABILITY CCI_REG8(0x1603) #define CCS_FAST_STANDBY_CAPABILITY_NO_FRAME_TRUNCATION 0U #define CCS_FAST_STANDBY_CAPABILITY_FRAME_TRUNCATION 1U -#define CCS_R_CSI_ADDRESS_CONTROL_CAPABILITY 0x1604 +#define CCS_R_CSI_ADDRESS_CONTROL_CAPABILITY CCI_REG8(0x1604) #define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_CCI_ADDR_CHANGE BIT(0) #define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_2ND_CCI_ADDR BIT(1) #define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_SW_CHANGEABLE_2ND_CCI_ADDR BIT(2) -#define CCS_R_DATA_TYPE_CAPABILITY 0x1605 +#define CCS_R_DATA_TYPE_CAPABILITY CCI_REG8(0x1605) #define CCS_DATA_TYPE_CAPABILITY_DPCM_PROGRAMMABLE BIT(0) #define CCS_DATA_TYPE_CAPABILITY_BOTTOM_EMBEDDED_DT_PROGRAMMABLE BIT(1) #define CCS_DATA_TYPE_CAPABILITY_BOTTOM_EMBEDDED_VC_PROGRAMMABLE BIT(2) #define CCS_DATA_TYPE_CAPABILITY_EXT_VC_RANGE BIT(3) -#define CCS_R_CSI_CPHY_LANE_MODE_CAPABILITY 0x1606 +#define CCS_R_CSI_CPHY_LANE_MODE_CAPABILITY CCI_REG8(0x1606) #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_1_LANE BIT(0) #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_2_LANE BIT(1) #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_3_LANE BIT(2) @@ -712,44 +712,44 @@ #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_6_LANE BIT(5) #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_7_LANE BIT(6) #define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_8_LANE BIT(7) -#define CCS_R_EMB_DATA_CAPABILITY 0x1607 +#define CCS_R_EMB_DATA_CAPABILITY CCI_REG8(0x1607) #define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW16 BIT(0) #define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW20 BIT(1) #define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW24 BIT(2) #define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW16 BIT(3) #define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW20 BIT(4) #define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW24 BIT(5) -#define CCS_R_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS(n) ((0x1608 | (CCS_FL_32BIT | CCS_FL_IREAL)) + ((n) < 4 ? (n) * 4 : 0x32 + ((n) - 4) * 4)) +#define CCS_R_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS(n) (CCI_REG32(0x1608 + ((n) < 4 ? (n) * 4 : 0x32 + ((n) - 4) * 4)) | CCS_FL_IREAL) #define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS_MIN_N 0U #define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS_MAX_N 7U -#define CCS_R_TEMP_SENSOR_CAPABILITY 0x1618 +#define CCS_R_TEMP_SENSOR_CAPABILITY CCI_REG8(0x1618) #define CCS_TEMP_SENSOR_CAPABILITY_SUPPORTED BIT(0) #define CCS_TEMP_SENSOR_CAPABILITY_CCS_FORMAT BIT(1) #define CCS_TEMP_SENSOR_CAPABILITY_RESET_0X80 BIT(2) -#define CCS_R_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS(n) ((0x161a | (CCS_FL_32BIT | CCS_FL_IREAL)) + ((n) < 4 ? (n) * 4 : 0x30 + ((n) - 4) * 4)) +#define CCS_R_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS(n) (CCI_REG32(0x161a + ((n) < 4 ? (n) * 4 : 0x30 + ((n) - 4) * 4)) | CCS_FL_IREAL) #define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS_MIN_N 0U #define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS_MAX_N 7U -#define CCS_R_DPHY_EQUALIZATION_CAPABILITY 0x162b +#define CCS_R_DPHY_EQUALIZATION_CAPABILITY CCI_REG8(0x162b) #define CCS_DPHY_EQUALIZATION_CAPABILITY_EQUALIZATION_CTRL BIT(0) #define CCS_DPHY_EQUALIZATION_CAPABILITY_EQ1 BIT(1) #define CCS_DPHY_EQUALIZATION_CAPABILITY_EQ2 BIT(2) -#define CCS_R_CPHY_EQUALIZATION_CAPABILITY 0x162c +#define CCS_R_CPHY_EQUALIZATION_CAPABILITY CCI_REG8(0x162c) #define CCS_CPHY_EQUALIZATION_CAPABILITY_EQUALIZATION_CTRL BIT(0) -#define CCS_R_DPHY_PREAMBLE_CAPABILITY 0x162d +#define CCS_R_DPHY_PREAMBLE_CAPABILITY CCI_REG8(0x162d) #define CCS_DPHY_PREAMBLE_CAPABILITY_PREAMBLE_SEQ_CTRL BIT(0) -#define CCS_R_DPHY_SSC_CAPABILITY 0x162e +#define CCS_R_DPHY_SSC_CAPABILITY CCI_REG8(0x162e) #define CCS_DPHY_SSC_CAPABILITY_SUPPORTED BIT(0) -#define CCS_R_CPHY_CALIBRATION_CAPABILITY 0x162f +#define CCS_R_CPHY_CALIBRATION_CAPABILITY CCI_REG8(0x162f) #define CCS_CPHY_CALIBRATION_CAPABILITY_MANUAL BIT(0) #define CCS_CPHY_CALIBRATION_CAPABILITY_MANUAL_STREAMING BIT(1) #define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_1_CTRL BIT(2) #define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_2_CTRL BIT(3) #define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_3_CTRL BIT(4) -#define CCS_R_DPHY_CALIBRATION_CAPABILITY 0x1630 +#define CCS_R_DPHY_CALIBRATION_CAPABILITY CCI_REG8(0x1630) #define CCS_DPHY_CALIBRATION_CAPABILITY_MANUAL BIT(0) #define CCS_DPHY_CALIBRATION_CAPABILITY_MANUAL_STREAMING BIT(1) #define CCS_DPHY_CALIBRATION_CAPABILITY_ALTERNATE_SEQ BIT(2) -#define CCS_R_PHY_CTRL_CAPABILITY_2 0x1631 +#define CCS_R_PHY_CTRL_CAPABILITY_2 CCI_REG8(0x1631) #define CCS_PHY_CTRL_CAPABILITY_2_TGR_LENGTH BIT(0) #define CCS_PHY_CTRL_CAPABILITY_2_TGR_PREAMBLE_PROG_SEQ BIT(1) #define CCS_PHY_CTRL_CAPABILITY_2_EXTRA_CPHY_MANUAL_TIMING BIT(2) @@ -758,13 +758,13 @@ #define CCS_PHY_CTRL_CAPABILITY_2_CLOCK_BASED_MANUAL_CPHY BIT(5) #define CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_DPHY BIT(6) #define CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_CPHY BIT(7) -#define CCS_R_LRTE_CPHY_CAPABILITY 0x1632 +#define CCS_R_LRTE_CPHY_CAPABILITY CCI_REG8(0x1632) #define CCS_LRTE_CPHY_CAPABILITY_PDQ_SHORT BIT(0) #define CCS_LRTE_CPHY_CAPABILITY_SPACER_SHORT BIT(1) #define CCS_LRTE_CPHY_CAPABILITY_PDQ_LONG BIT(2) #define CCS_LRTE_CPHY_CAPABILITY_SPACER_LONG BIT(3) #define CCS_LRTE_CPHY_CAPABILITY_SPACER_NO_PDQ BIT(4) -#define CCS_R_LRTE_DPHY_CAPABILITY 0x1633 +#define CCS_R_LRTE_DPHY_CAPABILITY CCI_REG8(0x1633) #define CCS_LRTE_DPHY_CAPABILITY_PDQ_SHORT_OPT1 BIT(0) #define CCS_LRTE_DPHY_CAPABILITY_SPACER_SHORT_OPT1 BIT(1) #define CCS_LRTE_DPHY_CAPABILITY_PDQ_LONG_OPT1 BIT(2) @@ -773,18 +773,18 @@ #define CCS_LRTE_DPHY_CAPABILITY_SPACER_LONG_OPT2 BIT(5) #define CCS_LRTE_DPHY_CAPABILITY_SPACER_NO_PDQ_OPT1 BIT(6) #define CCS_LRTE_DPHY_CAPABILITY_SPACER_VARIABLE_OPT2 BIT(7) -#define CCS_R_ALPS_CAPABILITY_DPHY 0x1634 +#define CCS_R_ALPS_CAPABILITY_DPHY CCI_REG8(0x1634) #define CCS_ALPS_CAPABILITY_DPHY_LVLP_NOT_SUPPORTED 0U #define CCS_ALPS_CAPABILITY_DPHY_LVLP_SUPPORTED 1U #define CCS_ALPS_CAPABILITY_DPHY_CONTROLLABLE_LVLP 2U -#define CCS_R_ALPS_CAPABILITY_CPHY 0x1635 +#define CCS_R_ALPS_CAPABILITY_CPHY CCI_REG8(0x1635) #define CCS_ALPS_CAPABILITY_CPHY_LVLP_NOT_SUPPORTED 0U #define CCS_ALPS_CAPABILITY_CPHY_LVLP_SUPPORTED 1U #define CCS_ALPS_CAPABILITY_CPHY_CONTROLLABLE_LVLP 2U #define CCS_ALPS_CAPABILITY_CPHY_ALP_NOT_SUPPORTED 0xc #define CCS_ALPS_CAPABILITY_CPHY_ALP_SUPPORTED 0xd #define CCS_ALPS_CAPABILITY_CPHY_CONTROLLABLE_ALP 0xe -#define CCS_R_SCRAMBLING_CAPABILITY 0x1636 +#define CCS_R_SCRAMBLING_CAPABILITY CCI_REG8(0x1636) #define CCS_SCRAMBLING_CAPABILITY_SCRAMBLING_SUPPORTED BIT(0) #define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_SHIFT 1U #define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_MASK 0x6 @@ -796,11 +796,11 @@ #define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_1 1U #define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_4 4U #define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_PER_LANE BIT(6) -#define CCS_R_DPHY_MANUAL_CONSTANT 0x1637 -#define CCS_R_CPHY_MANUAL_CONSTANT 0x1638 -#define CCS_R_CSI2_INTERFACE_CAPABILITY_MISC 0x1639 +#define CCS_R_DPHY_MANUAL_CONSTANT CCI_REG8(0x1637) +#define CCS_R_CPHY_MANUAL_CONSTANT CCI_REG8(0x1638) +#define CCS_R_CSI2_INTERFACE_CAPABILITY_MISC CCI_REG8(0x1639) #define CCS_CSI2_INTERFACE_CAPABILITY_MISC_EOTP_SHORT_PKT_OPT2 BIT(0) -#define CCS_R_PHY_CTRL_CAPABILITY_3 0x165c +#define CCS_R_PHY_CTRL_CAPABILITY_3 CCI_REG8(0x165c) #define CCS_PHY_CTRL_CAPABILITY_3_DPHY_TIMING_NOT_MULTIPLE BIT(0) #define CCS_PHY_CTRL_CAPABILITY_3_DPHY_MIN_TIMING_VALUE_1 BIT(1) #define CCS_PHY_CTRL_CAPABILITY_3_TWAKEUP_SUPPORTED BIT(2) @@ -808,130 +808,130 @@ #define CCS_PHY_CTRL_CAPABILITY_3_THS_EXIT_SUPPORTED BIT(4) #define CCS_PHY_CTRL_CAPABILITY_3_CPHY_TIMING_NOT_MULTIPLE BIT(5) #define CCS_PHY_CTRL_CAPABILITY_3_CPHY_MIN_TIMING_VALUE_1 BIT(6) -#define CCS_R_DPHY_SF 0x165d -#define CCS_R_CPHY_SF 0x165e +#define CCS_R_DPHY_SF CCI_REG8(0x165d) +#define CCS_R_CPHY_SF CCI_REG8(0x165e) #define CCS_CPHY_SF_TWAKEUP_SHIFT 0U #define CCS_CPHY_SF_TWAKEUP_MASK 0xf #define CCS_CPHY_SF_TINIT_SHIFT 4U #define CCS_CPHY_SF_TINIT_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_1 0x165f +#define CCS_R_DPHY_LIMITS_1 CCI_REG8(0x165f) #define CCS_DPHY_LIMITS_1_THS_PREPARE_SHIFT 0U #define CCS_DPHY_LIMITS_1_THS_PREPARE_MASK 0xf #define CCS_DPHY_LIMITS_1_THS_ZERO_SHIFT 4U #define CCS_DPHY_LIMITS_1_THS_ZERO_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_2 0x1660 +#define CCS_R_DPHY_LIMITS_2 CCI_REG8(0x1660) #define CCS_DPHY_LIMITS_2_THS_TRAIL_SHIFT 0U #define CCS_DPHY_LIMITS_2_THS_TRAIL_MASK 0xf #define CCS_DPHY_LIMITS_2_TCLK_TRAIL_MIN_SHIFT 4U #define CCS_DPHY_LIMITS_2_TCLK_TRAIL_MIN_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_3 0x1661 +#define CCS_R_DPHY_LIMITS_3 CCI_REG8(0x1661) #define CCS_DPHY_LIMITS_3_TCLK_PREPARE_SHIFT 0U #define CCS_DPHY_LIMITS_3_TCLK_PREPARE_MASK 0xf #define CCS_DPHY_LIMITS_3_TCLK_ZERO_SHIFT 4U #define CCS_DPHY_LIMITS_3_TCLK_ZERO_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_4 0x1662 +#define CCS_R_DPHY_LIMITS_4 CCI_REG8(0x1662) #define CCS_DPHY_LIMITS_4_TCLK_POST_SHIFT 0U #define CCS_DPHY_LIMITS_4_TCLK_POST_MASK 0xf #define CCS_DPHY_LIMITS_4_TLPX_SHIFT 4U #define CCS_DPHY_LIMITS_4_TLPX_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_5 0x1663 +#define CCS_R_DPHY_LIMITS_5 CCI_REG8(0x1663) #define CCS_DPHY_LIMITS_5_THS_EXIT_SHIFT 0U #define CCS_DPHY_LIMITS_5_THS_EXIT_MASK 0xf #define CCS_DPHY_LIMITS_5_TWAKEUP_SHIFT 4U #define CCS_DPHY_LIMITS_5_TWAKEUP_MASK 0xf0 -#define CCS_R_DPHY_LIMITS_6 0x1664 +#define CCS_R_DPHY_LIMITS_6 CCI_REG8(0x1664) #define CCS_DPHY_LIMITS_6_TINIT_SHIFT 0U #define CCS_DPHY_LIMITS_6_TINIT_MASK 0xf -#define CCS_R_CPHY_LIMITS_1 0x1665 +#define CCS_R_CPHY_LIMITS_1 CCI_REG8(0x1665) #define CCS_CPHY_LIMITS_1_T3_PREPARE_MAX_SHIFT 0U #define CCS_CPHY_LIMITS_1_T3_PREPARE_MAX_MASK 0xf #define CCS_CPHY_LIMITS_1_T3_LPX_MAX_SHIFT 4U #define CCS_CPHY_LIMITS_1_T3_LPX_MAX_MASK 0xf0 -#define CCS_R_CPHY_LIMITS_2 0x1666 +#define CCS_R_CPHY_LIMITS_2 CCI_REG8(0x1666) #define CCS_CPHY_LIMITS_2_THS_EXIT_MAX_SHIFT 0U #define CCS_CPHY_LIMITS_2_THS_EXIT_MAX_MASK 0xf #define CCS_CPHY_LIMITS_2_TWAKEUP_MAX_SHIFT 4U #define CCS_CPHY_LIMITS_2_TWAKEUP_MAX_MASK 0xf0 -#define CCS_R_CPHY_LIMITS_3 0x1667 +#define CCS_R_CPHY_LIMITS_3 CCI_REG8(0x1667) #define CCS_CPHY_LIMITS_3_TINIT_MAX_SHIFT 0U #define CCS_CPHY_LIMITS_3_TINIT_MAX_MASK 0xf -#define CCS_R_MIN_FRAME_LENGTH_LINES_BIN (0x1700 | CCS_FL_16BIT) -#define CCS_R_MAX_FRAME_LENGTH_LINES_BIN (0x1702 | CCS_FL_16BIT) -#define CCS_R_MIN_LINE_LENGTH_PCK_BIN (0x1704 | CCS_FL_16BIT) -#define CCS_R_MAX_LINE_LENGTH_PCK_BIN (0x1706 | CCS_FL_16BIT) -#define CCS_R_MIN_LINE_BLANKING_PCK_BIN (0x1708 | CCS_FL_16BIT) -#define CCS_R_FINE_INTEGRATION_TIME_MIN_BIN (0x170a | CCS_FL_16BIT) -#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN (0x170c | CCS_FL_16BIT) -#define CCS_R_BINNING_CAPABILITY 0x1710 +#define CCS_R_MIN_FRAME_LENGTH_LINES_BIN CCI_REG16(0x1700) +#define CCS_R_MAX_FRAME_LENGTH_LINES_BIN CCI_REG16(0x1702) +#define CCS_R_MIN_LINE_LENGTH_PCK_BIN CCI_REG16(0x1704) +#define CCS_R_MAX_LINE_LENGTH_PCK_BIN CCI_REG16(0x1706) +#define CCS_R_MIN_LINE_BLANKING_PCK_BIN CCI_REG16(0x1708) +#define CCS_R_FINE_INTEGRATION_TIME_MIN_BIN CCI_REG16(0x170a) +#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN CCI_REG16(0x170c) +#define CCS_R_BINNING_CAPABILITY CCI_REG8(0x1710) #define CCS_BINNING_CAPABILITY_UNSUPPORTED 0U #define CCS_BINNING_CAPABILITY_BINNING_THEN_SUBSAMPLING 1U #define CCS_BINNING_CAPABILITY_SUBSAMPLING_THEN_BINNING 2U -#define CCS_R_BINNING_WEIGHTING_CAPABILITY 0x1711 +#define CCS_R_BINNING_WEIGHTING_CAPABILITY CCI_REG8(0x1711) #define CCS_BINNING_WEIGHTING_CAPABILITY_AVERAGED BIT(0) #define CCS_BINNING_WEIGHTING_CAPABILITY_SUMMED BIT(1) #define CCS_BINNING_WEIGHTING_CAPABILITY_BAYER_CORRECTED BIT(2) #define CCS_BINNING_WEIGHTING_CAPABILITY_MODULE_SPECIFIC_WEIGHT BIT(3) -#define CCS_R_BINNING_SUB_TYPES 0x1712 -#define CCS_R_BINNING_SUB_TYPE(n) (0x1713 + (n)) +#define CCS_R_BINNING_SUB_TYPES CCI_REG8(0x1712) +#define CCS_R_BINNING_SUB_TYPE(n) CCI_REG8(0x1713 + (n)) #define CCS_LIM_BINNING_SUB_TYPE_MIN_N 0U #define CCS_LIM_BINNING_SUB_TYPE_MAX_N 63U #define CCS_BINNING_SUB_TYPE_ROW_SHIFT 0U #define CCS_BINNING_SUB_TYPE_ROW_MASK 0xf #define CCS_BINNING_SUB_TYPE_COLUMN_SHIFT 4U #define CCS_BINNING_SUB_TYPE_COLUMN_MASK 0xf0 -#define CCS_R_BINNING_WEIGHTING_MONO_CAPABILITY 0x1771 +#define CCS_R_BINNING_WEIGHTING_MONO_CAPABILITY CCI_REG8(0x1771) #define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_AVERAGED BIT(0) #define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_SUMMED BIT(1) #define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_BAYER_CORRECTED BIT(2) #define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_MODULE_SPECIFIC_WEIGHT BIT(3) -#define CCS_R_BINNING_SUB_TYPES_MONO 0x1772 -#define CCS_R_BINNING_SUB_TYPE_MONO(n) (0x1773 + (n)) +#define CCS_R_BINNING_SUB_TYPES_MONO CCI_REG8(0x1772) +#define CCS_R_BINNING_SUB_TYPE_MONO(n) CCI_REG8(0x1773 + (n)) #define CCS_LIM_BINNING_SUB_TYPE_MONO_MIN_N 0U #define CCS_LIM_BINNING_SUB_TYPE_MONO_MAX_N 63U -#define CCS_R_DATA_TRANSFER_IF_CAPABILITY 0x1800 +#define CCS_R_DATA_TRANSFER_IF_CAPABILITY CCI_REG8(0x1800) #define CCS_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) #define CCS_DATA_TRANSFER_IF_CAPABILITY_POLLING BIT(2) -#define CCS_R_SHADING_CORRECTION_CAPABILITY 0x1900 +#define CCS_R_SHADING_CORRECTION_CAPABILITY CCI_REG8(0x1900) #define CCS_SHADING_CORRECTION_CAPABILITY_COLOR_SHADING BIT(0) #define CCS_SHADING_CORRECTION_CAPABILITY_LUMINANCE_CORRECTION BIT(1) -#define CCS_R_GREEN_IMBALANCE_CAPABILITY 0x1901 +#define CCS_R_GREEN_IMBALANCE_CAPABILITY CCI_REG8(0x1901) #define CCS_GREEN_IMBALANCE_CAPABILITY_SUPPORTED BIT(0) -#define CCS_R_MODULE_SPECIFIC_CORRECTION_CAPABILITY 0x1903 -#define CCS_R_DEFECT_CORRECTION_CAPABILITY (0x1904 | CCS_FL_16BIT) +#define CCS_R_MODULE_SPECIFIC_CORRECTION_CAPABILITY CCI_REG8(0x1903) +#define CCS_R_DEFECT_CORRECTION_CAPABILITY CCI_REG16(0x1904) #define CCS_DEFECT_CORRECTION_CAPABILITY_MAPPED_DEFECT BIT(0) #define CCS_DEFECT_CORRECTION_CAPABILITY_DYNAMIC_COUPLET BIT(2) #define CCS_DEFECT_CORRECTION_CAPABILITY_DYNAMIC_SINGLE BIT(5) #define CCS_DEFECT_CORRECTION_CAPABILITY_COMBINED_DYNAMIC BIT(8) -#define CCS_R_DEFECT_CORRECTION_CAPABILITY_2 (0x1906 | CCS_FL_16BIT) +#define CCS_R_DEFECT_CORRECTION_CAPABILITY_2 CCI_REG16(0x1906) #define CCS_DEFECT_CORRECTION_CAPABILITY_2_DYNAMIC_TRIPLET BIT(3) -#define CCS_R_NF_CAPABILITY 0x1908 +#define CCS_R_NF_CAPABILITY CCI_REG8(0x1908) #define CCS_NF_CAPABILITY_LUMA BIT(0) #define CCS_NF_CAPABILITY_CHROMA BIT(1) #define CCS_NF_CAPABILITY_COMBINED BIT(2) -#define CCS_R_OB_READOUT_CAPABILITY 0x1980 +#define CCS_R_OB_READOUT_CAPABILITY CCI_REG8(0x1980) #define CCS_OB_READOUT_CAPABILITY_CONTROLLABLE_READOUT BIT(0) #define CCS_OB_READOUT_CAPABILITY_VISIBLE_PIXEL_READOUT BIT(1) #define CCS_OB_READOUT_CAPABILITY_DIFFERENT_VC_READOUT BIT(2) #define CCS_OB_READOUT_CAPABILITY_DIFFERENT_DT_READOUT BIT(3) #define CCS_OB_READOUT_CAPABILITY_PROG_DATA_FORMAT BIT(4) -#define CCS_R_COLOR_FEEDBACK_CAPABILITY 0x1987 +#define CCS_R_COLOR_FEEDBACK_CAPABILITY CCI_REG8(0x1987) #define CCS_COLOR_FEEDBACK_CAPABILITY_KELVIN BIT(0) #define CCS_COLOR_FEEDBACK_CAPABILITY_AWB_GAIN BIT(1) -#define CCS_R_CFA_PATTERN_CAPABILITY 0x1990 +#define CCS_R_CFA_PATTERN_CAPABILITY CCI_REG8(0x1990) #define CCS_CFA_PATTERN_CAPABILITY_BAYER 0U #define CCS_CFA_PATTERN_CAPABILITY_MONOCHROME 1U #define CCS_CFA_PATTERN_CAPABILITY_4X4_QUAD_BAYER 2U #define CCS_CFA_PATTERN_CAPABILITY_VENDOR_SPECIFIC 3U -#define CCS_R_CFA_PATTERN_CONVERSION_CAPABILITY 0x1991 +#define CCS_R_CFA_PATTERN_CONVERSION_CAPABILITY CCI_REG8(0x1991) #define CCS_CFA_PATTERN_CONVERSION_CAPABILITY_BAYER BIT(0) -#define CCS_R_FLASH_MODE_CAPABILITY 0x1a02 +#define CCS_R_FLASH_MODE_CAPABILITY CCI_REG8(0x1a02) #define CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) -#define CCS_R_SA_STROBE_MODE_CAPABILITY 0x1a03 +#define CCS_R_SA_STROBE_MODE_CAPABILITY CCI_REG8(0x1a03) #define CCS_SA_STROBE_MODE_CAPABILITY_FIXED_WIDTH BIT(0) #define CCS_SA_STROBE_MODE_CAPABILITY_EDGE_CTRL BIT(1) -#define CCS_R_RESET_MAX_DELAY 0x1a10 -#define CCS_R_RESET_MIN_TIME 0x1a11 -#define CCS_R_PDAF_CAPABILITY_1 0x1b80 +#define CCS_R_RESET_MAX_DELAY CCI_REG8(0x1a10) +#define CCS_R_RESET_MIN_TIME CCI_REG8(0x1a11) +#define CCS_R_PDAF_CAPABILITY_1 CCI_REG8(0x1b80) #define CCS_PDAF_CAPABILITY_1_SUPPORTED BIT(0) #define CCS_PDAF_CAPABILITY_1_PROCESSED_BOTTOM_EMBEDDED BIT(1) #define CCS_PDAF_CAPABILITY_1_PROCESSED_INTERLEAVED BIT(2) @@ -940,19 +940,19 @@ #define CCS_PDAF_CAPABILITY_1_VISIBLE_PDAF_CORRECTION BIT(5) #define CCS_PDAF_CAPABILITY_1_VC_INTERLEAVING BIT(6) #define CCS_PDAF_CAPABILITY_1_DT_INTERLEAVING BIT(7) -#define CCS_R_PDAF_CAPABILITY_2 0x1b81 +#define CCS_R_PDAF_CAPABILITY_2 CCI_REG8(0x1b81) #define CCS_PDAF_CAPABILITY_2_ROI BIT(0) #define CCS_PDAF_CAPABILITY_2_AFTER_DIGITAL_CROP BIT(1) #define CCS_PDAF_CAPABILITY_2_CTRL_RETIMED BIT(2) -#define CCS_R_BRACKETING_LUT_CAPABILITY_1 0x1c00 +#define CCS_R_BRACKETING_LUT_CAPABILITY_1 CCI_REG8(0x1c00) #define CCS_BRACKETING_LUT_CAPABILITY_1_COARSE_INTEGRATION BIT(0) #define CCS_BRACKETING_LUT_CAPABILITY_1_GLOBAL_ANALOG_GAIN BIT(1) #define CCS_BRACKETING_LUT_CAPABILITY_1_FLASH BIT(4) #define CCS_BRACKETING_LUT_CAPABILITY_1_GLOBAL_DIGITAL_GAIN BIT(5) #define CCS_BRACKETING_LUT_CAPABILITY_1_ALTERNATE_GLOBAL_ANALOG_GAIN BIT(6) -#define CCS_R_BRACKETING_LUT_CAPABILITY_2 0x1c01 +#define CCS_R_BRACKETING_LUT_CAPABILITY_2 CCI_REG8(0x1c01) #define CCS_BRACKETING_LUT_CAPABILITY_2_SINGLE_BRACKETING_MODE BIT(0) #define CCS_BRACKETING_LUT_CAPABILITY_2_LOOPED_BRACKETING_MODE BIT(1) -#define CCS_R_BRACKETING_LUT_SIZE 0x1c02 +#define CCS_R_BRACKETING_LUT_SIZE CCI_REG8(0x1c02) #endif /* __CCS_REGS_H__ */ diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 9c3587b2f..096573845 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -13,6 +13,7 @@ #define __CCS_H__ #include +#include #include #include @@ -211,6 +212,7 @@ struct ccs_sensor { struct clk *ext_clk; struct gpio_desc *xshutdown; struct gpio_desc *reset; + struct regmap *regmap; void *ccs_limits; u8 nbinning_subtypes; struct ccs_binning_subtype binning_subtypes[CCS_LIM_BINNING_SUB_TYPE_MAX_N + 1]; @@ -236,6 +238,7 @@ struct ccs_sensor { bool streaming; bool dev_init_done; + bool handler_setup_needed; u8 compressed_min_bpp; struct ccs_module_info minfo; diff --git a/drivers/media/i2c/ccs/smiapp-reg-defs.h b/drivers/media/i2c/ccs/smiapp-reg-defs.h index 177e3e512..ebd0f90e1 100644 --- a/drivers/media/i2c/ccs/smiapp-reg-defs.h +++ b/drivers/media/i2c/ccs/smiapp-reg-defs.h @@ -12,481 +12,484 @@ #ifndef __SMIAPP_REG_DEFS_H__ #define __SMIAPP_REG_DEFS_H__ +#include +#include + /* Register addresses */ -#define SMIAPP_REG_U16_MODEL_ID (0x0000 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR 0x0002 -#define SMIAPP_REG_U8_MANUFACTURER_ID 0x0003 -#define SMIAPP_REG_U8_SMIA_VERSION 0x0004 -#define SMIAPP_REG_U8_FRAME_COUNT 0x0005 -#define SMIAPP_REG_U8_PIXEL_ORDER 0x0006 -#define SMIAPP_REG_U16_DATA_PEDESTAL (0x0008 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_PIXEL_DEPTH 0x000c -#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR 0x0010 -#define SMIAPP_REG_U8_SMIAPP_VERSION 0x0011 -#define SMIAPP_REG_U8_MODULE_DATE_YEAR 0x0012 -#define SMIAPP_REG_U8_MODULE_DATE_MONTH 0x0013 -#define SMIAPP_REG_U8_MODULE_DATE_DAY 0x0014 -#define SMIAPP_REG_U8_MODULE_DATE_PHASE 0x0015 -#define SMIAPP_REG_U16_SENSOR_MODEL_ID (0x0016 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER 0x0018 -#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID 0x0019 -#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION 0x001a -#define SMIAPP_REG_U32_SERIAL_NUMBER (0x001c | CCS_FL_32BIT) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE 0x0040 -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE 0x0041 -#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) ((0x0042 + ((n) << 1)) | CCS_FL_16BIT) /* 0 <= n <= 14 */ -#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) ((0x0060 + ((n) << 2)) | CCS_FL_32BIT) /* 0 <= n <= 7 */ -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY (0x0080 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN (0x0084 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX (0x0086 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP (0x0088 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE (0x008a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 (0x008c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 (0x008e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 (0x0090 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 (0x0092 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE 0x00c0 -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE 0x00c1 -#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) ((0x00c2 + ((n) << 1)) | CCS_FL_16BIT) -#define SMIAPP_REG_U8_MODE_SELECT 0x0100 -#define SMIAPP_REG_U8_IMAGE_ORIENTATION 0x0101 -#define SMIAPP_REG_U8_SOFTWARE_RESET 0x0103 -#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD 0x0104 -#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES 0x0105 -#define SMIAPP_REG_U8_FAST_STANDBY_CTRL 0x0106 -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL 0x0107 -#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL 0x0108 -#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL 0x0109 -#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER 0x0110 -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE 0x0111 -#define SMIAPP_REG_U16_CSI_DATA_FORMAT (0x0112 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_CSI_LANE_MODE 0x0114 -#define SMIAPP_REG_U8_CSI2_10_TO_8_DT 0x0115 -#define SMIAPP_REG_U8_CSI2_10_TO_7_DT 0x0116 -#define SMIAPP_REG_U8_CSI2_10_TO_6_DT 0x0117 -#define SMIAPP_REG_U8_CSI2_12_TO_8_DT 0x0118 -#define SMIAPP_REG_U8_CSI2_12_TO_7_DT 0x0119 -#define SMIAPP_REG_U8_CSI2_12_TO_6_DT 0x011a -#define SMIAPP_REG_U8_CSI2_14_TO_10_DT 0x011b -#define SMIAPP_REG_U8_CSI2_14_TO_8_DT 0x011c -#define SMIAPP_REG_U8_CSI2_16_TO_10_DT 0x011d -#define SMIAPP_REG_U8_CSI2_16_TO_8_DT 0x011e -#define SMIAPP_REG_U8_GAIN_MODE 0x0120 -#define SMIAPP_REG_U16_VANA_VOLTAGE (0x0130 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VDIG_VOLTAGE (0x0132 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VIO_VOLTAGE (0x0134 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ (0x0136 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL 0x0138 -#define SMIAPP_REG_U8_TEMP_SENSOR_MODE 0x0139 -#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT 0x013a -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME (0x0200 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME (0x0202 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL (0x0204 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR (0x0206 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED (0x0208 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE (0x020a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB (0x020c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR (0x020e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_RED (0x0210 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE (0x0212 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB (0x0214 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VT_PIX_CLK_DIV (0x0300 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VT_SYS_CLK_DIV (0x0302 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV (0x0304 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_PLL_MULTIPLIER (0x0306 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_OP_PIX_CLK_DIV (0x0308 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_OP_SYS_CLK_DIV (0x030a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FRAME_LENGTH_LINES (0x0340 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_LINE_LENGTH_PCK (0x0342 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_ADDR_START (0x0344 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_ADDR_START (0x0346 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_ADDR_END (0x0348 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_ADDR_END (0x034a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_OUTPUT_SIZE (0x034c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_OUTPUT_SIZE (0x034e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_EVEN_INC (0x0380 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_ODD_INC (0x0382 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_EVEN_INC (0x0384 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_ODD_INC (0x0386 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALING_MODE (0x0400 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING (0x0402 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALE_M (0x0404 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALE_N (0x0406 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET (0x0408 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET (0x040a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH (0x040c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT (0x040e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_COMPRESSION_MODE (0x0500 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TEST_PATTERN_MODE (0x0600 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TEST_DATA_RED (0x0602 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TEST_DATA_GREENR (0x0604 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TEST_DATA_BLUE (0x0606 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TEST_DATA_GREENB (0x0608 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH (0x060a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION (0x060c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH (0x060e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION (0x0610 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS (0x0700 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_TCLK_POST 0x0800 -#define SMIAPP_REG_U8_THS_PREPARE 0x0801 -#define SMIAPP_REG_U8_THS_ZERO_MIN 0x0802 -#define SMIAPP_REG_U8_THS_TRAIL 0x0803 -#define SMIAPP_REG_U8_TCLK_TRAIL_MIN 0x0804 -#define SMIAPP_REG_U8_TCLK_PREPARE 0x0805 -#define SMIAPP_REG_U8_TCLK_ZERO 0x0806 -#define SMIAPP_REG_U8_TLPX 0x0807 -#define SMIAPP_REG_U8_DPHY_CTRL 0x0808 -#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS (0x0820 | CCS_FL_32BIT) -#define SMIAPP_REG_U8_BINNING_MODE 0x0900 -#define SMIAPP_REG_U8_BINNING_TYPE 0x0901 -#define SMIAPP_REG_U8_BINNING_WEIGHTING 0x0902 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL 0x0a00 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS 0x0a01 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT 0x0a02 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 0x0a04 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 0x0a05 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 0x0a06 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 0x0a07 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 0x0a08 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 0x0a09 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 0x0a10 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 0x0a11 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 0x0a12 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 0x0a13 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 0x0a14 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 0x0a15 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 0x0a16 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 0x0a17 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 0x0a18 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 0x0a19 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 0x0a1a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 0x0a1b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 0x0a1c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 0x0a1d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 0x0a1e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 0x0a1f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 0x0a20 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 0x0a21 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 0x0a22 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 0x0a23 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 0x0a24 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 0x0a25 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 0x0a26 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 0x0a27 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 0x0a28 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 0x0a29 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 0x0a2a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 0x0a2b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 0x0a2c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 0x0a2d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 0x0a2e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 0x0a2f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 0x0a30 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 0x0a31 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 0x0a32 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 0x0a33 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 0x0a34 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 0x0a35 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 0x0a36 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 0x0a37 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 0x0a38 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 0x0a39 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 0x0a3a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 0x0a3b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 0x0a3c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 0x0a3d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 0x0a3e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 0x0a3f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 0x0a40 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 0x0a41 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 0x0a42 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 0x0a43 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL 0x0a44 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS 0x0a45 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT 0x0a46 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 0x0a48 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 0x0a49 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 0x0a4a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 0x0a4b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 0x0a4c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 0x0a4d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 0x0a4e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 0x0a4f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 0x0a50 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 0x0a51 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 0x0a52 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 0x0a53 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 0x0a54 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 0x0a55 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 0x0a56 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 0x0a57 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 0x0a58 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 0x0a59 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 0x0a5a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 0x0a5b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 0x0a5c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 0x0a5d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 0x0a5e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 0x0a5f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 0x0a60 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 0x0a61 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 0x0a62 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 0x0a63 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 0x0a64 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 0x0a65 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 0x0a66 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 0x0a67 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 0x0a68 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 0x0a69 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 0x0a6a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 0x0a6b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 0x0a6c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 0x0a6d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 0x0a6e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 0x0a6f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 0x0a70 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 0x0a71 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 0x0a72 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 0x0a73 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 0x0a74 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 0x0a75 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 0x0a76 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 0x0a77 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 0x0a78 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 0x0a79 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 0x0a7a -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 0x0a7b -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 0x0a7c -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 0x0a7d -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 0x0a7e -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 0x0a7f -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 0x0a80 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 0x0a81 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 0x0a82 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 0x0a83 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 0x0a84 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 0x0a85 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 0x0a86 -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 0x0a87 -#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE 0x0b00 -#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL 0x0b01 -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE 0x0b02 -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT 0x0b03 -#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE 0x0b04 -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE 0x0b05 -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE 0x0b06 -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT 0x0b07 -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE 0x0b08 -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT 0x0b09 -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE 0x0b0a -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT 0x0b0b -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE 0x0b0c -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT 0x0b0d -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE 0x0b0e -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST 0x0b0f -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST 0x0b10 -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE 0x0b11 -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST 0x0b12 -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE 0x0b13 -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST 0x0b14 -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE 0x0b15 -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST 0x0b16 -#define SMIAPP_REG_U8_EDOF_MODE 0x0b80 -#define SMIAPP_REG_U8_SHARPNESS 0x0b83 -#define SMIAPP_REG_U8_DENOISING 0x0b84 -#define SMIAPP_REG_U8_MODULE_SPECIFIC 0x0b85 -#define SMIAPP_REG_U16_DEPTH_OF_FIELD (0x0b86 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FOCUS_DISTANCE (0x0b88 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL 0x0b8a -#define SMIAPP_REG_U16_COLOUR_TEMPERATURE (0x0b8c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR (0x0b8e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED (0x0b90 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE (0x0b92 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB (0x0b94 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE 0x0bc0 -#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING (0x0bc2 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START (0x0bc4 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START (0x0bc6 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH (0x0bc8 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT (0x0bca | CCS_FL_16BIT) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 0x0c00 -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 0x0c01 -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 0x0c02 -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 0x0c03 -#define SMIAPP_REG_U16_TRDY_CTRL (0x0c04 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TRDOUT_CTRL (0x0c06 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL (0x0c08 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL (0x0c0a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL (0x0c0c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL (0x0c0e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL (0x0c10 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT 0x0c12 -#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT (0x0c14 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL (0x0c16 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL (0x0c18 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_FLASH_MODE_RS 0x0c1a -#define SMIAPP_REG_U8_FLASH_TRIGGER_RS 0x0c1b -#define SMIAPP_REG_U8_FLASH_STATUS 0x0c1c -#define SMIAPP_REG_U8_SA_STROBE_MODE 0x0c1d -#define SMIAPP_REG_U16_SA_STROBE_START_POINT (0x0c1e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL (0x0c20 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL (0x0c22 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_SA_STROBE_TRIGGER 0x0c24 -#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS 0x0c25 -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL (0x0c26 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL (0x0c28 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL 0x0c2a -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL 0x0c2b -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL (0x0c2c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL (0x0c2e | CCS_FL_16BIT) -#define SMIAPP_REG_U8_LOW_LEVEL_CTRL 0x0c80 -#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT (0x0c82 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 (0x0c84 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT 0x0c86 -#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 (0x0c88 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT 0x0c8a -#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 (0x0c8c | CCS_FL_16BIT) -#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT 0x0c8e -#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL 0x0d00 -#define SMIAPP_REG_U8_OPERATION_MODE 0x0d01 -#define SMIAPP_REG_U8_ACT_STATE1 0x0d02 -#define SMIAPP_REG_U8_ACT_STATE2 0x0d03 -#define SMIAPP_REG_U16_FOCUS_CHANGE (0x0d80 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL (0x0d82 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 (0x0d84 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 (0x0d86 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 0x0d88 -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 0x0d89 -#define SMIAPP_REG_U8_POSITION 0x0d8a -#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL 0x0e00 -#define SMIAPP_REG_U8_BRACKETING_LUT_MODE 0x0e01 -#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL 0x0e02 -#define SMIAPP_REG_U8_LUT_PARAMETERS_START 0x0e10 -#define SMIAPP_REG_U8_LUT_PARAMETERS_END 0x0eff -#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY (0x1000 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN (0x1004 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN (0x1006 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN (0x1008 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN (0x100a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY (0x1080 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN (0x1084 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX (0x1086 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE (0x1088 | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ (0x1100 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ (0x1104 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV (0x1108 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV (0x110a | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ (0x110c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ (0x1110 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER (0x1114 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER (0x1116 | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ (0x1118 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ (0x111c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV (0x1120 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV (0x1122 | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ (0x1124 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ (0x1128 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ (0x112c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ (0x1130 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV (0x1134 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV (0x1136 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES (0x1140 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES (0x1142 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK (0x1144 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK (0x1146 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK (0x1148 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES (0x114a | CCS_FL_16BIT) -#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE 0x114c -#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV (0x1160 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV (0x1162 | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ (0x1164 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ (0x1168 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV (0x116c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV (0x116e | CCS_FL_16BIT) -#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ (0x1170 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ (0x1174 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) -#define SMIAPP_REG_U16_X_ADDR_MIN (0x1180 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_ADDR_MIN (0x1182 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_X_ADDR_MAX (0x1184 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_Y_ADDR_MAX (0x1186 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE (0x1188 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE (0x118a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE (0x118c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE (0x118e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_EVEN_INC (0x11c0 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_EVEN_INC (0x11c2 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_ODD_INC (0x11c4 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_ODD_INC (0x11c6 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALING_CAPABILITY (0x1200 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALER_M_MIN (0x1204 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALER_M_MAX (0x1206 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALER_N_MIN (0x1208 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SCALER_N_MAX (0x120a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY (0x120c | CCS_FL_16BIT) -#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY 0x120e -#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY (0x1300 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED (0x1400 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED (0x1402 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED (0x1404 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN (0x1406 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN (0x1408 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN (0x140a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE (0x140c | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE (0x140e | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE (0x1410 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS (0x1500 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY 0x1502 -#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY 0x1600 -#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY 0x1601 -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY 0x1602 -#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY 0x1603 -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY 0x1604 -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS (0x1608 | CCS_FL_32BIT) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS (0x160c | CCS_FL_32BIT) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS (0x1610 | CCS_FL_32BIT) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS (0x1614 | CCS_FL_32BIT) -#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY 0x1618 -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN (0x1700 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN (0x1702 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN (0x1704 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN (0x1706 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN (0x1708 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN (0x170a | CCS_FL_16BIT) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN (0x170c | CCS_FL_16BIT) -#define SMIAPP_REG_U8_BINNING_CAPABILITY 0x1710 -#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY 0x1711 -#define SMIAPP_REG_U8_BINNING_SUBTYPES 0x1712 -#define SMIAPP_REG_U8_BINNING_TYPE_n(n) (0x1713 + (n)) /* 1 <= n <= 237 */ -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY 0x1800 -#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY 0x1900 -#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY 0x1901 -#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY 0x1902 -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY 0x1903 -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY (0x1904 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 (0x1906 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_EDOF_CAPABILITY 0x1980 -#define SMIAPP_REG_U8_ESTIMATION_FRAMES 0x1981 -#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ 0x1982 -#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ 0x1983 -#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ 0x1984 -#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ 0x1985 -#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ 0x1986 -#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY 0x1987 -#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM 0x1988 -#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY 0x19c0 -#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY 0x19c1 -#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD (0x19c2 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE (0x19c4 | CCS_FL_16BIT) -#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN (0x1a00 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY 0x1a02 -#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR (0x1b02 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY 0x1b04 -#define SMIAPP_REG_U16_ACTUATOR_TYPE (0x1b40 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS 0x1b42 -#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS (0x1b44 | CCS_FL_16BIT) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 0x1c00 -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 0x1c01 -#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE 0x1c02 +#define SMIAPP_REG_U16_MODEL_ID CCI_REG16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR CCI_REG8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID CCI_REG8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION CCI_REG8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT CCI_REG8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER CCI_REG8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL CCI_REG16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH CCI_REG8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR CCI_REG8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION CCI_REG8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR CCI_REG8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH CCI_REG8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY CCI_REG8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE CCI_REG8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID CCI_REG16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER CCI_REG8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID CCI_REG8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION CCI_REG8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER CCI_REG32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE CCI_REG8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE CCI_REG8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) CCI_REG16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) CCI_REG32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY CCI_REG16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN CCI_REG16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX CCI_REG16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP CCI_REG16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE CCI_REG16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 CCI_REG16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 CCI_REG16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 CCI_REG16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 CCI_REG16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE CCI_REG8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE CCI_REG8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) CCI_REG16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT CCI_REG8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION CCI_REG8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET CCI_REG8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD CCI_REG8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES CCI_REG8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL CCI_REG8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL CCI_REG8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL CCI_REG8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL CCI_REG8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER CCI_REG8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE CCI_REG8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE CCI_REG8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT CCI_REG8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT CCI_REG8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT CCI_REG8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT CCI_REG8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT CCI_REG8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT CCI_REG8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT CCI_REG8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT CCI_REG8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT CCI_REG8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT CCI_REG8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE CCI_REG8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE CCI_REG16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE CCI_REG16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE CCI_REG16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ CCI_REG16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL CCI_REG8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE CCI_REG8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT CCI_REG8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME CCI_REG16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME CCI_REG16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL CCI_REG16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR CCI_REG16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED CCI_REG16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE CCI_REG16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB CCI_REG16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR CCI_REG16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED CCI_REG16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE CCI_REG16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB CCI_REG16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV CCI_REG16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV CCI_REG16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV CCI_REG16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER CCI_REG16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV CCI_REG16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV CCI_REG16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES CCI_REG16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START CCI_REG16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START CCI_REG16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END CCI_REG16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END CCI_REG16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC CCI_REG16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC CCI_REG16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC CCI_REG16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC CCI_REG16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE CCI_REG16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING CCI_REG16(0x0402) +#define SMIAPP_REG_U16_SCALE_M CCI_REG16(0x0404) +#define SMIAPP_REG_U16_SCALE_N CCI_REG16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET CCI_REG16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET CCI_REG16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH CCI_REG16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT CCI_REG16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE CCI_REG16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE CCI_REG16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED CCI_REG16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR CCI_REG16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE CCI_REG16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB CCI_REG16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH CCI_REG16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION CCI_REG16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH CCI_REG16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION CCI_REG16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS CCI_REG16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST CCI_REG8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE CCI_REG8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN CCI_REG8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL CCI_REG8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN CCI_REG8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE CCI_REG8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO CCI_REG8(0x0806) +#define SMIAPP_REG_U8_TLPX CCI_REG8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL CCI_REG8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS CCI_REG32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE CCI_REG8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE CCI_REG8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING CCI_REG8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL CCI_REG8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS CCI_REG8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT CCI_REG8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 CCI_REG8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 CCI_REG8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 CCI_REG8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 CCI_REG8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 CCI_REG8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 CCI_REG8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 CCI_REG8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 CCI_REG8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 CCI_REG8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 CCI_REG8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 CCI_REG8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 CCI_REG8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 CCI_REG8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 CCI_REG8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 CCI_REG8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 CCI_REG8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 CCI_REG8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 CCI_REG8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 CCI_REG8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 CCI_REG8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 CCI_REG8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 CCI_REG8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 CCI_REG8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 CCI_REG8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 CCI_REG8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 CCI_REG8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 CCI_REG8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 CCI_REG8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 CCI_REG8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 CCI_REG8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 CCI_REG8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 CCI_REG8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 CCI_REG8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 CCI_REG8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 CCI_REG8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 CCI_REG8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 CCI_REG8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 CCI_REG8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 CCI_REG8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 CCI_REG8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 CCI_REG8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 CCI_REG8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 CCI_REG8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 CCI_REG8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 CCI_REG8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 CCI_REG8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 CCI_REG8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 CCI_REG8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 CCI_REG8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 CCI_REG8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 CCI_REG8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 CCI_REG8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 CCI_REG8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 CCI_REG8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 CCI_REG8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 CCI_REG8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 CCI_REG8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 CCI_REG8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL CCI_REG8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS CCI_REG8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT CCI_REG8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 CCI_REG8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 CCI_REG8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 CCI_REG8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 CCI_REG8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 CCI_REG8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 CCI_REG8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 CCI_REG8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 CCI_REG8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 CCI_REG8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 CCI_REG8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 CCI_REG8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 CCI_REG8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 CCI_REG8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 CCI_REG8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 CCI_REG8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 CCI_REG8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 CCI_REG8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 CCI_REG8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 CCI_REG8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 CCI_REG8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 CCI_REG8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 CCI_REG8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 CCI_REG8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 CCI_REG8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 CCI_REG8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 CCI_REG8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 CCI_REG8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 CCI_REG8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 CCI_REG8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 CCI_REG8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 CCI_REG8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 CCI_REG8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 CCI_REG8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 CCI_REG8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 CCI_REG8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 CCI_REG8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 CCI_REG8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 CCI_REG8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 CCI_REG8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 CCI_REG8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 CCI_REG8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 CCI_REG8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 CCI_REG8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 CCI_REG8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 CCI_REG8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 CCI_REG8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 CCI_REG8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 CCI_REG8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 CCI_REG8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 CCI_REG8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 CCI_REG8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 CCI_REG8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 CCI_REG8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 CCI_REG8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 CCI_REG8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 CCI_REG8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 CCI_REG8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 CCI_REG8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 CCI_REG8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 CCI_REG8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 CCI_REG8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 CCI_REG8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 CCI_REG8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 CCI_REG8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE CCI_REG8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL CCI_REG8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE CCI_REG8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT CCI_REG8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE CCI_REG8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE CCI_REG8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT CCI_REG8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE CCI_REG8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT CCI_REG8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT CCI_REG8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE CCI_REG8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT CCI_REG8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST CCI_REG8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST CCI_REG8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST CCI_REG8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST CCI_REG8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE CCI_REG8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST CCI_REG8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE CCI_REG8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS CCI_REG8(0x0b83) +#define SMIAPP_REG_U8_DENOISING CCI_REG8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC CCI_REG8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD CCI_REG16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE CCI_REG16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL CCI_REG8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE CCI_REG16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR CCI_REG16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED CCI_REG16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE CCI_REG16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB CCI_REG16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE CCI_REG8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING CCI_REG16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START CCI_REG16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START CCI_REG16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH CCI_REG16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT CCI_REG16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 CCI_REG8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 CCI_REG8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 CCI_REG8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 CCI_REG8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL CCI_REG16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL CCI_REG16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL CCI_REG16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL CCI_REG16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL CCI_REG16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL CCI_REG16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL CCI_REG16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT CCI_REG8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT CCI_REG16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL CCI_REG16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL CCI_REG16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS CCI_REG8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS CCI_REG8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS CCI_REG8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE CCI_REG8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT CCI_REG16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL CCI_REG16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL CCI_REG16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER CCI_REG8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS CCI_REG8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL CCI_REG16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL CCI_REG16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL CCI_REG8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL CCI_REG8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL CCI_REG16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL CCI_REG16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL CCI_REG8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT CCI_REG16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 CCI_REG16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT CCI_REG8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 CCI_REG16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT CCI_REG8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 CCI_REG16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT CCI_REG8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL CCI_REG8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE CCI_REG8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 CCI_REG8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 CCI_REG8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE CCI_REG16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL CCI_REG16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 CCI_REG16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 CCI_REG16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 CCI_REG8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 CCI_REG8(0x0d89) +#define SMIAPP_REG_U8_POSITION CCI_REG8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL CCI_REG8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE CCI_REG8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL CCI_REG8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START CCI_REG8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END CCI_REG8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY CCI_REG16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN CCI_REG16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN CCI_REG16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN CCI_REG16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN CCI_REG16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY CCI_REG16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN CCI_REG16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX CCI_REG16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE CCI_REG16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ (CCI_REG32(0x1100) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ (CCI_REG32(0x1104) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV CCI_REG16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV CCI_REG16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ (CCI_REG32(0x110c) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ (CCI_REG32(0x1110) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER CCI_REG16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER CCI_REG16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ (CCI_REG32(0x1118) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ (CCI_REG32(0x111c) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV CCI_REG16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV CCI_REG16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ (CCI_REG32(0x1124) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ (CCI_REG32(0x1128) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ (CCI_REG32(0x112c) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ (CCI_REG32(0x1130) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV CCI_REG16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV CCI_REG16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES CCI_REG16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES CCI_REG16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK CCI_REG16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK CCI_REG16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK CCI_REG16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES CCI_REG16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE CCI_REG8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV CCI_REG16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV CCI_REG16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ (CCI_REG32(0x1164) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ (CCI_REG32(0x1168) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV CCI_REG16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV CCI_REG16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ (CCI_REG32(0x1170) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ (CCI_REG32(0x1174) | CCS_FL_FLOAT_IREAL) +#define SMIAPP_REG_U16_X_ADDR_MIN CCI_REG16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN CCI_REG16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX CCI_REG16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX CCI_REG16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE CCI_REG16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE CCI_REG16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE CCI_REG16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE CCI_REG16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC CCI_REG16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC CCI_REG16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC CCI_REG16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC CCI_REG16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY CCI_REG16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN CCI_REG16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX CCI_REG16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN CCI_REG16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX CCI_REG16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY CCI_REG16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY CCI_REG8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY CCI_REG16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED CCI_REG16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED CCI_REG16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED CCI_REG16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN CCI_REG16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN CCI_REG16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN CCI_REG16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE CCI_REG16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE CCI_REG16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE CCI_REG16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS CCI_REG16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY CCI_REG8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY CCI_REG8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY CCI_REG8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY CCI_REG8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY CCI_REG8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY CCI_REG8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS CCI_REG32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS CCI_REG32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS CCI_REG32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS CCI_REG32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY CCI_REG8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN CCI_REG16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN CCI_REG16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN CCI_REG16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN CCI_REG16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN CCI_REG16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN CCI_REG16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN CCI_REG16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY CCI_REG8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY CCI_REG8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES CCI_REG8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) CCI_REG8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY CCI_REG8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY CCI_REG8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY CCI_REG8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY CCI_REG8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY CCI_REG8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY CCI_REG16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 CCI_REG16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY CCI_REG8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES CCI_REG8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ CCI_REG8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ CCI_REG8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ CCI_REG8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ CCI_REG8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ CCI_REG8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY CCI_REG8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM CCI_REG8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY CCI_REG8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY CCI_REG8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD CCI_REG16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE CCI_REG16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN CCI_REG16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY CCI_REG8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR CCI_REG16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY CCI_REG8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE CCI_REG16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS CCI_REG8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS CCI_REG16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 CCI_REG8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 CCI_REG8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE CCI_REG8(0x1c02) /* Register bit definitions */ #define SMIAPP_IMAGE_ORIENTATION_HFLIP BIT(0) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 8e9ebed09..ca9bb29da 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -424,8 +424,7 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, } /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -444,8 +443,8 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub913_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub913_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -504,7 +503,6 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = { .get_frame_desc = ub913_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub913_set_fmt, - .init_cfg = ub913_init_cfg, }; static const struct v4l2_subdev_ops ub913_subdev_ops = { @@ -512,6 +510,10 @@ static const struct v4l2_subdev_ops ub913_subdev_ops = { .pad = &ub913_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub913_internal_ops = { + .init_state = ub913_init_state, +}; + static const struct media_entity_operations ub913_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -745,6 +747,7 @@ static int ub913_subdev_init(struct ub913_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); + priv->sd.internal_ops = &ub913_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; priv->sd.entity.ops = &ub913_entity_ops; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 644022312..16f88db14 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -558,8 +558,7 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return v4l2_subdev_get_fmt(sd, state, format); /* Set sink format */ - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -576,8 +575,8 @@ static int ub953_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub953_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub953_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_route routes[] = { { @@ -714,7 +713,6 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = { .get_frame_desc = ub953_get_frame_desc, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub953_set_fmt, - .init_cfg = ub953_init_cfg, }; static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = { @@ -728,6 +726,10 @@ static const struct v4l2_subdev_ops ub953_subdev_ops = { .pad = &ub953_pad_ops, }; +static const struct v4l2_subdev_internal_ops ub953_internal_ops = { + .init_state = ub953_init_state, +}; + static const struct media_entity_operations ub953_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1241,6 +1243,7 @@ static int ub953_subdev_init(struct ub953_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + priv->sd.internal_ops = &ub953_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index b8f3e5ca0..ffe5f25f8 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2451,9 +2451,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, if (rx_data[nport].num_streams > 2) return -EPIPE; - fmt = v4l2_subdev_state_get_stream_format(state, - route->sink_pad, - route->sink_stream); + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); if (!fmt) return -EPIPE; @@ -2842,8 +2841,8 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, const struct ub960_format_info *ub960_fmt; struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_state_get_stream_format(state, pad, - route->source_stream); + fmt = v4l2_subdev_state_get_format(state, pad, + route->source_stream); if (!fmt) { ret = -EINVAL; @@ -2891,8 +2890,7 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, if (!ub960_find_format(format->format.code)) format->format.code = ub960_formats[0].code; - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -2908,8 +2906,8 @@ static int ub960_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ub960_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ub960_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ub960_data *priv = sd_to_ub960(sd); @@ -2940,8 +2938,6 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ub960_set_fmt, - - .init_cfg = ub960_init_cfg, }; static int ub960_log_status(struct v4l2_subdev *sd) @@ -3093,6 +3089,10 @@ static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; +static const struct v4l2_subdev_internal_ops ub960_internal_ops = { + .init_state = ub960_init_state, +}; + static const struct v4l2_subdev_ops ub960_subdev_ops = { .core = &ub960_subdev_core_ops, .pad = &ub960_pad_ops, @@ -3652,6 +3652,7 @@ static int ub960_create_subdev(struct ub960_data *priv) int ret; v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + priv->sd.internal_ops = &ub960_internal_ops; v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); priv->sd.ctrl_handler = &priv->ctrl_handler; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index d6fc843f9..f548b1bb7 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -995,8 +995,7 @@ __et8ek8_get_pad_format(struct et8ek8_sensor *sensor, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: @@ -1047,10 +1046,18 @@ static int et8ek8_set_pad_format(struct v4l2_subdev *subdev, } static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + memset(fi, 0, sizeof(*fi)); fi->interval = sensor->current_reglist->mode.timeperframe; @@ -1058,11 +1065,19 @@ static int et8ek8_get_frame_interval(struct v4l2_subdev *subdev, } static int et8ek8_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); struct et8ek8_reglist *reglist; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + reglist = et8ek8_reglist_find_mode_ival(&meta_reglist, sensor->current_reglist, &fi->interval); @@ -1343,8 +1358,6 @@ static int et8ek8_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_video_ops et8ek8_video_ops = { .s_stream = et8ek8_s_stream, - .g_frame_interval = et8ek8_get_frame_interval, - .s_frame_interval = et8ek8_set_frame_interval, }; static const struct v4l2_subdev_core_ops et8ek8_core_ops = { @@ -1357,6 +1370,8 @@ static const struct v4l2_subdev_pad_ops et8ek8_pad_ops = { .enum_frame_interval = et8ek8_enum_frame_ival, .get_fmt = et8ek8_get_pad_format, .set_fmt = et8ek8_set_pad_format, + .get_frame_interval = et8ek8_get_frame_interval, + .set_frame_interval = et8ek8_set_frame_interval, }; static const struct v4l2_subdev_ops et8ek8_ops = { diff --git a/drivers/media/i2c/gc0308.c b/drivers/media/i2c/gc0308.c new file mode 100644 index 000000000..fa754a8a3 --- /dev/null +++ b/drivers/media/i2c/gc0308.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the GalaxyCore GC0308 camera sensor. + * + * Copyright (c) 2023 Sebastian Reichel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Analog & CISCTL*/ +#define GC0308_CHIP_ID CCI_REG8(0x000) +#define GC0308_HBLANK CCI_REG8(0x001) +#define GC0308_VBLANK CCI_REG8(0x002) +#define GC0308_EXP CCI_REG16(0x003) +#define GC0308_ROW_START CCI_REG16(0x005) +#define GC0308_COL_START CCI_REG16(0x007) +#define GC0308_WIN_HEIGHT CCI_REG16(0x009) +#define GC0308_WIN_WIDTH CCI_REG16(0x00b) +#define GC0308_VS_START_TIME CCI_REG8(0x00d) /* in rows */ +#define GC0308_VS_END_TIME CCI_REG8(0x00e) /* in rows */ +#define GC0308_VB_HB CCI_REG8(0x00f) +#define GC0308_RSH_WIDTH CCI_REG8(0x010) +#define GC0308_TSP_WIDTH CCI_REG8(0x011) +#define GC0308_SAMPLE_HOLD_DELAY CCI_REG8(0x012) +#define GC0308_ROW_TAIL_WIDTH CCI_REG8(0x013) +#define GC0308_CISCTL_MODE1 CCI_REG8(0x014) +#define GC0308_CISCTL_MODE2 CCI_REG8(0x015) +#define GC0308_CISCTL_MODE3 CCI_REG8(0x016) +#define GC0308_CISCTL_MODE4 CCI_REG8(0x017) +#define GC0308_ANALOG_MODE1 CCI_REG8(0x01a) +#define GC0308_ANALOG_MODE2 CCI_REG8(0x01b) +#define GC0308_HRST_RSG_V18 CCI_REG8(0x01c) +#define GC0308_VREF_V25 CCI_REG8(0x01d) +#define GC0308_ADC_R CCI_REG8(0x01e) +#define GC0308_PAD_DRV CCI_REG8(0x01f) +#define GC0308_SOFT_RESET CCI_REG8(0x0fe) + +/* ISP */ +#define GC0308_BLOCK_EN1 CCI_REG8(0x020) +#define GC0308_BLOCK_EN2 CCI_REG8(0x021) +#define GC0308_AAAA_EN CCI_REG8(0x022) +#define GC0308_SPECIAL_EFFECT CCI_REG8(0x023) +#define GC0308_OUT_FORMAT CCI_REG8(0x024) +#define GC0308_OUT_EN CCI_REG8(0x025) +#define GC0308_SYNC_MODE CCI_REG8(0x026) +#define GC0308_CLK_DIV_MODE CCI_REG8(0x028) +#define GC0308_BYPASS_MODE CCI_REG8(0x029) +#define GC0308_CLK_GATING CCI_REG8(0x02a) +#define GC0308_DITHER_MODE CCI_REG8(0x02b) +#define GC0308_DITHER_BIT CCI_REG8(0x02c) +#define GC0308_DEBUG_MODE1 CCI_REG8(0x02d) +#define GC0308_DEBUG_MODE2 CCI_REG8(0x02e) +#define GC0308_DEBUG_MODE3 CCI_REG8(0x02f) +#define GC0308_CROP_WIN_MODE CCI_REG8(0x046) +#define GC0308_CROP_WIN_Y1 CCI_REG8(0x047) +#define GC0308_CROP_WIN_X1 CCI_REG8(0x048) +#define GC0308_CROP_WIN_HEIGHT CCI_REG16(0x049) +#define GC0308_CROP_WIN_WIDTH CCI_REG16(0x04b) + +/* BLK */ +#define GC0308_BLK_MODE CCI_REG8(0x030) +#define GC0308_BLK_LIMIT_VAL CCI_REG8(0x031) +#define GC0308_GLOBAL_OFF CCI_REG8(0x032) +#define GC0308_CURRENT_R_OFF CCI_REG8(0x033) +#define GC0308_CURRENT_G_OFF CCI_REG8(0x034) +#define GC0308_CURRENT_B_OFF CCI_REG8(0x035) +#define GC0308_CURRENT_R_DARK_CURRENT CCI_REG8(0x036) +#define GC0308_CURRENT_G_DARK_CURRENT CCI_REG8(0x037) +#define GC0308_CURRENT_B_DARK_CURRENT CCI_REG8(0x038) +#define GC0308_EXP_RATE_DARKC CCI_REG8(0x039) +#define GC0308_OFF_SUBMODE CCI_REG8(0x03a) +#define GC0308_DARKC_SUBMODE CCI_REG8(0x03b) +#define GC0308_MANUAL_G1_OFF CCI_REG8(0x03c) +#define GC0308_MANUAL_R1_OFF CCI_REG8(0x03d) +#define GC0308_MANUAL_B2_OFF CCI_REG8(0x03e) +#define GC0308_MANUAL_G2_OFF CCI_REG8(0x03f) + +/* PREGAIN */ +#define GC0308_GLOBAL_GAIN CCI_REG8(0x050) +#define GC0308_AUTO_PREGAIN CCI_REG8(0x051) +#define GC0308_AUTO_POSTGAIN CCI_REG8(0x052) +#define GC0308_CHANNEL_GAIN_G1 CCI_REG8(0x053) +#define GC0308_CHANNEL_GAIN_R CCI_REG8(0x054) +#define GC0308_CHANNEL_GAIN_B CCI_REG8(0x055) +#define GC0308_CHANNEL_GAIN_G2 CCI_REG8(0x056) +#define GC0308_R_RATIO CCI_REG8(0x057) +#define GC0308_G_RATIO CCI_REG8(0x058) +#define GC0308_B_RATIO CCI_REG8(0x059) +#define GC0308_AWB_R_GAIN CCI_REG8(0x05a) +#define GC0308_AWB_G_GAIN CCI_REG8(0x05b) +#define GC0308_AWB_B_GAIN CCI_REG8(0x05c) +#define GC0308_LSC_DEC_LVL1 CCI_REG8(0x05d) +#define GC0308_LSC_DEC_LVL2 CCI_REG8(0x05e) +#define GC0308_LSC_DEC_LVL3 CCI_REG8(0x05f) + +/* DNDD */ +#define GC0308_DN_MODE_EN CCI_REG8(0x060) +#define GC0308_DN_MODE_RATIO CCI_REG8(0x061) +#define GC0308_DN_BILAT_B_BASE CCI_REG8(0x062) +#define GC0308_DN_B_INCR CCI_REG8(0x063) +#define GC0308_DN_BILAT_N_BASE CCI_REG8(0x064) +#define GC0308_DN_N_INCR CCI_REG8(0x065) +#define GC0308_DD_DARK_BRIGHT_TH CCI_REG8(0x066) +#define GC0308_DD_FLAT_TH CCI_REG8(0x067) +#define GC0308_DD_LIMIT CCI_REG8(0x068) + +/* ASDE - Auto Saturation De-noise and Edge-Enhancement */ +#define GC0308_ASDE_GAIN_TRESH CCI_REG8(0x069) +#define GC0308_ASDE_GAIN_MODE CCI_REG8(0x06a) +#define GC0308_ASDE_DN_SLOPE CCI_REG8(0x06b) +#define GC0308_ASDE_DD_BRIGHT CCI_REG8(0x06c) +#define GC0308_ASDE_DD_LIMIT CCI_REG8(0x06d) +#define GC0308_ASDE_AUTO_EE1 CCI_REG8(0x06e) +#define GC0308_ASDE_AUTO_EE2 CCI_REG8(0x06f) +#define GC0308_ASDE_AUTO_SAT_DEC_SLOPE CCI_REG8(0x070) +#define GC0308_ASDE_AUTO_SAT_LOW_LIMIT CCI_REG8(0x071) + +/* INTPEE - Interpolation and Edge-Enhancement */ +#define GC0308_EEINTP_MODE_1 CCI_REG8(0x072) +#define GC0308_EEINTP_MODE_2 CCI_REG8(0x073) +#define GC0308_DIRECTION_TH1 CCI_REG8(0x074) +#define GC0308_DIRECTION_TH2 CCI_REG8(0x075) +#define GC0308_DIFF_HV_TI_TH CCI_REG8(0x076) +#define GC0308_EDGE12_EFFECT CCI_REG8(0x077) +#define GC0308_EDGE_POS_RATIO CCI_REG8(0x078) +#define GC0308_EDGE1_MINMAX CCI_REG8(0x079) +#define GC0308_EDGE2_MINMAX CCI_REG8(0x07a) +#define GC0308_EDGE12_TH CCI_REG8(0x07b) +#define GC0308_EDGE_MAX CCI_REG8(0x07c) + +/* ABB - Auto Black Balance */ +#define GC0308_ABB_MODE CCI_REG8(0x080) +#define GC0308_ABB_TARGET_AVGH CCI_REG8(0x081) +#define GC0308_ABB_TARGET_AVGL CCI_REG8(0x082) +#define GC0308_ABB_LIMIT_VAL CCI_REG8(0x083) +#define GC0308_ABB_SPEED CCI_REG8(0x084) +#define GC0308_CURR_R_BLACK_LVL CCI_REG8(0x085) +#define GC0308_CURR_G_BLACK_LVL CCI_REG8(0x086) +#define GC0308_CURR_B_BLACK_LVL CCI_REG8(0x087) +#define GC0308_CURR_R_BLACK_FACTOR CCI_REG8(0x088) +#define GC0308_CURR_G_BLACK_FACTOR CCI_REG8(0x089) +#define GC0308_CURR_B_BLACK_FACTOR CCI_REG8(0x08a) + +/* LSC - Lens Shading Correction */ +#define GC0308_LSC_RED_B2 CCI_REG8(0x08b) +#define GC0308_LSC_GREEN_B2 CCI_REG8(0x08c) +#define GC0308_LSC_BLUE_B2 CCI_REG8(0x08d) +#define GC0308_LSC_RED_B4 CCI_REG8(0x08e) +#define GC0308_LSC_GREEN_B4 CCI_REG8(0x08f) +#define GC0308_LSC_BLUE_B4 CCI_REG8(0x090) +#define GC0308_LSC_ROW_CENTER CCI_REG8(0x091) +#define GC0308_LSC_COL_CENTER CCI_REG8(0x092) + +/* CC - Channel Coefficient */ +#define GC0308_CC_MATRIX_C11 CCI_REG8(0x093) +#define GC0308_CC_MATRIX_C12 CCI_REG8(0x094) +#define GC0308_CC_MATRIX_C13 CCI_REG8(0x095) +#define GC0308_CC_MATRIX_C21 CCI_REG8(0x096) +#define GC0308_CC_MATRIX_C22 CCI_REG8(0x097) +#define GC0308_CC_MATRIX_C23 CCI_REG8(0x098) +#define GC0308_CC_MATRIX_C41 CCI_REG8(0x09c) +#define GC0308_CC_MATRIX_C42 CCI_REG8(0x09d) +#define GC0308_CC_MATRIX_C43 CCI_REG8(0x09e) + +/* GAMMA */ +#define GC0308_GAMMA_OUT0 CCI_REG8(0x09f) +#define GC0308_GAMMA_OUT1 CCI_REG8(0x0a0) +#define GC0308_GAMMA_OUT2 CCI_REG8(0x0a1) +#define GC0308_GAMMA_OUT3 CCI_REG8(0x0a2) +#define GC0308_GAMMA_OUT4 CCI_REG8(0x0a3) +#define GC0308_GAMMA_OUT5 CCI_REG8(0x0a4) +#define GC0308_GAMMA_OUT6 CCI_REG8(0x0a5) +#define GC0308_GAMMA_OUT7 CCI_REG8(0x0a6) +#define GC0308_GAMMA_OUT8 CCI_REG8(0x0a7) +#define GC0308_GAMMA_OUT9 CCI_REG8(0x0a8) +#define GC0308_GAMMA_OUT10 CCI_REG8(0x0a9) +#define GC0308_GAMMA_OUT11 CCI_REG8(0x0aa) +#define GC0308_GAMMA_OUT12 CCI_REG8(0x0ab) +#define GC0308_GAMMA_OUT13 CCI_REG8(0x0ac) +#define GC0308_GAMMA_OUT14 CCI_REG8(0x0ad) +#define GC0308_GAMMA_OUT15 CCI_REG8(0x0ae) +#define GC0308_GAMMA_OUT16 CCI_REG8(0x0af) + +/* YCP */ +#define GC0308_GLOBAL_SATURATION CCI_REG8(0x0b0) +#define GC0308_SATURATION_CB CCI_REG8(0x0b1) +#define GC0308_SATURATION_CR CCI_REG8(0x0b2) +#define GC0308_LUMA_CONTRAST CCI_REG8(0x0b3) +#define GC0308_CONTRAST_CENTER CCI_REG8(0x0b4) +#define GC0308_LUMA_OFFSET CCI_REG8(0x0b5) +#define GC0308_SKIN_CB_CENTER CCI_REG8(0x0b6) +#define GC0308_SKIN_CR_CENTER CCI_REG8(0x0b7) +#define GC0308_SKIN_RADIUS_SQUARE CCI_REG8(0x0b8) +#define GC0308_SKIN_BRIGHTNESS CCI_REG8(0x0b9) +#define GC0308_FIXED_CB CCI_REG8(0x0ba) +#define GC0308_FIXED_CR CCI_REG8(0x0bb) +#define GC0308_EDGE_DEC_SA CCI_REG8(0x0bd) +#define GC0308_AUTO_GRAY_MODE CCI_REG8(0x0be) +#define GC0308_SATURATION_SUB_STRENGTH CCI_REG8(0x0bf) +#define GC0308_Y_GAMMA_OUT0 CCI_REG8(0x0c0) +#define GC0308_Y_GAMMA_OUT1 CCI_REG8(0x0c1) +#define GC0308_Y_GAMMA_OUT2 CCI_REG8(0x0c2) +#define GC0308_Y_GAMMA_OUT3 CCI_REG8(0x0c3) +#define GC0308_Y_GAMMA_OUT4 CCI_REG8(0x0c4) +#define GC0308_Y_GAMMA_OUT5 CCI_REG8(0x0c5) +#define GC0308_Y_GAMMA_OUT6 CCI_REG8(0x0c6) +#define GC0308_Y_GAMMA_OUT7 CCI_REG8(0x0c7) +#define GC0308_Y_GAMMA_OUT8 CCI_REG8(0x0c8) +#define GC0308_Y_GAMMA_OUT9 CCI_REG8(0x0c9) +#define GC0308_Y_GAMMA_OUT10 CCI_REG8(0x0ca) +#define GC0308_Y_GAMMA_OUT11 CCI_REG8(0x0cb) +#define GC0308_Y_GAMMA_OUT12 CCI_REG8(0x0cc) + +/* AEC - Automatic Exposure Control */ +#define GC0308_AEC_MODE1 CCI_REG8(0x0d0) +#define GC0308_AEC_MODE2 CCI_REG8(0x0d1) +#define GC0308_AEC_MODE3 CCI_REG8(0x0d2) +#define GC0308_AEC_TARGET_Y CCI_REG8(0x0d3) +#define GC0308_Y_AVG CCI_REG8(0x0d4) +#define GC0308_AEC_HIGH_LOW_RANGE CCI_REG8(0x0d5) +#define GC0308_AEC_IGNORE CCI_REG8(0x0d6) +#define GC0308_AEC_LIMIT_HIGH_RANGE CCI_REG8(0x0d7) +#define GC0308_AEC_R_OFFSET CCI_REG8(0x0d9) +#define GC0308_AEC_GB_OFFSET CCI_REG8(0x0da) +#define GC0308_AEC_SLOW_MARGIN CCI_REG8(0x0db) +#define GC0308_AEC_FAST_MARGIN CCI_REG8(0x0dc) +#define GC0308_AEC_EXP_CHANGE_GAIN CCI_REG8(0x0dd) +#define GC0308_AEC_STEP2_SUNLIGHT CCI_REG8(0x0de) +#define GC0308_AEC_I_FRAMES CCI_REG8(0x0df) +#define GC0308_AEC_I_STOP_L_MARGIN CCI_REG8(0x0e0) +#define GC0308_AEC_I_STOP_MARGIN CCI_REG8(0x0e1) +#define GC0308_ANTI_FLICKER_STEP CCI_REG16(0x0e2) +#define GC0308_EXP_LVL_1 CCI_REG16(0x0e4) +#define GC0308_EXP_LVL_2 CCI_REG16(0x0e6) +#define GC0308_EXP_LVL_3 CCI_REG16(0x0e8) +#define GC0308_EXP_LVL_4 CCI_REG16(0x0ea) +#define GC0308_MAX_EXP_LVL CCI_REG8(0x0ec) +#define GC0308_EXP_MIN_L CCI_REG8(0x0ed) +#define GC0308_MAX_POST_DF_GAIN CCI_REG8(0x0ee) +#define GC0308_MAX_PRE_DG_GAIN CCI_REG8(0x0ef) + +/* ABS */ +#define GC0308_ABS_RANGE_COMP CCI_REG8(0x0f0) +#define GC0308_ABS_STOP_MARGIN CCI_REG8(0x0f1) +#define GC0308_Y_S_COMP CCI_REG8(0x0f2) +#define GC0308_Y_STRETCH_LIMIT CCI_REG8(0x0f3) +#define GC0308_Y_TILT CCI_REG8(0x0f4) +#define GC0308_Y_STRETCH CCI_REG8(0x0f5) + +/* Measure Window */ +#define GC0308_BIG_WIN_X0 CCI_REG8(0x0f7) +#define GC0308_BIG_WIN_Y0 CCI_REG8(0x0f8) +#define GC0308_BIG_WIN_X1 CCI_REG8(0x0f9) +#define GC0308_BIG_WIN_Y1 CCI_REG8(0x0fa) +#define GC0308_DIFF_Y_BIG_THD CCI_REG8(0x0fb) + +/* OUT Module (P1) */ +#define GC0308_CLOSE_FRAME_EN CCI_REG8(0x150) +#define GC0308_CLOSE_FRAME_NUM1 CCI_REG8(0x151) +#define GC0308_CLOSE_FRAME_NUM2 CCI_REG8(0x152) +#define GC0308_BAYER_MODE CCI_REG8(0x153) +#define GC0308_SUBSAMPLE CCI_REG8(0x154) +#define GC0308_SUBMODE CCI_REG8(0x155) +#define GC0308_SUB_ROW_N1 CCI_REG8(0x156) +#define GC0308_SUB_ROW_N2 CCI_REG8(0x157) +#define GC0308_SUB_COL_N1 CCI_REG8(0x158) +#define GC0308_SUB_COL_N2 CCI_REG8(0x159) + +/* AWB (P1) - Auto White Balance */ +#define GC0308_AWB_RGB_HIGH_LOW CCI_REG8(0x100) +#define GC0308_AWB_Y_TO_C_DIFF2 CCI_REG8(0x102) +#define GC0308_AWB_C_MAX CCI_REG8(0x104) +#define GC0308_AWB_C_INTER CCI_REG8(0x105) +#define GC0308_AWB_C_INTER2 CCI_REG8(0x106) +#define GC0308_AWB_C_MAX_BIG CCI_REG8(0x108) +#define GC0308_AWB_Y_HIGH CCI_REG8(0x109) +#define GC0308_AWB_NUMBER_LIMIT CCI_REG8(0x10a) +#define GC0308_KWIN_RATIO CCI_REG8(0x10b) +#define GC0308_KWIN_THD CCI_REG8(0x10c) +#define GC0308_LIGHT_GAIN_RANGE CCI_REG8(0x10d) +#define GC0308_SMALL_WIN_WIDTH_STEP CCI_REG8(0x10e) +#define GC0308_SMALL_WIN_HEIGHT_STEP CCI_REG8(0x10f) +#define GC0308_AWB_YELLOW_TH CCI_REG8(0x110) +#define GC0308_AWB_MODE CCI_REG8(0x111) +#define GC0308_AWB_ADJUST_SPEED CCI_REG8(0x112) +#define GC0308_AWB_EVERY_N CCI_REG8(0x113) +#define GC0308_R_AVG_USE CCI_REG8(0x1d0) +#define GC0308_G_AVG_USE CCI_REG8(0x1d1) +#define GC0308_B_AVG_USE CCI_REG8(0x1d2) + +#define GC0308_HBLANK_MIN 0x021 +#define GC0308_HBLANK_MAX 0xfff +#define GC0308_HBLANK_DEF 0x040 + +#define GC0308_VBLANK_MIN 0x000 +#define GC0308_VBLANK_MAX 0xfff +#define GC0308_VBLANK_DEF 0x020 + +#define GC0308_PIXEL_RATE 24000000 + +/* + * frame_time = (BT + height + 8) * row_time + * width = 640 (driver does not change window size) + * height = 480 (driver does not change window size) + * row_time = HBLANK + SAMPLE_HOLD_DELAY + width + 8 + 4 + * + * When EXP_TIME > (BT + height): + * BT = EXP_TIME - height - 8 - VS_START_TIME + VS_END_TIME + * else: + * BT = VBLANK + VS_START_TIME + VS_END_TIME + * + * max is 30 FPS + * + * In my tests frame rate mostly depends on exposure time. Unfortuantely + * it's unclear how this is calculated exactly. Also since we enable AEC, + * the frame times vary depending on ambient light conditions. + */ +#define GC0308_FRAME_RATE_MAX 30 + +enum gc0308_exp_val { + GC0308_EXP_M4 = 0, + GC0308_EXP_M3, + GC0308_EXP_M2, + GC0308_EXP_M1, + GC0308_EXP_0, + GC0308_EXP_P1, + GC0308_EXP_P2, + GC0308_EXP_P3, + GC0308_EXP_P4, +}; + +static const s64 gc0308_exposure_menu[] = { + -4, -3, -2, -1, 0, 1, 2, 3, 4 +}; + +struct gc0308_exposure { + u8 luma_offset; + u8 aec_target_y; +}; + +#define GC0308_EXPOSURE(luma_offset_reg, aec_target_y_reg) \ + { .luma_offset = luma_offset_reg, .aec_target_y = aec_target_y_reg } + +static const struct gc0308_exposure gc0308_exposure_values[] = { + [GC0308_EXP_M4] = GC0308_EXPOSURE(0xc0, 0x30), + [GC0308_EXP_M3] = GC0308_EXPOSURE(0xd0, 0x38), + [GC0308_EXP_M2] = GC0308_EXPOSURE(0xe0, 0x40), + [GC0308_EXP_M1] = GC0308_EXPOSURE(0xf0, 0x48), + [GC0308_EXP_0] = GC0308_EXPOSURE(0x08, 0x50), + [GC0308_EXP_P1] = GC0308_EXPOSURE(0x10, 0x5c), + [GC0308_EXP_P2] = GC0308_EXPOSURE(0x20, 0x60), + [GC0308_EXP_P3] = GC0308_EXPOSURE(0x30, 0x68), + [GC0308_EXP_P4] = GC0308_EXPOSURE(0x40, 0x70), +}; + +struct gc0308_awb_gains { + u8 r; + u8 g; + u8 b; +}; + +#define GC0308_AWB_GAINS(red, green, blue) \ + { .r = red, .g = green, .b = blue } + +static const struct gc0308_awb_gains gc0308_awb_gains[] = { + [V4L2_WHITE_BALANCE_AUTO] = GC0308_AWB_GAINS(0x56, 0x40, 0x4a), + [V4L2_WHITE_BALANCE_CLOUDY] = GC0308_AWB_GAINS(0x8c, 0x50, 0x40), + [V4L2_WHITE_BALANCE_DAYLIGHT] = GC0308_AWB_GAINS(0x74, 0x52, 0x40), + [V4L2_WHITE_BALANCE_INCANDESCENT] = GC0308_AWB_GAINS(0x48, 0x40, 0x5c), + [V4L2_WHITE_BALANCE_FLUORESCENT] = GC0308_AWB_GAINS(0x40, 0x42, 0x50), +}; + +struct gc0308_format { + u32 code; + u8 regval; +}; + +#define GC0308_FORMAT(v4l2_code, gc0308_regval) \ + { .code = v4l2_code, .regval = gc0308_regval } + +static const struct gc0308_format gc0308_formats[] = { + GC0308_FORMAT(MEDIA_BUS_FMT_UYVY8_2X8, 0x00), + GC0308_FORMAT(MEDIA_BUS_FMT_VYUY8_2X8, 0x01), + GC0308_FORMAT(MEDIA_BUS_FMT_YUYV8_2X8, 0x02), + GC0308_FORMAT(MEDIA_BUS_FMT_YVYU8_2X8, 0x03), + GC0308_FORMAT(MEDIA_BUS_FMT_RGB565_2X8_BE, 0x06), + GC0308_FORMAT(MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, 0x07), + GC0308_FORMAT(MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, 0x09), +}; + +struct gc0308_frame_size { + u8 subsample; + u32 width; + u32 height; +}; + +#define GC0308_FRAME_SIZE(s, w, h) \ + { .subsample = s, .width = w, .height = h } + +static const struct gc0308_frame_size gc0308_frame_sizes[] = { + GC0308_FRAME_SIZE(0x11, 640, 480), + GC0308_FRAME_SIZE(0x22, 320, 240), + GC0308_FRAME_SIZE(0x44, 160, 120), +}; + +struct gc0308_mode_registers { + u8 out_format; + u8 subsample; + u16 width; + u16 height; +}; + +struct gc0308 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; + struct device *dev; + struct clk *clk; + struct regmap *regmap; + struct regulator *vdd; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *reset_gpio; + unsigned int mbus_config; + struct gc0308_mode_registers mode; + struct { + /* mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct { + /* blanking cluster */ + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + }; +}; + +static inline struct gc0308 *to_gc0308(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc0308, sd); +} + +static const struct regmap_range_cfg gc0308_ranges[] = { + { + .range_min = 0x0000, + .range_max = 0x01ff, + .selector_reg = 0xfe, + .selector_mask = 0x01, + .selector_shift = 0x00, + .window_start = 0x00, + .window_len = 0x100, + }, +}; + +static const struct regmap_config gc0308_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = 0x1ff, + .ranges = gc0308_ranges, + .num_ranges = ARRAY_SIZE(gc0308_ranges), + .disable_locking = true, +}; + +static const struct cci_reg_sequence sensor_default_regs[] = { + {GC0308_VB_HB, 0x00}, + {GC0308_HBLANK, 0x40}, + {GC0308_VBLANK, 0x20}, + {GC0308_EXP, 0x0258}, + {GC0308_AWB_R_GAIN, 0x56}, + {GC0308_AWB_G_GAIN, 0x40}, + {GC0308_AWB_B_GAIN, 0x4a}, + {GC0308_ANTI_FLICKER_STEP, 0x0078}, + {GC0308_EXP_LVL_1, 0x0258}, + {GC0308_EXP_LVL_2, 0x0258}, + {GC0308_EXP_LVL_3, 0x0258}, + {GC0308_EXP_LVL_4, 0x0ea6}, + {GC0308_MAX_EXP_LVL, 0x20}, + {GC0308_ROW_START, 0x0000}, + {GC0308_COL_START, 0x0000}, + {GC0308_WIN_HEIGHT, 488}, + {GC0308_WIN_WIDTH, 648}, + {GC0308_VS_START_TIME, 0x02}, + {GC0308_VS_END_TIME, 0x02}, + {GC0308_RSH_WIDTH, 0x22}, + {GC0308_TSP_WIDTH, 0x0d}, + {GC0308_SAMPLE_HOLD_DELAY, 0x50}, + {GC0308_ROW_TAIL_WIDTH, 0x0f}, + {GC0308_CISCTL_MODE1, 0x10}, + {GC0308_CISCTL_MODE2, 0x0a}, + {GC0308_CISCTL_MODE3, 0x05}, + {GC0308_CISCTL_MODE4, 0x01}, + {CCI_REG8(0x018), 0x44}, /* undocumented */ + {CCI_REG8(0x019), 0x44}, /* undocumented */ + {GC0308_ANALOG_MODE1, 0x2a}, + {GC0308_ANALOG_MODE2, 0x00}, + {GC0308_HRST_RSG_V18, 0x49}, + {GC0308_VREF_V25, 0x9a}, + {GC0308_ADC_R, 0x61}, + {GC0308_PAD_DRV, 0x01}, /* drv strength: pclk=4mA */ + {GC0308_BLOCK_EN1, 0x7f}, + {GC0308_BLOCK_EN2, 0xfa}, + {GC0308_AAAA_EN, 0x57}, + {GC0308_OUT_FORMAT, 0xa2}, /* YCbYCr */ + {GC0308_OUT_EN, 0x0f}, + {GC0308_SYNC_MODE, 0x03}, + {GC0308_CLK_DIV_MODE, 0x00}, + {GC0308_DEBUG_MODE1, 0x0a}, + {GC0308_DEBUG_MODE2, 0x00}, + {GC0308_DEBUG_MODE3, 0x01}, + {GC0308_BLK_MODE, 0xf7}, + {GC0308_BLK_LIMIT_VAL, 0x50}, + {GC0308_GLOBAL_OFF, 0x00}, + {GC0308_CURRENT_R_OFF, 0x28}, + {GC0308_CURRENT_G_OFF, 0x2a}, + {GC0308_CURRENT_B_OFF, 0x28}, + {GC0308_EXP_RATE_DARKC, 0x04}, + {GC0308_OFF_SUBMODE, 0x20}, + {GC0308_DARKC_SUBMODE, 0x20}, + {GC0308_MANUAL_G1_OFF, 0x00}, + {GC0308_MANUAL_R1_OFF, 0x00}, + {GC0308_MANUAL_B2_OFF, 0x00}, + {GC0308_MANUAL_G2_OFF, 0x00}, + {GC0308_GLOBAL_GAIN, 0x14}, + {GC0308_AUTO_POSTGAIN, 0x41}, + {GC0308_CHANNEL_GAIN_G1, 0x80}, + {GC0308_CHANNEL_GAIN_R, 0x80}, + {GC0308_CHANNEL_GAIN_B, 0x80}, + {GC0308_CHANNEL_GAIN_G2, 0x80}, + {GC0308_LSC_RED_B2, 0x20}, + {GC0308_LSC_GREEN_B2, 0x20}, + {GC0308_LSC_BLUE_B2, 0x20}, + {GC0308_LSC_RED_B4, 0x14}, + {GC0308_LSC_GREEN_B4, 0x10}, + {GC0308_LSC_BLUE_B4, 0x14}, + {GC0308_LSC_ROW_CENTER, 0x3c}, + {GC0308_LSC_COL_CENTER, 0x50}, + {GC0308_LSC_DEC_LVL1, 0x12}, + {GC0308_LSC_DEC_LVL2, 0x1a}, + {GC0308_LSC_DEC_LVL3, 0x24}, + {GC0308_DN_MODE_EN, 0x07}, + {GC0308_DN_MODE_RATIO, 0x15}, + {GC0308_DN_BILAT_B_BASE, 0x08}, + {GC0308_DN_BILAT_N_BASE, 0x03}, + {GC0308_DD_DARK_BRIGHT_TH, 0xe8}, + {GC0308_DD_FLAT_TH, 0x86}, + {GC0308_DD_LIMIT, 0x82}, + {GC0308_ASDE_GAIN_TRESH, 0x18}, + {GC0308_ASDE_GAIN_MODE, 0x0f}, + {GC0308_ASDE_DN_SLOPE, 0x00}, + {GC0308_ASDE_DD_BRIGHT, 0x5f}, + {GC0308_ASDE_DD_LIMIT, 0x8f}, + {GC0308_ASDE_AUTO_EE1, 0x55}, + {GC0308_ASDE_AUTO_EE2, 0x38}, + {GC0308_ASDE_AUTO_SAT_DEC_SLOPE, 0x15}, + {GC0308_ASDE_AUTO_SAT_LOW_LIMIT, 0x33}, + {GC0308_EEINTP_MODE_1, 0xdc}, + {GC0308_EEINTP_MODE_2, 0x00}, + {GC0308_DIRECTION_TH1, 0x02}, + {GC0308_DIRECTION_TH2, 0x3f}, + {GC0308_DIFF_HV_TI_TH, 0x02}, + {GC0308_EDGE12_EFFECT, 0x38}, + {GC0308_EDGE_POS_RATIO, 0x88}, + {GC0308_EDGE1_MINMAX, 0x81}, + {GC0308_EDGE2_MINMAX, 0x81}, + {GC0308_EDGE12_TH, 0x22}, + {GC0308_EDGE_MAX, 0xff}, + {GC0308_CC_MATRIX_C11, 0x48}, + {GC0308_CC_MATRIX_C12, 0x02}, + {GC0308_CC_MATRIX_C13, 0x07}, + {GC0308_CC_MATRIX_C21, 0xe0}, + {GC0308_CC_MATRIX_C22, 0x40}, + {GC0308_CC_MATRIX_C23, 0xf0}, + {GC0308_SATURATION_CB, 0x40}, + {GC0308_SATURATION_CR, 0x40}, + {GC0308_LUMA_CONTRAST, 0x40}, + {GC0308_SKIN_CB_CENTER, 0xe0}, + {GC0308_EDGE_DEC_SA, 0x38}, + {GC0308_AUTO_GRAY_MODE, 0x36}, + {GC0308_AEC_MODE1, 0xcb}, + {GC0308_AEC_MODE2, 0x10}, + {GC0308_AEC_MODE3, 0x90}, + {GC0308_AEC_TARGET_Y, 0x48}, + {GC0308_AEC_HIGH_LOW_RANGE, 0xf2}, + {GC0308_AEC_IGNORE, 0x16}, + {GC0308_AEC_SLOW_MARGIN, 0x92}, + {GC0308_AEC_FAST_MARGIN, 0xa5}, + {GC0308_AEC_I_FRAMES, 0x23}, + {GC0308_AEC_R_OFFSET, 0x00}, + {GC0308_AEC_GB_OFFSET, 0x00}, + {GC0308_AEC_I_STOP_L_MARGIN, 0x09}, + {GC0308_EXP_MIN_L, 0x04}, + {GC0308_MAX_POST_DF_GAIN, 0xa0}, + {GC0308_MAX_PRE_DG_GAIN, 0x40}, + {GC0308_ABB_MODE, 0x03}, + {GC0308_GAMMA_OUT0, 0x10}, + {GC0308_GAMMA_OUT1, 0x20}, + {GC0308_GAMMA_OUT2, 0x38}, + {GC0308_GAMMA_OUT3, 0x4e}, + {GC0308_GAMMA_OUT4, 0x63}, + {GC0308_GAMMA_OUT5, 0x76}, + {GC0308_GAMMA_OUT6, 0x87}, + {GC0308_GAMMA_OUT7, 0xa2}, + {GC0308_GAMMA_OUT8, 0xb8}, + {GC0308_GAMMA_OUT9, 0xca}, + {GC0308_GAMMA_OUT10, 0xd8}, + {GC0308_GAMMA_OUT11, 0xe3}, + {GC0308_GAMMA_OUT12, 0xeb}, + {GC0308_GAMMA_OUT13, 0xf0}, + {GC0308_GAMMA_OUT14, 0xf8}, + {GC0308_GAMMA_OUT15, 0xfd}, + {GC0308_GAMMA_OUT16, 0xff}, + {GC0308_Y_GAMMA_OUT0, 0x00}, + {GC0308_Y_GAMMA_OUT1, 0x10}, + {GC0308_Y_GAMMA_OUT2, 0x1c}, + {GC0308_Y_GAMMA_OUT3, 0x30}, + {GC0308_Y_GAMMA_OUT4, 0x43}, + {GC0308_Y_GAMMA_OUT5, 0x54}, + {GC0308_Y_GAMMA_OUT6, 0x65}, + {GC0308_Y_GAMMA_OUT7, 0x75}, + {GC0308_Y_GAMMA_OUT8, 0x93}, + {GC0308_Y_GAMMA_OUT9, 0xb0}, + {GC0308_Y_GAMMA_OUT10, 0xcb}, + {GC0308_Y_GAMMA_OUT11, 0xe6}, + {GC0308_Y_GAMMA_OUT12, 0xff}, + {GC0308_ABS_RANGE_COMP, 0x02}, + {GC0308_ABS_STOP_MARGIN, 0x01}, + {GC0308_Y_S_COMP, 0x02}, + {GC0308_Y_STRETCH_LIMIT, 0x30}, + {GC0308_BIG_WIN_X0, 0x12}, + {GC0308_BIG_WIN_Y0, 0x0a}, + {GC0308_BIG_WIN_X1, 0x9f}, + {GC0308_BIG_WIN_Y1, 0x78}, + {GC0308_AWB_RGB_HIGH_LOW, 0xf5}, + {GC0308_AWB_Y_TO_C_DIFF2, 0x20}, + {GC0308_AWB_C_MAX, 0x10}, + {GC0308_AWB_C_INTER, 0x08}, + {GC0308_AWB_C_INTER2, 0x20}, + {GC0308_AWB_C_MAX_BIG, 0x0a}, + {GC0308_AWB_NUMBER_LIMIT, 0xa0}, + {GC0308_KWIN_RATIO, 0x60}, + {GC0308_KWIN_THD, 0x08}, + {GC0308_SMALL_WIN_WIDTH_STEP, 0x44}, + {GC0308_SMALL_WIN_HEIGHT_STEP, 0x32}, + {GC0308_AWB_YELLOW_TH, 0x41}, + {GC0308_AWB_MODE, 0x37}, + {GC0308_AWB_ADJUST_SPEED, 0x22}, + {GC0308_AWB_EVERY_N, 0x19}, + {CCI_REG8(0x114), 0x44}, /* AWB set1 */ + {CCI_REG8(0x115), 0x44}, /* AWB set1 */ + {CCI_REG8(0x116), 0xc2}, /* AWB set1 */ + {CCI_REG8(0x117), 0xa8}, /* AWB set1 */ + {CCI_REG8(0x118), 0x18}, /* AWB set1 */ + {CCI_REG8(0x119), 0x50}, /* AWB set1 */ + {CCI_REG8(0x11a), 0xd8}, /* AWB set1 */ + {CCI_REG8(0x11b), 0xf5}, /* AWB set1 */ + {CCI_REG8(0x170), 0x40}, /* AWB set2 */ + {CCI_REG8(0x171), 0x58}, /* AWB set2 */ + {CCI_REG8(0x172), 0x30}, /* AWB set2 */ + {CCI_REG8(0x173), 0x48}, /* AWB set2 */ + {CCI_REG8(0x174), 0x20}, /* AWB set2 */ + {CCI_REG8(0x175), 0x60}, /* AWB set2 */ + {CCI_REG8(0x177), 0x20}, /* AWB set2 */ + {CCI_REG8(0x178), 0x32}, /* AWB set2 */ + {CCI_REG8(0x130), 0x03}, /* undocumented */ + {CCI_REG8(0x131), 0x40}, /* undocumented */ + {CCI_REG8(0x132), 0x10}, /* undocumented */ + {CCI_REG8(0x133), 0xe0}, /* undocumented */ + {CCI_REG8(0x134), 0xe0}, /* undocumented */ + {CCI_REG8(0x135), 0x00}, /* undocumented */ + {CCI_REG8(0x136), 0x80}, /* undocumented */ + {CCI_REG8(0x137), 0x00}, /* undocumented */ + {CCI_REG8(0x138), 0x04}, /* undocumented */ + {CCI_REG8(0x139), 0x09}, /* undocumented */ + {CCI_REG8(0x13a), 0x12}, /* undocumented */ + {CCI_REG8(0x13b), 0x1c}, /* undocumented */ + {CCI_REG8(0x13c), 0x28}, /* undocumented */ + {CCI_REG8(0x13d), 0x31}, /* undocumented */ + {CCI_REG8(0x13e), 0x44}, /* undocumented */ + {CCI_REG8(0x13f), 0x57}, /* undocumented */ + {CCI_REG8(0x140), 0x6c}, /* undocumented */ + {CCI_REG8(0x141), 0x81}, /* undocumented */ + {CCI_REG8(0x142), 0x94}, /* undocumented */ + {CCI_REG8(0x143), 0xa7}, /* undocumented */ + {CCI_REG8(0x144), 0xb8}, /* undocumented */ + {CCI_REG8(0x145), 0xd6}, /* undocumented */ + {CCI_REG8(0x146), 0xee}, /* undocumented */ + {CCI_REG8(0x147), 0x0d}, /* undocumented */ + {CCI_REG8(0x162), 0xf7}, /* undocumented */ + {CCI_REG8(0x163), 0x68}, /* undocumented */ + {CCI_REG8(0x164), 0xd3}, /* undocumented */ + {CCI_REG8(0x165), 0xd3}, /* undocumented */ + {CCI_REG8(0x166), 0x60}, /* undocumented */ +}; + +struct gc0308_colormode { + u8 special_effect; + u8 dbg_mode1; + u8 block_en1; + u8 aec_mode3; + u8 eeintp_mode_2; + u8 edge12_effect; + u8 luma_contrast; + u8 contrast_center; + u8 fixed_cb; + u8 fixed_cr; +}; + +#define GC0308_COLOR_FX(reg_special_effect, reg_dbg_mode1, reg_block_en1, \ + reg_aec_mode3, reg_eeintp_mode_2, reg_edge12_effect, \ + reg_luma_contrast, reg_contrast_center, \ + reg_fixed_cb, reg_fixed_cr) \ + { \ + .special_effect = reg_special_effect, \ + .dbg_mode1 = reg_dbg_mode1, \ + .block_en1 = reg_block_en1, \ + .aec_mode3 = reg_aec_mode3, \ + .eeintp_mode_2 = reg_eeintp_mode_2, \ + .edge12_effect = reg_edge12_effect, \ + .luma_contrast = reg_luma_contrast, \ + .contrast_center = reg_contrast_center, \ + .fixed_cb = reg_fixed_cb, \ + .fixed_cr = reg_fixed_cr, \ + } + +static const struct gc0308_colormode gc0308_colormodes[] = { + [V4L2_COLORFX_NONE] = + GC0308_COLOR_FX(0x00, 0x0a, 0xff, 0x90, 0x00, + 0x54, 0x3c, 0x80, 0x00, 0x00), + [V4L2_COLORFX_BW] = + GC0308_COLOR_FX(0x02, 0x0a, 0xff, 0x90, 0x00, + 0x54, 0x40, 0x80, 0x00, 0x00), + [V4L2_COLORFX_SEPIA] = + GC0308_COLOR_FX(0x02, 0x0a, 0xff, 0x90, 0x00, + 0x38, 0x40, 0x80, 0xd0, 0x28), + [V4L2_COLORFX_NEGATIVE] = + GC0308_COLOR_FX(0x01, 0x0a, 0xff, 0x90, 0x00, + 0x38, 0x40, 0x80, 0x00, 0x00), + [V4L2_COLORFX_EMBOSS] = + GC0308_COLOR_FX(0x02, 0x0a, 0xbf, 0x10, 0x01, + 0x38, 0x40, 0x80, 0x00, 0x00), + [V4L2_COLORFX_SKETCH] = + GC0308_COLOR_FX(0x02, 0x0a, 0xff, 0x10, 0x80, + 0x38, 0x80, 0x90, 0x00, 0x00), + [V4L2_COLORFX_SKY_BLUE] = + GC0308_COLOR_FX(0x02, 0x0a, 0xff, 0x90, 0x00, + 0x38, 0x40, 0x80, 0x50, 0xe0), + [V4L2_COLORFX_GRASS_GREEN] = + GC0308_COLOR_FX(0x02, 0x0a, 0xff, 0x90, 0x01, + 0x38, 0x40, 0x80, 0xc0, 0xc0), + [V4L2_COLORFX_SKIN_WHITEN] = + GC0308_COLOR_FX(0x02, 0x0a, 0xbf, 0x10, 0x01, + 0x38, 0x60, 0x40, 0x00, 0x00), +}; + +static int gc0308_power_on(struct device *dev) +{ + struct gc0308 *gc0308 = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(gc0308->vdd); + if (ret) + return ret; + + ret = clk_prepare_enable(gc0308->clk); + if (ret) + goto clk_fail; + + gpiod_set_value_cansleep(gc0308->pwdn_gpio, 0); + usleep_range(10000, 20000); + + gpiod_set_value_cansleep(gc0308->reset_gpio, 1); + usleep_range(10000, 20000); + gpiod_set_value_cansleep(gc0308->reset_gpio, 0); + msleep(30); + + return 0; + +clk_fail: + regulator_disable(gc0308->vdd); + return ret; +} + +static int gc0308_power_off(struct device *dev) +{ + struct gc0308 *gc0308 = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(gc0308->pwdn_gpio, 1); + clk_disable_unprepare(gc0308->clk); + regulator_disable(gc0308->vdd); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int gc0308_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct gc0308 *gc0308 = to_gc0308(sd); + + return cci_read(gc0308->regmap, CCI_REG8(reg->reg), ®->val, NULL); +} + +static int gc0308_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct gc0308 *gc0308 = to_gc0308(sd); + + return cci_write(gc0308->regmap, CCI_REG8(reg->reg), reg->val, NULL); +} +#endif + +static int gc0308_set_exposure(struct gc0308 *gc0308, enum gc0308_exp_val exp) +{ + const struct gc0308_exposure *regs = &gc0308_exposure_values[exp]; + struct cci_reg_sequence exposure_reg_seq[] = { + {GC0308_LUMA_OFFSET, regs->luma_offset}, + {GC0308_AEC_TARGET_Y, regs->aec_target_y}, + }; + + return cci_multi_reg_write(gc0308->regmap, exposure_reg_seq, + ARRAY_SIZE(exposure_reg_seq), NULL); +} + +static int gc0308_set_awb_mode(struct gc0308 *gc0308, + enum v4l2_auto_n_preset_white_balance val) +{ + const struct gc0308_awb_gains *regs = &gc0308_awb_gains[val]; + struct cci_reg_sequence awb_reg_seq[] = { + {GC0308_AWB_R_GAIN, regs->r}, + {GC0308_AWB_G_GAIN, regs->g}, + {GC0308_AWB_B_GAIN, regs->b}, + }; + int ret; + + ret = cci_update_bits(gc0308->regmap, GC0308_AAAA_EN, + BIT(1), val == V4L2_WHITE_BALANCE_AUTO, NULL); + ret = cci_multi_reg_write(gc0308->regmap, awb_reg_seq, + ARRAY_SIZE(awb_reg_seq), &ret); + + return ret; +} + +static int gc0308_set_colormode(struct gc0308 *gc0308, enum v4l2_colorfx mode) +{ + const struct gc0308_colormode *regs = &gc0308_colormodes[mode]; + struct cci_reg_sequence colormode_reg_seq[] = { + {GC0308_SPECIAL_EFFECT, regs->special_effect}, + {GC0308_DEBUG_MODE1, regs->dbg_mode1}, + {GC0308_BLOCK_EN1, regs->block_en1}, + {GC0308_AEC_MODE3, regs->aec_mode3}, + {GC0308_EEINTP_MODE_2, regs->eeintp_mode_2}, + {GC0308_EDGE12_EFFECT, regs->edge12_effect}, + {GC0308_LUMA_CONTRAST, regs->luma_contrast}, + {GC0308_CONTRAST_CENTER, regs->contrast_center}, + {GC0308_FIXED_CB, regs->fixed_cb}, + {GC0308_FIXED_CR, regs->fixed_cr}, + }; + + return cci_multi_reg_write(gc0308->regmap, colormode_reg_seq, + ARRAY_SIZE(colormode_reg_seq), NULL); +} + +static int gc0308_set_power_line_freq(struct gc0308 *gc0308, int frequency) +{ + static const struct cci_reg_sequence pwr_line_50hz[] = { + {GC0308_ANTI_FLICKER_STEP, 0x0078}, + {GC0308_EXP_LVL_1, 0x0258}, + {GC0308_EXP_LVL_2, 0x0348}, + {GC0308_EXP_LVL_3, 0x04b0}, + {GC0308_EXP_LVL_4, 0x05a0}, + }; + static const struct cci_reg_sequence pwr_line_60hz[] = { + {GC0308_ANTI_FLICKER_STEP, 0x0064}, + {GC0308_EXP_LVL_1, 0x0258}, + {GC0308_EXP_LVL_2, 0x0384}, + {GC0308_EXP_LVL_3, 0x04b0}, + {GC0308_EXP_LVL_4, 0x05dc}, + }; + + switch (frequency) { + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + return cci_multi_reg_write(gc0308->regmap, pwr_line_60hz, + ARRAY_SIZE(pwr_line_60hz), NULL); + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + return cci_multi_reg_write(gc0308->regmap, pwr_line_50hz, + ARRAY_SIZE(pwr_line_50hz), NULL); + } + + return -EINVAL; +} + +static int gc0308_update_mirror(struct gc0308 *gc0308) +{ + u8 regval = 0x00; + + if (gc0308->vflip->val) + regval |= BIT(1); + + if (gc0308->hflip->val) + regval |= BIT(0); + + return cci_update_bits(gc0308->regmap, GC0308_CISCTL_MODE1, + GENMASK(1, 0), regval, NULL); +} + +static int gc0308_update_blanking(struct gc0308 *gc0308) +{ + u16 vblank = gc0308->vblank->val; + u16 hblank = gc0308->hblank->val; + u8 vbhb = ((vblank >> 4) & 0xf0) | ((hblank >> 8) & 0x0f); + int ret = 0; + + cci_write(gc0308->regmap, GC0308_VB_HB, vbhb, &ret); + cci_write(gc0308->regmap, GC0308_HBLANK, hblank & 0xff, &ret); + cci_write(gc0308->regmap, GC0308_VBLANK, vblank & 0xff, &ret); + + return ret; +} + +static int _gc0308_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc0308 *gc0308 = container_of(ctrl->handler, struct gc0308, hdl); + u8 flipval = ctrl->val ? 0xff : 0x00; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + case V4L2_CID_VBLANK: + return gc0308_update_blanking(gc0308); + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return gc0308_update_mirror(gc0308); + case V4L2_CID_AUTO_WHITE_BALANCE: + return cci_update_bits(gc0308->regmap, GC0308_AAAA_EN, + BIT(1), flipval, NULL); + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + return gc0308_set_awb_mode(gc0308, ctrl->val); + case V4L2_CID_POWER_LINE_FREQUENCY: + return gc0308_set_power_line_freq(gc0308, ctrl->val); + case V4L2_CID_COLORFX: + return gc0308_set_colormode(gc0308, ctrl->val); + case V4L2_CID_TEST_PATTERN: + return cci_update_bits(gc0308->regmap, GC0308_DEBUG_MODE2, + GENMASK(1, 0), ctrl->val, NULL); + case V4L2_CID_AUTO_EXPOSURE_BIAS: + return gc0308_set_exposure(gc0308, ctrl->val); + } + + return -EINVAL; +} + +static int gc0308_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc0308 *gc0308 = container_of(ctrl->handler, struct gc0308, hdl); + int ret; + + if (!pm_runtime_get_if_in_use(gc0308->dev)) + return 0; + + ret = _gc0308_s_ctrl(ctrl); + if (ret) + dev_err(gc0308->dev, "failed to set control: %d\n", ret); + + pm_runtime_mark_last_busy(gc0308->dev); + pm_runtime_put_autosuspend(gc0308->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc0308_ctrl_ops = { + .s_ctrl = gc0308_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops gc0308_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = gc0308_g_register, + .s_register = gc0308_s_register, +#endif +}; + +static int gc0308_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(gc0308_formats)) + return -EINVAL; + + code->code = gc0308_formats[code->index].code; + + return 0; +} + +static int gc0308_get_format_idx(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(gc0308_formats); i++) { + if (gc0308_formats[i].code == code) + return i; + } + + return -1; +} + +static int gc0308_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(gc0308_frame_sizes)) + return -EINVAL; + + if (gc0308_get_format_idx(fse->code) < 0) + return -EINVAL; + + fse->min_width = gc0308_frame_sizes[fse->index].width; + fse->max_width = gc0308_frame_sizes[fse->index].width; + fse->min_height = gc0308_frame_sizes[fse->index].height; + fse->max_height = gc0308_frame_sizes[fse->index].height; + + return 0; +} + +static void gc0308_update_pad_format(const struct gc0308_frame_size *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = code; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int gc0308_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc0308 *gc0308 = to_gc0308(sd); + const struct gc0308_frame_size *mode; + int i = gc0308_get_format_idx(fmt->format.code); + + if (i < 0) + i = 0; + + mode = v4l2_find_nearest_size(gc0308_frame_sizes, + ARRAY_SIZE(gc0308_frame_sizes), width, + height, fmt->format.width, + fmt->format.height); + + gc0308_update_pad_format(mode, &fmt->format, gc0308_formats[i].code); + *v4l2_subdev_state_get_format(sd_state, 0) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + gc0308->mode.out_format = gc0308_formats[i].regval; + gc0308->mode.subsample = mode->subsample; + gc0308->mode.width = mode->width; + gc0308->mode.height = mode->height; + + return 0; +} + +static int gc0308_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(sd_state, 0); + + format->width = 640; + format->height = 480; + format->code = gc0308_formats[0].code; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->field = V4L2_FIELD_NONE; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_DEFAULT; + format->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static const struct v4l2_subdev_pad_ops gc0308_pad_ops = { + .enum_mbus_code = gc0308_enum_mbus_code, + .enum_frame_size = gc0308_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc0308_set_format, +}; + +static int gc0308_set_resolution(struct gc0308 *gc0308, int *ret) +{ + struct cci_reg_sequence resolution_regs[] = { + {GC0308_SUBSAMPLE, gc0308->mode.subsample}, + {GC0308_SUBMODE, 0x03}, + {GC0308_SUB_ROW_N1, 0x00}, + {GC0308_SUB_ROW_N2, 0x00}, + {GC0308_SUB_COL_N1, 0x00}, + {GC0308_SUB_COL_N2, 0x00}, + {GC0308_CROP_WIN_MODE, 0x80}, + {GC0308_CROP_WIN_Y1, 0x00}, + {GC0308_CROP_WIN_X1, 0x00}, + {GC0308_CROP_WIN_HEIGHT, gc0308->mode.height}, + {GC0308_CROP_WIN_WIDTH, gc0308->mode.width}, + }; + + return cci_multi_reg_write(gc0308->regmap, resolution_regs, + ARRAY_SIZE(resolution_regs), ret); +} + +static int gc0308_start_stream(struct gc0308 *gc0308) +{ + int ret, sync_mode; + + ret = pm_runtime_resume_and_get(gc0308->dev); + if (ret < 0) + return ret; + + cci_multi_reg_write(gc0308->regmap, sensor_default_regs, + ARRAY_SIZE(sensor_default_regs), &ret); + cci_update_bits(gc0308->regmap, GC0308_OUT_FORMAT, + GENMASK(4, 0), gc0308->mode.out_format, &ret); + gc0308_set_resolution(gc0308, &ret); + + if (ret) { + dev_err(gc0308->dev, "failed to update registers: %d\n", ret); + goto disable_pm; + } + + ret = __v4l2_ctrl_handler_setup(&gc0308->hdl); + if (ret) { + dev_err(gc0308->dev, "failed to setup controls\n"); + goto disable_pm; + } + + /* HSYNC/VSYNC polarity */ + sync_mode = 0x3; + if (gc0308->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW) + sync_mode &= ~BIT(0); + if (gc0308->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW) + sync_mode &= ~BIT(1); + ret = cci_write(gc0308->regmap, GC0308_SYNC_MODE, sync_mode, NULL); + if (ret) + goto disable_pm; + + return 0; + +disable_pm: + pm_runtime_mark_last_busy(gc0308->dev); + pm_runtime_put_autosuspend(gc0308->dev); + return ret; +} + +static int gc0308_stop_stream(struct gc0308 *gc0308) +{ + pm_runtime_mark_last_busy(gc0308->dev); + pm_runtime_put_autosuspend(gc0308->dev); + return 0; +} + +static int gc0308_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct gc0308 *gc0308 = to_gc0308(sd); + struct v4l2_subdev_state *sd_state; + int ret; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) + ret = gc0308_start_stream(gc0308); + else + ret = gc0308_stop_stream(gc0308); + + v4l2_subdev_unlock_state(sd_state); + return ret; +} + +static const struct v4l2_subdev_video_ops gc0308_video_ops = { + .s_stream = gc0308_s_stream, +}; + +static const struct v4l2_subdev_ops gc0308_subdev_ops = { + .core = &gc0308_core_ops, + .pad = &gc0308_pad_ops, + .video = &gc0308_video_ops, +}; + +static const struct v4l2_subdev_internal_ops gc0308_internal_ops = { + .init_state = gc0308_init_state, +}; + +static int gc0308_bus_config(struct gc0308 *gc0308) +{ + struct device *dev = gc0308->dev; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_PARALLEL + }; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + gc0308->mbus_config = bus_cfg.bus.parallel.flags; + + return 0; +} + +static const char * const gc0308_test_pattern_menu[] = { + "Disabled", + "Test Image 1", + "Test Image 2", +}; + +static int gc0308_init_controls(struct gc0308 *gc0308) +{ + int ret; + + v4l2_ctrl_handler_init(&gc0308->hdl, 11); + gc0308->hblank = v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_HBLANK, GC0308_HBLANK_MIN, + GC0308_HBLANK_MAX, 1, + GC0308_HBLANK_DEF); + gc0308->vblank = v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_VBLANK, GC0308_VBLANK_MIN, + GC0308_VBLANK_MAX, 1, + GC0308_VBLANK_DEF); + gc0308->hflip = v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + gc0308->vflip = v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, V4L2_CID_PIXEL_RATE, + GC0308_PIXEL_RATE, GC0308_PIXEL_RATE, 1, + GC0308_PIXEL_RATE); + v4l2_ctrl_new_std(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + v4l2_ctrl_new_std_menu_items(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc0308_test_pattern_menu) - 1, + 0, 0, gc0308_test_pattern_menu); + v4l2_ctrl_new_std_menu(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO); + v4l2_ctrl_new_std_menu(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_COLORFX, 8, 0, V4L2_COLORFX_NONE); + v4l2_ctrl_new_std_menu(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + ~0x6, V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + v4l2_ctrl_new_int_menu(&gc0308->hdl, &gc0308_ctrl_ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(gc0308_exposure_menu) - 1, + ARRAY_SIZE(gc0308_exposure_menu) / 2, + gc0308_exposure_menu); + + gc0308->sd.ctrl_handler = &gc0308->hdl; + if (gc0308->hdl.error) { + ret = gc0308->hdl.error; + v4l2_ctrl_handler_free(&gc0308->hdl); + return ret; + } + + v4l2_ctrl_cluster(2, &gc0308->hflip); + v4l2_ctrl_cluster(2, &gc0308->hblank); + + return 0; +} + +static int gc0308_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc0308 *gc0308; + unsigned long clkrate; + u64 regval; + int ret; + + gc0308 = devm_kzalloc(dev, sizeof(*gc0308), GFP_KERNEL); + if (!gc0308) + return -ENOMEM; + + gc0308->dev = dev; + dev_set_drvdata(dev, gc0308); + + ret = gc0308_bus_config(gc0308); + if (ret) + return dev_err_probe(dev, ret, "failed to get bus config\n"); + + gc0308->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(gc0308->clk)) + return dev_err_probe(dev, PTR_ERR(gc0308->clk), + "could not get clk\n"); + + gc0308->vdd = devm_regulator_get(dev, "vdd28"); + if (IS_ERR(gc0308->vdd)) + return dev_err_probe(dev, PTR_ERR(gc0308->vdd), + "failed to get vdd28 regulator\n"); + + gc0308->pwdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW); + if (IS_ERR(gc0308->pwdn_gpio)) + return dev_err_probe(dev, PTR_ERR(gc0308->pwdn_gpio), + "failed to get powerdown gpio\n"); + + gc0308->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc0308->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc0308->reset_gpio), + "failed to get reset gpio\n"); + + /* + * This is not using devm_cci_regmap_init_i2c(), because the driver + * makes use of regmap's pagination feature. The chosen settings are + * compatible with the CCI helpers. + */ + gc0308->regmap = devm_regmap_init_i2c(client, &gc0308_regmap_config); + if (IS_ERR(gc0308->regmap)) + return dev_err_probe(dev, PTR_ERR(gc0308->regmap), + "failed to init regmap\n"); + + v4l2_i2c_subdev_init(&gc0308->sd, client, &gc0308_subdev_ops); + gc0308->sd.internal_ops = &gc0308_internal_ops; + gc0308->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + gc0308->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + + ret = gc0308_init_controls(gc0308); + if (ret) + return dev_err_probe(dev, ret, "failed to init controls\n"); + + gc0308->sd.state_lock = gc0308->hdl.lock; + gc0308->pad.flags = MEDIA_PAD_FL_SOURCE; + gc0308->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&gc0308->sd.entity, 1, &gc0308->pad); + if (ret < 0) + goto fail_ctrl_hdl_cleanup; + + ret = v4l2_subdev_init_finalize(&gc0308->sd); + if (ret) + goto fail_media_entity_cleanup; + + ret = gc0308_power_on(dev); + if (ret) + goto fail_subdev_cleanup; + + if (gc0308->clk) { + clkrate = clk_get_rate(gc0308->clk); + if (clkrate != 24000000) + dev_warn(dev, "unexpected clock rate: %lu\n", clkrate); + } + + ret = cci_read(gc0308->regmap, GC0308_CHIP_ID, ®val, NULL); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to read chip ID\n"); + goto fail_power_off; + } + + if (regval != 0x9b) { + ret = -EINVAL; + dev_err_probe(dev, ret, "invalid chip ID (%02llx)\n", regval); + goto fail_power_off; + } + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = v4l2_async_register_subdev(&gc0308->sd); + if (ret) { + dev_err_probe(dev, ret, "failed to register v4l subdev\n"); + goto fail_rpm; + } + + return 0; + +fail_rpm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); +fail_power_off: + gc0308_power_off(dev); +fail_subdev_cleanup: + v4l2_subdev_cleanup(&gc0308->sd); +fail_media_entity_cleanup: + media_entity_cleanup(&gc0308->sd.entity); +fail_ctrl_hdl_cleanup: + v4l2_ctrl_handler_free(&gc0308->hdl); + return ret; +} + +static void gc0308_remove(struct i2c_client *client) +{ + struct gc0308 *gc0308 = i2c_get_clientdata(client); + struct device *dev = &client->dev; + + v4l2_async_unregister_subdev(&gc0308->sd); + v4l2_ctrl_handler_free(&gc0308->hdl); + media_entity_cleanup(&gc0308->sd.entity); + + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + gc0308_power_off(dev); + pm_runtime_set_suspended(dev); +} + +static const struct dev_pm_ops gc0308_pm_ops = { + SET_RUNTIME_PM_OPS(gc0308_power_off, gc0308_power_on, NULL) +}; + +static const struct of_device_id gc0308_of_match[] = { + { .compatible = "galaxycore,gc0308" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, gc0308_of_match); + +static struct i2c_driver gc0308_i2c_driver = { + .driver = { + .name = "gc0308", + .pm = &gc0308_pm_ops, + .of_match_table = gc0308_of_match, + }, + .probe = gc0308_probe, + .remove = gc0308_remove, +}; +module_i2c_driver(gc0308_i2c_driver); + +MODULE_DESCRIPTION("GalaxyCore GC0308 Camera Driver"); +MODULE_AUTHOR("Sebastian Reichel "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c new file mode 100644 index 000000000..bef7b0e05 --- /dev/null +++ b/drivers/media/i2c/gc2145.c @@ -0,0 +1,1450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Galaxycore GC2145 camera. + * Copyright (C) 2023, STMicroelectronics SA + * + * Inspired by the imx219.c driver + * + * Datasheet v1.0 available at http://files.pine64.org/doc/datasheet/PinebookPro/GC2145%20CSP%20DataSheet%20release%20V1.0_20131201.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Chip ID */ +#define GC2145_CHIP_ID 0x2145 + +/* Page 0 */ +#define GC2145_REG_EXPOSURE CCI_REG16(0x03) +#define GC2145_REG_HBLANK CCI_REG16(0x05) +#define GC2145_REG_VBLANK CCI_REG16(0x07) +#define GC2145_REG_ROW_START CCI_REG16(0x09) +#define GC2145_REG_COL_START CCI_REG16(0x0b) +#define GC2145_REG_WIN_HEIGHT CCI_REG16(0x0d) +#define GC2145_REG_WIN_WIDTH CCI_REG16(0x0f) +#define GC2145_REG_ANALOG_MODE1 CCI_REG8(0x17) +#define GC2145_REG_OUTPUT_FMT CCI_REG8(0x84) +#define GC2145_REG_SYNC_MODE CCI_REG8(0x86) +#define GC2145_SYNC_MODE_COL_SWITCH BIT(4) +#define GC2145_SYNC_MODE_ROW_SWITCH BIT(5) +#define GC2145_REG_BYPASS_MODE CCI_REG8(0x89) +#define GC2145_BYPASS_MODE_SWITCH BIT(5) +#define GC2145_REG_DEBUG_MODE2 CCI_REG8(0x8c) +#define GC2145_REG_DEBUG_MODE3 CCI_REG8(0x8d) +#define GC2145_REG_CROP_ENABLE CCI_REG8(0x90) +#define GC2145_REG_CROP_Y CCI_REG16(0x91) +#define GC2145_REG_CROP_X CCI_REG16(0x93) +#define GC2145_REG_CROP_HEIGHT CCI_REG16(0x95) +#define GC2145_REG_CROP_WIDTH CCI_REG16(0x97) +#define GC2145_REG_GLOBAL_GAIN CCI_REG8(0xb0) +#define GC2145_REG_CHIP_ID CCI_REG16(0xf0) +#define GC2145_REG_PAD_IO CCI_REG8(0xf2) +#define GC2145_REG_PAGE_SELECT CCI_REG8(0xfe) +/* Page 3 */ +#define GC2145_REG_DPHY_ANALOG_MODE1 CCI_REG8(0x01) +#define GC2145_DPHY_MODE_PHY_CLK_EN BIT(0) +#define GC2145_DPHY_MODE_PHY_LANE0_EN BIT(1) +#define GC2145_DPHY_MODE_PHY_LANE1_EN BIT(2) +#define GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL BIT(7) +#define GC2145_REG_DPHY_ANALOG_MODE2 CCI_REG8(0x02) +#define GC2145_DPHY_CLK_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_LANE0_DIFF(a) (((a) & 0x07) << 4) +#define GC2145_REG_DPHY_ANALOG_MODE3 CCI_REG8(0x03) +#define GC2145_DPHY_LANE1_DIFF(a) ((a) & 0x07) +#define GC2145_DPHY_CLK_DELAY BIT(4) +#define GC2145_DPHY_LANE0_DELAY BIT(5) +#define GC2145_DPHY_LANE1_DELAY BIT(6) +#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) +#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) +#define GC2145_REG_FIFO_MODE CCI_REG8(0x06) +#define GC2145_FIFO_MODE_READ_GATE BIT(3) +#define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) +#define GC2145_REG_BUF_CSI2_MODE CCI_REG8(0x10) +#define GC2145_CSI2_MODE_DOUBLE BIT(0) +#define GC2145_CSI2_MODE_RAW8 BIT(2) +#define GC2145_CSI2_MODE_MIPI_EN BIT(4) +#define GC2145_CSI2_MODE_EN BIT(7) +#define GC2145_REG_MIPI_DT CCI_REG8(0x11) +#define GC2145_REG_LWC_LOW CCI_REG8(0x12) +#define GC2145_REG_LWC_HIGH CCI_REG8(0x13) +#define GC2145_REG_DPHY_MODE CCI_REG8(0x15) +#define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) +#define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) +#define GC2145_REG_T_LPX CCI_REG8(0x21) +#define GC2145_REG_T_CLK_HS_PREPARE CCI_REG8(0x22) +#define GC2145_REG_T_CLK_ZERO CCI_REG8(0x23) +#define GC2145_REG_T_CLK_PRE CCI_REG8(0x24) +#define GC2145_REG_T_CLK_POST CCI_REG8(0x25) +#define GC2145_REG_T_CLK_TRAIL CCI_REG8(0x26) +#define GC2145_REG_T_HS_EXIT CCI_REG8(0x27) +#define GC2145_REG_T_WAKEUP CCI_REG8(0x28) +#define GC2145_REG_T_HS_PREPARE CCI_REG8(0x29) +#define GC2145_REG_T_HS_ZERO CCI_REG8(0x2a) +#define GC2145_REG_T_HS_TRAIL CCI_REG8(0x2b) + +/* External clock frequency is 24.0MHz */ +#define GC2145_XCLK_FREQ (24 * HZ_PER_MHZ) + +#define GC2145_NATIVE_WIDTH 1616U +#define GC2145_NATIVE_HEIGHT 1232U + +/** + * struct gc2145_mode - GC2145 mode description + * @width: frame width (in pixels) + * @height: frame height (in pixels) + * @reg_seq: registers config sequence to enter into the mode + * @reg_seq_size: size of the sequence + * @pixel_rate: pixel rate associated with the mode + * @crop: window area captured + * @hblank: default horizontal blanking + * @vblank: default vertical blanking + * @link_freq_index: index within the link frequency menu + */ +struct gc2145_mode { + unsigned int width; + unsigned int height; + const struct cci_reg_sequence *reg_seq; + size_t reg_seq_size; + unsigned long pixel_rate; + struct v4l2_rect crop; + unsigned int hblank; + unsigned int vblank; + unsigned int link_freq_index; +}; + +#define GC2145_DEFAULT_EXPOSURE 0x04e2 +#define GC2145_DEFAULT_GLOBAL_GAIN 0x55 +static const struct cci_reg_sequence gc2145_common_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x00}, + /* SH Delay */ + {CCI_REG8(0x12), 0x2e}, + /* Flip */ + {GC2145_REG_ANALOG_MODE1, 0x14}, + /* Analog Conf */ + {CCI_REG8(0x18), 0x22}, {CCI_REG8(0x19), 0x0e}, {CCI_REG8(0x1a), 0x01}, + {CCI_REG8(0x1b), 0x4b}, {CCI_REG8(0x1c), 0x07}, {CCI_REG8(0x1d), 0x10}, + {CCI_REG8(0x1e), 0x88}, {CCI_REG8(0x1f), 0x78}, {CCI_REG8(0x20), 0x03}, + {CCI_REG8(0x21), 0x40}, {CCI_REG8(0x22), 0xa0}, {CCI_REG8(0x24), 0x16}, + {CCI_REG8(0x25), 0x01}, {CCI_REG8(0x26), 0x10}, {CCI_REG8(0x2d), 0x60}, + {CCI_REG8(0x30), 0x01}, {CCI_REG8(0x31), 0x90}, {CCI_REG8(0x33), 0x06}, + {CCI_REG8(0x34), 0x01}, + /* ISP related */ + {CCI_REG8(0x80), 0x7f}, {CCI_REG8(0x81), 0x26}, {CCI_REG8(0x82), 0xfa}, + {CCI_REG8(0x83), 0x00}, {CCI_REG8(0x84), 0x02}, {CCI_REG8(0x86), 0x02}, + {CCI_REG8(0x88), 0x03}, + {GC2145_REG_BYPASS_MODE, 0x03}, + {CCI_REG8(0x85), 0x08}, {CCI_REG8(0x8a), 0x00}, {CCI_REG8(0x8b), 0x00}, + {GC2145_REG_GLOBAL_GAIN, GC2145_DEFAULT_GLOBAL_GAIN}, + {CCI_REG8(0xc3), 0x00}, {CCI_REG8(0xc4), 0x80}, {CCI_REG8(0xc5), 0x90}, + {CCI_REG8(0xc6), 0x3b}, {CCI_REG8(0xc7), 0x46}, + /* BLK */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0x40), 0x42}, {CCI_REG8(0x41), 0x00}, {CCI_REG8(0x43), 0x5b}, + {CCI_REG8(0x5e), 0x00}, {CCI_REG8(0x5f), 0x00}, {CCI_REG8(0x60), 0x00}, + {CCI_REG8(0x61), 0x00}, {CCI_REG8(0x62), 0x00}, {CCI_REG8(0x63), 0x00}, + {CCI_REG8(0x64), 0x00}, {CCI_REG8(0x65), 0x00}, {CCI_REG8(0x66), 0x20}, + {CCI_REG8(0x67), 0x20}, {CCI_REG8(0x68), 0x20}, {CCI_REG8(0x69), 0x20}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x6a), 0x08}, {CCI_REG8(0x6b), 0x08}, + {CCI_REG8(0x6c), 0x08}, {CCI_REG8(0x6d), 0x08}, {CCI_REG8(0x6e), 0x08}, + {CCI_REG8(0x6f), 0x08}, {CCI_REG8(0x70), 0x08}, {CCI_REG8(0x71), 0x08}, + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x72), 0xf0}, {CCI_REG8(0x7e), 0x3c}, + {CCI_REG8(0x7f), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x48), 0x15}, {CCI_REG8(0x49), 0x00}, {CCI_REG8(0x4b), 0x0b}, + /* AEC */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {GC2145_REG_EXPOSURE, GC2145_DEFAULT_EXPOSURE}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x01), 0x04}, {CCI_REG8(0x02), 0xc0}, {CCI_REG8(0x03), 0x04}, + {CCI_REG8(0x04), 0x90}, {CCI_REG8(0x05), 0x30}, {CCI_REG8(0x06), 0x90}, + {CCI_REG8(0x07), 0x30}, {CCI_REG8(0x08), 0x80}, {CCI_REG8(0x09), 0x00}, + {CCI_REG8(0x0a), 0x82}, {CCI_REG8(0x0b), 0x11}, {CCI_REG8(0x0c), 0x10}, + {CCI_REG8(0x11), 0x10}, {CCI_REG8(0x13), 0x7b}, {CCI_REG8(0x17), 0x00}, + {CCI_REG8(0x1c), 0x11}, {CCI_REG8(0x1e), 0x61}, {CCI_REG8(0x1f), 0x35}, + {CCI_REG8(0x20), 0x40}, {CCI_REG8(0x22), 0x40}, {CCI_REG8(0x23), 0x20}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x0f), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x12), 0x35}, {CCI_REG8(0x15), 0xb0}, {CCI_REG8(0x10), 0x31}, + {CCI_REG8(0x3e), 0x28}, {CCI_REG8(0x3f), 0xb0}, {CCI_REG8(0x40), 0x90}, + {CCI_REG8(0x41), 0x0f}, + /* INTPEE */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x90), 0x6c}, {CCI_REG8(0x91), 0x03}, {CCI_REG8(0x92), 0xcb}, + {CCI_REG8(0x94), 0x33}, {CCI_REG8(0x95), 0x84}, {CCI_REG8(0x97), 0x65}, + {CCI_REG8(0xa2), 0x11}, + /* DNDD */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x80), 0xc1}, {CCI_REG8(0x81), 0x08}, {CCI_REG8(0x82), 0x05}, + {CCI_REG8(0x83), 0x08}, {CCI_REG8(0x84), 0x0a}, {CCI_REG8(0x86), 0xf0}, + {CCI_REG8(0x87), 0x50}, {CCI_REG8(0x88), 0x15}, {CCI_REG8(0x89), 0xb0}, + {CCI_REG8(0x8a), 0x30}, {CCI_REG8(0x8b), 0x10}, + /* ASDE */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x21), 0x04}, + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xa3), 0x50}, {CCI_REG8(0xa4), 0x20}, {CCI_REG8(0xa5), 0x40}, + {CCI_REG8(0xa6), 0x80}, {CCI_REG8(0xab), 0x40}, {CCI_REG8(0xae), 0x0c}, + {CCI_REG8(0xb3), 0x46}, {CCI_REG8(0xb4), 0x64}, {CCI_REG8(0xb6), 0x38}, + {CCI_REG8(0xb7), 0x01}, {CCI_REG8(0xb9), 0x2b}, {CCI_REG8(0x3c), 0x04}, + {CCI_REG8(0x3d), 0x15}, {CCI_REG8(0x4b), 0x06}, {CCI_REG8(0x4c), 0x20}, + /* Gamma */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x10), 0x09}, {CCI_REG8(0x11), 0x0d}, {CCI_REG8(0x12), 0x13}, + {CCI_REG8(0x13), 0x19}, {CCI_REG8(0x14), 0x27}, {CCI_REG8(0x15), 0x37}, + {CCI_REG8(0x16), 0x45}, {CCI_REG8(0x17), 0x53}, {CCI_REG8(0x18), 0x69}, + {CCI_REG8(0x19), 0x7d}, {CCI_REG8(0x1a), 0x8f}, {CCI_REG8(0x1b), 0x9d}, + {CCI_REG8(0x1c), 0xa9}, {CCI_REG8(0x1d), 0xbd}, {CCI_REG8(0x1e), 0xcd}, + {CCI_REG8(0x1f), 0xd9}, {CCI_REG8(0x20), 0xe3}, {CCI_REG8(0x21), 0xea}, + {CCI_REG8(0x22), 0xef}, {CCI_REG8(0x23), 0xf5}, {CCI_REG8(0x24), 0xf9}, + {CCI_REG8(0x25), 0xff}, + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xc6), 0x20}, {CCI_REG8(0xc7), 0x2b}, + /* Gamma 2 */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x26), 0x0f}, {CCI_REG8(0x27), 0x14}, {CCI_REG8(0x28), 0x19}, + {CCI_REG8(0x29), 0x1e}, {CCI_REG8(0x2a), 0x27}, {CCI_REG8(0x2b), 0x33}, + {CCI_REG8(0x2c), 0x3b}, {CCI_REG8(0x2d), 0x45}, {CCI_REG8(0x2e), 0x59}, + {CCI_REG8(0x2f), 0x69}, {CCI_REG8(0x30), 0x7c}, {CCI_REG8(0x31), 0x89}, + {CCI_REG8(0x32), 0x98}, {CCI_REG8(0x33), 0xae}, {CCI_REG8(0x34), 0xc0}, + {CCI_REG8(0x35), 0xcf}, {CCI_REG8(0x36), 0xda}, {CCI_REG8(0x37), 0xe2}, + {CCI_REG8(0x38), 0xe9}, {CCI_REG8(0x39), 0xf3}, {CCI_REG8(0x3a), 0xf9}, + {CCI_REG8(0x3b), 0xff}, + /* YCP */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xd1), 0x32}, {CCI_REG8(0xd2), 0x32}, {CCI_REG8(0xd3), 0x40}, + {CCI_REG8(0xd6), 0xf0}, {CCI_REG8(0xd7), 0x10}, {CCI_REG8(0xd8), 0xda}, + {CCI_REG8(0xdd), 0x14}, {CCI_REG8(0xde), 0x86}, {CCI_REG8(0xed), 0x80}, + {CCI_REG8(0xee), 0x00}, {CCI_REG8(0xef), 0x3f}, {CCI_REG8(0xd8), 0xd8}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* LSC */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xc2), 0x14}, {CCI_REG8(0xc3), 0x0d}, {CCI_REG8(0xc4), 0x0c}, + {CCI_REG8(0xc8), 0x15}, {CCI_REG8(0xc9), 0x0d}, {CCI_REG8(0xca), 0x0a}, + {CCI_REG8(0xbc), 0x24}, {CCI_REG8(0xbd), 0x10}, {CCI_REG8(0xbe), 0x0b}, + {CCI_REG8(0xb6), 0x25}, {CCI_REG8(0xb7), 0x16}, {CCI_REG8(0xb8), 0x15}, + {CCI_REG8(0xc5), 0x00}, {CCI_REG8(0xc6), 0x00}, {CCI_REG8(0xc7), 0x00}, + {CCI_REG8(0xcb), 0x00}, {CCI_REG8(0xcc), 0x00}, {CCI_REG8(0xcd), 0x00}, + {CCI_REG8(0xbf), 0x07}, {CCI_REG8(0xc0), 0x00}, {CCI_REG8(0xc1), 0x00}, + {CCI_REG8(0xb9), 0x00}, {CCI_REG8(0xba), 0x00}, {CCI_REG8(0xbb), 0x00}, + {CCI_REG8(0xaa), 0x01}, {CCI_REG8(0xab), 0x01}, {CCI_REG8(0xac), 0x00}, + {CCI_REG8(0xad), 0x05}, {CCI_REG8(0xae), 0x06}, {CCI_REG8(0xaf), 0x0e}, + {CCI_REG8(0xb0), 0x0b}, {CCI_REG8(0xb1), 0x07}, {CCI_REG8(0xb2), 0x06}, + {CCI_REG8(0xb3), 0x17}, {CCI_REG8(0xb4), 0x0e}, {CCI_REG8(0xb5), 0x0e}, + {CCI_REG8(0xd0), 0x09}, {CCI_REG8(0xd1), 0x00}, {CCI_REG8(0xd2), 0x00}, + {CCI_REG8(0xd6), 0x08}, {CCI_REG8(0xd7), 0x00}, {CCI_REG8(0xd8), 0x00}, + {CCI_REG8(0xd9), 0x00}, {CCI_REG8(0xda), 0x00}, {CCI_REG8(0xdb), 0x00}, + {CCI_REG8(0xd3), 0x0a}, {CCI_REG8(0xd4), 0x00}, {CCI_REG8(0xd5), 0x00}, + {CCI_REG8(0xa4), 0x00}, {CCI_REG8(0xa5), 0x00}, {CCI_REG8(0xa6), 0x77}, + {CCI_REG8(0xa7), 0x77}, {CCI_REG8(0xa8), 0x77}, {CCI_REG8(0xa9), 0x77}, + {CCI_REG8(0xa1), 0x80}, {CCI_REG8(0xa2), 0x80}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0xdf), 0x0d}, {CCI_REG8(0xdc), 0x25}, {CCI_REG8(0xdd), 0x30}, + {CCI_REG8(0xe0), 0x77}, {CCI_REG8(0xe1), 0x80}, {CCI_REG8(0xe2), 0x77}, + {CCI_REG8(0xe3), 0x90}, {CCI_REG8(0xe6), 0x90}, {CCI_REG8(0xe7), 0xa0}, + {CCI_REG8(0xe8), 0x90}, {CCI_REG8(0xe9), 0xa0}, + /* AWB */ + /* measure window */ + {GC2145_REG_PAGE_SELECT, 0x00}, + {CCI_REG8(0xec), 0x06}, {CCI_REG8(0xed), 0x04}, {CCI_REG8(0xee), 0x60}, + {CCI_REG8(0xef), 0x90}, {CCI_REG8(0xb6), 0x01}, + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4b), 0x01}, + {CCI_REG8(0x4f), 0x00}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x71}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x91}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x70}, {CCI_REG8(0x4e), 0x01}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x90}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xb0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6f}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xd0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xf0}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcf}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xef}, {CCI_REG8(0x4e), 0x02}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8e}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xae}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xce}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8d}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xad}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcd}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8c}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xac}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcc}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcb}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8b}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xab}, {CCI_REG8(0x4e), 0x03}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaa}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x04}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0b}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xeb}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xea}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x29}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x2a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x4a}, {CCI_REG8(0x4e), 0x05}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x49}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x48}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x68}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe9}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe8}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe7}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x07}, {CCI_REG8(0x4e), 0x07}, + {CCI_REG8(0x4f), 0x01}, + {CCI_REG8(0x50), 0x80}, {CCI_REG8(0x51), 0xa8}, {CCI_REG8(0x52), 0x47}, + {CCI_REG8(0x53), 0x38}, {CCI_REG8(0x54), 0xc7}, {CCI_REG8(0x56), 0x0e}, + {CCI_REG8(0x58), 0x08}, {CCI_REG8(0x5b), 0x00}, {CCI_REG8(0x5c), 0x74}, + {CCI_REG8(0x5d), 0x8b}, {CCI_REG8(0x61), 0xdb}, {CCI_REG8(0x62), 0xb8}, + {CCI_REG8(0x63), 0x86}, {CCI_REG8(0x64), 0xc0}, {CCI_REG8(0x65), 0x04}, + {CCI_REG8(0x67), 0xa8}, {CCI_REG8(0x68), 0xb0}, {CCI_REG8(0x69), 0x00}, + {CCI_REG8(0x6a), 0xa8}, {CCI_REG8(0x6b), 0xb0}, {CCI_REG8(0x6c), 0xaf}, + {CCI_REG8(0x6d), 0x8b}, {CCI_REG8(0x6e), 0x50}, {CCI_REG8(0x6f), 0x18}, + {CCI_REG8(0x73), 0xf0}, {CCI_REG8(0x70), 0x0d}, {CCI_REG8(0x71), 0x60}, + {CCI_REG8(0x72), 0x80}, {CCI_REG8(0x74), 0x01}, {CCI_REG8(0x75), 0x01}, + {CCI_REG8(0x7f), 0x0c}, {CCI_REG8(0x76), 0x70}, {CCI_REG8(0x77), 0x58}, + {CCI_REG8(0x78), 0xa0}, {CCI_REG8(0x79), 0x5e}, {CCI_REG8(0x7a), 0x54}, + {CCI_REG8(0x7b), 0x58}, + /* CC */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0xc0), 0x01}, {CCI_REG8(0xc1), 0x44}, {CCI_REG8(0xc2), 0xfd}, + {CCI_REG8(0xc3), 0x04}, {CCI_REG8(0xc4), 0xf0}, {CCI_REG8(0xc5), 0x48}, + {CCI_REG8(0xc6), 0xfd}, {CCI_REG8(0xc7), 0x46}, {CCI_REG8(0xc8), 0xfd}, + {CCI_REG8(0xc9), 0x02}, {CCI_REG8(0xca), 0xe0}, {CCI_REG8(0xcb), 0x45}, + {CCI_REG8(0xcc), 0xec}, {CCI_REG8(0xcd), 0x48}, {CCI_REG8(0xce), 0xf0}, + {CCI_REG8(0xcf), 0xf0}, {CCI_REG8(0xe3), 0x0c}, {CCI_REG8(0xe4), 0x4b}, + {CCI_REG8(0xe5), 0xe0}, + /* ABS */ + {GC2145_REG_PAGE_SELECT, 0x01}, + {CCI_REG8(0x9f), 0x40}, + /* Dark sun */ + {GC2145_REG_PAGE_SELECT, 0x02}, + {CCI_REG8(0x40), 0xbf}, {CCI_REG8(0x46), 0xcf}, +}; + +#define GC2145_640_480_PIXELRATE 30000000 +#define GC2145_640_480_LINKFREQ 120000000 +#define GC2145_640_480_HBLANK 0x0130 +#define GC2145_640_480_VBLANK 0x000c +static const struct cci_reg_sequence gc2145_mode_640_480_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x86}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Scalar more */ + {CCI_REG8(0xfd), 0x01}, {CCI_REG8(0xfa), 0x00}, + /* Crop 640-480@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x01e0}, + {GC2145_REG_CROP_WIDTH, 0x0280}, + /* Subsampling configuration */ + {CCI_REG8(0x99), 0x55}, {CCI_REG8(0x9a), 0x06}, {CCI_REG8(0x9b), 0x01}, + {CCI_REG8(0x9c), 0x23}, {CCI_REG8(0x9d), 0x00}, {CCI_REG8(0x9e), 0x00}, + {CCI_REG8(0x9f), 0x01}, {CCI_REG8(0xa0), 0x23}, {CCI_REG8(0xa1), 0x00}, + {CCI_REG8(0xa2), 0x00}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x0175}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x045f}, {CCI_REG16(0x29), 0x045f}, + {CCI_REG16(0x2b), 0x045f}, {CCI_REG16(0x2d), 0x045f}, +}; + +#define GC2145_1280_720_PIXELRATE 48000000 +#define GC2145_1280_720_LINKFREQ 192000000 +#define GC2145_1280_720_HBLANK 0x0156 +#define GC2145_1280_720_VBLANK 0x0011 +static const struct cci_reg_sequence gc2145_mode_1280_720_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x83}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 240/160 */ + {GC2145_REG_ROW_START, 0x00f0}, + {GC2145_REG_COL_START, 0x00a0}, + /* Window size 736/1296 */ + {GC2145_REG_WIN_HEIGHT, 0x02e0}, + {GC2145_REG_WIN_WIDTH, 0x0510}, + /* Crop 1280-720@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x02d0}, + {GC2145_REG_CROP_WIDTH, 0x0500}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00e6}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x02b2}, {CCI_REG16(0x29), 0x02b2}, + {CCI_REG16(0x2b), 0x02b2}, {CCI_REG16(0x2d), 0x02b2}, +}; + +#define GC2145_1600_1200_PIXELRATE 60000000 +#define GC2145_1600_1200_LINKFREQ 240000000 +#define GC2145_1600_1200_HBLANK 0x0156 +#define GC2145_1600_1200_VBLANK 0x0010 +static const struct cci_reg_sequence gc2145_mode_1600_1200_regs[] = { + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x84}, + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, + /* Disable PAD IO */ + {GC2145_REG_PAD_IO, 0x00}, + {GC2145_REG_PAGE_SELECT, 0x00}, + /* Row/Col start - 0/0 */ + {GC2145_REG_ROW_START, 0x0000}, + {GC2145_REG_COL_START, 0x0000}, + /* Window size: 1216/1618 */ + {GC2145_REG_WIN_HEIGHT, 0x04c0}, + {GC2145_REG_WIN_WIDTH, 0x0652}, + /* Crop 1600-1200@0-0 */ + {GC2145_REG_CROP_ENABLE, 0x01}, + {GC2145_REG_CROP_Y, 0x0000}, + {GC2145_REG_CROP_X, 0x0000}, + {GC2145_REG_CROP_HEIGHT, 0x04b0}, + {GC2145_REG_CROP_WIDTH, 0x0640}, + {GC2145_REG_PAGE_SELECT, 0x01}, + /* AEC anti-flicker */ + {CCI_REG16(0x25), 0x00fa}, + /* AEC exposure level 1-5 */ + {CCI_REG16(0x27), 0x04e2}, {CCI_REG16(0x29), 0x04e2}, + {CCI_REG16(0x2b), 0x04e2}, {CCI_REG16(0x2d), 0x04e2}, +}; + +static const s64 gc2145_link_freq_menu[] = { + GC2145_640_480_LINKFREQ, + GC2145_1280_720_LINKFREQ, + GC2145_1600_1200_LINKFREQ, +}; + +/* Regulators supplies */ +static const char * const gc2145_supply_name[] = { + "iovdd", /* Digital I/O (1.7-3V) suppply */ + "avdd", /* Analog (2.7-3V) supply */ + "dvdd", /* Digital Core (1.7-1.9V) supply */ +}; + +#define GC2145_NUM_SUPPLIES ARRAY_SIZE(gc2145_supply_name) + +/* Mode configs */ +#define GC2145_MODE_640X480 0 +#define GC2145_MODE_1280X720 1 +#define GC2145_MODE_1600X1200 2 +static const struct gc2145_mode supported_modes[] = { + { + /* 640x480 30fps mode */ + .width = 640, + .height = 480, + .reg_seq = gc2145_mode_640_480_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_640_480_regs), + .pixel_rate = GC2145_640_480_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 640, + .height = 480, + }, + .hblank = GC2145_640_480_HBLANK, + .vblank = GC2145_640_480_VBLANK, + .link_freq_index = GC2145_MODE_640X480, + }, + { + /* 1280x720 30fps mode */ + .width = 1280, + .height = 720, + .reg_seq = gc2145_mode_1280_720_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1280_720_regs), + .pixel_rate = GC2145_1280_720_PIXELRATE, + .crop = { + .top = 160, + .left = 240, + .width = 1280, + .height = 720, + }, + .hblank = GC2145_1280_720_HBLANK, + .vblank = GC2145_1280_720_VBLANK, + .link_freq_index = GC2145_MODE_1280X720, + }, + { + /* 1600x1200 20fps mode */ + .width = 1600, + .height = 1200, + .reg_seq = gc2145_mode_1600_1200_regs, + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1600_1200_regs), + .pixel_rate = GC2145_1600_1200_PIXELRATE, + .crop = { + .top = 0, + .left = 0, + .width = 1600, + .height = 1200, + }, + .hblank = GC2145_1600_1200_HBLANK, + .vblank = GC2145_1600_1200_VBLANK, + .link_freq_index = GC2145_MODE_1600X1200, + }, +}; + +/** + * struct gc2145_format - GC2145 pixel format description + * @code: media bus (MBUS) associated code + * @datatype: MIPI CSI2 data type + * @output_fmt: GC2145 output format + * @switch_bit: GC2145 first/second switch + */ +struct gc2145_format { + unsigned int code; + unsigned char datatype; + unsigned char output_fmt; + bool switch_bit; +}; + +/* All supported formats */ +static const struct gc2145_format supported_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x00, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x01, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x02, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .output_fmt = 0x03, + }, + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .datatype = MIPI_CSI2_DT_RGB565, + .output_fmt = 0x06, + .switch_bit = true, + }, +}; + +struct gc2145_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; +}; + +struct gc2145 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct regmap *regmap; + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; + struct regulator_bulk_data supplies[GC2145_NUM_SUPPLIES]; + + /* V4L2 controls */ + struct gc2145_ctrls ctrls; + + /* Current mode */ + const struct gc2145_mode *mode; +}; + +static inline struct gc2145 *to_gc2145(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct gc2145, sd); +} + +static inline struct v4l2_subdev *gc2145_ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct gc2145, + ctrls.handler)->sd; +} + +static const struct gc2145_format * +gc2145_get_format_code(struct gc2145 *gc2145, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].code == code) + break; + } + + if (i >= ARRAY_SIZE(supported_formats)) + i = 0; + + return &supported_formats[i]; +} + +static void gc2145_update_pad_format(struct gc2145 *gc2145, + const struct gc2145_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int gc2145_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize pad format */ + format = v4l2_subdev_state_get_format(state, 0); + gc2145_update_pad_format(gc2145, &supported_modes[0], format, + MEDIA_BUS_FMT_RGB565_1X16); + + /* Initialize crop rectangle. */ + crop = v4l2_subdev_state_get_crop(state, 0); + *crop = supported_modes[0].crop; + + return 0; +} + +static int gc2145_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC2145_NATIVE_WIDTH; + sel->r.height = GC2145_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = 1600; + sel->r.height = 1200; + + return 0; + } + + return -EINVAL; +} + +static int gc2145_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + code->code = supported_formats[code->index].code; + return 0; +} + +static int gc2145_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_format *gc2145_format; + u32 code; + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + gc2145_format = gc2145_get_format_code(gc2145, fse->code); + code = gc2145_format->code; + if (fse->code != code) + 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 gc2145_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + const struct gc2145_mode *mode; + const struct gc2145_format *gc2145_fmt; + struct v4l2_mbus_framefmt *framefmt; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_rect *crop; + + gc2145_fmt = gc2145_get_format_code(gc2145, fmt->format.code); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + gc2145->mode = mode; + /* Update pixel_rate based on the mode */ + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mode->pixel_rate); + /* Update link_freq based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->link_freq, mode->link_freq_index); + /* Update hblank/vblank based on the mode */ + __v4l2_ctrl_s_ctrl(ctrls->hblank, mode->hblank); + __v4l2_ctrl_s_ctrl(ctrls->vblank, mode->vblank); + } + *framefmt = fmt->format; + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); + *crop = mode->crop; + + return 0; +} + +static const struct cci_reg_sequence gc2145_common_mipi_regs[] = { + {GC2145_REG_PAGE_SELECT, 0x03}, + {GC2145_REG_DPHY_ANALOG_MODE1, GC2145_DPHY_MODE_PHY_CLK_EN | + GC2145_DPHY_MODE_PHY_LANE0_EN | + GC2145_DPHY_MODE_PHY_LANE1_EN | + GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL}, + {GC2145_REG_DPHY_ANALOG_MODE2, GC2145_DPHY_CLK_DIFF(2) | + GC2145_DPHY_LANE0_DIFF(2)}, + {GC2145_REG_DPHY_ANALOG_MODE3, GC2145_DPHY_LANE1_DIFF(0) | + GC2145_DPHY_CLK_DELAY}, + {GC2145_REG_FIFO_MODE, GC2145_FIFO_MODE_READ_GATE | + GC2145_FIFO_MODE_MIPI_CLK_MODULE}, + {GC2145_REG_DPHY_MODE, GC2145_DPHY_MODE_TRIGGER_PROG}, + /* Clock & Data lanes timing */ + {GC2145_REG_T_LPX, 0x10}, + {GC2145_REG_T_CLK_HS_PREPARE, 0x04}, {GC2145_REG_T_CLK_ZERO, 0x10}, + {GC2145_REG_T_CLK_PRE, 0x10}, {GC2145_REG_T_CLK_POST, 0x10}, + {GC2145_REG_T_CLK_TRAIL, 0x05}, + {GC2145_REG_T_HS_PREPARE, 0x03}, {GC2145_REG_T_HS_ZERO, 0x0a}, + {GC2145_REG_T_HS_TRAIL, 0x06}, +}; + +static int gc2145_config_mipi_mode(struct gc2145 *gc2145, + const struct gc2145_format *gc2145_format) +{ + u16 lwc, fifo_full_lvl; + int ret = 0; + + /* Common MIPI settings */ + cci_multi_reg_write(gc2145->regmap, gc2145_common_mipi_regs, + ARRAY_SIZE(gc2145_common_mipi_regs), &ret); + + /* + * Adjust the MIPI buffer settings. + * For YUV/RGB, LWC = image width * 2 + * For RAW8, LWC = image width + * For RAW10, LWC = image width * 1.25 + */ + lwc = gc2145->mode->width * 2; + cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); + + /* + * Adjust the MIPI FIFO Full Level + * 640x480 RGB: 0x0190 + * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 + * 1600x1200 RAW: 0x0190 + */ + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) + fifo_full_lvl = 0x0001; + else + fifo_full_lvl = 0x0190; + + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, + fifo_full_lvl >> 8, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, + fifo_full_lvl & 0xff, &ret); + + /* + * Set the FIFO gate mode / MIPI wdiv set: + * 0xf1 in case of RAW mode and 0xf0 otherwise + */ + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); + + /* Set the MIPI data type */ + cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, + gc2145_format->datatype, &ret); + + /* Configure mode and enable CSI */ + cci_write(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_RAW8 | GC2145_CSI2_MODE_DOUBLE | + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, &ret); + + return ret; +} + +static int gc2145_start_streaming(struct gc2145 *gc2145, + struct v4l2_subdev_state *state) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct gc2145_format *gc2145_format; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + /* Apply default values of current mode */ + cci_multi_reg_write(gc2145->regmap, gc2145->mode->reg_seq, + gc2145->mode->reg_seq_size, &ret); + cci_multi_reg_write(gc2145->regmap, gc2145_common_regs, + ARRAY_SIZE(gc2145_common_regs), &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + gc2145_format = gc2145_get_format_code(gc2145, fmt->code); + + /* Set the output format */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + cci_write(gc2145->regmap, GC2145_REG_OUTPUT_FMT, + gc2145_format->output_fmt, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BYPASS_MODE, + GC2145_BYPASS_MODE_SWITCH, + gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH + : 0, &ret); + if (ret) { + dev_err(&client->dev, "%s failed to write regs\n", __func__); + goto err_rpm_put; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(&gc2145->ctrls.handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply ctrls\n", __func__); + goto err_rpm_put; + } + + /* Perform MIPI specific configuration */ + ret = gc2145_config_mipi_mode(gc2145, gc2145_format); + if (ret) { + dev_err(&client->dev, "%s failed to write mipi conf\n", + __func__); + goto err_rpm_put; + } + + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + + return 0; + +err_rpm_put: + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + return ret; +} + +static void gc2145_stop_streaming(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret = 0; + + /* Disable lanes & mipi streaming */ + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x03, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, 0, + &ret); + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); + if (ret) + dev_err(&client->dev, "%s failed to write regs\n", __func__); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); +} + +static int gc2145_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct gc2145 *gc2145 = to_gc2145(sd); + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) + ret = gc2145_start_streaming(gc2145, state); + else + gc2145_stop_streaming(gc2145); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +/* Power/clock management functions */ +static int gc2145_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + ret = regulator_bulk_enable(GC2145_NUM_SUPPLIES, gc2145->supplies); + if (ret) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(gc2145->xclk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + goto reg_off; + } + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 0); + gpiod_set_value_cansleep(gc2145->reset_gpio, 0); + + /* + * Datasheet doesn't mention timing between PWDN/RESETB control and + * i2c access however, experimentation shows that a rather big delay is + * needed. + */ + msleep(41); + + return 0; + +reg_off: + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return ret; +} + +static int gc2145_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc2145 *gc2145 = to_gc2145(sd); + + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 1); + gpiod_set_value_cansleep(gc2145->reset_gpio, 1); + clk_disable_unprepare(gc2145->xclk); + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); + + return 0; +} + +static int gc2145_get_regulators(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + unsigned int i; + + for (i = 0; i < GC2145_NUM_SUPPLIES; i++) + gc2145->supplies[i].supply = gc2145_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, GC2145_NUM_SUPPLIES, + gc2145->supplies); +} + +/* Verify chip ID */ +static int gc2145_identify_module(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + int ret; + u64 chip_id; + + ret = cci_read(gc2145->regmap, GC2145_REG_CHIP_ID, &chip_id, NULL); + if (ret) { + dev_err(&client->dev, "failed to read chip id (%d)\n", ret); + return ret; + } + + if (chip_id != GC2145_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", + GC2145_CHIP_ID, chip_id); + return -EIO; + } + + return 0; +} + +static const char * const test_pattern_menu[] = { + "Disabled", + "Colored patterns", + "Uniform white", + "Uniform yellow", + "Uniform cyan", + "Uniform green", + "Uniform magenta", + "Uniform red", + "Uniform black", +}; + +#define GC2145_TEST_PATTERN_ENABLE BIT(0) +#define GC2145_TEST_PATTERN_UXGA BIT(3) + +#define GC2145_TEST_UNIFORM BIT(3) +#define GC2145_TEST_WHITE (4 << 4) +#define GC2145_TEST_YELLOW (8 << 4) +#define GC2145_TEST_CYAN (9 << 4) +#define GC2145_TEST_GREEN (6 << 4) +#define GC2145_TEST_MAGENTA (10 << 4) +#define GC2145_TEST_RED (5 << 4) +#define GC2145_TEST_BLACK (0) + +static const u8 test_pattern_val[] = { + 0, + GC2145_TEST_PATTERN_ENABLE, + GC2145_TEST_UNIFORM | GC2145_TEST_WHITE, + GC2145_TEST_UNIFORM | GC2145_TEST_YELLOW, + GC2145_TEST_UNIFORM | GC2145_TEST_CYAN, + GC2145_TEST_UNIFORM | GC2145_TEST_GREEN, + GC2145_TEST_UNIFORM | GC2145_TEST_MAGENTA, + GC2145_TEST_UNIFORM | GC2145_TEST_RED, + GC2145_TEST_UNIFORM | GC2145_TEST_BLACK, +}; + +static const struct v4l2_subdev_core_ops gc2145_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops gc2145_video_ops = { + .s_stream = gc2145_set_stream, +}; + +static const struct v4l2_subdev_pad_ops gc2145_pad_ops = { + .enum_mbus_code = gc2145_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc2145_set_pad_format, + .get_selection = gc2145_get_selection, + .enum_frame_size = gc2145_enum_frame_size, +}; + +static const struct v4l2_subdev_ops gc2145_subdev_ops = { + .core = &gc2145_core_ops, + .video = &gc2145_video_ops, + .pad = &gc2145_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc2145_subdev_internal_ops = { + .init_state = gc2145_init_state, +}; + +static int gc2145_set_ctrl_test_pattern(struct gc2145 *gc2145, int value) +{ + int ret = 0; + + if (!value) { + /* Disable test pattern */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, 0, &ret); + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + } + + /* Enable test pattern, colored or uniform */ + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, + GC2145_TEST_PATTERN_ENABLE | GC2145_TEST_PATTERN_UXGA, &ret); + + if (!(test_pattern_val[value] & GC2145_TEST_UNIFORM)) + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, + &ret); + + /* Uniform */ + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, + test_pattern_val[value], &ret); +} + +static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = gc2145_ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct gc2145 *gc2145 = to_gc2145(sd); + int ret; + + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_HBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(gc2145->regmap, GC2145_REG_VBLANK, ctrl->val, + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc2145_set_ctrl_test_pattern(gc2145, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(0), (ctrl->val ? BIT(0) : 0), NULL); + break; + case V4L2_CID_VFLIP: + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, + BIT(1), (ctrl->val ? BIT(1) : 0), NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc2145_ctrl_ops = { + .s_ctrl = gc2145_s_ctrl, +}; + +/* Initialize control handlers */ +static int gc2145_init_controls(struct gc2145 *gc2145) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); + const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; + struct gc2145_ctrls *ctrls = &gc2145->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 12); + if (ret) + return ret; + + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + GC2145_640_480_PIXELRATE, + GC2145_1600_1200_PIXELRATE, 1, + supported_modes[0].pixel_rate); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc2145_link_freq_menu) - 1, + 0, gc2145_link_freq_menu); + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + 0, 0xfff, 1, GC2145_640_480_HBLANK); + + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + 0, 0x1fff, 1, GC2145_640_480_VBLANK); + + 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->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + if (hdl->error) { + ret = hdl->error; + dev_err(&client->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &gc2145_ctrl_ops, + &props); + if (ret) + goto error; + + gc2145->sd.ctrl_handler = hdl; + + return 0; + +error: + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +static int gc2145_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + ret = -EINVAL; + goto out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto out; + } + + if (ep_cfg.nr_of_link_frequencies != 3 || + ep_cfg.link_frequencies[0] != GC2145_640_480_LINKFREQ || + ep_cfg.link_frequencies[1] != GC2145_1280_720_LINKFREQ || + ep_cfg.link_frequencies[2] != GC2145_1600_1200_LINKFREQ) { + dev_err(dev, "Invalid link-frequencies provided\n"); + ret = -EINVAL; + } + +out: + v4l2_fwnode_endpoint_free(&ep_cfg); + + return ret; +} + +static int gc2145_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + unsigned int xclk_freq; + struct gc2145 *gc2145; + int ret; + + gc2145 = devm_kzalloc(&client->dev, sizeof(*gc2145), GFP_KERNEL); + if (!gc2145) + return -ENOMEM; + + v4l2_i2c_subdev_init(&gc2145->sd, client, &gc2145_subdev_ops); + gc2145->sd.internal_ops = &gc2145_subdev_internal_ops; + + /* Check the hardware configuration in device tree */ + if (gc2145_check_hwcfg(dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + gc2145->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc2145->xclk)) + return dev_err_probe(dev, PTR_ERR(gc2145->xclk), + "failed to get xclk\n"); + + xclk_freq = clk_get_rate(gc2145->xclk); + if (xclk_freq != GC2145_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + xclk_freq); + return -EINVAL; + } + + ret = gc2145_get_regulators(gc2145); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + /* Request optional reset pin */ + gc2145->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->reset_gpio), + "failed to get reset_gpio\n"); + + /* Request optional powerdown pin */ + gc2145->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(gc2145->powerdown_gpio)) + return dev_err_probe(dev, PTR_ERR(gc2145->powerdown_gpio), + "failed to get powerdown_gpio\n"); + + /* Initialise the regmap for further cci access */ + gc2145->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(gc2145->regmap)) + return dev_err_probe(dev, PTR_ERR(gc2145->regmap), + "failed to get cci regmap\n"); + + /* + * The sensor must be powered for gc2145_identify_module() + * to be able to read the CHIP_ID register + */ + ret = gc2145_power_on(dev); + if (ret) + return ret; + + ret = gc2145_identify_module(gc2145); + if (ret) + goto error_power_off; + + /* Set default mode */ + gc2145->mode = &supported_modes[0]; + + ret = gc2145_init_controls(gc2145); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc2145->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + gc2145->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&gc2145->sd.entity, 1, &gc2145->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + gc2145->sd.state_lock = gc2145->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(dev); + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + ret = v4l2_async_register_subdev_sensor(&gc2145->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&gc2145->sd); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_media_entity: + media_entity_cleanup(&gc2145->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + +error_power_off: + gc2145_power_off(dev); + + return ret; +} + +static void gc2145_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc2145 *gc2145 = to_gc2145(sd); + + v4l2_subdev_cleanup(sd); + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc2145_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc2145_dt_ids[] = { + { .compatible = "galaxycore,gc2145" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, gc2145_dt_ids); + +static const struct dev_pm_ops gc2145_pm_ops = { + RUNTIME_PM_OPS(gc2145_power_off, gc2145_power_on, NULL) +}; + +static struct i2c_driver gc2145_i2c_driver = { + .driver = { + .name = "gc2145", + .of_match_table = gc2145_dt_ids, + .pm = pm_ptr(&gc2145_pm_ops), + }, + .probe = gc2145_probe, + .remove = gc2145_remove, +}; + +module_i2c_driver(gc2145_i2c_driver); + +MODULE_AUTHOR("Alain Volmat "); +MODULE_DESCRIPTION("GalaxyCore GC2145 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index f6ea9b7b9..38c77d515 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -935,7 +935,7 @@ __hi556_get_pad_crop(struct hi556 *hi556, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&hi556->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &hi556->cur_mode->crop; } @@ -1075,7 +1075,7 @@ static int hi556_set_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); hi556_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi556->cur_mode = mode; __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); @@ -1109,9 +1109,8 @@ static int hi556_get_format(struct v4l2_subdev *sd, mutex_lock(&hi556->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi556_assign_pad_format(hi556->cur_mode, &fmt->format); @@ -1157,10 +1156,10 @@ static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi556->mutex); hi556_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); try_crop->top = HI556_PIXEL_ARRAY_TOP; try_crop->left = HI556_PIXEL_ARRAY_LEFT; try_crop->width = HI556_PIXEL_ARRAY_WIDTH; diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 825fc8dc4..9c565ec03 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1705,7 +1705,7 @@ static int hi846_set_format(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mf; return 0; } @@ -1783,9 +1783,8 @@ static int hi846_get_format(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(&hi846->sd, - sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); return 0; } @@ -1852,7 +1851,7 @@ static int hi846_get_selection(struct v4l2_subdev *sd, mutex_lock(&hi846->mutex); switch (sel->which) { case V4L2_SUBDEV_FORMAT_TRY: - v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: sel->r = hi846->cur_mode->crop; @@ -1872,13 +1871,13 @@ static int hi846_get_selection(struct v4l2_subdev *sd, } } -static int hi846_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int hi846_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct hi846 *hi846 = to_hi846(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&hi846->mutex); mf->code = HI846_MEDIA_BUS_FORMAT; @@ -1896,7 +1895,6 @@ static const struct v4l2_subdev_video_ops hi846_video_ops = { }; static const struct v4l2_subdev_pad_ops hi846_pad_ops = { - .init_cfg = hi846_init_cfg, .enum_frame_size = hi846_enum_frame_size, .enum_mbus_code = hi846_enum_mbus_code, .set_fmt = hi846_set_format, @@ -1909,6 +1907,10 @@ static const struct v4l2_subdev_ops hi846_subdev_ops = { .pad = &hi846_pad_ops, }; +static const struct v4l2_subdev_internal_ops hi846_internal_ops = { + .init_state = hi846_init_state, +}; + static const struct media_entity_operations hi846_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2072,6 +2074,7 @@ static int hi846_probe(struct i2c_client *client) return ret; v4l2_i2c_subdev_init(&hi846->sd, client, &hi846_subdev_ops); + hi846->sd.internal_ops = &hi846_internal_ops; mutex_init(&hi846->mutex); diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 4075c3898..72c60747a 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -2655,7 +2655,7 @@ static int hi847_set_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); hi847_assign_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { hi847->cur_mode = mode; @@ -2690,9 +2690,8 @@ static int hi847_get_format(struct v4l2_subdev *sd, mutex_lock(&hi847->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi847->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else hi847_assign_pad_format(hi847->cur_mode, &fmt->format); @@ -2737,7 +2736,7 @@ static int hi847_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&hi847->mutex); hi847_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&hi847->mutex); return 0; diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index a9b0aea1a..639e05340 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -395,7 +395,7 @@ static int imx208_write_regs(struct imx208 *imx208, static int imx208_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -548,9 +548,8 @@ static int __imx208_get_pad_format(struct imx208 *imx208, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx208->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx208_mode_to_pad_format(imx208, imx208->cur_mode, fmt); @@ -591,7 +590,7 @@ static int imx208_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx208_mode_to_pad_format(imx208, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { imx208->cur_mode = mode; __v4l2_ctrl_s_ctrl(imx208->link_freq, mode->link_freq_index); diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 4f77ea02c..b148b1bd2 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -19,12 +19,31 @@ #include #include +#define IMX214_REG_MODE_SELECT 0x0100 +#define IMX214_MODE_STANDBY 0x00 +#define IMX214_MODE_STREAMING 0x01 + #define IMX214_DEFAULT_CLK_FREQ 24000000 #define IMX214_DEFAULT_LINK_FREQ 480000000 #define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10) #define IMX214_FPS 30 #define IMX214_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 +/* Exposure control */ +#define IMX214_REG_EXPOSURE 0x0202 +#define IMX214_EXPOSURE_MIN 0 +#define IMX214_EXPOSURE_MAX 3184 +#define IMX214_EXPOSURE_STEP 1 +#define IMX214_EXPOSURE_DEFAULT 3184 + +/* IMX214 native and active pixel array size */ +#define IMX214_NATIVE_WIDTH 4224U +#define IMX214_NATIVE_HEIGHT 3136U +#define IMX214_PIXEL_ARRAY_LEFT 8U +#define IMX214_PIXEL_ARRAY_TOP 8U +#define IMX214_PIXEL_ARRAY_WIDTH 4208U +#define IMX214_PIXEL_ARRAY_HEIGHT 3120U + static const char * const imx214_supply_name[] = { "vdda", "vddd", @@ -538,7 +557,7 @@ __imx214_get_pad_format(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->fmt; default: @@ -568,7 +587,7 @@ __imx214_get_pad_crop(struct imx214 *imx214, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx214->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &imx214->crop; default: @@ -623,18 +642,35 @@ static int imx214_get_selection(struct v4l2_subdev *sd, { struct imx214 *imx214 = to_imx214(sd); - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&imx214->mutex); + sel->r = *__imx214_get_pad_crop(imx214, sd_state, sel->pad, + sel->which); + mutex_unlock(&imx214->mutex); + return 0; - mutex_lock(&imx214->mutex); - sel->r = *__imx214_get_pad_crop(imx214, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx214->mutex); - return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX214_NATIVE_WIDTH; + sel->r.height = IMX214_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX214_PIXEL_ARRAY_TOP; + sel->r.left = IMX214_PIXEL_ARRAY_LEFT; + sel->r.width = IMX214_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX214_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; } -static int imx214_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx214_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { }; @@ -665,7 +701,7 @@ static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE: vals[1] = ctrl->val; vals[0] = ctrl->val >> 8; - ret = regmap_bulk_write(imx214->regmap, 0x202, vals, 2); + ret = regmap_bulk_write(imx214->regmap, IMX214_REG_EXPOSURE, vals, 2); if (ret < 0) dev_err(imx214->dev, "Error %d\n", ret); ret = 0; @@ -684,6 +720,76 @@ static const struct v4l2_ctrl_ops imx214_ctrl_ops = { .s_ctrl = imx214_set_ctrl, }; +static int imx214_ctrls_init(struct imx214 *imx214) +{ + static const s64 link_freq[] = { + IMX214_DEFAULT_LINK_FREQ + }; + static const struct v4l2_area unit_size = { + .width = 1120, + .height = 1120, + }; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + int ret; + + ret = v4l2_fwnode_device_parse(imx214->dev, &props); + if (ret < 0) + return ret; + + ctrl_hdlr = &imx214->ctrls; + ret = v4l2_ctrl_handler_init(&imx214->ctrls, 6); + if (ret) + return ret; + + imx214->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, + V4L2_CID_PIXEL_RATE, 0, + IMX214_DEFAULT_PIXEL_RATE, 1, + IMX214_DEFAULT_PIXEL_RATE); + + imx214->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, + 0, link_freq); + if (imx214->link_freq) + imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* + * WARNING! + * Values obtained reverse engineering blobs and/or devices. + * Ranges and functionality might be wrong. + * + * Sony, please release some register set documentation for the + * device. + * + * Yours sincerely, Ricardo. + */ + imx214->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX214_EXPOSURE_MIN, + IMX214_EXPOSURE_MAX, + IMX214_EXPOSURE_STEP, + IMX214_EXPOSURE_DEFAULT); + + imx214->unit_size = v4l2_ctrl_new_std_compound(ctrl_hdlr, + NULL, + V4L2_CID_UNIT_CELL_SIZE, + v4l2_ctrl_ptr_create((void *)&unit_size)); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx214_ctrl_ops, &props); + + ret = ctrl_hdlr->error; + if (ret) { + v4l2_ctrl_handler_free(ctrl_hdlr); + dev_err(imx214->dev, "failed to add controls: %d\n", ret); + return ret; + } + + imx214->sd.ctrl_handler = ctrl_hdlr; + + return 0; +}; + #define MAX_CMD 4 static int imx214_write_table(struct imx214 *imx214, const struct reg_8 table[]) @@ -743,7 +849,7 @@ static int imx214_start_streaming(struct imx214 *imx214) dev_err(imx214->dev, "could not sync v4l2 controls\n"); goto error; } - ret = regmap_write(imx214->regmap, 0x100, 1); + ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STREAMING); if (ret < 0) { dev_err(imx214->dev, "could not sent start table %d\n", ret); goto error; @@ -761,7 +867,7 @@ static int imx214_stop_streaming(struct imx214 *imx214) { int ret; - ret = regmap_write(imx214->regmap, 0x100, 0); + ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STANDBY); if (ret < 0) dev_err(imx214->dev, "could not sent stop table %d\n", ret); @@ -795,9 +901,17 @@ err_rpm_put: return ret; } -static int imx214_g_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fival) +static int imx214_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fival) { + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + fival->interval.numerator = 1; fival->interval.denominator = IMX214_FPS; @@ -828,8 +942,6 @@ static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, static const struct v4l2_subdev_video_ops imx214_video_ops = { .s_stream = imx214_s_stream, - .g_frame_interval = imx214_g_frame_interval, - .s_frame_interval = imx214_g_frame_interval, }; static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { @@ -839,7 +951,8 @@ static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { .get_fmt = imx214_get_format, .set_fmt = imx214_set_format, .get_selection = imx214_get_selection, - .init_cfg = imx214_entity_init_cfg, + .get_frame_interval = imx214_get_frame_interval, + .set_frame_interval = imx214_get_frame_interval, }; static const struct v4l2_subdev_ops imx214_subdev_ops = { @@ -848,6 +961,10 @@ static const struct v4l2_subdev_ops imx214_subdev_ops = { .pad = &imx214_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx214_internal_ops = { + .init_state = imx214_entity_init_state, +}; + static const struct regmap_config sensor_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -907,13 +1024,6 @@ static int imx214_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct imx214 *imx214; - static const s64 link_freq[] = { - IMX214_DEFAULT_LINK_FREQ, - }; - static const struct v4l2_area unit_size = { - .width = 1120, - .height = 1120, - }; int ret; ret = imx214_parse_fwnode(dev); @@ -957,6 +1067,7 @@ static int imx214_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); + imx214->sd.internal_ops = &imx214_internal_ops; /* * Enable power initially, to avoid warnings @@ -968,45 +1079,10 @@ static int imx214_probe(struct i2c_client *client) pm_runtime_enable(imx214->dev); pm_runtime_idle(imx214->dev); - v4l2_ctrl_handler_init(&imx214->ctrls, 3); - - imx214->pixel_rate = v4l2_ctrl_new_std(&imx214->ctrls, NULL, - V4L2_CID_PIXEL_RATE, 0, - IMX214_DEFAULT_PIXEL_RATE, 1, - IMX214_DEFAULT_PIXEL_RATE); - imx214->link_freq = v4l2_ctrl_new_int_menu(&imx214->ctrls, NULL, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq) - 1, - 0, link_freq); - if (imx214->link_freq) - imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - /* - * WARNING! - * Values obtained reverse engineering blobs and/or devices. - * Ranges and functionality might be wrong. - * - * Sony, please release some register set documentation for the - * device. - * - * Yours sincerely, Ricardo. - */ - imx214->exposure = v4l2_ctrl_new_std(&imx214->ctrls, &imx214_ctrl_ops, - V4L2_CID_EXPOSURE, - 0, 3184, 1, 0x0c70); - - imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls, - NULL, - V4L2_CID_UNIT_CELL_SIZE, - v4l2_ctrl_ptr_create((void *)&unit_size)); - ret = imx214->ctrls.error; - if (ret) { - dev_err(&client->dev, "%s control init failed (%d)\n", - __func__, ret); - goto free_ctrl; - } + ret = imx214_ctrls_init(imx214); + if (ret < 0) + goto error_power_off; - imx214->sd.ctrl_handler = &imx214->ctrls; mutex_init(&imx214->mutex); imx214->ctrls.lock = &imx214->mutex; @@ -1021,7 +1097,7 @@ static int imx214_probe(struct i2c_client *client) goto free_ctrl; } - imx214_entity_init_cfg(&imx214->sd, NULL); + imx214_entity_init_state(&imx214->sd, NULL); ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { @@ -1036,6 +1112,7 @@ free_entity: free_ctrl: mutex_destroy(&imx214->mutex); v4l2_ctrl_handler_free(&imx214->ctrls); +error_power_off: pm_runtime_disable(imx214->dev); return ret; diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 8436880dc..e17ef2e9d 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -374,7 +374,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -593,8 +593,8 @@ static int imx219_set_framefmt(struct imx219 *imx219, u64 bin_h, bin_v; int ret = 0; - format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); - crop = v4l2_subdev_get_pad_crop(&imx219->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: @@ -826,7 +826,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); *format = fmt->format; /* @@ -836,7 +836,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); crop->width = format->width * bin_h; crop->height = format->height * bin_v; crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; @@ -880,7 +880,7 @@ static int imx219_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: { - sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); + sel->r = *v4l2_subdev_state_get_crop(state, 0); return 0; } @@ -905,8 +905,8 @@ static int imx219_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -static int imx219_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx219_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -933,7 +933,6 @@ static const struct v4l2_subdev_video_ops imx219_video_ops = { }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { - .init_cfg = imx219_init_cfg, .enum_mbus_code = imx219_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, @@ -947,6 +946,9 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { .pad = &imx219_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx219_internal_ops = { + .init_state = imx219_init_state, +}; /* ----------------------------------------------------------------------------- * Power management @@ -1098,6 +1100,7 @@ static int imx219_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); + imx219->sd.internal_ops = &imx219_internal_ops; /* Check the hardware configuration in device tree */ if (imx219_check_hwcfg(dev, imx219)) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index b3827f4bc..a577afb53 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -708,7 +708,7 @@ static int imx258_write_regs(struct imx258 *imx258, static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -862,9 +862,8 @@ static int __imx258_get_pad_format(struct imx258 *imx258, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else imx258_update_pad_format(imx258->cur_mode, fmt); @@ -908,7 +907,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx258_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx258->cur_mode = mode; diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f33b692e6..352da68b8 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -594,8 +594,8 @@ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl); static int imx274_set_exposure(struct stimx274 *priv, int val); static int imx274_set_vflip(struct stimx274 *priv, int val); static int imx274_set_test_pattern(struct stimx274 *priv, int val); -static int imx274_set_frame_interval(struct stimx274 *priv, - struct v4l2_fract frame_interval); +static int __imx274_set_frame_interval(struct stimx274 *priv, + struct v4l2_fract frame_interval); static inline void msleep_range(unsigned int delay_base) { @@ -1018,8 +1018,8 @@ static int __imx274_change_compose(struct stimx274 *imx274, int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { - cur_crop = &sd_state->pads->try_crop; - tgt_fmt = &sd_state->pads->try_fmt; + cur_crop = v4l2_subdev_state_get_crop(sd_state, 0); + tgt_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { cur_crop = &imx274->crop; tgt_fmt = &imx274->format; @@ -1112,7 +1112,7 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, */ fmt->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; else imx274->format = *fmt; @@ -1143,8 +1143,8 @@ static int imx274_get_selection(struct v4l2_subdev *sd, } if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - src_crop = &sd_state->pads->try_crop; - src_fmt = &sd_state->pads->try_fmt; + src_crop = v4l2_subdev_state_get_crop(sd_state, 0); + src_fmt = v4l2_subdev_state_get_format(sd_state, 0); } else { src_crop = &imx274->crop; src_fmt = &imx274->format; @@ -1215,7 +1215,7 @@ static int imx274_set_selection_crop(struct stimx274 *imx274, sel->r = new_crop; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - tgt_crop = &sd_state->pads->try_crop; + tgt_crop = v4l2_subdev_state_get_crop(sd_state, 0); else tgt_crop = &imx274->crop; @@ -1327,20 +1327,19 @@ static int imx274_apply_trimming(struct stimx274 *imx274) return err; } -/** - * imx274_g_frame_interval - Get the frame interval - * @sd: Pointer to V4L2 Sub device structure - * @fi: Pointer to V4l2 Sub device frame interval structure - * - * This function is used to get the frame interval. - * - * Return: 0 on success - */ -static int imx274_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int imx274_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct stimx274 *imx274 = to_imx274(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + fi->interval = imx274->frame_interval; dev_dbg(&imx274->client->dev, "%s frame rate = %d / %d\n", __func__, imx274->frame_interval.numerator, @@ -1349,29 +1348,28 @@ static int imx274_g_frame_interval(struct v4l2_subdev *sd, return 0; } -/** - * imx274_s_frame_interval - Set the frame interval - * @sd: Pointer to V4L2 Sub device structure - * @fi: Pointer to V4l2 Sub device frame interval structure - * - * This function is used to set the frame intervavl. - * - * Return: 0 on success - */ -static int imx274_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int imx274_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct stimx274 *imx274 = to_imx274(sd); struct v4l2_ctrl *ctrl = imx274->ctrls.exposure; int min, max, def; int ret; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + ret = pm_runtime_resume_and_get(&imx274->client->dev); if (ret < 0) return ret; mutex_lock(&imx274->lock); - ret = imx274_set_frame_interval(imx274, fi->interval); + ret = __imx274_set_frame_interval(imx274, fi->interval); if (!ret) { fi->interval = imx274->frame_interval; @@ -1466,8 +1464,8 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) * are changed. * gain is not affected. */ - ret = imx274_set_frame_interval(imx274, - imx274->frame_interval); + ret = __imx274_set_frame_interval(imx274, + imx274->frame_interval); if (ret) goto fail; @@ -1830,7 +1828,7 @@ fail: } /* - * imx274_set_frame_interval - Function called when setting frame interval + * __imx274_set_frame_interval - Function called when setting frame interval * @priv: Pointer to device structure * @frame_interval: Variable for frame interval * @@ -1839,8 +1837,8 @@ fail: * * Return: 0 on success */ -static int imx274_set_frame_interval(struct stimx274 *priv, - struct v4l2_fract frame_interval) +static int __imx274_set_frame_interval(struct stimx274 *priv, + struct v4l2_fract frame_interval) { int err; u32 frame_length, req_frame_rate; @@ -1927,11 +1925,11 @@ static const struct v4l2_subdev_pad_ops imx274_pad_ops = { .set_fmt = imx274_set_fmt, .get_selection = imx274_get_selection, .set_selection = imx274_set_selection, + .get_frame_interval = imx274_get_frame_interval, + .set_frame_interval = imx274_set_frame_interval, }; static const struct v4l2_subdev_video_ops imx274_video_ops = { - .g_frame_interval = imx274_g_frame_interval, - .s_frame_interval = imx274_s_frame_interval, .s_stream = imx274_s_stream, }; diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 6dacd38ae..4150e6e4b 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -758,7 +758,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&imx290->sd); - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: @@ -994,7 +994,7 @@ static int imx290_start_streaming(struct imx290 *imx290, } /* Apply the register values related to current frame format */ - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); ret = imx290_setup_format(imx290, format); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format - %d\n", ret); @@ -1132,7 +1132,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx290->current_mode = mode; @@ -1155,7 +1155,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: { - format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + format = v4l2_subdev_state_get_format(sd_state, 0); /* * The sensor moves the readout by 1 pixel based on flips to @@ -1195,8 +1195,8 @@ static int imx290_get_selection(struct v4l2_subdev *sd, } } -static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int imx290_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -1221,7 +1221,6 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { }; static const struct v4l2_subdev_pad_ops imx290_pad_ops = { - .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, .enum_frame_size = imx290_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -1235,6 +1234,10 @@ static const struct v4l2_subdev_ops imx290_subdev_ops = { .pad = &imx290_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx290_internal_ops = { + .init_state = imx290_entity_init_state, +}; + static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1248,6 +1251,7 @@ static int imx290_subdev_init(struct imx290 *imx290) imx290->current_mode = &imx290_modes_ptr(imx290)[0]; v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); + imx290->sd.internal_ops = &imx290_internal_ops; imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx290->sd.dev = imx290->dev; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 94aac9d27..83149fa72 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -323,7 +323,7 @@ static int imx296_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -511,8 +511,8 @@ static int imx296_setup(struct imx296 *sensor, struct v4l2_subdev_state *state) unsigned int i; int ret = 0; - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); - crop = v4l2_subdev_get_pad_crop(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); for (i = 0; i < ARRAY_SIZE(imx296_init_table); ++i) imx296_write(sensor, imx296_init_table[i].reg, @@ -662,7 +662,7 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); if (fse->index >= 2 || fse->code != format->code) return -EINVAL; @@ -683,8 +683,8 @@ static int imx296_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad); - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); /* * Binning is only allowed when cropping is disabled according to the @@ -732,7 +732,7 @@ static int imx296_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; case V4L2_SEL_TGT_CROP_DEFAULT: @@ -780,14 +780,14 @@ static int imx296_set_selection(struct v4l2_subdev *sd, rect.height = min_t(unsigned int, rect.height, IMX296_PIXEL_ARRAY_HEIGHT - rect.top); - crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad); + 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_get_pad_format(sd, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); format->width = rect.width; format->height = rect.height; } @@ -798,8 +798,8 @@ static int imx296_set_selection(struct v4l2_subdev *sd, return 0; } -static int imx296_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx296_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_selection sel = { .target = V4L2_SEL_TGT_CROP, @@ -830,7 +830,6 @@ static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { .set_fmt = imx296_set_format, .get_selection = imx296_get_selection, .set_selection = imx296_set_selection, - .init_cfg = imx296_init_cfg, }; static const struct v4l2_subdev_ops imx296_subdev_ops = { @@ -838,12 +837,17 @@ static const struct v4l2_subdev_ops imx296_subdev_ops = { .pad = &imx296_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx296_internal_ops = { + .init_state = imx296_init_state, +}; + static int imx296_subdev_init(struct imx296 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx296_subdev_ops); + sensor->subdev.internal_ops = &imx296_internal_ops; ret = imx296_ctrls_init(sensor); if (ret < 0) diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index 5378f607f..e47eff672 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -1860,7 +1860,7 @@ static int imx319_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx319 *imx319 = to_imx319(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx319->mutex); @@ -2001,10 +2001,9 @@ static int imx319_do_get_pad_format(struct imx319 *imx319, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx319->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx319_update_pad_format(imx319, imx319->cur_mode, fmt); @@ -2055,7 +2054,7 @@ imx319_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx319_update_pad_format(imx319, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx319->cur_mode = mode; @@ -2464,19 +2463,21 @@ static int imx319_probe(struct i2c_client *client) goto error_handler_free; } - ret = v4l2_async_register_subdev_sensor(&imx319->sd); - if (ret < 0) - goto error_media_entity; - /* Set the device's state to active if it's in D0 state. */ if (full_power) pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); + ret = v4l2_async_register_subdev_sensor(&imx319->sd); + if (ret < 0) + goto error_media_entity_pm; + return 0; -error_media_entity: +error_media_entity_pm: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); media_entity_cleanup(&imx319->sd.entity); error_handler_free: diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 1196fe935..6725b3e2a 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -879,7 +879,7 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { fmt->format.code = imx334->cur_code; @@ -920,7 +920,7 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else if (imx334->cur_mode != mode || imx334->cur_code != fmt->format.code) { imx334->cur_code = fmt->format.code; @@ -935,14 +935,14 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, } /** - * imx334_init_pad_cfg() - Initialize sub-device pad configuration + * imx334_init_state() - Initialize sub-device state * @sd: pointer to imx334 V4L2 sub-device structure * @sd_state: V4L2 sub-device state * * Return: 0 if successful, error code otherwise. */ -static int imx334_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx334_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx334 *imx334 = to_imx334(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1190,7 +1190,6 @@ static const struct v4l2_subdev_video_ops imx334_video_ops = { }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { - .init_cfg = imx334_init_pad_cfg, .enum_mbus_code = imx334_enum_mbus_code, .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, @@ -1202,6 +1201,10 @@ static const struct v4l2_subdev_ops imx334_subdev_ops = { .pad = &imx334_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx334_internal_ops = { + .init_state = imx334_init_state, +}; + /** * imx334_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1359,6 +1362,7 @@ static int imx334_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); + imx334->sd.internal_ops = &imx334_internal_ops; ret = imx334_parse_hw_config(imx334); if (ret) { diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 964a81bec..7a37eb327 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -55,6 +55,14 @@ #define IMX335_REG_MIN 0x00 #define IMX335_REG_MAX 0xfffff +/* IMX335 native and active pixel array size. */ +#define IMX335_NATIVE_WIDTH 2616U +#define IMX335_NATIVE_HEIGHT 1964U +#define IMX335_PIXEL_ARRAY_LEFT 12U +#define IMX335_PIXEL_ARRAY_TOP 12U +#define IMX335_PIXEL_ARRAY_WIDTH 2592U +#define IMX335_PIXEL_ARRAY_HEIGHT 1944U + /** * struct imx335_reg - imx335 sensor register * @address: Register address @@ -75,6 +83,12 @@ struct imx335_reg_list { const struct imx335_reg *regs; }; +static const char * const imx335_supply_name[] = { + "avdd", /* Analog (2.9V) supply */ + "ovdd", /* Digital I/O (1.8V) supply */ + "dvdd", /* Digital Core (1.2V) supply */ +}; + /** * struct imx335_mode - imx335 sensor mode structure * @width: Frame width @@ -108,6 +122,7 @@ struct imx335_mode { * @sd: V4L2 sub-device * @pad: Media pad. Only one pad supported * @reset_gpio: Sensor reset gpio + * @supplies: Regulator supplies to handle power control * @inclk: Sensor input clock * @ctrl_handler: V4L2 control handler * @link_freq_ctrl: Pointer to link frequency control @@ -119,6 +134,7 @@ struct imx335_mode { * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode * @mutex: Mutex for serializing sensor controls + * @cur_mbus_code: Currently selected media bus format code */ struct imx335 { struct device *dev; @@ -126,6 +142,8 @@ struct imx335 { struct v4l2_subdev sd; struct media_pad pad; struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)]; + struct clk *inclk; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *link_freq_ctrl; @@ -139,6 +157,7 @@ struct imx335 { u32 vblank; const struct imx335_mode *cur_mode; struct mutex mutex; + u32 cur_mbus_code; }; static const s64 link_freq[] = { @@ -233,6 +252,25 @@ static const struct imx335_reg mode_2592x1940_regs[] = { {0x3a00, 0x01}, }; +static const struct imx335_reg raw10_framefmt_regs[] = { + {0x3050, 0x00}, + {0x319d, 0x00}, + {0x341c, 0xff}, + {0x341d, 0x01}, +}; + +static const struct imx335_reg raw12_framefmt_regs[] = { + {0x3050, 0x01}, + {0x319d, 0x01}, + {0x341c, 0x47}, + {0x341d, 0x00}, +}; + +static const u32 imx335_mbus_codes[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + /* Supported sensor mode configurations */ static const struct imx335_mode supported_mode = { .width = 2592, @@ -243,7 +281,6 @@ static const struct imx335_mode supported_mode = { .vblank_max = 133060, .pclk = 396000000, .link_freq_idx = 0, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs), .regs = mode_2592x1940_regs, @@ -396,7 +433,7 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) lpfr = imx335->vblank + imx335->cur_mode->height; shutter = lpfr - exposure; - dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u", + dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n", exposure, gain, shutter, lpfr); ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1); @@ -443,7 +480,7 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: imx335->vblank = imx335->vblank_ctrl->val; - dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u", + dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u\n", imx335->vblank, imx335->vblank + imx335->cur_mode->height); @@ -462,7 +499,7 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) exposure = ctrl->val; analog_gain = imx335->again_ctrl->val; - dev_dbg(imx335->dev, "Received exp %u, analog gain %u", + dev_dbg(imx335->dev, "Received exp %u, analog gain %u\n", exposure, analog_gain); ret = imx335_update_exp_gain(imx335, exposure, analog_gain); @@ -471,7 +508,7 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) break; default: - dev_err(imx335->dev, "Invalid control %d", ctrl->id); + dev_err(imx335->dev, "Invalid control %d\n", ctrl->id); ret = -EINVAL; } @@ -483,6 +520,18 @@ static const struct v4l2_ctrl_ops imx335_ctrl_ops = { .s_ctrl = imx335_set_ctrl, }; +static int imx335_get_format_code(struct imx335 *imx335, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { + if (imx335_mbus_codes[i] == code) + return imx335_mbus_codes[i]; + } + + return imx335_mbus_codes[0]; +} + /** * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes * @sd: pointer to imx335 V4L2 sub-device structure @@ -495,10 +544,10 @@ static int imx335_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(imx335_mbus_codes)) return -EINVAL; - code->code = supported_mode.code; + code->code = imx335_mbus_codes[code->index]; return 0; } @@ -515,10 +564,14 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) { - if (fsize->index > 0) + struct imx335 *imx335 = to_imx335(sd); + u32 code; + + if (fsize->index > ARRAY_SIZE(imx335_mbus_codes)) return -EINVAL; - if (fsize->code != supported_mode.code) + code = imx335_get_format_code(imx335, fsize->code); + if (fsize->code != code) return -EINVAL; fsize->min_width = supported_mode.width; @@ -542,7 +595,7 @@ static void imx335_fill_pad_format(struct imx335 *imx335, { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = mode->code; + fmt->format.code = imx335->cur_mbus_code; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; @@ -569,7 +622,7 @@ static int imx335_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); @@ -594,17 +647,22 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, { struct imx335 *imx335 = to_imx335(sd); const struct imx335_mode *mode; - int ret = 0; + int i, ret = 0; mutex_lock(&imx335->mutex); mode = &supported_mode; + for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { + if (imx335_mbus_codes[i] == fmt->format.code) + imx335->cur_mbus_code = imx335_mbus_codes[i]; + } + imx335_fill_pad_format(imx335, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx335_update_controls(imx335, mode); @@ -618,14 +676,14 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, } /** - * imx335_init_pad_cfg() - Initialize sub-device pad configuration + * imx335_init_state() - Initialize sub-device state * @sd: pointer to imx335 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx335_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx335_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx335 *imx335 = to_imx335(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -636,6 +694,56 @@ static int imx335_init_pad_cfg(struct v4l2_subdev *sd, return imx335_set_pad_format(sd, sd_state, &fmt); } +/** + * imx335_get_selection() - Selection API + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @sel: V4L2 selection info + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX335_NATIVE_WIDTH; + sel->r.height = IMX335_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX335_PIXEL_ARRAY_TOP; + sel->r.left = IMX335_PIXEL_ARRAY_LEFT; + sel->r.width = IMX335_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX335_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int imx335_set_framefmt(struct imx335 *imx335) +{ + switch (imx335->cur_mbus_code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + return imx335_write_regs(imx335, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs)); + + case MEDIA_BUS_FMT_SRGGB12_1X12: + return imx335_write_regs(imx335, raw12_framefmt_regs, + ARRAY_SIZE(raw12_framefmt_regs)); + } + + return -EINVAL; +} + /** * imx335_start_streaming() - Start sensor stream * @imx335: pointer to imx335 device @@ -652,14 +760,21 @@ static int imx335_start_streaming(struct imx335 *imx335) ret = imx335_write_regs(imx335, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(imx335->dev, "fail to write initial registers"); + dev_err(imx335->dev, "fail to write initial registers\n"); + return ret; + } + + ret = imx335_set_framefmt(imx335); + if (ret) { + dev_err(imx335->dev, "%s failed to set frame format: %d\n", + __func__, ret); return ret; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); if (ret) { - dev_err(imx335->dev, "fail to setup handler"); + dev_err(imx335->dev, "fail to setup handler\n"); return ret; } @@ -667,7 +782,7 @@ static int imx335_start_streaming(struct imx335 *imx335) ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, 1, IMX335_MODE_STREAMING); if (ret) { - dev_err(imx335->dev, "fail to start streaming"); + dev_err(imx335->dev, "fail to start streaming\n"); return ret; } @@ -744,7 +859,7 @@ static int imx335_detect(struct imx335 *imx335) return ret; if (val != IMX335_ID) { - dev_err(imx335->dev, "chip id mismatch: %x!=%x", + dev_err(imx335->dev, "chip id mismatch: %x!=%x\n", IMX335_ID, val); return -ENXIO; } @@ -776,27 +891,40 @@ static int imx335_parse_hw_config(struct imx335 *imx335) imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(imx335->reset_gpio)) { - dev_err(imx335->dev, "failed to get reset gpio %ld", + dev_err(imx335->dev, "failed to get reset gpio %ld\n", PTR_ERR(imx335->reset_gpio)); return PTR_ERR(imx335->reset_gpio); } + for (i = 0; i < ARRAY_SIZE(imx335_supply_name); i++) + imx335->supplies[i].supply = imx335_supply_name[i]; + + ret = devm_regulator_bulk_get(imx335->dev, + ARRAY_SIZE(imx335_supply_name), + imx335->supplies); + if (ret) { + dev_err(imx335->dev, "Failed to get regulators\n"); + return ret; + } + /* Get sensor input clock */ imx335->inclk = devm_clk_get(imx335->dev, NULL); if (IS_ERR(imx335->inclk)) { - dev_err(imx335->dev, "could not get inclk"); + dev_err(imx335->dev, "could not get inclk\n"); return PTR_ERR(imx335->inclk); } rate = clk_get_rate(imx335->inclk); if (rate != IMX335_INCLK_RATE) { - dev_err(imx335->dev, "inclk frequency mismatch"); + dev_err(imx335->dev, "inclk frequency mismatch\n"); return -EINVAL; } ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) + if (!ep) { + dev_err(imx335->dev, "Failed to get next endpoint\n"); return -ENXIO; + } ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); @@ -805,14 +933,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) { dev_err(imx335->dev, - "number of CSI2 data lanes %d is not supported", + "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto done_endpoint_free; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(imx335->dev, "no link frequencies defined"); + dev_err(imx335->dev, "no link frequencies defined\n"); ret = -EINVAL; goto done_endpoint_free; } @@ -821,6 +949,8 @@ static int imx335_parse_hw_config(struct imx335 *imx335) if (bus_cfg.link_frequencies[i] == IMX335_LINK_FREQ) goto done_endpoint_free; + dev_err(imx335->dev, "no compatible link frequencies found\n"); + ret = -EINVAL; done_endpoint_free: @@ -835,9 +965,10 @@ static const struct v4l2_subdev_video_ops imx335_video_ops = { }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { - .init_cfg = imx335_init_pad_cfg, .enum_mbus_code = imx335_enum_mbus_code, .enum_frame_size = imx335_enum_frame_size, + .get_selection = imx335_get_selection, + .set_selection = imx335_get_selection, .get_fmt = imx335_get_pad_format, .set_fmt = imx335_set_pad_format, }; @@ -847,6 +978,10 @@ static const struct v4l2_subdev_ops imx335_subdev_ops = { .pad = &imx335_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx335_internal_ops = { + .init_state = imx335_init_state, +}; + /** * imx335_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -859,20 +994,32 @@ static int imx335_power_on(struct device *dev) struct imx335 *imx335 = to_imx335(sd); int ret; + ret = regulator_bulk_enable(ARRAY_SIZE(imx335_supply_name), + imx335->supplies); + if (ret) { + dev_err(dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + usleep_range(500, 550); /* Tlow */ + + /* Set XCLR */ gpiod_set_value_cansleep(imx335->reset_gpio, 1); ret = clk_prepare_enable(imx335->inclk); if (ret) { - dev_err(imx335->dev, "fail to enable inclk"); + dev_err(imx335->dev, "fail to enable inclk\n"); goto error_reset; } - usleep_range(20, 22); + usleep_range(20, 22); /* T4 */ return 0; error_reset: gpiod_set_value_cansleep(imx335->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(imx335_supply_name), imx335->supplies); return ret; } @@ -889,8 +1036,8 @@ static int imx335_power_off(struct device *dev) struct imx335 *imx335 = to_imx335(sd); gpiod_set_value_cansleep(imx335->reset_gpio, 0); - clk_disable_unprepare(imx335->inclk); + regulator_bulk_disable(ARRAY_SIZE(imx335_supply_name), imx335->supplies); return 0; } @@ -969,7 +1116,7 @@ static int imx335_init_controls(struct imx335 *imx335) imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl_hdlr->error) { - dev_err(imx335->dev, "control init failed: %d", + dev_err(imx335->dev, "control init failed: %d\n", ctrl_hdlr->error); v4l2_ctrl_handler_free(ctrl_hdlr); return ctrl_hdlr->error; @@ -999,10 +1146,11 @@ static int imx335_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); + imx335->sd.internal_ops = &imx335_internal_ops; ret = imx335_parse_hw_config(imx335); if (ret) { - dev_err(imx335->dev, "HW configuration is not supported"); + dev_err(imx335->dev, "HW configuration is not supported\n"); return ret; } @@ -1010,24 +1158,25 @@ static int imx335_probe(struct i2c_client *client) ret = imx335_power_on(imx335->dev); if (ret) { - dev_err(imx335->dev, "failed to power-on the sensor"); + dev_err(imx335->dev, "failed to power-on the sensor\n"); goto error_mutex_destroy; } /* Check module identity */ ret = imx335_detect(imx335); if (ret) { - dev_err(imx335->dev, "failed to find sensor: %d", ret); + dev_err(imx335->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } /* Set default mode to max resolution */ imx335->cur_mode = &supported_mode; + imx335->cur_mbus_code = imx335_mbus_codes[0]; imx335->vblank = imx335->cur_mode->vblank; ret = imx335_init_controls(imx335); if (ret) { - dev_err(imx335->dev, "failed to init controls: %d", ret); + dev_err(imx335->dev, "failed to init controls: %d\n", ret); goto error_power_off; } @@ -1039,14 +1188,14 @@ static int imx335_probe(struct i2c_client *client) imx335->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx335->sd.entity, 1, &imx335->pad); if (ret) { - dev_err(imx335->dev, "failed to init entity pads: %d", ret); + dev_err(imx335->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } ret = v4l2_async_register_subdev_sensor(&imx335->sd); if (ret < 0) { dev_err(imx335->dev, - "failed to register async subdev: %d", ret); + "failed to register async subdev: %d\n", ret); goto error_media_entity; } diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 81bb61407..8c995c587 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1158,7 +1158,7 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&imx355->mutex); @@ -1299,10 +1299,9 @@ static int imx355_do_get_pad_format(struct imx355 *imx355, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &imx355->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx355_update_pad_format(imx355, imx355->cur_mode, fmt); @@ -1353,7 +1352,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { imx355->cur_mode = mode; diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 962b3136c..0efce3295 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -721,7 +721,7 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { imx412_fill_pad_format(imx412, imx412->cur_mode, fmt); @@ -756,7 +756,7 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = imx412_update_controls(imx412, mode); @@ -770,14 +770,14 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, } /** - * imx412_init_pad_cfg() - Initialize sub-device pad configuration + * imx412_init_state() - Initialize sub-device state * @sd: pointer to imx412 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int imx412_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx412_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct imx412 *imx412 = to_imx412(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -997,7 +997,6 @@ static const struct v4l2_subdev_video_ops imx412_video_ops = { }; static const struct v4l2_subdev_pad_ops imx412_pad_ops = { - .init_cfg = imx412_init_pad_cfg, .enum_mbus_code = imx412_enum_mbus_code, .enum_frame_size = imx412_enum_frame_size, .get_fmt = imx412_get_pad_format, @@ -1009,6 +1008,10 @@ static const struct v4l2_subdev_ops imx412_subdev_ops = { .pad = &imx412_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx412_internal_ops = { + .init_state = imx412_init_state, +}; + /** * imx412_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1177,6 +1180,7 @@ static int imx412_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); + imx412->sd.internal_ops = &imx412_internal_ops; ret = imx412_parse_hw_config(imx412); if (ret) { diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index b3fa71a16..1e5f20c3e 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -546,7 +546,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->subdev); - format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -828,7 +828,7 @@ static int imx415_enum_frame_size(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); if (fse->index > 0 || fse->code != format->code) return -EINVAL; @@ -846,7 +846,7 @@ static int imx415_set_format(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); format->width = fmt->format.width; format->height = fmt->format.height; @@ -880,8 +880,8 @@ static int imx415_get_selection(struct v4l2_subdev *sd, return -EINVAL; } -static int imx415_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int imx415_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .format = { @@ -905,7 +905,6 @@ static const struct v4l2_subdev_pad_ops imx415_subdev_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx415_set_format, .get_selection = imx415_get_selection, - .init_cfg = imx415_init_cfg, }; static const struct v4l2_subdev_ops imx415_subdev_ops = { @@ -913,12 +912,17 @@ static const struct v4l2_subdev_ops imx415_subdev_ops = { .pad = &imx415_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx415_internal_ops = { + .init_state = imx415_init_state, +}; + static int imx415_subdev_init(struct imx415 *sensor) { struct i2c_client *client = to_i2c_client(sensor->dev); int ret; v4l2_i2c_subdev_init(&sensor->subdev, client, &imx415_subdev_ops); + sensor->subdev.internal_ops = &imx415_internal_ops; ret = imx415_ctrls_init(sensor); if (ret) diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 73460688c..89e13ebbc 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1007,8 +1007,8 @@ static int isl7998x_get_fmt(struct v4l2_subdev *sd, mutex_lock(&isl7998x->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + format->format = *v4l2_subdev_state_get_format(sd_state, + format->pad); goto out; } @@ -1044,7 +1044,7 @@ static int isl7998x_set_fmt(struct v4l2_subdev *sd, mf->field = mode->field; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = format->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = format->format; mutex_unlock(&isl7998x->lock); diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index fc1cf196e..d685d445c 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -868,11 +868,19 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int max9286_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) +static int max9286_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) { struct max9286_priv *priv = sd_to_max9286(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; @@ -881,11 +889,19 @@ static int max9286_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int max9286_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) +static int max9286_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) { struct max9286_priv *priv = sd_to_max9286(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; @@ -913,7 +929,7 @@ max9286_get_pad_format(struct max9286_priv *priv, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &priv->fmt[pad]; default: @@ -983,14 +999,14 @@ static int max9286_get_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops max9286_video_ops = { .s_stream = max9286_s_stream, - .g_frame_interval = max9286_g_frame_interval, - .s_frame_interval = max9286_s_frame_interval, }; static const struct v4l2_subdev_pad_ops max9286_pad_ops = { .enum_mbus_code = max9286_enum_mbus_code, .get_fmt = max9286_get_fmt, .set_fmt = max9286_set_fmt, + .get_frame_interval = max9286_get_frame_interval, + .set_frame_interval = max9286_set_frame_interval, }; static const struct v4l2_subdev_ops max9286_subdev_ops = { @@ -1020,7 +1036,7 @@ static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) unsigned int i; for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_get_try_format(subdev, fh->state, i); + format = v4l2_subdev_state_get_format(fh->state, i); max9286_init_format(format); } diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 79192cf79..ad1a3ab77 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -325,7 +325,7 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; } @@ -405,7 +405,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -650,13 +650,13 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #endif }; -static int mt9m001_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m001_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); try_fmt->width = MT9M001_MAX_WIDTH; try_fmt->height = MT9M001_MAX_HEIGHT; @@ -708,7 +708,6 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { }; static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { - .init_cfg = mt9m001_init_cfg, .enum_mbus_code = mt9m001_enum_mbus_code, .get_selection = mt9m001_get_selection, .set_selection = mt9m001_set_selection, @@ -724,6 +723,10 @@ static const struct v4l2_subdev_ops mt9m001_subdev_ops = { .pad = &mt9m001_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m001_internal_ops = { + .init_state = mt9m001_init_state, +}; + static int mt9m001_probe(struct i2c_client *client) { struct mt9m001 *mt9m001; @@ -755,6 +758,7 @@ static int mt9m001_probe(struct i2c_client *client) return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + mt9m001->subdev.internal_ops = &mt9m001_internal_ops; mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 1f44b72e8..ceeeb94c3 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -525,7 +525,7 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + mf = v4l2_subdev_state_get_format(sd_state, format->pad); format->format = *mf; return 0; } @@ -671,7 +671,7 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -1045,18 +1045,27 @@ static const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { #endif }; -static int mt9m111_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int mt9m111_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + fi->interval = mt9m111->frame_interval; return 0; } -static int mt9m111_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int mt9m111_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); const struct mt9m111_mode_info *mode; @@ -1066,6 +1075,13 @@ static int mt9m111_s_frame_interval(struct v4l2_subdev *sd, if (mt9m111->is_streaming) return -EBUSY; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (fi->pad != 0) return -EINVAL; @@ -1111,11 +1127,11 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m111_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mt9m111_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); format->width = MT9M111_MAX_WIDTH; format->height = MT9M111_MAX_HEIGHT; @@ -1151,17 +1167,16 @@ static int mt9m111_get_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { .s_stream = mt9m111_s_stream, - .g_frame_interval = mt9m111_g_frame_interval, - .s_frame_interval = mt9m111_s_frame_interval, }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { - .init_cfg = mt9m111_init_cfg, .enum_mbus_code = mt9m111_enum_mbus_code, .get_selection = mt9m111_get_selection, .set_selection = mt9m111_set_selection, .get_fmt = mt9m111_get_fmt, .set_fmt = mt9m111_set_fmt, + .get_frame_interval = mt9m111_get_frame_interval, + .set_frame_interval = mt9m111_set_frame_interval, .get_mbus_config = mt9m111_get_mbus_config, }; @@ -1171,6 +1186,10 @@ static const struct v4l2_subdev_ops mt9m111_subdev_ops = { .pad = &mt9m111_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m111_internal_ops = { + .init_state = mt9m111_init_state, +}; + /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one @@ -1275,6 +1294,7 @@ static int mt9m111_probe(struct i2c_client *client) mt9m111->ctx = &context_b; v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); + mt9m111->subdev.internal_ops = &mt9m111_internal_ops; mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c index 1a535c098..5f0b0ad8f 100644 --- a/drivers/media/i2c/mt9m114.c +++ b/drivers/media/i2c/mt9m114.c @@ -796,13 +796,13 @@ static int mt9m114_configure(struct mt9m114 *sensor, u64 read_mode; int ret = 0; - pa_format = v4l2_subdev_get_pad_format(&sensor->pa.sd, pa_state, 0); - pa_crop = v4l2_subdev_get_pad_crop(&sensor->pa.sd, pa_state, 0); + pa_format = v4l2_subdev_state_get_format(pa_state, 0); + pa_crop = v4l2_subdev_state_get_crop(pa_state, 0); - ifp_format = v4l2_subdev_get_pad_format(&sensor->ifp.sd, ifp_state, 1); + ifp_format = v4l2_subdev_state_get_format(ifp_state, 1); ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code); - ifp_crop = v4l2_subdev_get_pad_crop(&sensor->ifp.sd, ifp_state, 0); - ifp_compose = v4l2_subdev_get_pad_compose(&sensor->ifp.sd, ifp_state, 0); + ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0); + ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0); ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, &read_mode, NULL); @@ -1045,7 +1045,7 @@ static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl) return 0; state = v4l2_subdev_get_locked_active_state(&sensor->pa.sd); - format = v4l2_subdev_get_pad_format(&sensor->pa.sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); switch (ctrl->id) { case V4L2_CID_HBLANK: @@ -1152,20 +1152,20 @@ static inline struct mt9m114 *pa_to_mt9m114(struct v4l2_subdev *sd) return container_of(sd, struct mt9m114, pa.sd); } -static int mt9m114_pa_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mt9m114_pa_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); crop->left = 0; crop->top = 0; crop->width = MT9M114_PIXEL_ARRAY_WIDTH; crop->height = MT9M114_PIXEL_ARRAY_HEIGHT; - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); format->width = MT9M114_PIXEL_ARRAY_WIDTH; format->height = MT9M114_PIXEL_ARRAY_HEIGHT; @@ -1220,8 +1220,8 @@ static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd, unsigned int hscale; unsigned int vscale; - crop = v4l2_subdev_get_pad_crop(sd, state, fmt->pad); - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + crop = v4l2_subdev_state_get_crop(state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); /* The sensor can bin horizontally and vertically. */ hscale = DIV_ROUND_CLOSEST(crop->width, fmt->format.width ? : 1); @@ -1243,7 +1243,7 @@ static int mt9m114_pa_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); return 0; case V4L2_SEL_TGT_CROP_DEFAULT: @@ -1271,8 +1271,8 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - crop = v4l2_subdev_get_pad_crop(sd, state, sel->pad); - format = v4l2_subdev_get_pad_format(sd, state, sel->pad); + crop = v4l2_subdev_state_get_crop(state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); /* * Clamp the crop rectangle. The vertical coordinates must be even, and @@ -1304,7 +1304,6 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mt9m114_pa_pad_ops = { - .init_cfg = mt9m114_pa_init_cfg, .enum_mbus_code = mt9m114_pa_enum_mbus_code, .enum_frame_size = mt9m114_pa_enum_framesizes, .get_fmt = v4l2_subdev_get_fmt, @@ -1317,6 +1316,10 @@ static const struct v4l2_subdev_ops mt9m114_pa_ops = { .pad = &mt9m114_pa_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9m114_pa_internal_ops = { + .init_state = mt9m114_pa_init_state, +}; + static int mt9m114_pa_init(struct mt9m114 *sensor) { struct v4l2_ctrl_handler *hdl = &sensor->pa.hdl; @@ -1329,6 +1332,7 @@ static int mt9m114_pa_init(struct mt9m114 *sensor) /* Initialize the subdev. */ v4l2_subdev_init(sd, &mt9m114_pa_ops); + sd->internal_ops = &mt9m114_pa_internal_ops; v4l2_i2c_subdev_set_name(sd, sensor->client, NULL, " pixel array"); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1402,7 +1406,7 @@ static int mt9m114_pa_init(struct mt9m114 *sensor) /* Update the range of the blanking controls based on the format. */ state = v4l2_subdev_lock_and_get_active_state(sd); - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); mt9m114_pa_ctrl_update_blanking(sensor, format); v4l2_subdev_unlock_state(state); @@ -1581,12 +1585,20 @@ static int mt9m114_ifp_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int mt9m114_ifp_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) +static int mt9m114_ifp_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) { struct v4l2_fract *ival = &interval->interval; struct mt9m114 *sensor = ifp_to_mt9m114(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(sensor->ifp.hdl.lock); ival->numerator = 1; @@ -1597,13 +1609,21 @@ static int mt9m114_ifp_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int mt9m114_ifp_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) +static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) { struct v4l2_fract *ival = &interval->interval; struct mt9m114 *sensor = ifp_to_mt9m114(sd); int ret = 0; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(sensor->ifp.hdl.lock); if (ival->numerator != 0 && ival->denominator != 0) @@ -1624,15 +1644,15 @@ static int mt9m114_ifp_s_frame_interval(struct v4l2_subdev *sd, return ret; } -static int mt9m114_ifp_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mt9m114_ifp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mt9m114 *sensor = ifp_to_mt9m114(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; struct v4l2_rect *compose; - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); format->width = MT9M114_PIXEL_ARRAY_WIDTH; format->height = MT9M114_PIXEL_ARRAY_HEIGHT; @@ -1643,21 +1663,21 @@ static int mt9m114_ifp_init_cfg(struct v4l2_subdev *sd, format->quantization = V4L2_QUANTIZATION_FULL_RANGE; format->xfer_func = V4L2_XFER_FUNC_NONE; - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); crop->left = 4; crop->top = 4; crop->width = format->width - 8; crop->height = format->height - 8; - compose = v4l2_subdev_get_pad_compose(sd, state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); compose->left = 0; compose->top = 0; compose->width = crop->width; compose->height = crop->height; - format = v4l2_subdev_get_pad_format(sd, state, 1); + format = v4l2_subdev_state_get_format(state, 1); format->width = compose->width; format->height = compose->height; @@ -1738,7 +1758,7 @@ static int mt9m114_ifp_enum_framesizes(struct v4l2_subdev *sd, } else { const struct v4l2_rect *crop; - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); fse->max_width = crop->width; fse->max_height = crop->height; @@ -1777,7 +1797,7 @@ static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, struct mt9m114 *sensor = ifp_to_mt9m114(sd); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == 0) { /* Only the size can be changed on the sink pad. */ @@ -1797,7 +1817,7 @@ static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, /* If the output format is RAW10, bypass the scaler. */ if (format->code == MEDIA_BUS_FMT_SGRBG10_1X10) - *format = *v4l2_subdev_get_pad_format(sd, state, 0); + *format = *v4l2_subdev_state_get_format(state, 0); } fmt->format = *format; @@ -1819,7 +1839,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, state, 0); + sel->r = *v4l2_subdev_state_get_crop(state, 0); break; case V4L2_SEL_TGT_CROP_DEFAULT: @@ -1828,7 +1848,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, * The crop default and bounds are equal to the sink * format size minus 4 pixels on each side for demosaicing. */ - format = v4l2_subdev_get_pad_format(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); sel->r.left = 4; sel->r.top = 4; @@ -1837,7 +1857,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_COMPOSE: - sel->r = *v4l2_subdev_get_pad_compose(sd, state, 0); + sel->r = *v4l2_subdev_state_get_compose(state, 0); break; case V4L2_SEL_TGT_COMPOSE_DEFAULT: @@ -1846,7 +1866,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, * The compose default and bounds sizes are equal to the sink * crop rectangle size. */ - crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -1877,9 +1897,9 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, if (sel->pad != 0) return -EINVAL; - format = v4l2_subdev_get_pad_format(sd, state, 0); - crop = v4l2_subdev_get_pad_crop(sd, state, 0); - compose = v4l2_subdev_get_pad_compose(sd, state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); if (sel->target == V4L2_SEL_TGT_CROP) { /* @@ -1921,7 +1941,7 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, } /* Propagate the compose rectangle to the source format. */ - format = v4l2_subdev_get_pad_format(sd, state, 1); + format = v4l2_subdev_state_get_format(state, 1); format->width = compose->width; format->height = compose->height; @@ -1963,12 +1983,9 @@ static int mt9m114_ifp_registered(struct v4l2_subdev *sd) static const struct v4l2_subdev_video_ops mt9m114_ifp_video_ops = { .s_stream = mt9m114_ifp_s_stream, - .g_frame_interval = mt9m114_ifp_g_frame_interval, - .s_frame_interval = mt9m114_ifp_s_frame_interval, }; static const struct v4l2_subdev_pad_ops mt9m114_ifp_pad_ops = { - .init_cfg = mt9m114_ifp_init_cfg, .enum_mbus_code = mt9m114_ifp_enum_mbus_code, .enum_frame_size = mt9m114_ifp_enum_framesizes, .enum_frame_interval = mt9m114_ifp_enum_frameintervals, @@ -1976,6 +1993,8 @@ static const struct v4l2_subdev_pad_ops mt9m114_ifp_pad_ops = { .set_fmt = mt9m114_ifp_set_fmt, .get_selection = mt9m114_ifp_get_selection, .set_selection = mt9m114_ifp_set_selection, + .get_frame_interval = mt9m114_ifp_get_frame_interval, + .set_frame_interval = mt9m114_ifp_set_frame_interval, }; static const struct v4l2_subdev_ops mt9m114_ifp_ops = { @@ -1984,6 +2003,7 @@ static const struct v4l2_subdev_ops mt9m114_ifp_ops = { }; static const struct v4l2_subdev_internal_ops mt9m114_ifp_internal_ops = { + .init_state = mt9m114_ifp_init_state, .registered = mt9m114_ifp_registered, .unregistered = mt9m114_ifp_unregistered, }; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 348f1e109..596200d02 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -549,8 +549,7 @@ __mt9p031_get_pad_format(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -565,8 +564,7 @@ __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9p031->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: @@ -698,8 +696,8 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev, return 0; } -static int mt9p031_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9p031_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *format; @@ -1043,7 +1041,6 @@ static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { - .init_cfg = mt9p031_init_cfg, .enum_mbus_code = mt9p031_enum_mbus_code, .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, @@ -1059,6 +1056,7 @@ static const struct v4l2_subdev_ops mt9p031_subdev_ops = { }; static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .init_state = mt9p031_init_state, .registered = mt9p031_registered, .open = mt9p031_open, .close = mt9p031_close, @@ -1191,7 +1189,7 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ret = mt9p031_init_cfg(&mt9p031->subdev, NULL); + ret = mt9p031_init_state(&mt9p031->subdev, NULL); if (ret) goto done; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 93f34b767..fb1588c57 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -982,7 +982,6 @@ static int mt9t112_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9t112_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; return 0; } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 37a634b92..8834ff878 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -356,15 +356,23 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd, set_res(sd); } else { - sd_state->pads->try_fmt = *fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; } return 0; } -static int mt9v011_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int mt9v011_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + calc_fps(sd, &ival->interval.numerator, &ival->interval.denominator); @@ -372,12 +380,20 @@ static int mt9v011_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int mt9v011_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int mt9v011_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct v4l2_fract *tpf = &ival->interval; u16 speed; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + speed = calc_speed(sd, tpf->numerator, tpf->denominator); mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); @@ -455,19 +471,15 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = { #endif }; -static const struct v4l2_subdev_video_ops mt9v011_video_ops = { - .g_frame_interval = mt9v011_g_frame_interval, - .s_frame_interval = mt9v011_s_frame_interval, -}; - static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = { .enum_mbus_code = mt9v011_enum_mbus_code, .set_fmt = mt9v011_set_fmt, + .get_frame_interval = mt9v011_get_frame_interval, + .set_frame_interval = mt9v011_set_frame_interval, }; static const struct v4l2_subdev_ops mt9v011_ops = { .core = &mt9v011_core_ops, - .video = &mt9v011_video_ops, .pad = &mt9v011_pad_ops, }; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 1c6f6cea1..3ca76eeae 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -356,8 +356,7 @@ __mt9v032_get_pad_format(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -372,8 +371,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9v032->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -931,13 +929,13 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); + crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index f859b49e1..b0b98ed3c 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -35,7 +35,7 @@ * The IFP can produce several output image formats from the sensor core * output. This driver currently supports only YUYV format permutations. * - * The driver allows manual frame rate control through s_frame_interval subdev + * The driver allows manual frame rate control through set_frame_interval subdev * operation or V4L2_CID_V/HBLANK controls, but it is known that the * auto-exposure algorithm might modify the programmed frame rate. While the * driver initially programs the sensor with auto-exposure and @@ -719,8 +719,9 @@ error_unlock: return ret; } -static int mt9v111_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int mt9v111_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); struct v4l2_fract *tpf = &ival->interval; @@ -729,6 +730,13 @@ static int mt9v111_s_frame_interval(struct v4l2_subdev *sd, tpf->denominator; unsigned int max_fps; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (!tpf->numerator) tpf->numerator = 1; @@ -771,12 +779,20 @@ static int mt9v111_s_frame_interval(struct v4l2_subdev *sd, return 0; } -static int mt9v111_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int mt9v111_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); struct v4l2_fract *tpf = &ival->interval; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&mt9v111->stream_mutex); tpf->numerator = 1; @@ -795,7 +811,7 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9v111->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v111->fmt; default: @@ -948,10 +964,10 @@ done: return 0; } -static int mt9v111_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int mt9v111_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { - sd_state->pads->try_fmt = mt9v111_def_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = mt9v111_def_fmt; return 0; } @@ -962,17 +978,16 @@ static const struct v4l2_subdev_core_ops mt9v111_core_ops = { static const struct v4l2_subdev_video_ops mt9v111_video_ops = { .s_stream = mt9v111_s_stream, - .s_frame_interval = mt9v111_s_frame_interval, - .g_frame_interval = mt9v111_g_frame_interval, }; static const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { - .init_cfg = mt9v111_init_cfg, .enum_mbus_code = mt9v111_enum_mbus_code, .enum_frame_size = mt9v111_enum_frame_size, .enum_frame_interval = mt9v111_enum_frame_interval, .get_fmt = mt9v111_get_format, .set_fmt = mt9v111_set_format, + .get_frame_interval = mt9v111_get_frame_interval, + .set_frame_interval = mt9v111_set_frame_interval, }; static const struct v4l2_subdev_ops mt9v111_ops = { @@ -981,6 +996,10 @@ static const struct v4l2_subdev_ops mt9v111_ops = { .pad = &mt9v111_pad_ops, }; +static const struct v4l2_subdev_internal_ops mt9v111_internal_ops = { + .init_state = mt9v111_init_state, +}; + static const struct media_entity_operations mt9v111_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1194,6 +1213,7 @@ static int mt9v111_probe(struct i2c_client *client) mt9v111->pending = true; v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); + mt9v111->sd.internal_ops = &mt9v111_internal_ops; mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 51378ba16..bac9597fa 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -769,8 +769,7 @@ static int og01a1b_set_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad) = fmt->format; + *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); @@ -803,9 +802,8 @@ static int og01a1b_get_format(struct v4l2_subdev *sd, mutex_lock(&og01a1b->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&og01a1b->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format); @@ -850,7 +848,7 @@ static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&og01a1b->mutex); og01a1b_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&og01a1b->mutex); return 0; diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index 02de09edc..5606437f3 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -723,14 +723,14 @@ static int ov01a10_set_format(struct v4l2_subdev *sd, h_blank); } - format = v4l2_subdev_get_pad_format(sd, sd_state, fmt->stream); + format = v4l2_subdev_state_get_format(sd_state, fmt->stream); *format = fmt->format; return 0; } -static int ov01a10_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov01a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -813,7 +813,6 @@ static const struct v4l2_subdev_video_ops ov01a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = { - .init_cfg = ov01a10_init_cfg, .set_fmt = ov01a10_set_format, .get_fmt = v4l2_subdev_get_fmt, .get_selection = ov01a10_get_selection, @@ -827,6 +826,10 @@ static const struct v4l2_subdev_ops ov01a10_subdev_ops = { .pad = &ov01a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov01a10_internal_ops = { + .init_state = ov01a10_init_state, +}; + static const struct media_entity_operations ov01a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -873,6 +876,7 @@ static int ov01a10_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); + ov01a10->sd.internal_ops = &ov01a10_internal_ops; ret = ov01a10_identify_module(ov01a10); if (ret) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 848e47a46..6c30e1a0d 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -315,7 +315,7 @@ static int ov02a10_set_fmt(struct v4l2_subdev *sd, ov02a10_fill_fmt(ov02a10->cur_mode, mbus_fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - frame_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + frame_fmt = v4l2_subdev_state_get_format(sd_state, 0); else frame_fmt = &ov02a10->fmt; @@ -336,8 +336,8 @@ static int ov02a10_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov02a10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format = ov02a10->fmt; mbus_fmt->code = ov02a10->fmt.code; @@ -511,8 +511,8 @@ static int __ov02a10_stop_stream(struct ov02a10 *ov02a10) SC_CTRL_MODE_STANDBY); } -static int ov02a10_entity_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov02a10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_TRY, @@ -709,7 +709,6 @@ static const struct v4l2_subdev_video_ops ov02a10_video_ops = { }; static const struct v4l2_subdev_pad_ops ov02a10_pad_ops = { - .init_cfg = ov02a10_entity_init_cfg, .enum_mbus_code = ov02a10_enum_mbus_code, .enum_frame_size = ov02a10_enum_frame_sizes, .get_fmt = ov02a10_get_fmt, @@ -721,6 +720,10 @@ static const struct v4l2_subdev_ops ov02a10_subdev_ops = { .pad = &ov02a10_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov02a10_internal_ops = { + .init_state = ov02a10_init_state, +}; + static const struct media_entity_operations ov02a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -869,6 +872,7 @@ static int ov02a10_probe(struct i2c_client *client) "failed to check HW configuration\n"); v4l2_i2c_subdev_init(&ov02a10->subdev, client, &ov02a10_subdev_ops); + ov02a10->subdev.internal_ops = &ov02a10_internal_ops; ov02a10->mipi_clock_voltage = OV02A10_MIPI_TX_SPEED_DEFAULT; ov02a10->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c index 3d49e3fa8..1bacbdfa4 100644 --- a/drivers/media/i2c/ov08d10.c +++ b/drivers/media/i2c/ov08d10.c @@ -1145,7 +1145,7 @@ static int ov08d10_set_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov08d10->cur_mode = mode; @@ -1184,9 +1184,8 @@ static int ov08d10_get_format(struct v4l2_subdev *sd, mutex_lock(&ov08d10->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov08d10->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov08d10_update_pad_format(ov08d10, ov08d10->cur_mode, &fmt->format); @@ -1242,7 +1241,7 @@ static int ov08d10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov08d10->mutex); ov08d10_update_pad_format(ov08d10, &ov08d10->priv_lane->sp_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov08d10->mutex); return 0; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index b41b6866a..abbb0b774 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -2536,7 +2536,7 @@ static int ov08x40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) const struct ov08x40_mode *default_mode = &supported_modes[0]; struct ov08x40 *ov08x = to_ov08x40(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov08x->mutex); @@ -2774,10 +2774,9 @@ static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov08x->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov08x40_update_pad_format(ov08x->cur_mode, fmt); @@ -2826,7 +2825,7 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov08x40_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov08x->cur_mode = mode; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 4c419014d..09387e335 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1147,9 +1147,8 @@ static int ov13858_write_reg_list(struct ov13858 *ov13858, static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov13858 *ov13858 = to_ov13858(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13858->mutex); @@ -1317,10 +1316,9 @@ static int ov13858_do_get_pad_format(struct ov13858 *ov13858, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13858->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13858_update_pad_format(ov13858->cur_mode, fmt); @@ -1369,7 +1367,7 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13858->cur_mode = mode; diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 2793e4675..73c844aa5 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -755,9 +755,8 @@ static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { const struct ov13b10_mode *default_mode = &supported_modes[0]; struct ov13b10 *ov13b = to_ov13b10(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, + 0); mutex_lock(&ov13b->mutex); @@ -1002,10 +1001,9 @@ static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13b->sd; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov13b10_update_pad_format(ov13b->cur_mode, fmt); @@ -1054,7 +1052,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov13b10_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ov13b->cur_mode = mode; diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 28a01c6ef..67c4bd291 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -920,7 +920,7 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mf; return 0; } @@ -988,7 +988,7 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, /* select format */ priv->cfmt_code = mf->code; } else { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; } out: mutex_unlock(&priv->lock); @@ -996,11 +996,11 @@ out: return ret; } -static int ov2640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); const struct ov2640_win_size *win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); @@ -1125,7 +1125,6 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { - .init_cfg = ov2640_init_cfg, .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, @@ -1142,6 +1141,10 @@ static const struct v4l2_subdev_ops ov2640_subdev_ops = { .video = &ov2640_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops ov2640_internal_ops = { + .init_state = ov2640_init_state, +}; + static int ov2640_probe_dt(struct i2c_client *client, struct ov2640_priv *priv) { @@ -1211,6 +1214,7 @@ static int ov2640_probe(struct i2c_client *client) priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + priv->subdev.internal_ops = &ov2640_internal_ops; priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; mutex_init(&priv->lock); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 2c3dbe164..1d0ef72a6 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1033,7 +1033,7 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mutex_lock(&ov2659->lock); fmt->format = *mf; mutex_unlock(&ov2659->lock); @@ -1109,7 +1109,7 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2659->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } else { s64 val; @@ -1304,7 +1304,7 @@ static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); dev_dbg(&client->dev, "%s:\n", __func__); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 72bab0ff8..39d321e2b 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -309,7 +309,7 @@ __ov2680_get_pad_format(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&sensor->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); return &sensor->mode.fmt; } @@ -321,7 +321,7 @@ __ov2680_get_pad_crop(struct ov2680_dev *sensor, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); return &sensor->mode.crop; } @@ -552,11 +552,19 @@ err_disable_regulators: return ret; } -static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, +static int ov2680_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct ov2680_dev *sensor = to_ov2680_dev(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&sensor->lock); fi->interval = sensor->mode.frame_interval; mutex_unlock(&sensor->lock); @@ -650,7 +658,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, ov2680_fill_format(sensor, &format->format, width, height); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + try_fmt = v4l2_subdev_state_get_format(sd_state, 0); *try_fmt = format->format; return 0; } @@ -755,14 +763,14 @@ static int ov2680_set_selection(struct v4l2_subdev *sd, return 0; } -static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2680_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - sd_state->pads[0].try_crop = ov2680_default_crop; + *v4l2_subdev_state_get_crop(sd_state, 0) = ov2680_default_crop; - ov2680_fill_format(sensor, &sd_state->pads[0].try_fmt, + ov2680_fill_format(sensor, v4l2_subdev_state_get_format(sd_state, 0), OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); return 0; } @@ -870,13 +878,10 @@ static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { }; static const struct v4l2_subdev_video_ops ov2680_video_ops = { - .g_frame_interval = ov2680_s_g_frame_interval, - .s_frame_interval = ov2680_s_g_frame_interval, .s_stream = ov2680_s_stream, }; static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { - .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, @@ -884,6 +889,8 @@ static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { .set_fmt = ov2680_set_fmt, .get_selection = ov2680_get_selection, .set_selection = ov2680_set_selection, + .get_frame_interval = ov2680_get_frame_interval, + .set_frame_interval = ov2680_get_frame_interval, }; static const struct v4l2_subdev_ops ov2680_subdev_ops = { @@ -891,6 +898,10 @@ static const struct v4l2_subdev_ops ov2680_subdev_ops = { .pad = &ov2680_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov2680_internal_ops = { + .init_state = ov2680_init_state, +}; + static int ov2680_mode_init(struct ov2680_dev *sensor) { /* set initial mode */ @@ -915,6 +926,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) int ret = 0; v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); + sensor->sd.internal_ops = &ov2680_internal_ops; sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 396583826..9b8481b8d 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -404,7 +404,7 @@ __ov2685_get_pad_crop(struct ov2685 *ov2685, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov2685->subdev, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } @@ -547,7 +547,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov2685->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov2685_fill_fmt(&supported_modes[0], try_fmt); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 6be22586c..552935ccb 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include #include #include @@ -14,6 +16,7 @@ #include #define OV2740_LINK_FREQ_360MHZ 360000000ULL +#define OV2740_LINK_FREQ_180MHZ 180000000ULL #define OV2740_SCLK 72000000LL #define OV2740_MCLK 19200000 #define OV2740_DATA_LANES 2 @@ -28,9 +31,6 @@ /* vertical-timings from sensor */ #define OV2740_REG_VTS 0x380e -#define OV2740_VTS_DEF 0x088a -#define OV2740_VTS_MIN 0x0460 -#define OV2740_VTS_MAX 0x7fff /* horizontal-timings from sensor */ #define OV2740_REG_HTS 0x380c @@ -84,6 +84,7 @@ struct nvm_data { enum { OV2740_LINK_FREQ_360MHZ_INDEX, + OV2740_LINK_FREQ_180MHZ_INDEX, }; struct ov2740_reg { @@ -116,6 +117,9 @@ struct ov2740_mode { /* Min vertical timining size */ u32 vts_min; + /* Max vertical timining size */ + u32 vts_max; + /* Link frequency needed for this resolution */ u32 link_freq_index; @@ -124,7 +128,6 @@ struct ov2740_mode { }; static const struct ov2740_reg mipi_data_rate_720mbps[] = { - {0x0103, 0x01}, {0x0302, 0x4b}, {0x030d, 0x4b}, {0x030e, 0x02}, @@ -132,7 +135,17 @@ static const struct ov2740_reg mipi_data_rate_720mbps[] = { {0x0312, 0x11}, }; -static const struct ov2740_reg mode_1932x1092_regs[] = { +static const struct ov2740_reg mipi_data_rate_360mbps[] = { + {0x0302, 0x4b}, + {0x0303, 0x01}, + {0x030d, 0x4b}, + {0x030e, 0x02}, + {0x030a, 0x01}, + {0x0312, 0x11}, + {0x4837, 0x2c}, +}; + +static const struct ov2740_reg mode_1932x1092_regs_360mhz[] = { {0x3000, 0x00}, {0x3018, 0x32}, {0x3031, 0x0a}, @@ -285,6 +298,159 @@ static const struct ov2740_reg mode_1932x1092_regs[] = { {0x3813, 0x01}, }; +static const struct ov2740_reg mode_1932x1092_regs_180mhz[] = { + {0x3000, 0x00}, + {0x3018, 0x32}, /* 0x32 for 2 lanes, 0x12 for 1 lane */ + {0x3031, 0x0a}, + {0x3080, 0x08}, + {0x3083, 0xB4}, + {0x3103, 0x00}, + {0x3104, 0x01}, + {0x3106, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x44}, + {0x3502, 0x40}, + {0x3503, 0x88}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x00}, + {0x3512, 0x20}, + {0x3632, 0x00}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3645, 0x13}, + {0x3646, 0x81}, + {0x3636, 0x10}, + {0x3651, 0x0a}, + {0x3656, 0x02}, + {0x3659, 0x04}, + {0x365a, 0xda}, + {0x365b, 0xa2}, + {0x365c, 0x04}, + {0x365d, 0x1d}, + {0x365e, 0x1a}, + {0x3662, 0xd7}, + {0x3667, 0x78}, + {0x3669, 0x0a}, + {0x366a, 0x92}, + {0x3700, 0x54}, + {0x3702, 0x10}, + {0x3706, 0x42}, + {0x3709, 0x30}, + {0x370b, 0xc2}, + {0x3714, 0x63}, + {0x3715, 0x01}, + {0x3716, 0x00}, + {0x371a, 0x3e}, + {0x3732, 0x0e}, + {0x3733, 0x10}, + {0x375f, 0x0e}, + {0x3768, 0x30}, + {0x3769, 0x44}, + {0x376a, 0x22}, + {0x377b, 0x20}, + {0x377c, 0x00}, + {0x377d, 0x0c}, + {0x3798, 0x00}, + {0x37a1, 0x55}, + {0x37a8, 0x6d}, + {0x37c2, 0x04}, + {0x37c5, 0x00}, + {0x37c8, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x40}, + {0x380c, 0x08}, + {0x380d, 0x70}, + {0x380e, 0x04}, + {0x380f, 0x56}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x3822, 0x84}, + {0x3829, 0x00}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x04}, + {0x3836, 0x01}, + {0x3837, 0x08}, + {0x3839, 0x01}, + {0x383a, 0x00}, + {0x383b, 0x08}, + {0x383c, 0x00}, + {0x3f0b, 0x00}, + {0x4001, 0x20}, + {0x4009, 0x07}, + {0x4003, 0x10}, + {0x4010, 0xe0}, + {0x4016, 0x00}, + {0x4017, 0x10}, + {0x4044, 0x02}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4320, 0x80}, + {0x4322, 0x00}, + {0x4323, 0x00}, + {0x4324, 0x00}, + {0x4325, 0x00}, + {0x4326, 0x00}, + {0x4327, 0x00}, + {0x4328, 0x00}, + {0x4329, 0x00}, + {0x432c, 0x03}, + {0x432d, 0x81}, + {0x4501, 0x84}, + {0x4502, 0x40}, + {0x4503, 0x18}, + {0x4504, 0x04}, + {0x4508, 0x02}, + {0x4601, 0x10}, + {0x4800, 0x00}, + {0x4816, 0x52}, + {0x5000, 0x73}, /* 0x7f enable DPC */ + {0x5001, 0x00}, + {0x5005, 0x38}, + {0x501e, 0x0d}, + {0x5040, 0x00}, + {0x5901, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x8c}, + {0x380a, 0x04}, + {0x380b, 0x44}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x01}, + {0x4003, 0x40}, /* set Black level to 0x40 */ +}; + static const char * const ov2740_test_pattern_menu[] = { "Disabled", "Color Bar", @@ -295,6 +461,7 @@ static const char * const ov2740_test_pattern_menu[] = { static const s64 link_freq_menu_items[] = { OV2740_LINK_FREQ_360MHZ, + OV2740_LINK_FREQ_180MHZ, }; static const struct ov2740_link_freq_config link_freq_configs[] = { @@ -304,23 +471,46 @@ static const struct ov2740_link_freq_config link_freq_configs[] = { .regs = mipi_data_rate_720mbps, } }, + [OV2740_LINK_FREQ_180MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, + } + }, }; -static const struct ov2740_mode supported_modes[] = { +static const struct ov2740_mode supported_modes_360mhz[] = { { .width = 1932, .height = 1092, .hts = 2160, - .vts_def = OV2740_VTS_DEF, - .vts_min = OV2740_VTS_MIN, + .vts_min = 1120, + .vts_def = 2186, + .vts_max = 32767, .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs), - .regs = mode_1932x1092_regs, + .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_360mhz), + .regs = mode_1932x1092_regs_360mhz, }, .link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX, }, }; +static const struct ov2740_mode supported_modes_180mhz[] = { + { + .width = 1932, + .height = 1092, + .hts = 2160, + .vts_min = 1110, + .vts_def = 1110, + .vts_max = 2047, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_180mhz), + .regs = mode_1932x1092_regs_180mhz, + }, + .link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX, + }, +}; + struct ov2740 { struct v4l2_subdev sd; struct media_pad pad; @@ -333,12 +523,20 @@ struct ov2740 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + /* GPIOs, clocks */ + struct gpio_desc *reset_gpio; + struct clk *clk; + /* Current mode */ const struct ov2740_mode *cur_mode; /* NVM data inforamtion */ struct nvm_data *nvm; + /* Supported modes */ + const struct ov2740_mode *supported_modes; + int supported_modes_count; + /* True if the device has been identified */ bool identified; }; @@ -583,7 +781,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740) pixel_rate, 1, pixel_rate); vblank_min = cur_mode->vts_min - cur_mode->height; - vblank_max = OV2740_VTS_MAX - cur_mode->height; + vblank_max = cur_mode->vts_max - cur_mode->height; vblank_default = cur_mode->vts_def - cur_mode->height; ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_VBLANK, vblank_min, @@ -735,6 +933,15 @@ static int ov2740_start_streaming(struct ov2740 *ov2740) if (ov2740->nvm) ov2740_load_otp_data(ov2740->nvm); + /* Reset the sensor */ + ret = ov2740_write_reg(ov2740, 0x0103, 1, 0x01); + if (ret) { + dev_err(&client->dev, "failed to reset\n"); + return ret; + } + + usleep_range(10000, 15000); + link_freq_index = ov2740->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov2740_write_reg_list(ov2740, reg_list); @@ -810,13 +1017,13 @@ static int ov2740_set_format(struct v4l2_subdev *sd, const struct ov2740_mode *mode; s32 vblank_def, h_blank; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), width, - height, fmt->format.width, - fmt->format.height); + mode = v4l2_find_nearest_size(ov2740->supported_modes, + ov2740->supported_modes_count, + width, height, + fmt->format.width, fmt->format.height); ov2740_update_pad_format(mode, &fmt->format); - *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) return 0; @@ -830,7 +1037,7 @@ static int ov2740_set_format(struct v4l2_subdev *sd, vblank_def = mode->vts_def - mode->height; __v4l2_ctrl_modify_range(ov2740->vblank, mode->vts_min - mode->height, - OV2740_VTS_MAX - mode->height, 1, vblank_def); + mode->vts_max - mode->height, 1, vblank_def); __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); h_blank = mode->hts - mode->width; __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, h_blank); @@ -854,7 +1061,10 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct ov2740 *ov2740 = to_ov2740(sd); + const struct ov2740_mode *supported_modes = ov2740->supported_modes; + + if (fse->index >= ov2740->supported_modes_count) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -868,12 +1078,13 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int ov2740_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov2740_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { - ov2740_update_pad_format(&supported_modes[0], - v4l2_subdev_get_pad_format(sd, sd_state, 0)); + struct ov2740 *ov2740 = to_ov2740(sd); + ov2740_update_pad_format(&ov2740->supported_modes[0], + v4l2_subdev_state_get_format(sd_state, 0)); return 0; } @@ -886,7 +1097,6 @@ static const struct v4l2_subdev_pad_ops ov2740_pad_ops = { .set_fmt = ov2740_set_format, .enum_mbus_code = ov2740_enum_mbus_code, .enum_frame_size = ov2740_enum_frame_size, - .init_cfg = ov2740_init_cfg, }; static const struct v4l2_subdev_ops ov2740_subdev_ops = { @@ -894,12 +1104,18 @@ static const struct v4l2_subdev_ops ov2740_subdev_ops = { .pad = &ov2740_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { + .init_state = ov2740_init_state, +}; + static const struct media_entity_operations ov2740_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; static int ov2740_check_hwcfg(struct device *dev) { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2740 *ov2740 = to_ov2740(sd); struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { @@ -909,23 +1125,32 @@ static int ov2740_check_hwcfg(struct device *dev) int ret; unsigned int i, j; + /* + * Sometimes the fwnode graph is initialized by the bridge driver, + * wait for this. + */ + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EPROBE_DEFER; + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) - return ret; + if (ret) { + fwnode_handle_put(ep); + return dev_err_probe(dev, ret, + "reading clock-frequency property\n"); + } - if (mclk != OV2740_MCLK) + if (mclk != OV2740_MCLK) { + fwnode_handle_put(ep); return dev_err_probe(dev, -EINVAL, "external clock %d is not supported\n", mclk); - - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -EPROBE_DEFER; + } ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) - return ret; + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV2740_DATA_LANES) { ret = dev_err_probe(dev, -EINVAL, @@ -946,14 +1171,29 @@ static int ov2740_check_hwcfg(struct device *dev) break; } - if (j == bus_cfg.nr_of_link_frequencies) { - ret = dev_err_probe(dev, -EINVAL, - "no link frequency %lld supported\n", - link_freq_menu_items[i]); - goto check_hwcfg_error; + if (j == bus_cfg.nr_of_link_frequencies) + continue; + + switch (i) { + case OV2740_LINK_FREQ_360MHZ_INDEX: + ov2740->supported_modes = supported_modes_360mhz; + ov2740->supported_modes_count = + ARRAY_SIZE(supported_modes_360mhz); + break; + case OV2740_LINK_FREQ_180MHZ_INDEX: + ov2740->supported_modes = supported_modes_180mhz; + ov2740->supported_modes_count = + ARRAY_SIZE(supported_modes_180mhz); + break; } + + break; /* Prefer modes from first available link-freq */ } + if (!ov2740->supported_modes) + ret = dev_err_probe(dev, -EINVAL, + "no supported link frequencies\n"); + check_hwcfg_error: v4l2_fwnode_endpoint_free(&bus_cfg); @@ -1047,6 +1287,32 @@ static int ov2740_register_nvmem(struct i2c_client *client, return 0; } +static int ov2740_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2740 *ov2740 = to_ov2740(sd); + + gpiod_set_value_cansleep(ov2740->reset_gpio, 1); + clk_disable_unprepare(ov2740->clk); + return 0; +} + +static int ov2740_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov2740 *ov2740 = to_ov2740(sd); + int ret; + + ret = clk_prepare_enable(ov2740->clk); + if (ret) + return ret; + + gpiod_set_value_cansleep(ov2740->reset_gpio, 0); + msleep(20); + + return 0; +} + static int ov2740_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1054,23 +1320,42 @@ static int ov2740_probe(struct i2c_client *client) bool full_power; int ret; - ret = ov2740_check_hwcfg(&client->dev); - if (ret) - return dev_err_probe(dev, ret, "failed to check HW configuration\n"); - ov2740 = devm_kzalloc(&client->dev, sizeof(*ov2740), GFP_KERNEL); if (!ov2740) return -ENOMEM; v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); + ov2740->sd.internal_ops = &ov2740_internal_ops; + + ret = ov2740_check_hwcfg(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to check HW configuration\n"); + + ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov2740->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), + "failed to get reset GPIO\n"); + + ov2740->clk = devm_clk_get_optional(dev, "clk"); + if (IS_ERR(ov2740->clk)) + return dev_err_probe(dev, PTR_ERR(ov2740->clk), + "failed to get clock\n"); + full_power = acpi_dev_state_d0(&client->dev); if (full_power) { - ret = ov2740_identify_module(ov2740); + /* ACPI does not always clear the reset GPIO / enable the clock */ + ret = ov2740_resume(dev); if (ret) - return dev_err_probe(dev, ret, "failed to find sensor\n"); + return dev_err_probe(dev, ret, "failed to power on sensor\n"); + + ret = ov2740_identify_module(ov2740); + if (ret) { + dev_err_probe(dev, ret, "failed to find sensor\n"); + goto probe_error_power_off; + } } - ov2740->cur_mode = &supported_modes[0]; + ov2740->cur_mode = &ov2740->supported_modes[0]; ret = ov2740_init_controls(ov2740); if (ret) { dev_err_probe(dev, ret, "failed to init controls\n"); @@ -1121,9 +1406,16 @@ probe_error_media_entity_cleanup: probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); +probe_error_power_off: + if (full_power) + ov2740_suspend(dev); + return ret; } +static DEFINE_RUNTIME_DEV_PM_OPS(ov2740_pm_ops, ov2740_suspend, ov2740_resume, + NULL); + static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} @@ -1135,6 +1427,7 @@ static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", .acpi_match_table = ov2740_acpi_ids, + .pm = pm_sleep_ptr(&ov2740_pm_ops), }, .probe = ov2740_probe, .remove = ov2740_remove, diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c index 3bd972a82..403091651 100644 --- a/drivers/media/i2c/ov4689.c +++ b/drivers/media/i2c/ov4689.c @@ -570,7 +570,7 @@ static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov4689->mutex); - try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + try_fmt = v4l2_subdev_state_get_format(fh->state, 0); /* Initialize try_fmt */ ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 40532f7bc..5162d45fe 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -399,7 +399,7 @@ struct ov5640_mode_info { const struct reg_value *reg_data; u32 reg_data_size; - /* Used by s_frame_interval only. */ + /* Used by set_frame_interval only. */ u32 max_fps; u32 def_fps; }; @@ -2797,8 +2797,7 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -2971,7 +2970,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, 0) = *mbus_fmt; + *v4l2_subdev_state_get_format(sd_state, 0) = *mbus_fmt; goto out; } @@ -3605,11 +3604,19 @@ static int ov5640_enum_frame_interval( return 0; } -static int ov5640_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int ov5640_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct ov5640_dev *sensor = to_ov5640_dev(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&sensor->lock); fi->interval = sensor->frame_interval; mutex_unlock(&sensor->lock); @@ -3617,13 +3624,21 @@ static int ov5640_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int ov5640_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int ov5640_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct ov5640_dev *sensor = to_ov5640_dev(sd); const struct ov5640_mode_info *mode; int frame_rate, ret = 0; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (fi->pad != 0) return -EINVAL; @@ -3745,13 +3760,13 @@ out: return ret; } -static int ov5640_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5640_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct ov5640_dev *sensor = to_ov5640_dev(sd); struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); *fmt = ov5640_is_csi2(sensor) ? ov5640_csi2_default_fmt : ov5640_dvp_default_fmt; @@ -3771,17 +3786,16 @@ static const struct v4l2_subdev_core_ops ov5640_core_ops = { }; static const struct v4l2_subdev_video_ops ov5640_video_ops = { - .g_frame_interval = ov5640_g_frame_interval, - .s_frame_interval = ov5640_s_frame_interval, .s_stream = ov5640_s_stream, }; static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { - .init_cfg = ov5640_init_cfg, .enum_mbus_code = ov5640_enum_mbus_code, .get_fmt = ov5640_get_fmt, .set_fmt = ov5640_set_fmt, .get_selection = ov5640_get_selection, + .get_frame_interval = ov5640_get_frame_interval, + .set_frame_interval = ov5640_set_frame_interval, .enum_frame_size = ov5640_enum_frame_size, .enum_frame_interval = ov5640_enum_frame_interval, }; @@ -3792,6 +3806,10 @@ static const struct v4l2_subdev_ops ov5640_subdev_ops = { .pad = &ov5640_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5640_internal_ops = { + .init_state = ov5640_init_state, +}; + static int ov5640_get_regulators(struct ov5640_dev *sensor) { int i; @@ -3906,6 +3924,7 @@ static int ov5640_probe(struct i2c_client *client) return PTR_ERR(sensor->reset_gpio); v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); + sensor->sd.internal_ops = &ov5640_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index a70db7e60..a26ac11c9 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -851,7 +851,7 @@ __ov5645_get_pad_format(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->fmt; default: @@ -878,7 +878,7 @@ __ov5645_get_pad_crop(struct ov5645 *ov5645, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5645->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5645->crop; default: @@ -934,8 +934,8 @@ static int ov5645_set_format(struct v4l2_subdev *sd, return 0; } -static int ov5645_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov5645_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { 0 }; @@ -1023,7 +1023,6 @@ static const struct v4l2_subdev_video_ops ov5645_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5645_subdev_pad_ops = { - .init_cfg = ov5645_entity_init_cfg, .enum_mbus_code = ov5645_enum_mbus_code, .enum_frame_size = ov5645_enum_frame_size, .get_fmt = ov5645_get_format, @@ -1036,6 +1035,10 @@ static const struct v4l2_subdev_ops ov5645_subdev_ops = { .pad = &ov5645_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov5645_internal_ops = { + .init_state = ov5645_init_state, +}; + static int ov5645_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1162,6 +1165,7 @@ static int ov5645_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&ov5645->sd, client, &ov5645_subdev_ops); + ov5645->sd.internal_ops = &ov5645_internal_ops; ov5645->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov5645->pad.flags = MEDIA_PAD_FL_SOURCE; ov5645->sd.dev = &client->dev; @@ -1220,7 +1224,7 @@ static int ov5645_probe(struct i2c_client *client) pm_runtime_get_noresume(dev); pm_runtime_enable(dev); - ov5645_entity_init_cfg(&ov5645->sd, NULL); + ov5645_init_state(&ov5645->sd, NULL); ret = v4l2_async_register_subdev(&ov5645->sd); if (ret < 0) { diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index dcfe3129c..96c0fd4ff 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -882,7 +882,7 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5647->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5647->mode->crop; } @@ -975,8 +975,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); switch (format->which) { case V4L2_SUBDEV_FORMAT_TRY: - sensor_format = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + sensor_format = v4l2_subdev_state_get_format(sd_state, + format->pad); break; default: sensor_format = &sensor->mode->format; @@ -1004,7 +1004,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, /* Update the sensor mode and apply at it at streamon time. */ mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = mode->format; + *v4l2_subdev_state_get_format(sd_state, format->pad) = mode->format; } else { int exposure_max, exposure_def; int hblank, vblank; @@ -1121,8 +1121,8 @@ static int ov5647_detect(struct v4l2_subdev *sd) static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); crop->left = OV5647_PIXEL_ARRAY_LEFT; crop->top = OV5647_PIXEL_ARRAY_TOP; diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index aa10eb4e3..4b86d2631 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -2158,37 +2158,8 @@ static int ov5648_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } -static int ov5648_g_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *interval) -{ - struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); - const struct ov5648_mode *mode; - int ret = 0; - - mutex_lock(&sensor->mutex); - - mode = sensor->state.mode; - - switch (sensor->state.mbus_code) { - case MEDIA_BUS_FMT_SBGGR8_1X8: - interval->interval = mode->frame_interval[0]; - break; - case MEDIA_BUS_FMT_SBGGR10_1X10: - interval->interval = mode->frame_interval[1]; - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&sensor->mutex); - - return ret; -} - static const struct v4l2_subdev_video_ops ov5648_subdev_video_ops = { .s_stream = ov5648_s_stream, - .g_frame_interval = ov5648_g_frame_interval, - .s_frame_interval = ov5648_g_frame_interval, }; /* Subdev Pad Operations */ @@ -2232,8 +2203,8 @@ static int ov5648_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov5648_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2285,7 +2256,7 @@ static int ov5648_set_fmt(struct v4l2_subdev *subdev, ov5648_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) @@ -2297,6 +2268,41 @@ complete: return ret; } +static int ov5648_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + const struct ov5648_mode *mode; + int ret = 0; + + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + mutex_lock(&sensor->mutex); + + mode = sensor->state.mode; + + switch (sensor->state.mbus_code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + interval->interval = mode->frame_interval[0]; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + interval->interval = mode->frame_interval[1]; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&sensor->mutex); + + return ret; +} + static int ov5648_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *size_enum) @@ -2363,6 +2369,8 @@ static const struct v4l2_subdev_pad_ops ov5648_subdev_pad_ops = { .enum_mbus_code = ov5648_enum_mbus_code, .get_fmt = ov5648_get_fmt, .set_fmt = ov5648_set_fmt, + .get_frame_interval = ov5648_get_frame_interval, + .set_frame_interval = ov5648_get_frame_interval, .enum_frame_size = ov5648_enum_frame_size, .enum_frame_interval = ov5648_enum_frame_interval, }; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index e80db3ecd..2aee85965 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2196,13 +2196,13 @@ error: return ret; } -static int ov5670_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int ov5670_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, state, 0); + v4l2_subdev_state_get_format(state, 0); const struct ov5670_mode *default_mode = &supported_modes[0]; - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); fmt->width = default_mode->width; fmt->height = default_mode->height; @@ -2263,9 +2263,8 @@ static int ov5670_do_get_pad_format(struct ov5670 *ov5670, struct v4l2_subdev_format *fmt) { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5670->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5670_update_pad_format(ov5670->cur_mode, fmt); @@ -2310,7 +2309,7 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); ov5670_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5670->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); @@ -2550,7 +2549,7 @@ __ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return mode->analog_crop; } @@ -2593,7 +2592,6 @@ static const struct v4l2_subdev_video_ops ov5670_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5670_pad_ops = { - .init_cfg = ov5670_init_cfg, .enum_mbus_code = ov5670_enum_mbus_code, .get_fmt = ov5670_get_pad_format, .set_fmt = ov5670_set_pad_format, @@ -2613,6 +2611,10 @@ static const struct v4l2_subdev_ops ov5670_subdev_ops = { .sensor = &ov5670_sensor_ops, }; +static const struct v4l2_subdev_internal_ops ov5670_internal_ops = { + .init_state = ov5670_init_state, +}; + static const struct media_entity_operations ov5670_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -2676,6 +2678,7 @@ static int ov5670_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); + ov5670->sd.internal_ops = &ov5670_internal_ops; ret = ov5670_regulators_probe(ov5670); if (ret) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index e63d9d402..3641911bc 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1036,7 +1036,7 @@ static int ov5675_set_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); ov5675_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5675->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5675->link_freq, mode->link_freq_index); @@ -1069,9 +1069,8 @@ static int ov5675_get_format(struct v4l2_subdev *sd, mutex_lock(&ov5675->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov5675_update_pad_format(ov5675->cur_mode, &fmt->format); @@ -1141,7 +1140,7 @@ static int ov5675_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov5675->mutex); ov5675_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov5675->mutex); return 0; diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 819425e21..8deb28b55 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -775,7 +775,7 @@ __ov5693_get_pad_format(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.format; default: @@ -790,7 +790,7 @@ __ov5693_get_pad_crop(struct ov5693_device *ov5693, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov5693->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov5693->mode.crop; } @@ -1004,14 +1004,22 @@ err_power_down: return ret; } -static int ov5693_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) +static int ov5693_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) { struct ov5693_device *ov5693 = to_ov5693_sensor(sd); unsigned int framesize = OV5693_FIXED_PPL * (ov5693->mode.format.height + ov5693->ctrls.vblank->val); unsigned int fps = DIV_ROUND_CLOSEST(OV5693_PIXEL_RATE, framesize); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + interval->interval.numerator = 1; interval->interval.denominator = fps; @@ -1054,7 +1062,6 @@ static int ov5693_enum_frame_size(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops ov5693_video_ops = { .s_stream = ov5693_s_stream, - .g_frame_interval = ov5693_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { @@ -1064,6 +1071,7 @@ static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { .set_fmt = ov5693_set_fmt, .get_selection = ov5693_get_selection, .set_selection = ov5693_set_selection, + .get_frame_interval = ov5693_get_frame_interval, }; static const struct v4l2_subdev_ops ov5693_ops = { diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index c8f57ce15..663eccdfe 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -820,7 +820,7 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov5695->cur_mode = mode; h_blank = mode->hts_def - mode->width; @@ -846,8 +846,8 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov5695->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); } else { fmt->format.width = mode->width; fmt->format.height = mode->height; @@ -1039,7 +1039,7 @@ static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov5695 *ov5695 = to_ov5695(sd); struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); const struct ov5695_mode *def_mode = &supported_modes[0]; mutex_lock(&ov5695->mutex); diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c new file mode 100644 index 000000000..4fba4c2cb --- /dev/null +++ b/drivers/media/i2c/ov64a40.c @@ -0,0 +1,3690 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 sensor driver for OmniVision OV64A40 + * + * Copyright (C) 2023 Ideas On Board Oy + * Copyright (C) 2023 Arducam + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define OV64A40_XCLK_FREQ 24000000 + +#define OV64A40_NATIVE_WIDTH 9286 +#define OV64A40_NATIVE_HEIGHT 6976 +#define OV64A40_PIXEL_ARRAY_TOP 0 +#define OV64A40_PIXEL_ARRAY_LEFT 0 +#define OV64A40_PIXEL_ARRAY_WIDTH 9248 +#define OV64A40_PIXEL_ARRAY_HEIGHT 6944 + +#define OV64A40_PIXEL_RATE 300000000 + +#define OV64A40_LINK_FREQ_360M 360000000 +#define OV64A40_LINK_FREQ_456M 456000000 + +#define OV64A40_PLL1_PRE_DIV0 CCI_REG8(0x0301) +#define OV64A40_PLL1_PRE_DIV CCI_REG8(0x0303) +#define OV64A40_PLL1_MULTIPLIER CCI_REG16(0x0304) +#define OV64A40_PLL1_M_DIV CCI_REG8(0x0307) +#define OV64A40_PLL2_SEL_BAK_SA1 CCI_REG8(0x0320) +#define OV64A40_PLL2_PRE_DIV CCI_REG8(0x0323) +#define OV64A40_PLL2_MULTIPLIER CCI_REG16(0x0324) +#define OV64A40_PLL2_PRE_DIV0 CCI_REG8(0x0326) +#define OV64A40_PLL2_DIVDAC CCI_REG8(0x0329) +#define OV64A40_PLL2_DIVSP CCI_REG8(0x032d) +#define OV64A40_PLL2_DACPREDIV CCI_REG8(0x032e) + +/* TODO: validate vblank_min, it's not characterized in the datasheet. */ +#define OV64A40_VBLANK_MIN 128 +#define OV64A40_VTS_MAX 0xffffff + +#define OV64A40_REG_MEC_LONG_EXPO CCI_REG24(0x3500) +#define OV64A40_EXPOSURE_MIN 16 +#define OV64A40_EXPOSURE_MARGIN 32 + +#define OV64A40_REG_MEC_LONG_GAIN CCI_REG16(0x3508) +#define OV64A40_ANA_GAIN_MIN 0x80 +#define OV64A40_ANA_GAIN_MAX 0x7ff +#define OV64A40_ANA_GAIN_DEFAULT 0x80 + +#define OV64A40_REG_TIMING_CTRL0 CCI_REG16(0x3800) +#define OV64A40_REG_TIMING_CTRL2 CCI_REG16(0x3802) +#define OV64A40_REG_TIMING_CTRL4 CCI_REG16(0x3804) +#define OV64A40_REG_TIMING_CTRL6 CCI_REG16(0x3806) +#define OV64A40_REG_TIMING_CTRL8 CCI_REG16(0x3808) +#define OV64A40_REG_TIMING_CTRLA CCI_REG16(0x380a) +#define OV64A40_REG_TIMING_CTRLC CCI_REG16(0x380c) +#define OV64A40_REG_TIMING_CTRLE CCI_REG16(0x380e) +#define OV64A40_REG_TIMING_CTRL10 CCI_REG16(0x3810) +#define OV64A40_REG_TIMING_CTRL12 CCI_REG16(0x3812) + +/* + * Careful: a typo in the datasheet calls this register + * OV64A40_REG_TIMING_CTRL20. + */ +#define OV64A40_REG_TIMING_CTRL14 CCI_REG8(0x3814) +#define OV64A40_REG_TIMING_CTRL15 CCI_REG8(0x3815) +#define OV64A40_ODD_INC_SHIFT 4 +#define OV64A40_SKIPPING_CONFIG(_odd, _even) \ + (((_odd) << OV64A40_ODD_INC_SHIFT) | (_even)) + +#define OV64A40_REG_TIMING_CTRL_20 CCI_REG8(0x3820) +#define OV64A40_TIMING_CTRL_20_VFLIP BIT(2) +#define OV64A40_TIMING_CTRL_20_VBIN BIT(1) + +#define OV64A40_REG_TIMING_CTRL_21 CCI_REG8(0x3821) +#define OV64A40_TIMING_CTRL_21_HBIN BIT(4) +#define OV64A40_TIMING_CTRL_21_HFLIP BIT(2) +#define OV64A40_TIMING_CTRL_21_DSPEED BIT(0) +#define OV64A40_TIMING_CTRL_21_HBIN_CONF \ + (OV64A40_TIMING_CTRL_21_HBIN | \ + OV64A40_TIMING_CTRL_21_DSPEED) + +#define OV64A40_REG_TIMINGS_VTS_HIGH CCI_REG8(0x3840) +#define OV64A40_REG_TIMINGS_VTS_MID CCI_REG8(0x380e) +#define OV64A40_REG_TIMINGS_VTS_LOW CCI_REG8(0x380f) + +/* The test pattern control is weirdly named PRE_ISP_2325_D2V2_TOP_1 in TRM. */ +#define OV64A40_REG_TEST_PATTERN CCI_REG8(0x50c1) +#define OV64A40_TEST_PATTERN_DISABLED 0x00 +#define OV64A40_TEST_PATTERN_TYPE1 BIT(0) +#define OV64A40_TEST_PATTERN_TYPE2 (BIT(4) | BIT(0)) +#define OV64A40_TEST_PATTERN_TYPE3 (BIT(5) | BIT(0)) +#define OV64A40_TEST_PATTERN_TYPE4 (BIT(5) | BIT(4) | BIT(0)) + +#define OV64A40_REG_CHIP_ID CCI_REG24(0x300a) +#define OV64A40_CHIP_ID 0x566441 + +#define OV64A40_REG_SMIA CCI_REG8(0x0100) +#define OV64A40_REG_SMIA_STREAMING BIT(0) + +enum ov64a40_link_freq_ids { + OV64A40_LINK_FREQ_456M_ID, + OV64A40_LINK_FREQ_360M_ID, + OV64A40_NUM_LINK_FREQ, +}; + +static const char * const ov64a40_supply_names[] = { + /* Supplies can be enabled in any order */ + "avdd", /* Analog (2.8V) supply */ + "dovdd", /* Digital Core (1.8V) supply */ + "dvdd", /* IF (1.1V) supply */ +}; + +static const char * const ov64a40_test_pattern_menu[] = { + "Disabled", + "Type1", + "Type2", + "Type3", + "Type4", +}; + +static const int ov64a40_test_pattern_val[] = { + OV64A40_TEST_PATTERN_DISABLED, + OV64A40_TEST_PATTERN_TYPE1, + OV64A40_TEST_PATTERN_TYPE2, + OV64A40_TEST_PATTERN_TYPE3, + OV64A40_TEST_PATTERN_TYPE4, +}; + +static const unsigned int ov64a40_mbus_codes[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + +static const struct cci_reg_sequence ov64a40_init[] = { + { CCI_REG8(0x0103), 0x01 }, { CCI_REG8(0x0301), 0x88 }, + { CCI_REG8(0x0304), 0x00 }, { CCI_REG8(0x0305), 0x96 }, + { CCI_REG8(0x0306), 0x03 }, { CCI_REG8(0x0307), 0x00 }, + { CCI_REG8(0x0345), 0x2c }, { CCI_REG8(0x034a), 0x02 }, + { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x0350), 0xc0 }, + { CCI_REG8(0x0360), 0x09 }, { CCI_REG8(0x3012), 0x31 }, + { CCI_REG8(0x3015), 0xf0 }, { CCI_REG8(0x3017), 0xf0 }, + { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 }, + { CCI_REG8(0x3022), 0xf0 }, { CCI_REG8(0x3400), 0x08 }, + { CCI_REG8(0x3608), 0x41 }, { CCI_REG8(0x3421), 0x02 }, + { CCI_REG8(0x3500), 0x00 }, { CCI_REG8(0x3501), 0x00 }, + { CCI_REG8(0x3502), 0x18 }, { CCI_REG8(0x3504), 0x0c }, + { CCI_REG8(0x3508), 0x01 }, { CCI_REG8(0x3509), 0x00 }, + { CCI_REG8(0x350a), 0x01 }, { CCI_REG8(0x350b), 0x00 }, + { CCI_REG8(0x350b), 0x00 }, { CCI_REG8(0x3540), 0x00 }, + { CCI_REG8(0x3541), 0x00 }, { CCI_REG8(0x3542), 0x08 }, + { CCI_REG8(0x3548), 0x01 }, { CCI_REG8(0x3549), 0xa0 }, + { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3549), 0x00 }, + { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3580), 0x00 }, + { CCI_REG8(0x3581), 0x00 }, { CCI_REG8(0x3582), 0x04 }, + { CCI_REG8(0x3588), 0x01 }, { CCI_REG8(0x3589), 0xf0 }, + { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x3589), 0x00 }, + { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x360d), 0x83 }, + { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3617), 0x31 }, + { CCI_REG8(0x3623), 0x10 }, { CCI_REG8(0x3633), 0x03 }, + { CCI_REG8(0x3634), 0x03 }, { CCI_REG8(0x3635), 0x77 }, + { CCI_REG8(0x3640), 0x19 }, { CCI_REG8(0x3641), 0x80 }, + { CCI_REG8(0x364d), 0x0f }, { CCI_REG8(0x3680), 0x80 }, + { CCI_REG8(0x3682), 0x00 }, { CCI_REG8(0x3683), 0x00 }, + { CCI_REG8(0x3684), 0x07 }, { CCI_REG8(0x3688), 0x01 }, + { CCI_REG8(0x3689), 0x08 }, { CCI_REG8(0x368a), 0x26 }, + { CCI_REG8(0x368b), 0xc8 }, { CCI_REG8(0x368e), 0x70 }, + { CCI_REG8(0x368f), 0x00 }, { CCI_REG8(0x3692), 0x04 }, + { CCI_REG8(0x3693), 0x00 }, { CCI_REG8(0x3696), 0xd1 }, + { CCI_REG8(0x3697), 0xe0 }, { CCI_REG8(0x3698), 0x80 }, + { CCI_REG8(0x3699), 0x2b }, { CCI_REG8(0x369a), 0x00 }, + { CCI_REG8(0x369d), 0x00 }, { CCI_REG8(0x369e), 0x14 }, + { CCI_REG8(0x369f), 0x20 }, { CCI_REG8(0x36a5), 0x80 }, + { CCI_REG8(0x36a6), 0x00 }, { CCI_REG8(0x36a7), 0x00 }, + { CCI_REG8(0x36a8), 0x00 }, { CCI_REG8(0x36b5), 0x17 }, + { CCI_REG8(0x3701), 0x30 }, { CCI_REG8(0x3706), 0x2b }, + { CCI_REG8(0x3709), 0x8d }, { CCI_REG8(0x370b), 0x4f }, + { CCI_REG8(0x3711), 0x00 }, { CCI_REG8(0x3712), 0x01 }, + { CCI_REG8(0x3713), 0x00 }, { CCI_REG8(0x3720), 0x08 }, + { CCI_REG8(0x3727), 0x22 }, { CCI_REG8(0x3728), 0x01 }, + { CCI_REG8(0x375e), 0x00 }, { CCI_REG8(0x3760), 0x08 }, + { CCI_REG8(0x3761), 0x10 }, { CCI_REG8(0x3762), 0x08 }, + { CCI_REG8(0x3765), 0x10 }, { CCI_REG8(0x3766), 0x18 }, + { CCI_REG8(0x376a), 0x08 }, { CCI_REG8(0x376b), 0x00 }, + { CCI_REG8(0x376d), 0x1b }, { CCI_REG8(0x3791), 0x2b }, + { CCI_REG8(0x3793), 0x2b }, { CCI_REG8(0x3795), 0x2b }, + { CCI_REG8(0x3797), 0x4f }, { CCI_REG8(0x3799), 0x4f }, + { CCI_REG8(0x379b), 0x4f }, { CCI_REG8(0x37a0), 0x22 }, + { CCI_REG8(0x37da), 0x04 }, { CCI_REG8(0x37f9), 0x02 }, + { CCI_REG8(0x37fa), 0x02 }, { CCI_REG8(0x37fb), 0x02 }, + { CCI_REG8(0x3814), 0x11 }, { CCI_REG8(0x3815), 0x11 }, + { CCI_REG8(0x3820), 0x40 }, { CCI_REG8(0x3821), 0x04 }, + { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3823), 0x04 }, + { CCI_REG8(0x3827), 0x08 }, { CCI_REG8(0x3828), 0x00 }, + { CCI_REG8(0x382a), 0x81 }, { CCI_REG8(0x382e), 0x70 }, + { CCI_REG8(0x3837), 0x10 }, { CCI_REG8(0x3839), 0x00 }, + { CCI_REG8(0x383b), 0x00 }, { CCI_REG8(0x383c), 0x00 }, + { CCI_REG8(0x383d), 0x10 }, { CCI_REG8(0x383f), 0x00 }, + { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0x8c }, + { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x10 }, + { CCI_REG8(0x3857), 0x10 }, { CCI_REG8(0x3858), 0x20 }, + { CCI_REG8(0x3859), 0x20 }, { CCI_REG8(0x3894), 0x00 }, + { CCI_REG8(0x3895), 0x00 }, { CCI_REG8(0x3896), 0x00 }, + { CCI_REG8(0x3897), 0x00 }, { CCI_REG8(0x3900), 0x40 }, + { CCI_REG8(0x3aed), 0x6e }, { CCI_REG8(0x3af1), 0x73 }, + { CCI_REG8(0x3d86), 0x12 }, { CCI_REG8(0x3d87), 0x30 }, + { CCI_REG8(0x3d8c), 0xab }, { CCI_REG8(0x3d8d), 0xb0 }, + { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f00), 0x12 }, + { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f01), 0x03 }, + { CCI_REG8(0x4009), 0x01 }, { CCI_REG8(0x400e), 0xc6 }, + { CCI_REG8(0x400f), 0x00 }, { CCI_REG8(0x4010), 0x28 }, + { CCI_REG8(0x4011), 0x01 }, { CCI_REG8(0x4012), 0x0c }, + { CCI_REG8(0x4015), 0x00 }, { CCI_REG8(0x4016), 0x1f }, + { CCI_REG8(0x4017), 0x00 }, { CCI_REG8(0x4018), 0x07 }, + { CCI_REG8(0x401a), 0x40 }, { CCI_REG8(0x4028), 0x01 }, + { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4506), 0x01 }, + { CCI_REG8(0x4508), 0x00 }, { CCI_REG8(0x4509), 0x35 }, + { CCI_REG8(0x450a), 0x08 }, { CCI_REG8(0x450c), 0x00 }, + { CCI_REG8(0x450d), 0x20 }, { CCI_REG8(0x450e), 0x00 }, + { CCI_REG8(0x450f), 0x20 }, { CCI_REG8(0x451e), 0x00 }, + { CCI_REG8(0x451f), 0x00 }, { CCI_REG8(0x4523), 0x00 }, + { CCI_REG8(0x4526), 0x00 }, { CCI_REG8(0x4527), 0x18 }, + { CCI_REG8(0x4580), 0x01 }, { CCI_REG8(0x4583), 0x00 }, + { CCI_REG8(0x4584), 0x00 }, { CCI_REG8(0x45c0), 0xa1 }, + { CCI_REG8(0x4602), 0x08 }, { CCI_REG8(0x4603), 0x05 }, + { CCI_REG8(0x4606), 0x12 }, { CCI_REG8(0x4607), 0x30 }, + { CCI_REG8(0x460b), 0x00 }, { CCI_REG8(0x460d), 0x00 }, + { CCI_REG8(0x4640), 0x00 }, { CCI_REG8(0x4641), 0x24 }, + { CCI_REG8(0x4643), 0x08 }, { CCI_REG8(0x4645), 0x14 }, + { CCI_REG8(0x4648), 0x0a }, { CCI_REG8(0x4649), 0x06 }, + { CCI_REG8(0x464a), 0x00 }, { CCI_REG8(0x464b), 0x30 }, + { CCI_REG8(0x4800), 0x04 }, { CCI_REG8(0x4802), 0x02 }, + { CCI_REG8(0x480b), 0x10 }, { CCI_REG8(0x480c), 0x80 }, + { CCI_REG8(0x480e), 0x04 }, { CCI_REG8(0x480f), 0x32 }, + { CCI_REG8(0x481b), 0x12 }, { CCI_REG8(0x4833), 0x30 }, + { CCI_REG8(0x4837), 0x08 }, { CCI_REG8(0x484b), 0x27 }, + { CCI_REG8(0x4850), 0x42 }, { CCI_REG8(0x4851), 0xaa }, + { CCI_REG8(0x4860), 0x01 }, { CCI_REG8(0x4861), 0xec }, + { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x4888), 0x00 }, + { CCI_REG8(0x4889), 0x03 }, { CCI_REG8(0x488c), 0x60 }, + { CCI_REG8(0x4910), 0x28 }, { CCI_REG8(0x4911), 0x01 }, + { CCI_REG8(0x4912), 0x0c }, { CCI_REG8(0x491a), 0x40 }, + { CCI_REG8(0x4915), 0x00 }, { CCI_REG8(0x4916), 0x0f }, + { CCI_REG8(0x4917), 0x00 }, { CCI_REG8(0x4918), 0x07 }, + { CCI_REG8(0x4a10), 0x28 }, { CCI_REG8(0x4a11), 0x01 }, + { CCI_REG8(0x4a12), 0x0c }, { CCI_REG8(0x4a1a), 0x40 }, + { CCI_REG8(0x4a15), 0x00 }, { CCI_REG8(0x4a16), 0x0f }, + { CCI_REG8(0x4a17), 0x00 }, { CCI_REG8(0x4a18), 0x07 }, + { CCI_REG8(0x4d00), 0x04 }, { CCI_REG8(0x4d01), 0x5a }, + { CCI_REG8(0x4d02), 0xbb }, { CCI_REG8(0x4d03), 0x84 }, + { CCI_REG8(0x4d04), 0xd1 }, { CCI_REG8(0x4d05), 0x68 }, + { CCI_REG8(0xc4fa), 0x10 }, { CCI_REG8(0x3b56), 0x0a }, + { CCI_REG8(0x3b57), 0x0a }, { CCI_REG8(0x3b58), 0x0c }, + { CCI_REG8(0x3b59), 0x10 }, { CCI_REG8(0x3a1d), 0x30 }, + { CCI_REG8(0x3a1e), 0x30 }, { CCI_REG8(0x3a21), 0x30 }, + { CCI_REG8(0x3a22), 0x30 }, { CCI_REG8(0x3992), 0x02 }, + { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x39fb), 0x30 }, + { CCI_REG8(0x39fc), 0x30 }, { CCI_REG8(0x39fd), 0x30 }, + { CCI_REG8(0x39fe), 0x30 }, { CCI_REG8(0x3a6d), 0x83 }, + { CCI_REG8(0x3a5e), 0x83 }, { CCI_REG8(0xc500), 0x12 }, + { CCI_REG8(0xc501), 0x12 }, { CCI_REG8(0xc502), 0x12 }, + { CCI_REG8(0xc503), 0x12 }, { CCI_REG8(0xc505), 0x12 }, + { CCI_REG8(0xc506), 0x12 }, { CCI_REG8(0xc507), 0x12 }, + { CCI_REG8(0xc508), 0x12 }, { CCI_REG8(0x3a77), 0x12 }, + { CCI_REG8(0x3a73), 0x12 }, { CCI_REG8(0x3a7b), 0x12 }, + { CCI_REG8(0x3a7f), 0x12 }, { CCI_REG8(0x3b2e), 0x13 }, + { CCI_REG8(0x3b29), 0x13 }, { CCI_REG8(0xc439), 0x13 }, + { CCI_REG8(0xc469), 0x13 }, { CCI_REG8(0xc41c), 0x89 }, + { CCI_REG8(0x3618), 0x80 }, { CCI_REG8(0xc514), 0x51 }, + { CCI_REG8(0xc515), 0x2c }, { CCI_REG8(0xc516), 0x16 }, + { CCI_REG8(0xc517), 0x0d }, { CCI_REG8(0x3615), 0x7f }, + { CCI_REG8(0x3632), 0x99 }, { CCI_REG8(0x3642), 0x00 }, + { CCI_REG8(0x3645), 0x80 }, { CCI_REG8(0x3702), 0x2a }, + { CCI_REG8(0x3703), 0x2a }, { CCI_REG8(0x3708), 0x2f }, + { CCI_REG8(0x3721), 0x15 }, { CCI_REG8(0x3744), 0x28 }, + { CCI_REG8(0x3991), 0x0c }, { CCI_REG8(0x371d), 0x24 }, + { CCI_REG8(0x371f), 0x0c }, { CCI_REG8(0x374b), 0x03 }, + { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x391d), 0x55 }, + { CCI_REG8(0x391e), 0x52 }, { CCI_REG8(0x399d), 0x0c }, + { CCI_REG8(0x3a2f), 0x01 }, { CCI_REG8(0x3a30), 0x01 }, + { CCI_REG8(0x3a31), 0x01 }, { CCI_REG8(0x3a32), 0x01 }, + { CCI_REG8(0x3a34), 0x01 }, { CCI_REG8(0x3a35), 0x01 }, + { CCI_REG8(0x3a36), 0x01 }, { CCI_REG8(0x3a37), 0x01 }, + { CCI_REG8(0x3a43), 0x01 }, { CCI_REG8(0x3a44), 0x01 }, + { CCI_REG8(0x3a45), 0x01 }, { CCI_REG8(0x3a46), 0x01 }, + { CCI_REG8(0x3a48), 0x01 }, { CCI_REG8(0x3a49), 0x01 }, + { CCI_REG8(0x3a4a), 0x01 }, { CCI_REG8(0x3a4b), 0x01 }, + { CCI_REG8(0x3a50), 0x14 }, { CCI_REG8(0x3a54), 0x14 }, + { CCI_REG8(0x3a60), 0x20 }, { CCI_REG8(0x3a6f), 0x20 }, + { CCI_REG8(0x3ac5), 0x01 }, { CCI_REG8(0x3ac6), 0x01 }, + { CCI_REG8(0x3ac7), 0x01 }, { CCI_REG8(0x3ac8), 0x01 }, + { CCI_REG8(0x3ac9), 0x01 }, { CCI_REG8(0x3aca), 0x01 }, + { CCI_REG8(0x3acb), 0x01 }, { CCI_REG8(0x3acc), 0x01 }, + { CCI_REG8(0x3acd), 0x01 }, { CCI_REG8(0x3ace), 0x01 }, + { CCI_REG8(0x3acf), 0x01 }, { CCI_REG8(0x3ad0), 0x01 }, + { CCI_REG8(0x3ad1), 0x01 }, { CCI_REG8(0x3ad2), 0x01 }, + { CCI_REG8(0x3ad3), 0x01 }, { CCI_REG8(0x3ad4), 0x01 }, + { CCI_REG8(0x3add), 0x1f }, { CCI_REG8(0x3adf), 0x24 }, + { CCI_REG8(0x3aef), 0x1f }, { CCI_REG8(0x3af0), 0x24 }, + { CCI_REG8(0x3b92), 0x08 }, { CCI_REG8(0x3b93), 0x08 }, + { CCI_REG8(0x3b94), 0x08 }, { CCI_REG8(0x3b95), 0x08 }, + { CCI_REG8(0x3be7), 0x1e }, { CCI_REG8(0x3be8), 0x26 }, + { CCI_REG8(0xc44a), 0x20 }, { CCI_REG8(0xc44c), 0x20 }, + { CCI_REG8(0xc483), 0x00 }, { CCI_REG8(0xc484), 0x00 }, + { CCI_REG8(0xc485), 0x00 }, { CCI_REG8(0xc486), 0x00 }, + { CCI_REG8(0xc487), 0x01 }, { CCI_REG8(0xc488), 0x01 }, + { CCI_REG8(0xc489), 0x01 }, { CCI_REG8(0xc48a), 0x01 }, + { CCI_REG8(0xc4c1), 0x00 }, { CCI_REG8(0xc4c2), 0x00 }, + { CCI_REG8(0xc4c3), 0x00 }, { CCI_REG8(0xc4c4), 0x00 }, + { CCI_REG8(0xc4c6), 0x10 }, { CCI_REG8(0xc4c7), 0x10 }, + { CCI_REG8(0xc4c8), 0x10 }, { CCI_REG8(0xc4c9), 0x10 }, + { CCI_REG8(0xc4ca), 0x10 }, { CCI_REG8(0xc4cb), 0x10 }, + { CCI_REG8(0xc4cc), 0x10 }, { CCI_REG8(0xc4cd), 0x10 }, + { CCI_REG8(0xc4ea), 0x07 }, { CCI_REG8(0xc4eb), 0x07 }, + { CCI_REG8(0xc4ec), 0x07 }, { CCI_REG8(0xc4ed), 0x07 }, + { CCI_REG8(0xc4ee), 0x07 }, { CCI_REG8(0xc4f6), 0x10 }, + { CCI_REG8(0xc4f7), 0x10 }, { CCI_REG8(0xc4f8), 0x10 }, + { CCI_REG8(0xc4f9), 0x10 }, { CCI_REG8(0xc518), 0x0e }, + { CCI_REG8(0xc519), 0x0e }, { CCI_REG8(0xc51a), 0x0e }, + { CCI_REG8(0xc51b), 0x0e }, { CCI_REG8(0xc51c), 0x0e }, + { CCI_REG8(0xc51d), 0x0e }, { CCI_REG8(0xc51e), 0x0e }, + { CCI_REG8(0xc51f), 0x0e }, { CCI_REG8(0xc520), 0x0e }, + { CCI_REG8(0xc521), 0x0e }, { CCI_REG8(0xc522), 0x0e }, + { CCI_REG8(0xc523), 0x0e }, { CCI_REG8(0xc524), 0x0e }, + { CCI_REG8(0xc525), 0x0e }, { CCI_REG8(0xc526), 0x0e }, + { CCI_REG8(0xc527), 0x0e }, { CCI_REG8(0xc528), 0x0e }, + { CCI_REG8(0xc529), 0x0e }, { CCI_REG8(0xc52a), 0x0e }, + { CCI_REG8(0xc52b), 0x0e }, { CCI_REG8(0xc52c), 0x0e }, + { CCI_REG8(0xc52d), 0x0e }, { CCI_REG8(0xc52e), 0x0e }, + { CCI_REG8(0xc52f), 0x0e }, { CCI_REG8(0xc530), 0x0e }, + { CCI_REG8(0xc531), 0x0e }, { CCI_REG8(0xc532), 0x0e }, + { CCI_REG8(0xc533), 0x0e }, { CCI_REG8(0xc534), 0x0e }, + { CCI_REG8(0xc535), 0x0e }, { CCI_REG8(0xc536), 0x0e }, + { CCI_REG8(0xc537), 0x0e }, { CCI_REG8(0xc538), 0x0e }, + { CCI_REG8(0xc539), 0x0e }, { CCI_REG8(0xc53a), 0x0e }, + { CCI_REG8(0xc53b), 0x0e }, { CCI_REG8(0xc53c), 0x0e }, + { CCI_REG8(0xc53d), 0x0e }, { CCI_REG8(0xc53e), 0x0e }, + { CCI_REG8(0xc53f), 0x0e }, { CCI_REG8(0xc540), 0x0e }, + { CCI_REG8(0xc541), 0x0e }, { CCI_REG8(0xc542), 0x0e }, + { CCI_REG8(0xc543), 0x0e }, { CCI_REG8(0xc544), 0x0e }, + { CCI_REG8(0xc545), 0x0e }, { CCI_REG8(0xc546), 0x0e }, + { CCI_REG8(0xc547), 0x0e }, { CCI_REG8(0xc548), 0x0e }, + { CCI_REG8(0xc549), 0x0e }, { CCI_REG8(0xc57f), 0x22 }, + { CCI_REG8(0xc580), 0x22 }, { CCI_REG8(0xc581), 0x22 }, + { CCI_REG8(0xc582), 0x22 }, { CCI_REG8(0xc583), 0x22 }, + { CCI_REG8(0xc584), 0x22 }, { CCI_REG8(0xc585), 0x22 }, + { CCI_REG8(0xc586), 0x22 }, { CCI_REG8(0xc587), 0x22 }, + { CCI_REG8(0xc588), 0x22 }, { CCI_REG8(0xc589), 0x22 }, + { CCI_REG8(0xc58a), 0x22 }, { CCI_REG8(0xc58b), 0x22 }, + { CCI_REG8(0xc58c), 0x22 }, { CCI_REG8(0xc58d), 0x22 }, + { CCI_REG8(0xc58e), 0x22 }, { CCI_REG8(0xc58f), 0x22 }, + { CCI_REG8(0xc590), 0x22 }, { CCI_REG8(0xc591), 0x22 }, + { CCI_REG8(0xc592), 0x22 }, { CCI_REG8(0xc598), 0x22 }, + { CCI_REG8(0xc599), 0x22 }, { CCI_REG8(0xc59a), 0x22 }, + { CCI_REG8(0xc59b), 0x22 }, { CCI_REG8(0xc59c), 0x22 }, + { CCI_REG8(0xc59d), 0x22 }, { CCI_REG8(0xc59e), 0x22 }, + { CCI_REG8(0xc59f), 0x22 }, { CCI_REG8(0xc5a0), 0x22 }, + { CCI_REG8(0xc5a1), 0x22 }, { CCI_REG8(0xc5a2), 0x22 }, + { CCI_REG8(0xc5a3), 0x22 }, { CCI_REG8(0xc5a4), 0x22 }, + { CCI_REG8(0xc5a5), 0x22 }, { CCI_REG8(0xc5a6), 0x22 }, + { CCI_REG8(0xc5a7), 0x22 }, { CCI_REG8(0xc5a8), 0x22 }, + { CCI_REG8(0xc5a9), 0x22 }, { CCI_REG8(0xc5aa), 0x22 }, + { CCI_REG8(0xc5ab), 0x22 }, { CCI_REG8(0xc5b1), 0x2a }, + { CCI_REG8(0xc5b2), 0x2a }, { CCI_REG8(0xc5b3), 0x2a }, + { CCI_REG8(0xc5b4), 0x2a }, { CCI_REG8(0xc5b5), 0x2a }, + { CCI_REG8(0xc5b6), 0x2a }, { CCI_REG8(0xc5b7), 0x2a }, + { CCI_REG8(0xc5b8), 0x2a }, { CCI_REG8(0xc5b9), 0x2a }, + { CCI_REG8(0xc5ba), 0x2a }, { CCI_REG8(0xc5bb), 0x2a }, + { CCI_REG8(0xc5bc), 0x2a }, { CCI_REG8(0xc5bd), 0x2a }, + { CCI_REG8(0xc5be), 0x2a }, { CCI_REG8(0xc5bf), 0x2a }, + { CCI_REG8(0xc5c0), 0x2a }, { CCI_REG8(0xc5c1), 0x2a }, + { CCI_REG8(0xc5c2), 0x2a }, { CCI_REG8(0xc5c3), 0x2a }, + { CCI_REG8(0xc5c4), 0x2a }, { CCI_REG8(0xc5ca), 0x2a }, + { CCI_REG8(0xc5cb), 0x2a }, { CCI_REG8(0xc5cc), 0x2a }, + { CCI_REG8(0xc5cd), 0x2a }, { CCI_REG8(0xc5ce), 0x2a }, + { CCI_REG8(0xc5cf), 0x2a }, { CCI_REG8(0xc5d0), 0x2a }, + { CCI_REG8(0xc5d1), 0x2a }, { CCI_REG8(0xc5d2), 0x2a }, + { CCI_REG8(0xc5d3), 0x2a }, { CCI_REG8(0xc5d4), 0x2a }, + { CCI_REG8(0xc5d5), 0x2a }, { CCI_REG8(0xc5d6), 0x2a }, + { CCI_REG8(0xc5d7), 0x2a }, { CCI_REG8(0xc5d8), 0x2a }, + { CCI_REG8(0xc5d9), 0x2a }, { CCI_REG8(0xc5da), 0x2a }, + { CCI_REG8(0xc5db), 0x2a }, { CCI_REG8(0xc5dc), 0x2a }, + { CCI_REG8(0xc5dd), 0x2a }, { CCI_REG8(0xc5e8), 0x22 }, + { CCI_REG8(0xc5ea), 0x22 }, { CCI_REG8(0x4540), 0x12 }, + { CCI_REG8(0x4541), 0x30 }, { CCI_REG8(0x3d86), 0x12 }, + { CCI_REG8(0x3d87), 0x30 }, { CCI_REG8(0x4606), 0x12 }, + { CCI_REG8(0x4607), 0x30 }, { CCI_REG8(0x4648), 0x0a }, + { CCI_REG8(0x4649), 0x06 }, { CCI_REG8(0x3220), 0x12 }, + { CCI_REG8(0x3221), 0x30 }, { CCI_REG8(0x40c2), 0x12 }, + { CCI_REG8(0x49c2), 0x12 }, { CCI_REG8(0x4ac2), 0x12 }, + { CCI_REG8(0x40c3), 0x30 }, { CCI_REG8(0x49c3), 0x30 }, + { CCI_REG8(0x4ac3), 0x30 }, { CCI_REG8(0x36b0), 0x12 }, + { CCI_REG8(0x36b1), 0x30 }, { CCI_REG8(0x45cb), 0x12 }, + { CCI_REG8(0x45cc), 0x30 }, { CCI_REG8(0x4585), 0x12 }, + { CCI_REG8(0x4586), 0x30 }, { CCI_REG8(0x36b2), 0x12 }, + { CCI_REG8(0x36b3), 0x30 }, { CCI_REG8(0x5a40), 0x75 }, + { CCI_REG8(0x5a41), 0x75 }, { CCI_REG8(0x5a42), 0x75 }, + { CCI_REG8(0x5a43), 0x75 }, { CCI_REG8(0x5a44), 0x75 }, + { CCI_REG8(0x5a45), 0x75 }, { CCI_REG8(0x5a46), 0x75 }, + { CCI_REG8(0x5a47), 0x75 }, { CCI_REG8(0x5a48), 0x75 }, + { CCI_REG8(0x5a49), 0x75 }, { CCI_REG8(0x5a4a), 0x75 }, + { CCI_REG8(0x5a4b), 0x75 }, { CCI_REG8(0x5a4c), 0x75 }, + { CCI_REG8(0x5a4d), 0x75 }, { CCI_REG8(0x5a4e), 0x75 }, + { CCI_REG8(0x5a4f), 0x75 }, { CCI_REG8(0x5a50), 0x75 }, + { CCI_REG8(0x5a51), 0x75 }, { CCI_REG8(0x5a52), 0x75 }, + { CCI_REG8(0x5a53), 0x75 }, { CCI_REG8(0x5a54), 0x75 }, + { CCI_REG8(0x5a55), 0x75 }, { CCI_REG8(0x5a56), 0x75 }, + { CCI_REG8(0x5a57), 0x75 }, { CCI_REG8(0x5a58), 0x75 }, + { CCI_REG8(0x5a59), 0x75 }, { CCI_REG8(0x5a5a), 0x75 }, + { CCI_REG8(0x5a5b), 0x75 }, { CCI_REG8(0x5a5c), 0x75 }, + { CCI_REG8(0x5a5d), 0x75 }, { CCI_REG8(0x5a5e), 0x75 }, + { CCI_REG8(0x5a5f), 0x75 }, { CCI_REG8(0x5a60), 0x75 }, + { CCI_REG8(0x5a61), 0x75 }, { CCI_REG8(0x5a62), 0x75 }, + { CCI_REG8(0x5a63), 0x75 }, { CCI_REG8(0x5a64), 0x75 }, + { CCI_REG8(0x5a65), 0x75 }, { CCI_REG8(0x5a66), 0x75 }, + { CCI_REG8(0x5a67), 0x75 }, { CCI_REG8(0x5a68), 0x75 }, + { CCI_REG8(0x5a69), 0x75 }, { CCI_REG8(0x5a6a), 0x75 }, + { CCI_REG8(0x5a6b), 0x75 }, { CCI_REG8(0x5a6c), 0x75 }, + { CCI_REG8(0x5a6d), 0x75 }, { CCI_REG8(0x5a6e), 0x75 }, + { CCI_REG8(0x5a6f), 0x75 }, { CCI_REG8(0x5a70), 0x75 }, + { CCI_REG8(0x5a71), 0x75 }, { CCI_REG8(0x5a72), 0x75 }, + { CCI_REG8(0x5a73), 0x75 }, { CCI_REG8(0x5a74), 0x75 }, + { CCI_REG8(0x5a75), 0x75 }, { CCI_REG8(0x5a76), 0x75 }, + { CCI_REG8(0x5a77), 0x75 }, { CCI_REG8(0x5a78), 0x75 }, + { CCI_REG8(0x5a79), 0x75 }, { CCI_REG8(0x5a7a), 0x75 }, + { CCI_REG8(0x5a7b), 0x75 }, { CCI_REG8(0x5a7c), 0x75 }, + { CCI_REG8(0x5a7d), 0x75 }, { CCI_REG8(0x5a7e), 0x75 }, + { CCI_REG8(0x5a7f), 0x75 }, { CCI_REG8(0x5a80), 0x75 }, + { CCI_REG8(0x5a81), 0x75 }, { CCI_REG8(0x5a82), 0x75 }, + { CCI_REG8(0x5a83), 0x75 }, { CCI_REG8(0x5a84), 0x75 }, + { CCI_REG8(0x5a85), 0x75 }, { CCI_REG8(0x5a86), 0x75 }, + { CCI_REG8(0x5a87), 0x75 }, { CCI_REG8(0x5a88), 0x75 }, + { CCI_REG8(0x5a89), 0x75 }, { CCI_REG8(0x5a8a), 0x75 }, + { CCI_REG8(0x5a8b), 0x75 }, { CCI_REG8(0x5a8c), 0x75 }, + { CCI_REG8(0x5a8d), 0x75 }, { CCI_REG8(0x5a8e), 0x75 }, + { CCI_REG8(0x5a8f), 0x75 }, { CCI_REG8(0x5a90), 0x75 }, + { CCI_REG8(0x5a91), 0x75 }, { CCI_REG8(0x5a92), 0x75 }, + { CCI_REG8(0x5a93), 0x75 }, { CCI_REG8(0x5a94), 0x75 }, + { CCI_REG8(0x5a95), 0x75 }, { CCI_REG8(0x5a96), 0x75 }, + { CCI_REG8(0x5a97), 0x75 }, { CCI_REG8(0x5a98), 0x75 }, + { CCI_REG8(0x5a99), 0x75 }, { CCI_REG8(0x5a9a), 0x75 }, + { CCI_REG8(0x5a9b), 0x75 }, { CCI_REG8(0x5a9c), 0x75 }, + { CCI_REG8(0x5a9d), 0x75 }, { CCI_REG8(0x5a9e), 0x75 }, + { CCI_REG8(0x5a9f), 0x75 }, { CCI_REG8(0x5aa0), 0x75 }, + { CCI_REG8(0x5aa1), 0x75 }, { CCI_REG8(0x5aa2), 0x75 }, + { CCI_REG8(0x5aa3), 0x75 }, { CCI_REG8(0x5aa4), 0x75 }, + { CCI_REG8(0x5aa5), 0x75 }, { CCI_REG8(0x5aa6), 0x75 }, + { CCI_REG8(0x5aa7), 0x75 }, { CCI_REG8(0x5aa8), 0x75 }, + { CCI_REG8(0x5aa9), 0x75 }, { CCI_REG8(0x5aaa), 0x75 }, + { CCI_REG8(0x5aab), 0x75 }, { CCI_REG8(0x5aac), 0x75 }, + { CCI_REG8(0x5aad), 0x75 }, { CCI_REG8(0x5aae), 0x75 }, + { CCI_REG8(0x5aaf), 0x75 }, { CCI_REG8(0x5ab0), 0x75 }, + { CCI_REG8(0x5ab1), 0x75 }, { CCI_REG8(0x5ab2), 0x75 }, + { CCI_REG8(0x5ab3), 0x75 }, { CCI_REG8(0x5ab4), 0x75 }, + { CCI_REG8(0x5ab5), 0x75 }, { CCI_REG8(0x5ab6), 0x75 }, + { CCI_REG8(0x5ab7), 0x75 }, { CCI_REG8(0x5ab8), 0x75 }, + { CCI_REG8(0x5ab9), 0x75 }, { CCI_REG8(0x5aba), 0x75 }, + { CCI_REG8(0x5abb), 0x75 }, { CCI_REG8(0x5abc), 0x75 }, + { CCI_REG8(0x5abd), 0x75 }, { CCI_REG8(0x5abe), 0x75 }, + { CCI_REG8(0x5abf), 0x75 }, { CCI_REG8(0x5ac0), 0x75 }, + { CCI_REG8(0x5ac1), 0x75 }, { CCI_REG8(0x5ac2), 0x75 }, + { CCI_REG8(0x5ac3), 0x75 }, { CCI_REG8(0x5ac4), 0x75 }, + { CCI_REG8(0x5ac5), 0x75 }, { CCI_REG8(0x5ac6), 0x75 }, + { CCI_REG8(0x5ac7), 0x75 }, { CCI_REG8(0x5ac8), 0x75 }, + { CCI_REG8(0x5ac9), 0x75 }, { CCI_REG8(0x5aca), 0x75 }, + { CCI_REG8(0x5acb), 0x75 }, { CCI_REG8(0x5acc), 0x75 }, + { CCI_REG8(0x5acd), 0x75 }, { CCI_REG8(0x5ace), 0x75 }, + { CCI_REG8(0x5acf), 0x75 }, { CCI_REG8(0x5ad0), 0x75 }, + { CCI_REG8(0x5ad1), 0x75 }, { CCI_REG8(0x5ad2), 0x75 }, + { CCI_REG8(0x5ad3), 0x75 }, { CCI_REG8(0x5ad4), 0x75 }, + { CCI_REG8(0x5ad5), 0x75 }, { CCI_REG8(0x5ad6), 0x75 }, + { CCI_REG8(0x5ad7), 0x75 }, { CCI_REG8(0x5ad8), 0x75 }, + { CCI_REG8(0x5ad9), 0x75 }, { CCI_REG8(0x5ada), 0x75 }, + { CCI_REG8(0x5adb), 0x75 }, { CCI_REG8(0x5adc), 0x75 }, + { CCI_REG8(0x5add), 0x75 }, { CCI_REG8(0x5ade), 0x75 }, + { CCI_REG8(0x5adf), 0x75 }, { CCI_REG8(0x5ae0), 0x75 }, + { CCI_REG8(0x5ae1), 0x75 }, { CCI_REG8(0x5ae2), 0x75 }, + { CCI_REG8(0x5ae3), 0x75 }, { CCI_REG8(0x5ae4), 0x75 }, + { CCI_REG8(0x5ae5), 0x75 }, { CCI_REG8(0x5ae6), 0x75 }, + { CCI_REG8(0x5ae7), 0x75 }, { CCI_REG8(0x5ae8), 0x75 }, + { CCI_REG8(0x5ae9), 0x75 }, { CCI_REG8(0x5aea), 0x75 }, + { CCI_REG8(0x5aeb), 0x75 }, { CCI_REG8(0x5aec), 0x75 }, + { CCI_REG8(0x5aed), 0x75 }, { CCI_REG8(0x5aee), 0x75 }, + { CCI_REG8(0x5aef), 0x75 }, { CCI_REG8(0x5af0), 0x75 }, + { CCI_REG8(0x5af1), 0x75 }, { CCI_REG8(0x5af2), 0x75 }, + { CCI_REG8(0x5af3), 0x75 }, { CCI_REG8(0x5af4), 0x75 }, + { CCI_REG8(0x5af5), 0x75 }, { CCI_REG8(0x5af6), 0x75 }, + { CCI_REG8(0x5af7), 0x75 }, { CCI_REG8(0x5af8), 0x75 }, + { CCI_REG8(0x5af9), 0x75 }, { CCI_REG8(0x5afa), 0x75 }, + { CCI_REG8(0x5afb), 0x75 }, { CCI_REG8(0x5afc), 0x75 }, + { CCI_REG8(0x5afd), 0x75 }, { CCI_REG8(0x5afe), 0x75 }, + { CCI_REG8(0x5aff), 0x75 }, { CCI_REG8(0x5b00), 0x75 }, + { CCI_REG8(0x5b01), 0x75 }, { CCI_REG8(0x5b02), 0x75 }, + { CCI_REG8(0x5b03), 0x75 }, { CCI_REG8(0x5b04), 0x75 }, + { CCI_REG8(0x5b05), 0x75 }, { CCI_REG8(0x5b06), 0x75 }, + { CCI_REG8(0x5b07), 0x75 }, { CCI_REG8(0x5b08), 0x75 }, + { CCI_REG8(0x5b09), 0x75 }, { CCI_REG8(0x5b0a), 0x75 }, + { CCI_REG8(0x5b0b), 0x75 }, { CCI_REG8(0x5b0c), 0x75 }, + { CCI_REG8(0x5b0d), 0x75 }, { CCI_REG8(0x5b0e), 0x75 }, + { CCI_REG8(0x5b0f), 0x75 }, { CCI_REG8(0x5b10), 0x75 }, + { CCI_REG8(0x5b11), 0x75 }, { CCI_REG8(0x5b12), 0x75 }, + { CCI_REG8(0x5b13), 0x75 }, { CCI_REG8(0x5b14), 0x75 }, + { CCI_REG8(0x5b15), 0x75 }, { CCI_REG8(0x5b16), 0x75 }, + { CCI_REG8(0x5b17), 0x75 }, { CCI_REG8(0x5b18), 0x75 }, + { CCI_REG8(0x5b19), 0x75 }, { CCI_REG8(0x5b1a), 0x75 }, + { CCI_REG8(0x5b1b), 0x75 }, { CCI_REG8(0x5b1c), 0x75 }, + { CCI_REG8(0x5b1d), 0x75 }, { CCI_REG8(0x5b1e), 0x75 }, + { CCI_REG8(0x5b1f), 0x75 }, { CCI_REG8(0x5b20), 0x75 }, + { CCI_REG8(0x5b21), 0x75 }, { CCI_REG8(0x5b22), 0x75 }, + { CCI_REG8(0x5b23), 0x75 }, { CCI_REG8(0x5b24), 0x75 }, + { CCI_REG8(0x5b25), 0x75 }, { CCI_REG8(0x5b26), 0x75 }, + { CCI_REG8(0x5b27), 0x75 }, { CCI_REG8(0x5b28), 0x75 }, + { CCI_REG8(0x5b29), 0x75 }, { CCI_REG8(0x5b2a), 0x75 }, + { CCI_REG8(0x5b2b), 0x75 }, { CCI_REG8(0x5b2c), 0x75 }, + { CCI_REG8(0x5b2d), 0x75 }, { CCI_REG8(0x5b2e), 0x75 }, + { CCI_REG8(0x5b2f), 0x75 }, { CCI_REG8(0x5b30), 0x75 }, + { CCI_REG8(0x5b31), 0x75 }, { CCI_REG8(0x5b32), 0x75 }, + { CCI_REG8(0x5b33), 0x75 }, { CCI_REG8(0x5b34), 0x75 }, + { CCI_REG8(0x5b35), 0x75 }, { CCI_REG8(0x5b36), 0x75 }, + { CCI_REG8(0x5b37), 0x75 }, { CCI_REG8(0x5b38), 0x75 }, + { CCI_REG8(0x5b39), 0x75 }, { CCI_REG8(0x5b3a), 0x75 }, + { CCI_REG8(0x5b3b), 0x75 }, { CCI_REG8(0x5b3c), 0x75 }, + { CCI_REG8(0x5b3d), 0x75 }, { CCI_REG8(0x5b3e), 0x75 }, + { CCI_REG8(0x5b3f), 0x75 }, { CCI_REG8(0x5b40), 0x75 }, + { CCI_REG8(0x5b41), 0x75 }, { CCI_REG8(0x5b42), 0x75 }, + { CCI_REG8(0x5b43), 0x75 }, { CCI_REG8(0x5b44), 0x75 }, + { CCI_REG8(0x5b45), 0x75 }, { CCI_REG8(0x5b46), 0x75 }, + { CCI_REG8(0x5b47), 0x75 }, { CCI_REG8(0x5b48), 0x75 }, + { CCI_REG8(0x5b49), 0x75 }, { CCI_REG8(0x5b4a), 0x75 }, + { CCI_REG8(0x5b4b), 0x75 }, { CCI_REG8(0x5b4c), 0x75 }, + { CCI_REG8(0x5b4d), 0x75 }, { CCI_REG8(0x5b4e), 0x75 }, + { CCI_REG8(0x5b4f), 0x75 }, { CCI_REG8(0x5b50), 0x75 }, + { CCI_REG8(0x5b51), 0x75 }, { CCI_REG8(0x5b52), 0x75 }, + { CCI_REG8(0x5b53), 0x75 }, { CCI_REG8(0x5b54), 0x75 }, + { CCI_REG8(0x5b55), 0x75 }, { CCI_REG8(0x5b56), 0x75 }, + { CCI_REG8(0x5b57), 0x75 }, { CCI_REG8(0x5b58), 0x75 }, + { CCI_REG8(0x5b59), 0x75 }, { CCI_REG8(0x5b5a), 0x75 }, + { CCI_REG8(0x5b5b), 0x75 }, { CCI_REG8(0x5b5c), 0x75 }, + { CCI_REG8(0x5b5d), 0x75 }, { CCI_REG8(0x5b5e), 0x75 }, + { CCI_REG8(0x5b5f), 0x75 }, { CCI_REG8(0x5b80), 0x75 }, + { CCI_REG8(0x5b81), 0x75 }, { CCI_REG8(0x5b82), 0x75 }, + { CCI_REG8(0x5b83), 0x75 }, { CCI_REG8(0x5b84), 0x75 }, + { CCI_REG8(0x5b85), 0x75 }, { CCI_REG8(0x5b86), 0x75 }, + { CCI_REG8(0x5b87), 0x75 }, { CCI_REG8(0x5b88), 0x75 }, + { CCI_REG8(0x5b89), 0x75 }, { CCI_REG8(0x5b8a), 0x75 }, + { CCI_REG8(0x5b8b), 0x75 }, { CCI_REG8(0x5b8c), 0x75 }, + { CCI_REG8(0x5b8d), 0x75 }, { CCI_REG8(0x5b8e), 0x75 }, + { CCI_REG8(0x5b8f), 0x75 }, { CCI_REG8(0x5b90), 0x75 }, + { CCI_REG8(0x5b91), 0x75 }, { CCI_REG8(0x5b92), 0x75 }, + { CCI_REG8(0x5b93), 0x75 }, { CCI_REG8(0x5b94), 0x75 }, + { CCI_REG8(0x5b95), 0x75 }, { CCI_REG8(0x5b96), 0x75 }, + { CCI_REG8(0x5b97), 0x75 }, { CCI_REG8(0x5b98), 0x75 }, + { CCI_REG8(0x5b99), 0x75 }, { CCI_REG8(0x5b9a), 0x75 }, + { CCI_REG8(0x5b9b), 0x75 }, { CCI_REG8(0x5b9c), 0x75 }, + { CCI_REG8(0x5b9d), 0x75 }, { CCI_REG8(0x5b9e), 0x75 }, + { CCI_REG8(0x5b9f), 0x75 }, { CCI_REG8(0x5ba0), 0x75 }, + { CCI_REG8(0x5ba1), 0x75 }, { CCI_REG8(0x5ba2), 0x75 }, + { CCI_REG8(0x5ba3), 0x75 }, { CCI_REG8(0x5ba4), 0x75 }, + { CCI_REG8(0x5ba5), 0x75 }, { CCI_REG8(0x5ba6), 0x75 }, + { CCI_REG8(0x5ba7), 0x75 }, { CCI_REG8(0x5ba8), 0x75 }, + { CCI_REG8(0x5ba9), 0x75 }, { CCI_REG8(0x5baa), 0x75 }, + { CCI_REG8(0x5bab), 0x75 }, { CCI_REG8(0x5bac), 0x75 }, + { CCI_REG8(0x5bad), 0x75 }, { CCI_REG8(0x5bae), 0x75 }, + { CCI_REG8(0x5baf), 0x75 }, { CCI_REG8(0x5bb0), 0x75 }, + { CCI_REG8(0x5bb1), 0x75 }, { CCI_REG8(0x5bb2), 0x75 }, + { CCI_REG8(0x5bb3), 0x75 }, { CCI_REG8(0x5bb4), 0x75 }, + { CCI_REG8(0x5bb5), 0x75 }, { CCI_REG8(0x5bb6), 0x75 }, + { CCI_REG8(0x5bb7), 0x75 }, { CCI_REG8(0x5bb8), 0x75 }, + { CCI_REG8(0x5bb9), 0x75 }, { CCI_REG8(0x5bba), 0x75 }, + { CCI_REG8(0x5bbb), 0x75 }, { CCI_REG8(0x5bbc), 0x75 }, + { CCI_REG8(0x5bbd), 0x75 }, { CCI_REG8(0x5bbe), 0x75 }, + { CCI_REG8(0x5bbf), 0x75 }, { CCI_REG8(0x5bc0), 0x75 }, + { CCI_REG8(0x5bc1), 0x75 }, { CCI_REG8(0x5bc2), 0x75 }, + { CCI_REG8(0x5bc3), 0x75 }, { CCI_REG8(0x5bc4), 0x75 }, + { CCI_REG8(0x5bc5), 0x75 }, { CCI_REG8(0x5bc6), 0x75 }, + { CCI_REG8(0x5bc7), 0x75 }, { CCI_REG8(0x5bc8), 0x75 }, + { CCI_REG8(0x5bc9), 0x75 }, { CCI_REG8(0x5bca), 0x75 }, + { CCI_REG8(0x5bcb), 0x75 }, { CCI_REG8(0x5bcc), 0x75 }, + { CCI_REG8(0x5bcd), 0x75 }, { CCI_REG8(0x5bce), 0x75 }, + { CCI_REG8(0x5bcf), 0x75 }, { CCI_REG8(0x5bd0), 0x75 }, + { CCI_REG8(0x5bd1), 0x75 }, { CCI_REG8(0x5bd2), 0x75 }, + { CCI_REG8(0x5bd3), 0x75 }, { CCI_REG8(0x5bd4), 0x75 }, + { CCI_REG8(0x5bd5), 0x75 }, { CCI_REG8(0x5bd6), 0x75 }, + { CCI_REG8(0x5bd7), 0x75 }, { CCI_REG8(0x5bd8), 0x75 }, + { CCI_REG8(0x5bd9), 0x75 }, { CCI_REG8(0x5bda), 0x75 }, + { CCI_REG8(0x5bdb), 0x75 }, { CCI_REG8(0x5bdc), 0x75 }, + { CCI_REG8(0x5bdd), 0x75 }, { CCI_REG8(0x5bde), 0x75 }, + { CCI_REG8(0x5bdf), 0x75 }, { CCI_REG8(0x5be0), 0x75 }, + { CCI_REG8(0x5be1), 0x75 }, { CCI_REG8(0x5be2), 0x75 }, + { CCI_REG8(0x5be3), 0x75 }, { CCI_REG8(0x5be4), 0x75 }, + { CCI_REG8(0x5be5), 0x75 }, { CCI_REG8(0x5be6), 0x75 }, + { CCI_REG8(0x5be7), 0x75 }, { CCI_REG8(0x5be8), 0x75 }, + { CCI_REG8(0x5be9), 0x75 }, { CCI_REG8(0x5bea), 0x75 }, + { CCI_REG8(0x5beb), 0x75 }, { CCI_REG8(0x5bec), 0x75 }, + { CCI_REG8(0x5bed), 0x75 }, { CCI_REG8(0x5bee), 0x75 }, + { CCI_REG8(0x5bef), 0x75 }, { CCI_REG8(0x5bf0), 0x75 }, + { CCI_REG8(0x5bf1), 0x75 }, { CCI_REG8(0x5bf2), 0x75 }, + { CCI_REG8(0x5bf3), 0x75 }, { CCI_REG8(0x5bf4), 0x75 }, + { CCI_REG8(0x5bf5), 0x75 }, { CCI_REG8(0x5bf6), 0x75 }, + { CCI_REG8(0x5bf7), 0x75 }, { CCI_REG8(0x5bf8), 0x75 }, + { CCI_REG8(0x5bf9), 0x75 }, { CCI_REG8(0x5bfa), 0x75 }, + { CCI_REG8(0x5bfb), 0x75 }, { CCI_REG8(0x5bfc), 0x75 }, + { CCI_REG8(0x5bfd), 0x75 }, { CCI_REG8(0x5bfe), 0x75 }, + { CCI_REG8(0x5bff), 0x75 }, { CCI_REG8(0x5c00), 0x75 }, + { CCI_REG8(0x5c01), 0x75 }, { CCI_REG8(0x5c02), 0x75 }, + { CCI_REG8(0x5c03), 0x75 }, { CCI_REG8(0x5c04), 0x75 }, + { CCI_REG8(0x5c05), 0x75 }, { CCI_REG8(0x5c06), 0x75 }, + { CCI_REG8(0x5c07), 0x75 }, { CCI_REG8(0x5c08), 0x75 }, + { CCI_REG8(0x5c09), 0x75 }, { CCI_REG8(0x5c0a), 0x75 }, + { CCI_REG8(0x5c0b), 0x75 }, { CCI_REG8(0x5c0c), 0x75 }, + { CCI_REG8(0x5c0d), 0x75 }, { CCI_REG8(0x5c0e), 0x75 }, + { CCI_REG8(0x5c0f), 0x75 }, { CCI_REG8(0x5c10), 0x75 }, + { CCI_REG8(0x5c11), 0x75 }, { CCI_REG8(0x5c12), 0x75 }, + { CCI_REG8(0x5c13), 0x75 }, { CCI_REG8(0x5c14), 0x75 }, + { CCI_REG8(0x5c15), 0x75 }, { CCI_REG8(0x5c16), 0x75 }, + { CCI_REG8(0x5c17), 0x75 }, { CCI_REG8(0x5c18), 0x75 }, + { CCI_REG8(0x5c19), 0x75 }, { CCI_REG8(0x5c1a), 0x75 }, + { CCI_REG8(0x5c1b), 0x75 }, { CCI_REG8(0x5c1c), 0x75 }, + { CCI_REG8(0x5c1d), 0x75 }, { CCI_REG8(0x5c1e), 0x75 }, + { CCI_REG8(0x5c1f), 0x75 }, { CCI_REG8(0x5c20), 0x75 }, + { CCI_REG8(0x5c21), 0x75 }, { CCI_REG8(0x5c22), 0x75 }, + { CCI_REG8(0x5c23), 0x75 }, { CCI_REG8(0x5c24), 0x75 }, + { CCI_REG8(0x5c25), 0x75 }, { CCI_REG8(0x5c26), 0x75 }, + { CCI_REG8(0x5c27), 0x75 }, { CCI_REG8(0x5c28), 0x75 }, + { CCI_REG8(0x5c29), 0x75 }, { CCI_REG8(0x5c2a), 0x75 }, + { CCI_REG8(0x5c2b), 0x75 }, { CCI_REG8(0x5c2c), 0x75 }, + { CCI_REG8(0x5c2d), 0x75 }, { CCI_REG8(0x5c2e), 0x75 }, + { CCI_REG8(0x5c2f), 0x75 }, { CCI_REG8(0x5c30), 0x75 }, + { CCI_REG8(0x5c31), 0x75 }, { CCI_REG8(0x5c32), 0x75 }, + { CCI_REG8(0x5c33), 0x75 }, { CCI_REG8(0x5c34), 0x75 }, + { CCI_REG8(0x5c35), 0x75 }, { CCI_REG8(0x5c36), 0x75 }, + { CCI_REG8(0x5c37), 0x75 }, { CCI_REG8(0x5c38), 0x75 }, + { CCI_REG8(0x5c39), 0x75 }, { CCI_REG8(0x5c3a), 0x75 }, + { CCI_REG8(0x5c3b), 0x75 }, { CCI_REG8(0x5c3c), 0x75 }, + { CCI_REG8(0x5c3d), 0x75 }, { CCI_REG8(0x5c3e), 0x75 }, + { CCI_REG8(0x5c3f), 0x75 }, { CCI_REG8(0x5c40), 0x75 }, + { CCI_REG8(0x5c41), 0x75 }, { CCI_REG8(0x5c42), 0x75 }, + { CCI_REG8(0x5c43), 0x75 }, { CCI_REG8(0x5c44), 0x75 }, + { CCI_REG8(0x5c45), 0x75 }, { CCI_REG8(0x5c46), 0x75 }, + { CCI_REG8(0x5c47), 0x75 }, { CCI_REG8(0x5c48), 0x75 }, + { CCI_REG8(0x5c49), 0x75 }, { CCI_REG8(0x5c4a), 0x75 }, + { CCI_REG8(0x5c4b), 0x75 }, { CCI_REG8(0x5c4c), 0x75 }, + { CCI_REG8(0x5c4d), 0x75 }, { CCI_REG8(0x5c4e), 0x75 }, + { CCI_REG8(0x5c4f), 0x75 }, { CCI_REG8(0x5c50), 0x75 }, + { CCI_REG8(0x5c51), 0x75 }, { CCI_REG8(0x5c52), 0x75 }, + { CCI_REG8(0x5c53), 0x75 }, { CCI_REG8(0x5c54), 0x75 }, + { CCI_REG8(0x5c55), 0x75 }, { CCI_REG8(0x5c56), 0x75 }, + { CCI_REG8(0x5c57), 0x75 }, { CCI_REG8(0x5c58), 0x75 }, + { CCI_REG8(0x5c59), 0x75 }, { CCI_REG8(0x5c5a), 0x75 }, + { CCI_REG8(0x5c5b), 0x75 }, { CCI_REG8(0x5c5c), 0x75 }, + { CCI_REG8(0x5c5d), 0x75 }, { CCI_REG8(0x5c5e), 0x75 }, + { CCI_REG8(0x5c5f), 0x75 }, { CCI_REG8(0x5c60), 0x75 }, + { CCI_REG8(0x5c61), 0x75 }, { CCI_REG8(0x5c62), 0x75 }, + { CCI_REG8(0x5c63), 0x75 }, { CCI_REG8(0x5c64), 0x75 }, + { CCI_REG8(0x5c65), 0x75 }, { CCI_REG8(0x5c66), 0x75 }, + { CCI_REG8(0x5c67), 0x75 }, { CCI_REG8(0x5c68), 0x75 }, + { CCI_REG8(0x5c69), 0x75 }, { CCI_REG8(0x5c6a), 0x75 }, + { CCI_REG8(0x5c6b), 0x75 }, { CCI_REG8(0x5c6c), 0x75 }, + { CCI_REG8(0x5c6d), 0x75 }, { CCI_REG8(0x5c6e), 0x75 }, + { CCI_REG8(0x5c6f), 0x75 }, { CCI_REG8(0x5c70), 0x75 }, + { CCI_REG8(0x5c71), 0x75 }, { CCI_REG8(0x5c72), 0x75 }, + { CCI_REG8(0x5c73), 0x75 }, { CCI_REG8(0x5c74), 0x75 }, + { CCI_REG8(0x5c75), 0x75 }, { CCI_REG8(0x5c76), 0x75 }, + { CCI_REG8(0x5c77), 0x75 }, { CCI_REG8(0x5c78), 0x75 }, + { CCI_REG8(0x5c79), 0x75 }, { CCI_REG8(0x5c7a), 0x75 }, + { CCI_REG8(0x5c7b), 0x75 }, { CCI_REG8(0x5c7c), 0x75 }, + { CCI_REG8(0x5c7d), 0x75 }, { CCI_REG8(0x5c7e), 0x75 }, + { CCI_REG8(0x5c7f), 0x75 }, { CCI_REG8(0x5c80), 0x75 }, + { CCI_REG8(0x5c81), 0x75 }, { CCI_REG8(0x5c82), 0x75 }, + { CCI_REG8(0x5c83), 0x75 }, { CCI_REG8(0x5c84), 0x75 }, + { CCI_REG8(0x5c85), 0x75 }, { CCI_REG8(0x5c86), 0x75 }, + { CCI_REG8(0x5c87), 0x75 }, { CCI_REG8(0x5c88), 0x75 }, + { CCI_REG8(0x5c89), 0x75 }, { CCI_REG8(0x5c8a), 0x75 }, + { CCI_REG8(0x5c8b), 0x75 }, { CCI_REG8(0x5c8c), 0x75 }, + { CCI_REG8(0x5c8d), 0x75 }, { CCI_REG8(0x5c8e), 0x75 }, + { CCI_REG8(0x5c8f), 0x75 }, { CCI_REG8(0x5c90), 0x75 }, + { CCI_REG8(0x5c91), 0x75 }, { CCI_REG8(0x5c92), 0x75 }, + { CCI_REG8(0x5c93), 0x75 }, { CCI_REG8(0x5c94), 0x75 }, + { CCI_REG8(0x5c95), 0x75 }, { CCI_REG8(0x5c96), 0x75 }, + { CCI_REG8(0x5c97), 0x75 }, { CCI_REG8(0x5c98), 0x75 }, + { CCI_REG8(0x5c99), 0x75 }, { CCI_REG8(0x5c9a), 0x75 }, + { CCI_REG8(0x5c9b), 0x75 }, { CCI_REG8(0x5c9c), 0x75 }, + { CCI_REG8(0x5c9d), 0x75 }, { CCI_REG8(0x5c9e), 0x75 }, + { CCI_REG8(0x5c9f), 0x75 }, { CCI_REG8(0x5ca0), 0x75 }, + { CCI_REG8(0x5ca1), 0x75 }, { CCI_REG8(0x5ca2), 0x75 }, + { CCI_REG8(0x5ca3), 0x75 }, { CCI_REG8(0x5ca4), 0x75 }, + { CCI_REG8(0x5ca5), 0x75 }, { CCI_REG8(0x5ca6), 0x75 }, + { CCI_REG8(0x5ca7), 0x75 }, { CCI_REG8(0x5ca8), 0x75 }, + { CCI_REG8(0x5ca9), 0x75 }, { CCI_REG8(0x5caa), 0x75 }, + { CCI_REG8(0x5cab), 0x75 }, { CCI_REG8(0x5cac), 0x75 }, + { CCI_REG8(0x5cad), 0x75 }, { CCI_REG8(0x5cae), 0x75 }, + { CCI_REG8(0x5caf), 0x75 }, { CCI_REG8(0x5cb0), 0x75 }, + { CCI_REG8(0x5cb1), 0x75 }, { CCI_REG8(0x5cb2), 0x75 }, + { CCI_REG8(0x5cb3), 0x75 }, { CCI_REG8(0x5cb4), 0x75 }, + { CCI_REG8(0x5cb5), 0x75 }, { CCI_REG8(0x5cb6), 0x75 }, + { CCI_REG8(0x5cb7), 0x75 }, { CCI_REG8(0x5cb8), 0x75 }, + { CCI_REG8(0x5cb9), 0x75 }, { CCI_REG8(0x5cba), 0x75 }, + { CCI_REG8(0x5cbb), 0x75 }, { CCI_REG8(0x5cbc), 0x75 }, + { CCI_REG8(0x5cbd), 0x75 }, { CCI_REG8(0x5cbe), 0x75 }, + { CCI_REG8(0x5cbf), 0x75 }, { CCI_REG8(0x5cc0), 0x75 }, + { CCI_REG8(0x5cc1), 0x75 }, { CCI_REG8(0x5cc2), 0x75 }, + { CCI_REG8(0x5cc3), 0x75 }, { CCI_REG8(0x5cc4), 0x75 }, + { CCI_REG8(0x5cc5), 0x75 }, { CCI_REG8(0x5cc6), 0x75 }, + { CCI_REG8(0x5cc7), 0x75 }, { CCI_REG8(0x5cc8), 0x75 }, + { CCI_REG8(0x5cc9), 0x75 }, { CCI_REG8(0x5cca), 0x75 }, + { CCI_REG8(0x5ccb), 0x75 }, { CCI_REG8(0x5ccc), 0x75 }, + { CCI_REG8(0x5ccd), 0x75 }, { CCI_REG8(0x5cce), 0x75 }, + { CCI_REG8(0x5ccf), 0x75 }, { CCI_REG8(0x5cd0), 0x75 }, + { CCI_REG8(0x5cd1), 0x75 }, { CCI_REG8(0x5cd2), 0x75 }, + { CCI_REG8(0x5cd3), 0x75 }, { CCI_REG8(0x5cd4), 0x75 }, + { CCI_REG8(0x5cd5), 0x75 }, { CCI_REG8(0x5cd6), 0x75 }, + { CCI_REG8(0x5cd7), 0x75 }, { CCI_REG8(0x5cd8), 0x75 }, + { CCI_REG8(0x5cd9), 0x75 }, { CCI_REG8(0x5cda), 0x75 }, + { CCI_REG8(0x5cdb), 0x75 }, { CCI_REG8(0x5cdc), 0x75 }, + { CCI_REG8(0x5cdd), 0x75 }, { CCI_REG8(0x5cde), 0x75 }, + { CCI_REG8(0x5cdf), 0x75 }, { CCI_REG8(0x5ce0), 0x75 }, + { CCI_REG8(0x5ce1), 0x75 }, { CCI_REG8(0x5ce2), 0x75 }, + { CCI_REG8(0x5ce3), 0x75 }, { CCI_REG8(0x5ce4), 0x75 }, + { CCI_REG8(0x5ce5), 0x75 }, { CCI_REG8(0x5ce6), 0x75 }, + { CCI_REG8(0x5ce7), 0x75 }, { CCI_REG8(0x5ce8), 0x75 }, + { CCI_REG8(0x5ce9), 0x75 }, { CCI_REG8(0x5cea), 0x75 }, + { CCI_REG8(0x5ceb), 0x75 }, { CCI_REG8(0x5cec), 0x75 }, + { CCI_REG8(0x5ced), 0x75 }, { CCI_REG8(0x5cee), 0x75 }, + { CCI_REG8(0x5cef), 0x75 }, { CCI_REG8(0x5cf0), 0x75 }, + { CCI_REG8(0x5cf1), 0x75 }, { CCI_REG8(0x5cf2), 0x75 }, + { CCI_REG8(0x5cf3), 0x75 }, { CCI_REG8(0x5cf4), 0x75 }, + { CCI_REG8(0x5cf5), 0x75 }, { CCI_REG8(0x5cf6), 0x75 }, + { CCI_REG8(0x5cf7), 0x75 }, { CCI_REG8(0x5cf8), 0x75 }, + { CCI_REG8(0x5cf9), 0x75 }, { CCI_REG8(0x5cfa), 0x75 }, + { CCI_REG8(0x5cfb), 0x75 }, { CCI_REG8(0x5cfc), 0x75 }, + { CCI_REG8(0x5cfd), 0x75 }, { CCI_REG8(0x5cfe), 0x75 }, + { CCI_REG8(0x5cff), 0x75 }, { CCI_REG8(0x5d00), 0x75 }, + { CCI_REG8(0x5d01), 0x75 }, { CCI_REG8(0x5d02), 0x75 }, + { CCI_REG8(0x5d03), 0x75 }, { CCI_REG8(0x5d04), 0x75 }, + { CCI_REG8(0x5d05), 0x75 }, { CCI_REG8(0x5d06), 0x75 }, + { CCI_REG8(0x5d07), 0x75 }, { CCI_REG8(0x5d08), 0x75 }, + { CCI_REG8(0x5d09), 0x75 }, { CCI_REG8(0x5d0a), 0x75 }, + { CCI_REG8(0x5d0b), 0x75 }, { CCI_REG8(0x5d0c), 0x75 }, + { CCI_REG8(0x5d0d), 0x75 }, { CCI_REG8(0x5d0e), 0x75 }, + { CCI_REG8(0x5d0f), 0x75 }, { CCI_REG8(0x5d10), 0x75 }, + { CCI_REG8(0x5d11), 0x75 }, { CCI_REG8(0x5d12), 0x75 }, + { CCI_REG8(0x5d13), 0x75 }, { CCI_REG8(0x5d14), 0x75 }, + { CCI_REG8(0x5d15), 0x75 }, { CCI_REG8(0x5d16), 0x75 }, + { CCI_REG8(0x5d17), 0x75 }, { CCI_REG8(0x5d18), 0x75 }, + { CCI_REG8(0x5d19), 0x75 }, { CCI_REG8(0x5d1a), 0x75 }, + { CCI_REG8(0x5d1b), 0x75 }, { CCI_REG8(0x5d1c), 0x75 }, + { CCI_REG8(0x5d1d), 0x75 }, { CCI_REG8(0x5d1e), 0x75 }, + { CCI_REG8(0x5d1f), 0x75 }, { CCI_REG8(0x5d20), 0x75 }, + { CCI_REG8(0x5d21), 0x75 }, { CCI_REG8(0x5d22), 0x75 }, + { CCI_REG8(0x5d23), 0x75 }, { CCI_REG8(0x5d24), 0x75 }, + { CCI_REG8(0x5d25), 0x75 }, { CCI_REG8(0x5d26), 0x75 }, + { CCI_REG8(0x5d27), 0x75 }, { CCI_REG8(0x5d28), 0x75 }, + { CCI_REG8(0x5d29), 0x75 }, { CCI_REG8(0x5d2a), 0x75 }, + { CCI_REG8(0x5d2b), 0x75 }, { CCI_REG8(0x5d2c), 0x75 }, + { CCI_REG8(0x5d2d), 0x75 }, { CCI_REG8(0x5d2e), 0x75 }, + { CCI_REG8(0x5d2f), 0x75 }, { CCI_REG8(0x5d30), 0x75 }, + { CCI_REG8(0x5d31), 0x75 }, { CCI_REG8(0x5d32), 0x75 }, + { CCI_REG8(0x5d33), 0x75 }, { CCI_REG8(0x5d34), 0x75 }, + { CCI_REG8(0x5d35), 0x75 }, { CCI_REG8(0x5d36), 0x75 }, + { CCI_REG8(0x5d37), 0x75 }, { CCI_REG8(0x5d38), 0x75 }, + { CCI_REG8(0x5d39), 0x75 }, { CCI_REG8(0x5d3a), 0x75 }, + { CCI_REG8(0x5d3b), 0x75 }, { CCI_REG8(0x5d3c), 0x75 }, + { CCI_REG8(0x5d3d), 0x75 }, { CCI_REG8(0x5d3e), 0x75 }, + { CCI_REG8(0x5d3f), 0x75 }, { CCI_REG8(0x5d40), 0x75 }, + { CCI_REG8(0x5d41), 0x75 }, { CCI_REG8(0x5d42), 0x75 }, + { CCI_REG8(0x5d43), 0x75 }, { CCI_REG8(0x5d44), 0x75 }, + { CCI_REG8(0x5d45), 0x75 }, { CCI_REG8(0x5d46), 0x75 }, + { CCI_REG8(0x5d47), 0x75 }, { CCI_REG8(0x5d48), 0x75 }, + { CCI_REG8(0x5d49), 0x75 }, { CCI_REG8(0x5d4a), 0x75 }, + { CCI_REG8(0x5d4b), 0x75 }, { CCI_REG8(0x5d4c), 0x75 }, + { CCI_REG8(0x5d4d), 0x75 }, { CCI_REG8(0x5d4e), 0x75 }, + { CCI_REG8(0x5d4f), 0x75 }, { CCI_REG8(0x5d50), 0x75 }, + { CCI_REG8(0x5d51), 0x75 }, { CCI_REG8(0x5d52), 0x75 }, + { CCI_REG8(0x5d53), 0x75 }, { CCI_REG8(0x5d54), 0x75 }, + { CCI_REG8(0x5d55), 0x75 }, { CCI_REG8(0x5d56), 0x75 }, + { CCI_REG8(0x5d57), 0x75 }, { CCI_REG8(0x5d58), 0x75 }, + { CCI_REG8(0x5d59), 0x75 }, { CCI_REG8(0x5d5a), 0x75 }, + { CCI_REG8(0x5d5b), 0x75 }, { CCI_REG8(0x5d5c), 0x75 }, + { CCI_REG8(0x5d5d), 0x75 }, { CCI_REG8(0x5d5e), 0x75 }, + { CCI_REG8(0x5d5f), 0x75 }, { CCI_REG8(0x5d60), 0x75 }, + { CCI_REG8(0x5d61), 0x75 }, { CCI_REG8(0x5d62), 0x75 }, + { CCI_REG8(0x5d63), 0x75 }, { CCI_REG8(0x5d64), 0x75 }, + { CCI_REG8(0x5d65), 0x75 }, { CCI_REG8(0x5d66), 0x75 }, + { CCI_REG8(0x5d67), 0x75 }, { CCI_REG8(0x5d68), 0x75 }, + { CCI_REG8(0x5d69), 0x75 }, { CCI_REG8(0x5d6a), 0x75 }, + { CCI_REG8(0x5d6b), 0x75 }, { CCI_REG8(0x5d6c), 0x75 }, + { CCI_REG8(0x5d6d), 0x75 }, { CCI_REG8(0x5d6e), 0x75 }, + { CCI_REG8(0x5d6f), 0x75 }, { CCI_REG8(0x5d70), 0x75 }, + { CCI_REG8(0x5d71), 0x75 }, { CCI_REG8(0x5d72), 0x75 }, + { CCI_REG8(0x5d73), 0x75 }, { CCI_REG8(0x5d74), 0x75 }, + { CCI_REG8(0x5d75), 0x75 }, { CCI_REG8(0x5d76), 0x75 }, + { CCI_REG8(0x5d77), 0x75 }, { CCI_REG8(0x5d78), 0x75 }, + { CCI_REG8(0x5d79), 0x75 }, { CCI_REG8(0x5d7a), 0x75 }, + { CCI_REG8(0x5d7b), 0x75 }, { CCI_REG8(0x5d7c), 0x75 }, + { CCI_REG8(0x5d7d), 0x75 }, { CCI_REG8(0x5d7e), 0x75 }, + { CCI_REG8(0x5d7f), 0x75 }, { CCI_REG8(0x5d80), 0x75 }, + { CCI_REG8(0x5d81), 0x75 }, { CCI_REG8(0x5d82), 0x75 }, + { CCI_REG8(0x5d83), 0x75 }, { CCI_REG8(0x5d84), 0x75 }, + { CCI_REG8(0x5d85), 0x75 }, { CCI_REG8(0x5d86), 0x75 }, + { CCI_REG8(0x5d87), 0x75 }, { CCI_REG8(0x5d88), 0x75 }, + { CCI_REG8(0x5d89), 0x75 }, { CCI_REG8(0x5d8a), 0x75 }, + { CCI_REG8(0x5d8b), 0x75 }, { CCI_REG8(0x5d8c), 0x75 }, + { CCI_REG8(0x5d8d), 0x75 }, { CCI_REG8(0x5d8e), 0x75 }, + { CCI_REG8(0x5d8f), 0x75 }, { CCI_REG8(0x5d90), 0x75 }, + { CCI_REG8(0x5d91), 0x75 }, { CCI_REG8(0x5d92), 0x75 }, + { CCI_REG8(0x5d93), 0x75 }, { CCI_REG8(0x5d94), 0x75 }, + { CCI_REG8(0x5d95), 0x75 }, { CCI_REG8(0x5d96), 0x75 }, + { CCI_REG8(0x5d97), 0x75 }, { CCI_REG8(0x5d98), 0x75 }, + { CCI_REG8(0x5d99), 0x75 }, { CCI_REG8(0x5d9a), 0x75 }, + { CCI_REG8(0x5d9b), 0x75 }, { CCI_REG8(0x5d9c), 0x75 }, + { CCI_REG8(0x5d9d), 0x75 }, { CCI_REG8(0x5d9e), 0x75 }, + { CCI_REG8(0x5d9f), 0x75 }, { CCI_REG8(0x5da0), 0x75 }, + { CCI_REG8(0x5da1), 0x75 }, { CCI_REG8(0x5da2), 0x75 }, + { CCI_REG8(0x5da3), 0x75 }, { CCI_REG8(0x5da4), 0x75 }, + { CCI_REG8(0x5da5), 0x75 }, { CCI_REG8(0x5da6), 0x75 }, + { CCI_REG8(0x5da7), 0x75 }, { CCI_REG8(0x5da8), 0x75 }, + { CCI_REG8(0x5da9), 0x75 }, { CCI_REG8(0x5daa), 0x75 }, + { CCI_REG8(0x5dab), 0x75 }, { CCI_REG8(0x5dac), 0x75 }, + { CCI_REG8(0x5dad), 0x75 }, { CCI_REG8(0x5dae), 0x75 }, + { CCI_REG8(0x5daf), 0x75 }, { CCI_REG8(0x5db0), 0x75 }, + { CCI_REG8(0x5db1), 0x75 }, { CCI_REG8(0x5db2), 0x75 }, + { CCI_REG8(0x5db3), 0x75 }, { CCI_REG8(0x5db4), 0x75 }, + { CCI_REG8(0x5db5), 0x75 }, { CCI_REG8(0x5db6), 0x75 }, + { CCI_REG8(0x5db7), 0x75 }, { CCI_REG8(0x5db8), 0x75 }, + { CCI_REG8(0x5db9), 0x75 }, { CCI_REG8(0x5dba), 0x75 }, + { CCI_REG8(0x5dbb), 0x75 }, { CCI_REG8(0x5dbc), 0x75 }, + { CCI_REG8(0x5dbd), 0x75 }, { CCI_REG8(0x5dbe), 0x75 }, + { CCI_REG8(0x5dbf), 0x75 }, { CCI_REG8(0x5dc0), 0x75 }, + { CCI_REG8(0x5dc1), 0x75 }, { CCI_REG8(0x5dc2), 0x75 }, + { CCI_REG8(0x5dc3), 0x75 }, { CCI_REG8(0x5dc4), 0x75 }, + { CCI_REG8(0x5dc5), 0x75 }, { CCI_REG8(0x5dc6), 0x75 }, + { CCI_REG8(0x5dc7), 0x75 }, { CCI_REG8(0x5dc8), 0x75 }, + { CCI_REG8(0x5dc9), 0x75 }, { CCI_REG8(0x5dca), 0x75 }, + { CCI_REG8(0x5dcb), 0x75 }, { CCI_REG8(0x5dcc), 0x75 }, + { CCI_REG8(0x5dcd), 0x75 }, { CCI_REG8(0x5dce), 0x75 }, + { CCI_REG8(0x5dcf), 0x75 }, { CCI_REG8(0x5dd0), 0x75 }, + { CCI_REG8(0x5dd1), 0x75 }, { CCI_REG8(0x5dd2), 0x75 }, + { CCI_REG8(0x5dd3), 0x75 }, { CCI_REG8(0x5dd4), 0x75 }, + { CCI_REG8(0x5dd5), 0x75 }, { CCI_REG8(0x5dd6), 0x75 }, + { CCI_REG8(0x5dd7), 0x75 }, { CCI_REG8(0x5dd8), 0x75 }, + { CCI_REG8(0x5dd9), 0x75 }, { CCI_REG8(0x5dda), 0x75 }, + { CCI_REG8(0x5ddb), 0x75 }, { CCI_REG8(0x5ddc), 0x75 }, + { CCI_REG8(0x5ddd), 0x75 }, { CCI_REG8(0x5dde), 0x75 }, + { CCI_REG8(0x5ddf), 0x75 }, { CCI_REG8(0x5de0), 0x75 }, + { CCI_REG8(0x5de1), 0x75 }, { CCI_REG8(0x5de2), 0x75 }, + { CCI_REG8(0x5de3), 0x75 }, { CCI_REG8(0x5de4), 0x75 }, + { CCI_REG8(0x5de5), 0x75 }, { CCI_REG8(0x5de6), 0x75 }, + { CCI_REG8(0x5de7), 0x75 }, { CCI_REG8(0x5de8), 0x75 }, + { CCI_REG8(0x5de9), 0x75 }, { CCI_REG8(0x5dea), 0x75 }, + { CCI_REG8(0x5deb), 0x75 }, { CCI_REG8(0x5dec), 0x75 }, + { CCI_REG8(0x5ded), 0x75 }, { CCI_REG8(0x5dee), 0x75 }, + { CCI_REG8(0x5def), 0x75 }, { CCI_REG8(0x5df0), 0x75 }, + { CCI_REG8(0x5df1), 0x75 }, { CCI_REG8(0x5df2), 0x75 }, + { CCI_REG8(0x5df3), 0x75 }, { CCI_REG8(0x5df4), 0x75 }, + { CCI_REG8(0x5df5), 0x75 }, { CCI_REG8(0x5df6), 0x75 }, + { CCI_REG8(0x5df7), 0x75 }, { CCI_REG8(0x5df8), 0x75 }, + { CCI_REG8(0x5df9), 0x75 }, { CCI_REG8(0x5dfa), 0x75 }, + { CCI_REG8(0x5dfb), 0x75 }, { CCI_REG8(0x5dfc), 0x75 }, + { CCI_REG8(0x5dfd), 0x75 }, { CCI_REG8(0x5dfe), 0x75 }, + { CCI_REG8(0x5dff), 0x75 }, { CCI_REG8(0x5e00), 0x75 }, + { CCI_REG8(0x5e01), 0x75 }, { CCI_REG8(0x5e02), 0x75 }, + { CCI_REG8(0x5e03), 0x75 }, { CCI_REG8(0x5e04), 0x75 }, + { CCI_REG8(0x5e05), 0x75 }, { CCI_REG8(0x5e06), 0x75 }, + { CCI_REG8(0x5e07), 0x75 }, { CCI_REG8(0x5e08), 0x75 }, + { CCI_REG8(0x5e09), 0x75 }, { CCI_REG8(0x5e0a), 0x75 }, + { CCI_REG8(0x5e0b), 0x75 }, { CCI_REG8(0x5e0c), 0x75 }, + { CCI_REG8(0x5e0d), 0x75 }, { CCI_REG8(0x5e0e), 0x75 }, + { CCI_REG8(0x5e0f), 0x75 }, { CCI_REG8(0x5e10), 0x75 }, + { CCI_REG8(0x5e11), 0x75 }, { CCI_REG8(0x5e12), 0x75 }, + { CCI_REG8(0x5e13), 0x75 }, { CCI_REG8(0x5e14), 0x75 }, + { CCI_REG8(0x5e15), 0x75 }, { CCI_REG8(0x5e16), 0x75 }, + { CCI_REG8(0x5e17), 0x75 }, { CCI_REG8(0x5e18), 0x75 }, + { CCI_REG8(0x5e19), 0x75 }, { CCI_REG8(0x5e1a), 0x75 }, + { CCI_REG8(0x5e1b), 0x75 }, { CCI_REG8(0x5e1c), 0x75 }, + { CCI_REG8(0x5e1d), 0x75 }, { CCI_REG8(0x5e1e), 0x75 }, + { CCI_REG8(0x5e1f), 0x75 }, { CCI_REG8(0x5e20), 0x75 }, + { CCI_REG8(0x5e21), 0x75 }, { CCI_REG8(0x5e22), 0x75 }, + { CCI_REG8(0x5e23), 0x75 }, { CCI_REG8(0x5e24), 0x75 }, + { CCI_REG8(0x5e25), 0x75 }, { CCI_REG8(0x5e26), 0x75 }, + { CCI_REG8(0x5e27), 0x75 }, { CCI_REG8(0x5e28), 0x75 }, + { CCI_REG8(0x5e29), 0x75 }, { CCI_REG8(0x5e2a), 0x75 }, + { CCI_REG8(0x5e2b), 0x75 }, { CCI_REG8(0x5e2c), 0x75 }, + { CCI_REG8(0x5e2d), 0x75 }, { CCI_REG8(0x5e2e), 0x75 }, + { CCI_REG8(0x5e2f), 0x75 }, { CCI_REG8(0x5e30), 0x75 }, + { CCI_REG8(0x5e31), 0x75 }, { CCI_REG8(0x5e32), 0x75 }, + { CCI_REG8(0x5e33), 0x75 }, { CCI_REG8(0x5e34), 0x75 }, + { CCI_REG8(0x5e35), 0x75 }, { CCI_REG8(0x5e36), 0x75 }, + { CCI_REG8(0x5e37), 0x75 }, { CCI_REG8(0x5e38), 0x75 }, + { CCI_REG8(0x5e39), 0x75 }, { CCI_REG8(0x5e3a), 0x75 }, + { CCI_REG8(0x5e3b), 0x75 }, { CCI_REG8(0x5e3c), 0x75 }, + { CCI_REG8(0x5e3d), 0x75 }, { CCI_REG8(0x5e3e), 0x75 }, + { CCI_REG8(0x5e3f), 0x75 }, { CCI_REG8(0x5e40), 0x75 }, + { CCI_REG8(0x5e41), 0x75 }, { CCI_REG8(0x5e42), 0x75 }, + { CCI_REG8(0x5e43), 0x75 }, { CCI_REG8(0x5e44), 0x75 }, + { CCI_REG8(0x5e45), 0x75 }, { CCI_REG8(0x5e46), 0x75 }, + { CCI_REG8(0x5e47), 0x75 }, { CCI_REG8(0x5e48), 0x75 }, + { CCI_REG8(0x5e49), 0x75 }, { CCI_REG8(0x5e4a), 0x75 }, + { CCI_REG8(0x5e4b), 0x75 }, { CCI_REG8(0x5e4c), 0x75 }, + { CCI_REG8(0x5e4d), 0x75 }, { CCI_REG8(0x5e4e), 0x75 }, + { CCI_REG8(0x5e4f), 0x75 }, { CCI_REG8(0x5e50), 0x75 }, + { CCI_REG8(0x5e51), 0x75 }, { CCI_REG8(0x5e52), 0x75 }, + { CCI_REG8(0x5e53), 0x75 }, { CCI_REG8(0x5e54), 0x75 }, + { CCI_REG8(0x5e55), 0x75 }, { CCI_REG8(0x5e56), 0x75 }, + { CCI_REG8(0x5e57), 0x75 }, { CCI_REG8(0x5e58), 0x75 }, + { CCI_REG8(0x5e59), 0x75 }, { CCI_REG8(0x5e5a), 0x75 }, + { CCI_REG8(0x5e5b), 0x75 }, { CCI_REG8(0x5e5c), 0x75 }, + { CCI_REG8(0x5e5d), 0x75 }, { CCI_REG8(0x5e5e), 0x75 }, + { CCI_REG8(0x5e5f), 0x75 }, { CCI_REG8(0x5e60), 0x75 }, + { CCI_REG8(0x5e61), 0x75 }, { CCI_REG8(0x5e62), 0x75 }, + { CCI_REG8(0x5e63), 0x75 }, { CCI_REG8(0x5e64), 0x75 }, + { CCI_REG8(0x5e65), 0x75 }, { CCI_REG8(0x5e66), 0x75 }, + { CCI_REG8(0x5e67), 0x75 }, { CCI_REG8(0x5e68), 0x75 }, + { CCI_REG8(0x5e69), 0x75 }, { CCI_REG8(0x5e6a), 0x75 }, + { CCI_REG8(0x5e6b), 0x75 }, { CCI_REG8(0x5e6c), 0x75 }, + { CCI_REG8(0x5e6d), 0x75 }, { CCI_REG8(0x5e6e), 0x75 }, + { CCI_REG8(0x5e6f), 0x75 }, { CCI_REG8(0x5e70), 0x75 }, + { CCI_REG8(0x5e71), 0x75 }, { CCI_REG8(0x5e72), 0x75 }, + { CCI_REG8(0x5e73), 0x75 }, { CCI_REG8(0x5e74), 0x75 }, + { CCI_REG8(0x5e75), 0x75 }, { CCI_REG8(0x5e76), 0x75 }, + { CCI_REG8(0x5e77), 0x75 }, { CCI_REG8(0x5e78), 0x75 }, + { CCI_REG8(0x5e79), 0x75 }, { CCI_REG8(0x5e7a), 0x75 }, + { CCI_REG8(0x5e7b), 0x75 }, { CCI_REG8(0x5e7c), 0x75 }, + { CCI_REG8(0x5e7d), 0x75 }, { CCI_REG8(0x5e7e), 0x75 }, + { CCI_REG8(0x5e7f), 0x75 }, { CCI_REG8(0x5e80), 0x75 }, + { CCI_REG8(0x5e81), 0x75 }, { CCI_REG8(0x5e82), 0x75 }, + { CCI_REG8(0x5e83), 0x75 }, { CCI_REG8(0x5e84), 0x75 }, + { CCI_REG8(0x5e85), 0x75 }, { CCI_REG8(0x5e86), 0x75 }, + { CCI_REG8(0x5e87), 0x75 }, { CCI_REG8(0x5e88), 0x75 }, + { CCI_REG8(0x5e89), 0x75 }, { CCI_REG8(0x5e8a), 0x75 }, + { CCI_REG8(0x5e8b), 0x75 }, { CCI_REG8(0x5e8c), 0x75 }, + { CCI_REG8(0x5e8d), 0x75 }, { CCI_REG8(0x5e8e), 0x75 }, + { CCI_REG8(0x5e8f), 0x75 }, { CCI_REG8(0x5e90), 0x75 }, + { CCI_REG8(0x5e91), 0x75 }, { CCI_REG8(0x5e92), 0x75 }, + { CCI_REG8(0x5e93), 0x75 }, { CCI_REG8(0x5e94), 0x75 }, + { CCI_REG8(0x5e95), 0x75 }, { CCI_REG8(0x5e96), 0x75 }, + { CCI_REG8(0x5e97), 0x75 }, { CCI_REG8(0x5e98), 0x75 }, + { CCI_REG8(0x5e99), 0x75 }, { CCI_REG8(0x5e9a), 0x75 }, + { CCI_REG8(0x5e9b), 0x75 }, { CCI_REG8(0x5e9c), 0x75 }, + { CCI_REG8(0x5e9d), 0x75 }, { CCI_REG8(0x5e9e), 0x75 }, + { CCI_REG8(0x5e9f), 0x75 }, { CCI_REG8(0x5ea0), 0x75 }, + { CCI_REG8(0x5ea1), 0x75 }, { CCI_REG8(0x5ea2), 0x75 }, + { CCI_REG8(0x5ea3), 0x75 }, { CCI_REG8(0x5ea4), 0x75 }, + { CCI_REG8(0x5ea5), 0x75 }, { CCI_REG8(0x5ea6), 0x75 }, + { CCI_REG8(0x5ea7), 0x75 }, { CCI_REG8(0x5ea8), 0x75 }, + { CCI_REG8(0x5ea9), 0x75 }, { CCI_REG8(0x5eaa), 0x75 }, + { CCI_REG8(0x5eab), 0x75 }, { CCI_REG8(0x5eac), 0x75 }, + { CCI_REG8(0x5ead), 0x75 }, { CCI_REG8(0x5eae), 0x75 }, + { CCI_REG8(0x5eaf), 0x75 }, { CCI_REG8(0x5eb0), 0x75 }, + { CCI_REG8(0x5eb1), 0x75 }, { CCI_REG8(0x5eb2), 0x75 }, + { CCI_REG8(0x5eb3), 0x75 }, { CCI_REG8(0x5eb4), 0x75 }, + { CCI_REG8(0x5eb5), 0x75 }, { CCI_REG8(0x5eb6), 0x75 }, + { CCI_REG8(0x5eb7), 0x75 }, { CCI_REG8(0x5eb8), 0x75 }, + { CCI_REG8(0x5eb9), 0x75 }, { CCI_REG8(0x5eba), 0x75 }, + { CCI_REG8(0x5ebb), 0x75 }, { CCI_REG8(0x5ebc), 0x75 }, + { CCI_REG8(0x5ebd), 0x75 }, { CCI_REG8(0x5ebe), 0x75 }, + { CCI_REG8(0x5ebf), 0x75 }, { CCI_REG8(0x5ec0), 0x75 }, + { CCI_REG8(0x5ec1), 0x75 }, { CCI_REG8(0x5ec2), 0x75 }, + { CCI_REG8(0x5ec3), 0x75 }, { CCI_REG8(0x5ec4), 0x75 }, + { CCI_REG8(0x5ec5), 0x75 }, { CCI_REG8(0x5ec6), 0x75 }, + { CCI_REG8(0x5ec7), 0x75 }, { CCI_REG8(0x5ec8), 0x75 }, + { CCI_REG8(0x5ec9), 0x75 }, { CCI_REG8(0x5eca), 0x75 }, + { CCI_REG8(0x5ecb), 0x75 }, { CCI_REG8(0x5ecc), 0x75 }, + { CCI_REG8(0x5ecd), 0x75 }, { CCI_REG8(0x5ece), 0x75 }, + { CCI_REG8(0x5ecf), 0x75 }, { CCI_REG8(0x5ed0), 0x75 }, + { CCI_REG8(0x5ed1), 0x75 }, { CCI_REG8(0x5ed2), 0x75 }, + { CCI_REG8(0x5ed3), 0x75 }, { CCI_REG8(0x5ed4), 0x75 }, + { CCI_REG8(0x5ed5), 0x75 }, { CCI_REG8(0x5ed6), 0x75 }, + { CCI_REG8(0x5ed7), 0x75 }, { CCI_REG8(0x5ed8), 0x75 }, + { CCI_REG8(0x5ed9), 0x75 }, { CCI_REG8(0x5eda), 0x75 }, + { CCI_REG8(0x5edb), 0x75 }, { CCI_REG8(0x5edc), 0x75 }, + { CCI_REG8(0x5edd), 0x75 }, { CCI_REG8(0x5ede), 0x75 }, + { CCI_REG8(0x5edf), 0x75 }, { CCI_REG8(0xfff9), 0x08 }, + { CCI_REG8(0x1570), 0x00 }, { CCI_REG8(0x15d0), 0x00 }, + { CCI_REG8(0x15a0), 0x02 }, { CCI_REG8(0x15a1), 0x00 }, + { CCI_REG8(0x15a2), 0x02 }, { CCI_REG8(0x15a3), 0x76 }, + { CCI_REG8(0x15a4), 0x03 }, { CCI_REG8(0x15a5), 0x08 }, + { CCI_REG8(0x15a6), 0x00 }, { CCI_REG8(0x15a7), 0x60 }, + { CCI_REG8(0x15a8), 0x01 }, { CCI_REG8(0x15a9), 0x00 }, + { CCI_REG8(0x15aa), 0x02 }, { CCI_REG8(0x15ab), 0x00 }, + { CCI_REG8(0x1600), 0x02 }, { CCI_REG8(0x1601), 0x00 }, + { CCI_REG8(0x1602), 0x02 }, { CCI_REG8(0x1603), 0x76 }, + { CCI_REG8(0x1604), 0x03 }, { CCI_REG8(0x1605), 0x08 }, + { CCI_REG8(0x1606), 0x00 }, { CCI_REG8(0x1607), 0x60 }, + { CCI_REG8(0x1608), 0x01 }, { CCI_REG8(0x1609), 0x00 }, + { CCI_REG8(0x160a), 0x02 }, { CCI_REG8(0x160b), 0x00 }, + { CCI_REG8(0x1633), 0x03 }, { CCI_REG8(0x1634), 0x01 }, + { CCI_REG8(0x163c), 0x3a }, { CCI_REG8(0x163d), 0x01 }, + { CCI_REG8(0x1648), 0x32 }, { CCI_REG8(0x1658), 0x01 }, + { CCI_REG8(0x1659), 0x01 }, { CCI_REG8(0x165f), 0x01 }, + { CCI_REG8(0x1677), 0x01 }, { CCI_REG8(0x1690), 0x08 }, + { CCI_REG8(0x1691), 0x00 }, { CCI_REG8(0x1692), 0x20 }, + { CCI_REG8(0x1693), 0x00 }, { CCI_REG8(0x1694), 0x10 }, + { CCI_REG8(0x1695), 0x14 }, { CCI_REG8(0x1696), 0x10 }, + { CCI_REG8(0x1697), 0x0e }, { CCI_REG8(0x1730), 0x01 }, + { CCI_REG8(0x1732), 0x00 }, { CCI_REG8(0x1733), 0x10 }, + { CCI_REG8(0x1734), 0x01 }, { CCI_REG8(0x1735), 0x00 }, + { CCI_REG8(0x1748), 0x01 }, { CCI_REG8(0xfff9), 0x06 }, + { CCI_REG8(0x5000), 0xff }, { CCI_REG8(0x5001), 0x3d }, + { CCI_REG8(0x5002), 0xf5 }, { CCI_REG8(0x5004), 0x80 }, + { CCI_REG8(0x5006), 0x04 }, { CCI_REG8(0x5061), 0x20 }, + { CCI_REG8(0x5063), 0x20 }, { CCI_REG8(0x5064), 0x24 }, + { CCI_REG8(0x5065), 0x00 }, { CCI_REG8(0x5066), 0x1b }, + { CCI_REG8(0x5067), 0x00 }, { CCI_REG8(0x5068), 0x03 }, + { CCI_REG8(0x5069), 0x10 }, { CCI_REG8(0x506a), 0x20 }, + { CCI_REG8(0x506b), 0x04 }, { CCI_REG8(0x506c), 0x04 }, + { CCI_REG8(0x506d), 0x0c }, { CCI_REG8(0x506e), 0x0c }, + { CCI_REG8(0x506f), 0x04 }, { CCI_REG8(0x5070), 0x0c }, + { CCI_REG8(0x5071), 0x14 }, { CCI_REG8(0x5072), 0x1c }, + { CCI_REG8(0x5073), 0x01 }, { CCI_REG8(0x5074), 0x01 }, + { CCI_REG8(0x5075), 0xbe }, { CCI_REG8(0x5083), 0x00 }, + { CCI_REG8(0x5114), 0x03 }, { CCI_REG8(0x51b0), 0x00 }, + { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x02 }, + { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 }, + { CCI_REG8(0x51b8), 0x00 }, { CCI_REG8(0x51b9), 0x70 }, + { CCI_REG8(0x51ba), 0x00 }, { CCI_REG8(0x51bb), 0x10 }, + { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 }, + { CCI_REG8(0x51d2), 0xff }, { CCI_REG8(0x51d3), 0x1c }, + { CCI_REG8(0x5250), 0x34 }, { CCI_REG8(0x5251), 0x00 }, + { CCI_REG8(0x525b), 0x00 }, { CCI_REG8(0x525d), 0x00 }, + { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x38 }, + { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x4b }, + { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 }, + { CCI_REG8(0x5290), 0x00 }, { CCI_REG8(0x5291), 0x50 }, + { CCI_REG8(0x5292), 0x00 }, { CCI_REG8(0x5293), 0x50 }, + { CCI_REG8(0x5294), 0x00 }, { CCI_REG8(0x5295), 0x50 }, + { CCI_REG8(0x5296), 0x00 }, { CCI_REG8(0x5297), 0x50 }, + { CCI_REG8(0x5298), 0x00 }, { CCI_REG8(0x5299), 0x50 }, + { CCI_REG8(0x529a), 0x01 }, { CCI_REG8(0x529b), 0x00 }, + { CCI_REG8(0x529c), 0x01 }, { CCI_REG8(0x529d), 0x00 }, + { CCI_REG8(0x529e), 0x00 }, { CCI_REG8(0x529f), 0x50 }, + { CCI_REG8(0x52a0), 0x00 }, { CCI_REG8(0x52a1), 0x50 }, + { CCI_REG8(0x52a2), 0x01 }, { CCI_REG8(0x52a3), 0x00 }, + { CCI_REG8(0x52a4), 0x01 }, { CCI_REG8(0x52a5), 0x00 }, + { CCI_REG8(0x52a6), 0x00 }, { CCI_REG8(0x52a7), 0x50 }, + { CCI_REG8(0x52a8), 0x00 }, { CCI_REG8(0x52a9), 0x50 }, + { CCI_REG8(0x52aa), 0x00 }, { CCI_REG8(0x52ab), 0x50 }, + { CCI_REG8(0x52ac), 0x00 }, { CCI_REG8(0x52ad), 0x50 }, + { CCI_REG8(0x52ae), 0x00 }, { CCI_REG8(0x52af), 0x50 }, + { CCI_REG8(0x52b0), 0x00 }, { CCI_REG8(0x52b1), 0x50 }, + { CCI_REG8(0x52b2), 0x00 }, { CCI_REG8(0x52b3), 0x50 }, + { CCI_REG8(0x52b4), 0x00 }, { CCI_REG8(0x52b5), 0x50 }, + { CCI_REG8(0x52b6), 0x00 }, { CCI_REG8(0x52b7), 0x50 }, + { CCI_REG8(0x52b8), 0x00 }, { CCI_REG8(0x52b9), 0x50 }, + { CCI_REG8(0x52ba), 0x01 }, { CCI_REG8(0x52bb), 0x00 }, + { CCI_REG8(0x52bc), 0x01 }, { CCI_REG8(0x52bd), 0x00 }, + { CCI_REG8(0x52be), 0x00 }, { CCI_REG8(0x52bf), 0x50 }, + { CCI_REG8(0x52c0), 0x00 }, { CCI_REG8(0x52c1), 0x50 }, + { CCI_REG8(0x52c2), 0x01 }, { CCI_REG8(0x52c3), 0x00 }, + { CCI_REG8(0x52c4), 0x01 }, { CCI_REG8(0x52c5), 0x00 }, + { CCI_REG8(0x52c6), 0x00 }, { CCI_REG8(0x52c7), 0x50 }, + { CCI_REG8(0x52c8), 0x00 }, { CCI_REG8(0x52c9), 0x50 }, + { CCI_REG8(0x52ca), 0x00 }, { CCI_REG8(0x52cb), 0x50 }, + { CCI_REG8(0x52cc), 0x00 }, { CCI_REG8(0x52cd), 0x50 }, + { CCI_REG8(0x52ce), 0x00 }, { CCI_REG8(0x52cf), 0x50 }, + { CCI_REG8(0x52f0), 0x04 }, { CCI_REG8(0x52f1), 0x03 }, + { CCI_REG8(0x52f2), 0x02 }, { CCI_REG8(0x52f3), 0x01 }, + { CCI_REG8(0x52f4), 0x08 }, { CCI_REG8(0x52f5), 0x07 }, + { CCI_REG8(0x52f6), 0x06 }, { CCI_REG8(0x52f7), 0x05 }, + { CCI_REG8(0x52f8), 0x0c }, { CCI_REG8(0x52f9), 0x0b }, + { CCI_REG8(0x52fa), 0x0a }, { CCI_REG8(0x52fb), 0x09 }, + { CCI_REG8(0x52fc), 0x10 }, { CCI_REG8(0x52fd), 0x0f }, + { CCI_REG8(0x52fe), 0x0e }, { CCI_REG8(0x52ff), 0x0d }, + { CCI_REG8(0x5300), 0x14 }, { CCI_REG8(0x5301), 0x13 }, + { CCI_REG8(0x5302), 0x12 }, { CCI_REG8(0x5303), 0x11 }, + { CCI_REG8(0x5304), 0x18 }, { CCI_REG8(0x5305), 0x17 }, + { CCI_REG8(0x5306), 0x16 }, { CCI_REG8(0x5307), 0x15 }, + { CCI_REG8(0x5308), 0x1c }, { CCI_REG8(0x5309), 0x1b }, + { CCI_REG8(0x530a), 0x1a }, { CCI_REG8(0x530b), 0x19 }, + { CCI_REG8(0x530c), 0x20 }, { CCI_REG8(0x530d), 0x1f }, + { CCI_REG8(0x530e), 0x1e }, { CCI_REG8(0x530f), 0x1d }, + { CCI_REG8(0x5310), 0x03 }, { CCI_REG8(0x5311), 0xe8 }, + { CCI_REG8(0x5331), 0x0a }, { CCI_REG8(0x5332), 0x43 }, + { CCI_REG8(0x5333), 0x45 }, { CCI_REG8(0x5353), 0x09 }, + { CCI_REG8(0x5354), 0x00 }, { CCI_REG8(0x5414), 0x03 }, + { CCI_REG8(0x54b0), 0x10 }, { CCI_REG8(0x54b3), 0x0e }, + { CCI_REG8(0x54b5), 0x02 }, { CCI_REG8(0x54b6), 0x00 }, + { CCI_REG8(0x54b7), 0x00 }, { CCI_REG8(0x54b8), 0x00 }, + { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54ba), 0x00 }, + { CCI_REG8(0x54bb), 0x10 }, { CCI_REG8(0x54bc), 0x00 }, + { CCI_REG8(0x54bd), 0x00 }, { CCI_REG8(0x54d2), 0xff }, + { CCI_REG8(0x54d3), 0x1c }, { CCI_REG8(0x5510), 0x03 }, + { CCI_REG8(0x5511), 0xe8 }, { CCI_REG8(0x5550), 0x6c }, + { CCI_REG8(0x5551), 0x00 }, { CCI_REG8(0x557a), 0x00 }, + { CCI_REG8(0x557b), 0x38 }, { CCI_REG8(0x557c), 0x00 }, + { CCI_REG8(0x557d), 0x4b }, { CCI_REG8(0x5590), 0x00 }, + { CCI_REG8(0x5591), 0x50 }, { CCI_REG8(0x5592), 0x00 }, + { CCI_REG8(0x5593), 0x50 }, { CCI_REG8(0x5594), 0x00 }, + { CCI_REG8(0x5595), 0x50 }, { CCI_REG8(0x5596), 0x00 }, + { CCI_REG8(0x5597), 0x50 }, { CCI_REG8(0x5598), 0x00 }, + { CCI_REG8(0x5599), 0x50 }, { CCI_REG8(0x559a), 0x01 }, + { CCI_REG8(0x559b), 0x00 }, { CCI_REG8(0x559c), 0x01 }, + { CCI_REG8(0x559d), 0x00 }, { CCI_REG8(0x559e), 0x00 }, + { CCI_REG8(0x559f), 0x50 }, { CCI_REG8(0x55a0), 0x00 }, + { CCI_REG8(0x55a1), 0x50 }, { CCI_REG8(0x55a2), 0x01 }, + { CCI_REG8(0x55a3), 0x00 }, { CCI_REG8(0x55a4), 0x01 }, + { CCI_REG8(0x55a5), 0x00 }, { CCI_REG8(0x55a6), 0x00 }, + { CCI_REG8(0x55a7), 0x50 }, { CCI_REG8(0x55a8), 0x00 }, + { CCI_REG8(0x55a9), 0x50 }, { CCI_REG8(0x55aa), 0x00 }, + { CCI_REG8(0x55ab), 0x50 }, { CCI_REG8(0x55ac), 0x00 }, + { CCI_REG8(0x55ad), 0x50 }, { CCI_REG8(0x55ae), 0x00 }, + { CCI_REG8(0x55af), 0x50 }, { CCI_REG8(0x55b0), 0x00 }, + { CCI_REG8(0x55b1), 0x50 }, { CCI_REG8(0x55b2), 0x00 }, + { CCI_REG8(0x55b3), 0x50 }, { CCI_REG8(0x55b4), 0x00 }, + { CCI_REG8(0x55b5), 0x50 }, { CCI_REG8(0x55b6), 0x00 }, + { CCI_REG8(0x55b7), 0x50 }, { CCI_REG8(0x55b8), 0x00 }, + { CCI_REG8(0x55b9), 0x50 }, { CCI_REG8(0x55ba), 0x01 }, + { CCI_REG8(0x55bb), 0x00 }, { CCI_REG8(0x55bc), 0x01 }, + { CCI_REG8(0x55bd), 0x00 }, { CCI_REG8(0x55be), 0x00 }, + { CCI_REG8(0x55bf), 0x50 }, { CCI_REG8(0x55c0), 0x00 }, + { CCI_REG8(0x55c1), 0x50 }, { CCI_REG8(0x55c2), 0x01 }, + { CCI_REG8(0x55c3), 0x00 }, { CCI_REG8(0x55c4), 0x01 }, + { CCI_REG8(0x55c5), 0x00 }, { CCI_REG8(0x55c6), 0x00 }, + { CCI_REG8(0x55c7), 0x50 }, { CCI_REG8(0x55c8), 0x00 }, + { CCI_REG8(0x55c9), 0x50 }, { CCI_REG8(0x55ca), 0x00 }, + { CCI_REG8(0x55cb), 0x50 }, { CCI_REG8(0x55cc), 0x00 }, + { CCI_REG8(0x55cd), 0x50 }, { CCI_REG8(0x55ce), 0x00 }, + { CCI_REG8(0x55cf), 0x50 }, { CCI_REG8(0x55f0), 0x04 }, + { CCI_REG8(0x55f1), 0x03 }, { CCI_REG8(0x55f2), 0x02 }, + { CCI_REG8(0x55f3), 0x01 }, { CCI_REG8(0x55f4), 0x08 }, + { CCI_REG8(0x55f5), 0x07 }, { CCI_REG8(0x55f6), 0x06 }, + { CCI_REG8(0x55f7), 0x05 }, { CCI_REG8(0x55f8), 0x0c }, + { CCI_REG8(0x55f9), 0x0b }, { CCI_REG8(0x55fa), 0x0a }, + { CCI_REG8(0x55fb), 0x09 }, { CCI_REG8(0x55fc), 0x10 }, + { CCI_REG8(0x55fd), 0x0f }, { CCI_REG8(0x55fe), 0x0e }, + { CCI_REG8(0x55ff), 0x0d }, { CCI_REG8(0x5600), 0x14 }, + { CCI_REG8(0x5601), 0x13 }, { CCI_REG8(0x5602), 0x12 }, + { CCI_REG8(0x5603), 0x11 }, { CCI_REG8(0x5604), 0x18 }, + { CCI_REG8(0x5605), 0x17 }, { CCI_REG8(0x5606), 0x16 }, + { CCI_REG8(0x5607), 0x15 }, { CCI_REG8(0x5608), 0x1c }, + { CCI_REG8(0x5609), 0x1b }, { CCI_REG8(0x560a), 0x1a }, + { CCI_REG8(0x560b), 0x19 }, { CCI_REG8(0x560c), 0x20 }, + { CCI_REG8(0x560d), 0x1f }, { CCI_REG8(0x560e), 0x1e }, + { CCI_REG8(0x560f), 0x1d }, { CCI_REG8(0x5631), 0x02 }, + { CCI_REG8(0x5632), 0x42 }, { CCI_REG8(0x5633), 0x24 }, + { CCI_REG8(0x5653), 0x09 }, { CCI_REG8(0x5654), 0x00 }, + { CCI_REG8(0x5714), 0x03 }, { CCI_REG8(0x57b0), 0x10 }, + { CCI_REG8(0x57b3), 0x0e }, { CCI_REG8(0x57b5), 0x02 }, + { CCI_REG8(0x57b6), 0x00 }, { CCI_REG8(0x57b7), 0x00 }, + { CCI_REG8(0x57b8), 0x00 }, { CCI_REG8(0x57b9), 0x70 }, + { CCI_REG8(0x57ba), 0x00 }, { CCI_REG8(0x57bb), 0x10 }, + { CCI_REG8(0x57bc), 0x00 }, { CCI_REG8(0x57bd), 0x00 }, + { CCI_REG8(0x57d2), 0xff }, { CCI_REG8(0x57d3), 0x1c }, + { CCI_REG8(0x5810), 0x03 }, { CCI_REG8(0x5811), 0xe8 }, + { CCI_REG8(0x5850), 0x6c }, { CCI_REG8(0x5851), 0x00 }, + { CCI_REG8(0x587a), 0x00 }, { CCI_REG8(0x587b), 0x38 }, + { CCI_REG8(0x587c), 0x00 }, { CCI_REG8(0x587d), 0x4b }, + { CCI_REG8(0x5890), 0x00 }, { CCI_REG8(0x5891), 0x50 }, + { CCI_REG8(0x5892), 0x00 }, { CCI_REG8(0x5893), 0x50 }, + { CCI_REG8(0x5894), 0x00 }, { CCI_REG8(0x5895), 0x50 }, + { CCI_REG8(0x5896), 0x00 }, { CCI_REG8(0x5897), 0x50 }, + { CCI_REG8(0x5898), 0x00 }, { CCI_REG8(0x5899), 0x50 }, + { CCI_REG8(0x589a), 0x01 }, { CCI_REG8(0x589b), 0x00 }, + { CCI_REG8(0x589c), 0x01 }, { CCI_REG8(0x589d), 0x00 }, + { CCI_REG8(0x589e), 0x00 }, { CCI_REG8(0x589f), 0x50 }, + { CCI_REG8(0x58a0), 0x00 }, { CCI_REG8(0x58a1), 0x50 }, + { CCI_REG8(0x58a2), 0x01 }, { CCI_REG8(0x58a3), 0x00 }, + { CCI_REG8(0x58a4), 0x01 }, { CCI_REG8(0x58a5), 0x00 }, + { CCI_REG8(0x58a6), 0x00 }, { CCI_REG8(0x58a7), 0x50 }, + { CCI_REG8(0x58a8), 0x00 }, { CCI_REG8(0x58a9), 0x50 }, + { CCI_REG8(0x58aa), 0x00 }, { CCI_REG8(0x58ab), 0x50 }, + { CCI_REG8(0x58ac), 0x00 }, { CCI_REG8(0x58ad), 0x50 }, + { CCI_REG8(0x58ae), 0x00 }, { CCI_REG8(0x58af), 0x50 }, + { CCI_REG8(0x58b0), 0x00 }, { CCI_REG8(0x58b1), 0x50 }, + { CCI_REG8(0x58b2), 0x00 }, { CCI_REG8(0x58b3), 0x50 }, + { CCI_REG8(0x58b4), 0x00 }, { CCI_REG8(0x58b5), 0x50 }, + { CCI_REG8(0x58b6), 0x00 }, { CCI_REG8(0x58b7), 0x50 }, + { CCI_REG8(0x58b8), 0x00 }, { CCI_REG8(0x58b9), 0x50 }, + { CCI_REG8(0x58ba), 0x01 }, { CCI_REG8(0x58bb), 0x00 }, + { CCI_REG8(0x58bc), 0x01 }, { CCI_REG8(0x58bd), 0x00 }, + { CCI_REG8(0x58be), 0x00 }, { CCI_REG8(0x58bf), 0x50 }, + { CCI_REG8(0x58c0), 0x00 }, { CCI_REG8(0x58c1), 0x50 }, + { CCI_REG8(0x58c2), 0x01 }, { CCI_REG8(0x58c3), 0x00 }, + { CCI_REG8(0x58c4), 0x01 }, { CCI_REG8(0x58c5), 0x00 }, + { CCI_REG8(0x58c6), 0x00 }, { CCI_REG8(0x58c7), 0x50 }, + { CCI_REG8(0x58c8), 0x00 }, { CCI_REG8(0x58c9), 0x50 }, + { CCI_REG8(0x58ca), 0x00 }, { CCI_REG8(0x58cb), 0x50 }, + { CCI_REG8(0x58cc), 0x00 }, { CCI_REG8(0x58cd), 0x50 }, + { CCI_REG8(0x58ce), 0x00 }, { CCI_REG8(0x58cf), 0x50 }, + { CCI_REG8(0x58f0), 0x04 }, { CCI_REG8(0x58f1), 0x03 }, + { CCI_REG8(0x58f2), 0x02 }, { CCI_REG8(0x58f3), 0x01 }, + { CCI_REG8(0x58f4), 0x08 }, { CCI_REG8(0x58f5), 0x07 }, + { CCI_REG8(0x58f6), 0x06 }, { CCI_REG8(0x58f7), 0x05 }, + { CCI_REG8(0x58f8), 0x0c }, { CCI_REG8(0x58f9), 0x0b }, + { CCI_REG8(0x58fa), 0x0a }, { CCI_REG8(0x58fb), 0x09 }, + { CCI_REG8(0x58fc), 0x10 }, { CCI_REG8(0x58fd), 0x0f }, + { CCI_REG8(0x58fe), 0x0e }, { CCI_REG8(0x58ff), 0x0d }, + { CCI_REG8(0x5900), 0x14 }, { CCI_REG8(0x5901), 0x13 }, + { CCI_REG8(0x5902), 0x12 }, { CCI_REG8(0x5903), 0x11 }, + { CCI_REG8(0x5904), 0x18 }, { CCI_REG8(0x5905), 0x17 }, + { CCI_REG8(0x5906), 0x16 }, { CCI_REG8(0x5907), 0x15 }, + { CCI_REG8(0x5908), 0x1c }, { CCI_REG8(0x5909), 0x1b }, + { CCI_REG8(0x590a), 0x1a }, { CCI_REG8(0x590b), 0x19 }, + { CCI_REG8(0x590c), 0x20 }, { CCI_REG8(0x590d), 0x1f }, + { CCI_REG8(0x590e), 0x1e }, { CCI_REG8(0x590f), 0x1d }, + { CCI_REG8(0x5931), 0x02 }, { CCI_REG8(0x5932), 0x42 }, + { CCI_REG8(0x5933), 0x24 }, { CCI_REG8(0x5953), 0x09 }, + { CCI_REG8(0x5954), 0x00 }, { CCI_REG8(0x5989), 0x84 }, + { CCI_REG8(0x59c3), 0x04 }, { CCI_REG8(0x59c4), 0x24 }, + { CCI_REG8(0x59c5), 0x40 }, { CCI_REG8(0x59c6), 0x1b }, + { CCI_REG8(0x59c7), 0x40 }, { CCI_REG8(0x5a02), 0x0f }, + { CCI_REG8(0x5f00), 0x29 }, { CCI_REG8(0x5f2d), 0x28 }, + { CCI_REG8(0x5f2e), 0x28 }, { CCI_REG8(0x6801), 0x11 }, + { CCI_REG8(0x6802), 0x3f }, { CCI_REG8(0x6803), 0xe7 }, + { CCI_REG8(0x6825), 0x0f }, { CCI_REG8(0x6826), 0x20 }, + { CCI_REG8(0x6827), 0x00 }, { CCI_REG8(0x6829), 0x16 }, + { CCI_REG8(0x682b), 0xb3 }, { CCI_REG8(0x682c), 0x01 }, + { CCI_REG8(0x6832), 0xff }, { CCI_REG8(0x6833), 0xff }, + { CCI_REG8(0x6898), 0x80 }, { CCI_REG8(0x6899), 0x80 }, + { CCI_REG8(0x689b), 0x40 }, { CCI_REG8(0x689c), 0x20 }, + { CCI_REG8(0x689d), 0x20 }, { CCI_REG8(0x689e), 0x80 }, + { CCI_REG8(0x689f), 0x60 }, { CCI_REG8(0x68a0), 0x40 }, + { CCI_REG8(0x68a4), 0x40 }, { CCI_REG8(0x68a5), 0x20 }, + { CCI_REG8(0x68a6), 0x00 }, { CCI_REG8(0x68b6), 0x80 }, + { CCI_REG8(0x68b7), 0x80 }, { CCI_REG8(0x68b8), 0x80 }, + { CCI_REG8(0x68bc), 0x80 }, { CCI_REG8(0x68bd), 0x80 }, + { CCI_REG8(0x68be), 0x80 }, { CCI_REG8(0x68bf), 0x40 }, + { CCI_REG8(0x68c2), 0x80 }, { CCI_REG8(0x68c3), 0x80 }, + { CCI_REG8(0x68c4), 0x60 }, { CCI_REG8(0x68c5), 0x30 }, + { CCI_REG8(0x6918), 0x80 }, { CCI_REG8(0x6919), 0x80 }, + { CCI_REG8(0x691b), 0x40 }, { CCI_REG8(0x691c), 0x20 }, + { CCI_REG8(0x691d), 0x20 }, { CCI_REG8(0x691e), 0x80 }, + { CCI_REG8(0x691f), 0x60 }, { CCI_REG8(0x6920), 0x40 }, + { CCI_REG8(0x6924), 0x40 }, { CCI_REG8(0x6925), 0x20 }, + { CCI_REG8(0x6926), 0x00 }, { CCI_REG8(0x6936), 0x40 }, + { CCI_REG8(0x6937), 0x40 }, { CCI_REG8(0x6938), 0x20 }, + { CCI_REG8(0x6939), 0x20 }, { CCI_REG8(0x693a), 0x10 }, + { CCI_REG8(0x693b), 0x10 }, { CCI_REG8(0x693c), 0x20 }, + { CCI_REG8(0x693d), 0x20 }, { CCI_REG8(0x693e), 0x10 }, + { CCI_REG8(0x693f), 0x10 }, { CCI_REG8(0x6940), 0x00 }, + { CCI_REG8(0x6941), 0x00 }, { CCI_REG8(0x6942), 0x08 }, + { CCI_REG8(0x6943), 0x08 }, { CCI_REG8(0x6944), 0x00 }, + { CCI_REG8(0x69c2), 0x07 }, { CCI_REG8(0x6a20), 0x01 }, + { CCI_REG8(0x6a23), 0x10 }, { CCI_REG8(0x6a26), 0x3d }, + { CCI_REG8(0x6a27), 0x3e }, { CCI_REG8(0x6a38), 0x02 }, + { CCI_REG8(0x6a39), 0x20 }, { CCI_REG8(0x6a3a), 0x02 }, + { CCI_REG8(0x6a3b), 0x84 }, { CCI_REG8(0x6a3e), 0x02 }, + { CCI_REG8(0x6a3f), 0x20 }, { CCI_REG8(0x6a47), 0x3b }, + { CCI_REG8(0x6a63), 0x04 }, { CCI_REG8(0x6a65), 0x00 }, + { CCI_REG8(0x6a67), 0x0f }, { CCI_REG8(0x6b22), 0x07 }, + { CCI_REG8(0x6b23), 0xc2 }, { CCI_REG8(0x6b2f), 0x00 }, + { CCI_REG8(0x6b60), 0x1f }, { CCI_REG8(0x6bd2), 0x5a }, + { CCI_REG8(0x6c20), 0x50 }, { CCI_REG8(0x6c60), 0x50 }, + { CCI_REG8(0x6c61), 0x06 }, { CCI_REG8(0x7318), 0x04 }, + { CCI_REG8(0x7319), 0x01 }, { CCI_REG8(0x731a), 0x04 }, + { CCI_REG8(0x731b), 0x01 }, { CCI_REG8(0x731c), 0x00 }, + { CCI_REG8(0x731d), 0x00 }, { CCI_REG8(0x731e), 0x04 }, + { CCI_REG8(0x731f), 0x01 }, { CCI_REG8(0x7320), 0x04 }, + { CCI_REG8(0x7321), 0x00 }, { CCI_REG8(0x7322), 0x04 }, + { CCI_REG8(0x7323), 0x00 }, { CCI_REG8(0x7324), 0x04 }, + { CCI_REG8(0x7325), 0x00 }, { CCI_REG8(0x7326), 0x04 }, + { CCI_REG8(0x7327), 0x00 }, { CCI_REG8(0x7600), 0x00 }, + { CCI_REG8(0x7601), 0x00 }, { CCI_REG8(0x7602), 0x10 }, + { CCI_REG8(0x7603), 0x00 }, { CCI_REG8(0x7604), 0x00 }, + { CCI_REG8(0x7605), 0x00 }, { CCI_REG8(0x7606), 0x10 }, + { CCI_REG8(0x7607), 0x00 }, { CCI_REG8(0x7608), 0x00 }, + { CCI_REG8(0x7609), 0x00 }, { CCI_REG8(0x760a), 0x10 }, + { CCI_REG8(0x760b), 0x00 }, { CCI_REG8(0x760c), 0x00 }, + { CCI_REG8(0x760d), 0x00 }, { CCI_REG8(0x760e), 0x10 }, + { CCI_REG8(0x760f), 0x00 }, { CCI_REG8(0x7610), 0x00 }, + { CCI_REG8(0x7611), 0x00 }, { CCI_REG8(0x7612), 0x10 }, + { CCI_REG8(0x7613), 0x00 }, { CCI_REG8(0x7614), 0x00 }, + { CCI_REG8(0x7615), 0x00 }, { CCI_REG8(0x7616), 0x10 }, + { CCI_REG8(0x7617), 0x00 }, { CCI_REG8(0x7618), 0x00 }, + { CCI_REG8(0x7619), 0x00 }, { CCI_REG8(0x761a), 0x10 }, + { CCI_REG8(0x761b), 0x00 }, { CCI_REG8(0x761c), 0x00 }, + { CCI_REG8(0x761d), 0x00 }, { CCI_REG8(0x761e), 0x10 }, + { CCI_REG8(0x761f), 0x00 }, { CCI_REG8(0x7620), 0x00 }, + { CCI_REG8(0x7621), 0x00 }, { CCI_REG8(0x7622), 0x10 }, + { CCI_REG8(0x7623), 0x00 }, { CCI_REG8(0x7624), 0x00 }, + { CCI_REG8(0x7625), 0x00 }, { CCI_REG8(0x7626), 0x10 }, + { CCI_REG8(0x7627), 0x00 }, { CCI_REG8(0x7628), 0x00 }, + { CCI_REG8(0x7629), 0x00 }, { CCI_REG8(0x762a), 0x10 }, + { CCI_REG8(0x762b), 0x00 }, { CCI_REG8(0x762c), 0x00 }, + { CCI_REG8(0x762d), 0x00 }, { CCI_REG8(0x762e), 0x10 }, + { CCI_REG8(0x762f), 0x00 }, { CCI_REG8(0x7630), 0x00 }, + { CCI_REG8(0x7631), 0x00 }, { CCI_REG8(0x7632), 0x10 }, + { CCI_REG8(0x7633), 0x00 }, { CCI_REG8(0x7634), 0x00 }, + { CCI_REG8(0x7635), 0x00 }, { CCI_REG8(0x7636), 0x10 }, + { CCI_REG8(0x7637), 0x00 }, { CCI_REG8(0x7638), 0x00 }, + { CCI_REG8(0x7639), 0x00 }, { CCI_REG8(0x763a), 0x10 }, + { CCI_REG8(0x763b), 0x00 }, { CCI_REG8(0x763c), 0x00 }, + { CCI_REG8(0x763d), 0x00 }, { CCI_REG8(0x763e), 0x10 }, + { CCI_REG8(0x763f), 0x00 }, { CCI_REG8(0x7640), 0x00 }, + { CCI_REG8(0x7641), 0x00 }, { CCI_REG8(0x7642), 0x10 }, + { CCI_REG8(0x7643), 0x00 }, { CCI_REG8(0x7644), 0x00 }, + { CCI_REG8(0x7645), 0x00 }, { CCI_REG8(0x7646), 0x10 }, + { CCI_REG8(0x7647), 0x00 }, { CCI_REG8(0x7648), 0x00 }, + { CCI_REG8(0x7649), 0x00 }, { CCI_REG8(0x764a), 0x10 }, + { CCI_REG8(0x764b), 0x00 }, { CCI_REG8(0x764c), 0x00 }, + { CCI_REG8(0x764d), 0x00 }, { CCI_REG8(0x764e), 0x10 }, + { CCI_REG8(0x764f), 0x00 }, { CCI_REG8(0x7650), 0x00 }, + { CCI_REG8(0x7651), 0x00 }, { CCI_REG8(0x7652), 0x10 }, + { CCI_REG8(0x7653), 0x00 }, { CCI_REG8(0x7654), 0x00 }, + { CCI_REG8(0x7655), 0x00 }, { CCI_REG8(0x7656), 0x10 }, + { CCI_REG8(0x7657), 0x00 }, { CCI_REG8(0x7658), 0x00 }, + { CCI_REG8(0x7659), 0x00 }, { CCI_REG8(0x765a), 0x10 }, + { CCI_REG8(0x765b), 0x00 }, { CCI_REG8(0x765c), 0x00 }, + { CCI_REG8(0x765d), 0x00 }, { CCI_REG8(0x765e), 0x10 }, + { CCI_REG8(0x765f), 0x00 }, { CCI_REG8(0x7660), 0x00 }, + { CCI_REG8(0x7661), 0x00 }, { CCI_REG8(0x7662), 0x10 }, + { CCI_REG8(0x7663), 0x00 }, { CCI_REG8(0x7664), 0x00 }, + { CCI_REG8(0x7665), 0x00 }, { CCI_REG8(0x7666), 0x10 }, + { CCI_REG8(0x7667), 0x00 }, { CCI_REG8(0x7668), 0x00 }, + { CCI_REG8(0x7669), 0x00 }, { CCI_REG8(0x766a), 0x10 }, + { CCI_REG8(0x766b), 0x00 }, { CCI_REG8(0x766c), 0x00 }, + { CCI_REG8(0x766d), 0x00 }, { CCI_REG8(0x766e), 0x10 }, + { CCI_REG8(0x766f), 0x00 }, { CCI_REG8(0x7670), 0x00 }, + { CCI_REG8(0x7671), 0x00 }, { CCI_REG8(0x7672), 0x10 }, + { CCI_REG8(0x7673), 0x00 }, { CCI_REG8(0x7674), 0x00 }, + { CCI_REG8(0x7675), 0x00 }, { CCI_REG8(0x7676), 0x10 }, + { CCI_REG8(0x7677), 0x00 }, { CCI_REG8(0x7678), 0x00 }, + { CCI_REG8(0x7679), 0x00 }, { CCI_REG8(0x767a), 0x10 }, + { CCI_REG8(0x767b), 0x00 }, { CCI_REG8(0x767c), 0x00 }, + { CCI_REG8(0x767d), 0x00 }, { CCI_REG8(0x767e), 0x10 }, + { CCI_REG8(0x767f), 0x00 }, { CCI_REG8(0x7680), 0x00 }, + { CCI_REG8(0x7681), 0x00 }, { CCI_REG8(0x7682), 0x10 }, + { CCI_REG8(0x7683), 0x00 }, { CCI_REG8(0x7684), 0x00 }, + { CCI_REG8(0x7685), 0x00 }, { CCI_REG8(0x7686), 0x10 }, + { CCI_REG8(0x7687), 0x00 }, { CCI_REG8(0x7688), 0x00 }, + { CCI_REG8(0x7689), 0x00 }, { CCI_REG8(0x768a), 0x10 }, + { CCI_REG8(0x768b), 0x00 }, { CCI_REG8(0x768c), 0x00 }, + { CCI_REG8(0x768d), 0x00 }, { CCI_REG8(0x768e), 0x10 }, + { CCI_REG8(0x768f), 0x00 }, { CCI_REG8(0x7690), 0x00 }, + { CCI_REG8(0x7691), 0x00 }, { CCI_REG8(0x7692), 0x10 }, + { CCI_REG8(0x7693), 0x00 }, { CCI_REG8(0x7694), 0x00 }, + { CCI_REG8(0x7695), 0x00 }, { CCI_REG8(0x7696), 0x10 }, + { CCI_REG8(0x7697), 0x00 }, { CCI_REG8(0x7698), 0x00 }, + { CCI_REG8(0x7699), 0x00 }, { CCI_REG8(0x769a), 0x10 }, + { CCI_REG8(0x769b), 0x00 }, { CCI_REG8(0x769c), 0x00 }, + { CCI_REG8(0x769d), 0x00 }, { CCI_REG8(0x769e), 0x10 }, + { CCI_REG8(0x769f), 0x00 }, { CCI_REG8(0x76a0), 0x00 }, + { CCI_REG8(0x76a1), 0x00 }, { CCI_REG8(0x76a2), 0x10 }, + { CCI_REG8(0x76a3), 0x00 }, { CCI_REG8(0x76a4), 0x00 }, + { CCI_REG8(0x76a5), 0x00 }, { CCI_REG8(0x76a6), 0x10 }, + { CCI_REG8(0x76a7), 0x00 }, { CCI_REG8(0x76a8), 0x00 }, + { CCI_REG8(0x76a9), 0x00 }, { CCI_REG8(0x76aa), 0x10 }, + { CCI_REG8(0x76ab), 0x00 }, { CCI_REG8(0x76ac), 0x00 }, + { CCI_REG8(0x76ad), 0x00 }, { CCI_REG8(0x76ae), 0x10 }, + { CCI_REG8(0x76af), 0x00 }, { CCI_REG8(0x76b0), 0x00 }, + { CCI_REG8(0x76b1), 0x00 }, { CCI_REG8(0x76b2), 0x10 }, + { CCI_REG8(0x76b3), 0x00 }, { CCI_REG8(0x76b4), 0x00 }, + { CCI_REG8(0x76b5), 0x00 }, { CCI_REG8(0x76b6), 0x10 }, + { CCI_REG8(0x76b7), 0x00 }, { CCI_REG8(0x76b8), 0x00 }, + { CCI_REG8(0x76b9), 0x00 }, { CCI_REG8(0x76ba), 0x10 }, + { CCI_REG8(0x76bb), 0x00 }, { CCI_REG8(0x76bc), 0x00 }, + { CCI_REG8(0x76bd), 0x00 }, { CCI_REG8(0x76be), 0x10 }, + { CCI_REG8(0x76bf), 0x00 }, { CCI_REG8(0x76c0), 0x00 }, + { CCI_REG8(0x76c1), 0x00 }, { CCI_REG8(0x76c2), 0x10 }, + { CCI_REG8(0x76c3), 0x00 }, { CCI_REG8(0x76c4), 0x00 }, + { CCI_REG8(0x76c5), 0x00 }, { CCI_REG8(0x76c6), 0x10 }, + { CCI_REG8(0x76c7), 0x00 }, { CCI_REG8(0x76c8), 0x00 }, + { CCI_REG8(0x76c9), 0x00 }, { CCI_REG8(0x76ca), 0x10 }, + { CCI_REG8(0x76cb), 0x00 }, { CCI_REG8(0x76cc), 0x00 }, + { CCI_REG8(0x76cd), 0x00 }, { CCI_REG8(0x76ce), 0x10 }, + { CCI_REG8(0x76cf), 0x00 }, { CCI_REG8(0x76d0), 0x00 }, + { CCI_REG8(0x76d1), 0x00 }, { CCI_REG8(0x76d2), 0x10 }, + { CCI_REG8(0x76d3), 0x00 }, { CCI_REG8(0x76d4), 0x00 }, + { CCI_REG8(0x76d5), 0x00 }, { CCI_REG8(0x76d6), 0x10 }, + { CCI_REG8(0x76d7), 0x00 }, { CCI_REG8(0x76d8), 0x00 }, + { CCI_REG8(0x76d9), 0x00 }, { CCI_REG8(0x76da), 0x10 }, + { CCI_REG8(0x76db), 0x00 }, { CCI_REG8(0x76dc), 0x00 }, + { CCI_REG8(0x76dd), 0x00 }, { CCI_REG8(0x76de), 0x10 }, + { CCI_REG8(0x76df), 0x00 }, { CCI_REG8(0x76e0), 0x00 }, + { CCI_REG8(0x76e1), 0x00 }, { CCI_REG8(0x76e2), 0x10 }, + { CCI_REG8(0x76e3), 0x00 }, { CCI_REG8(0x76e4), 0x00 }, + { CCI_REG8(0x76e5), 0x00 }, { CCI_REG8(0x76e6), 0x10 }, + { CCI_REG8(0x76e7), 0x00 }, { CCI_REG8(0x76e8), 0x00 }, + { CCI_REG8(0x76e9), 0x00 }, { CCI_REG8(0x76ea), 0x10 }, + { CCI_REG8(0x76eb), 0x00 }, { CCI_REG8(0x76ec), 0x00 }, + { CCI_REG8(0x76ed), 0x00 }, { CCI_REG8(0x76ee), 0x10 }, + { CCI_REG8(0x76ef), 0x00 }, { CCI_REG8(0x76f0), 0x00 }, + { CCI_REG8(0x76f1), 0x00 }, { CCI_REG8(0x76f2), 0x10 }, + { CCI_REG8(0x76f3), 0x00 }, { CCI_REG8(0x76f4), 0x00 }, + { CCI_REG8(0x76f5), 0x00 }, { CCI_REG8(0x76f6), 0x10 }, + { CCI_REG8(0x76f7), 0x00 }, { CCI_REG8(0x76f8), 0x00 }, + { CCI_REG8(0x76f9), 0x00 }, { CCI_REG8(0x76fa), 0x10 }, + { CCI_REG8(0x76fb), 0x00 }, { CCI_REG8(0x76fc), 0x00 }, + { CCI_REG8(0x76fd), 0x00 }, { CCI_REG8(0x76fe), 0x10 }, + { CCI_REG8(0x76ff), 0x00 }, { CCI_REG8(0x7700), 0x00 }, + { CCI_REG8(0x7701), 0x00 }, { CCI_REG8(0x7702), 0x10 }, + { CCI_REG8(0x7703), 0x00 }, { CCI_REG8(0x7704), 0x00 }, + { CCI_REG8(0x7705), 0x00 }, { CCI_REG8(0x7706), 0x10 }, + { CCI_REG8(0x7707), 0x00 }, { CCI_REG8(0x7708), 0x00 }, + { CCI_REG8(0x7709), 0x00 }, { CCI_REG8(0x770a), 0x10 }, + { CCI_REG8(0x770b), 0x00 }, { CCI_REG8(0x770c), 0x00 }, + { CCI_REG8(0x770d), 0x00 }, { CCI_REG8(0x770e), 0x10 }, + { CCI_REG8(0x770f), 0x00 }, { CCI_REG8(0x7710), 0x00 }, + { CCI_REG8(0x7711), 0x00 }, { CCI_REG8(0x7712), 0x10 }, + { CCI_REG8(0x7713), 0x00 }, { CCI_REG8(0x7714), 0x00 }, + { CCI_REG8(0x7715), 0x00 }, { CCI_REG8(0x7716), 0x10 }, + { CCI_REG8(0x7717), 0x00 }, { CCI_REG8(0x7718), 0x00 }, + { CCI_REG8(0x7719), 0x00 }, { CCI_REG8(0x771a), 0x10 }, + { CCI_REG8(0x771b), 0x00 }, { CCI_REG8(0x771c), 0x00 }, + { CCI_REG8(0x771d), 0x00 }, { CCI_REG8(0x771e), 0x10 }, + { CCI_REG8(0x771f), 0x00 }, { CCI_REG8(0x7720), 0x00 }, + { CCI_REG8(0x7721), 0x00 }, { CCI_REG8(0x7722), 0x10 }, + { CCI_REG8(0x7723), 0x00 }, { CCI_REG8(0x7724), 0x00 }, + { CCI_REG8(0x7725), 0x00 }, { CCI_REG8(0x7726), 0x10 }, + { CCI_REG8(0x7727), 0x00 }, { CCI_REG8(0x7728), 0x00 }, + { CCI_REG8(0x7729), 0x00 }, { CCI_REG8(0x772a), 0x10 }, + { CCI_REG8(0x772b), 0x00 }, { CCI_REG8(0x772c), 0x00 }, + { CCI_REG8(0x772d), 0x00 }, { CCI_REG8(0x772e), 0x10 }, + { CCI_REG8(0x772f), 0x00 }, { CCI_REG8(0x7730), 0x00 }, + { CCI_REG8(0x7731), 0x00 }, { CCI_REG8(0x7732), 0x10 }, + { CCI_REG8(0x7733), 0x00 }, { CCI_REG8(0x7734), 0x00 }, + { CCI_REG8(0x7735), 0x00 }, { CCI_REG8(0x7736), 0x10 }, + { CCI_REG8(0x7737), 0x00 }, { CCI_REG8(0x7738), 0x00 }, + { CCI_REG8(0x7739), 0x00 }, { CCI_REG8(0x773a), 0x10 }, + { CCI_REG8(0x773b), 0x00 }, { CCI_REG8(0x773c), 0x00 }, + { CCI_REG8(0x773d), 0x00 }, { CCI_REG8(0x773e), 0x10 }, + { CCI_REG8(0x773f), 0x00 }, { CCI_REG8(0x7740), 0x00 }, + { CCI_REG8(0x7741), 0x00 }, { CCI_REG8(0x7742), 0x10 }, + { CCI_REG8(0x7743), 0x00 }, { CCI_REG8(0x3421), 0x02 }, + { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x3632), 0x99 }, + { CCI_REG8(0xc518), 0x1f }, { CCI_REG8(0xc519), 0x1f }, + { CCI_REG8(0xc51a), 0x1f }, { CCI_REG8(0xc51b), 0x1f }, + { CCI_REG8(0xc51c), 0x1f }, { CCI_REG8(0xc51d), 0x1f }, + { CCI_REG8(0xc51e), 0x1f }, { CCI_REG8(0xc51f), 0x1f }, + { CCI_REG8(0xc520), 0x1f }, { CCI_REG8(0xc521), 0x1f }, + { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3615), 0xc5 }, + { CCI_REG8(0xc4c1), 0x02 }, { CCI_REG8(0xc4c2), 0x02 }, + { CCI_REG8(0xc4c3), 0x03 }, { CCI_REG8(0xc4c4), 0x03 }, + { CCI_REG8(0xc4f6), 0x0a }, { CCI_REG8(0xc4f7), 0x0a }, + { CCI_REG8(0xc4f8), 0x0a }, { CCI_REG8(0xc4f9), 0x0a }, + { CCI_REG8(0xc4fa), 0x0a }, { CCI_REG8(0xc4c6), 0x0a }, + { CCI_REG8(0xc4c7), 0x0a }, { CCI_REG8(0xc4c8), 0x0a }, + { CCI_REG8(0xc4c9), 0x0a }, { CCI_REG8(0xc4ca), 0x14 }, + { CCI_REG8(0xc4cb), 0x14 }, { CCI_REG8(0xc4cc), 0x14 }, + { CCI_REG8(0xc4cd), 0x14 }, { CCI_REG8(0x3b92), 0x05 }, + { CCI_REG8(0x3b93), 0x05 }, { CCI_REG8(0x3b94), 0x05 }, + { CCI_REG8(0x3b95), 0x05 }, { CCI_REG8(0x3623), 0x10 }, + { CCI_REG8(0xc522), 0x18 }, { CCI_REG8(0xc523), 0x12 }, + { CCI_REG8(0xc524), 0x0e }, { CCI_REG8(0xc525), 0x0b }, + { CCI_REG8(0xc526), 0x18 }, { CCI_REG8(0xc527), 0x12 }, + { CCI_REG8(0xc528), 0x0c }, { CCI_REG8(0xc529), 0x08 }, + { CCI_REG8(0xc52a), 0x18 }, { CCI_REG8(0xc52b), 0x12 }, + { CCI_REG8(0xc52c), 0x0e }, { CCI_REG8(0xc52d), 0x0b }, + { CCI_REG8(0xc52e), 0x18 }, { CCI_REG8(0xc52f), 0x12 }, + { CCI_REG8(0xc530), 0x0e }, { CCI_REG8(0xc531), 0x0b }, + { CCI_REG8(0xc532), 0x18 }, { CCI_REG8(0xc533), 0x12 }, + { CCI_REG8(0xc534), 0x0e }, { CCI_REG8(0xc535), 0x0b }, + { CCI_REG8(0xc536), 0x18 }, { CCI_REG8(0xc537), 0x12 }, + { CCI_REG8(0xc538), 0x0e }, { CCI_REG8(0xc539), 0x0b }, + { CCI_REG8(0xc53a), 0x18 }, { CCI_REG8(0xc53b), 0x12 }, + { CCI_REG8(0xc53c), 0x0c }, { CCI_REG8(0xc53d), 0x08 }, + { CCI_REG8(0xc53e), 0x18 }, { CCI_REG8(0xc53f), 0x12 }, + { CCI_REG8(0xc540), 0x0e }, { CCI_REG8(0xc541), 0x0b }, + { CCI_REG8(0xc542), 0x18 }, { CCI_REG8(0xc543), 0x12 }, + { CCI_REG8(0xc544), 0x0e }, { CCI_REG8(0xc545), 0x0b }, + { CCI_REG8(0xc546), 0x18 }, { CCI_REG8(0xc547), 0x12 }, + { CCI_REG8(0xc548), 0x0e }, { CCI_REG8(0xc549), 0x0b }, + { CCI_REG8(0x3701), 0x18 }, { CCI_REG8(0x3702), 0x38 }, + { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3708), 0x26 }, + { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a1d), 0x18 }, + { CCI_REG8(0x3a1e), 0x18 }, { CCI_REG8(0x3a21), 0x18 }, + { CCI_REG8(0x3a22), 0x18 }, { CCI_REG8(0x39fb), 0x18 }, + { CCI_REG8(0x39fc), 0x18 }, { CCI_REG8(0x39fd), 0x18 }, + { CCI_REG8(0x39fe), 0x18 }, { CCI_REG8(0xc44a), 0x08 }, + { CCI_REG8(0xc44c), 0x08 }, { CCI_REG8(0xc5e8), 0x0a }, + { CCI_REG8(0xc5ea), 0x0a }, { CCI_REG8(0x391d), 0x54 }, + { CCI_REG8(0x391e), 0xca }, { CCI_REG8(0x3991), 0x0c }, + { CCI_REG8(0x399d), 0x0c }, { CCI_REG8(0x3744), 0x24 }, + { CCI_REG8(0x374b), 0x0c }, { CCI_REG8(0x3be7), 0x1e }, + { CCI_REG8(0x3be8), 0x26 }, { CCI_REG8(0x3a50), 0x14 }, + { CCI_REG8(0x3a54), 0x14 }, { CCI_REG8(0x3add), 0x1f }, + { CCI_REG8(0x3adf), 0x24 }, { CCI_REG8(0x3aef), 0x1f }, + { CCI_REG8(0x3af0), 0x24 }, { CCI_REG8(0xc57f), 0x30 }, + { CCI_REG8(0xc580), 0x30 }, { CCI_REG8(0xc581), 0x30 }, + { CCI_REG8(0xc582), 0x30 }, { CCI_REG8(0xc583), 0x30 }, + { CCI_REG8(0xc584), 0x30 }, { CCI_REG8(0xc585), 0x30 }, + { CCI_REG8(0xc586), 0x30 }, { CCI_REG8(0xc587), 0x30 }, + { CCI_REG8(0xc588), 0x30 }, { CCI_REG8(0xc589), 0x30 }, + { CCI_REG8(0xc58a), 0x30 }, { CCI_REG8(0xc58b), 0x30 }, + { CCI_REG8(0xc58c), 0x30 }, { CCI_REG8(0xc58d), 0x30 }, + { CCI_REG8(0xc58e), 0x30 }, { CCI_REG8(0xc58f), 0x30 }, + { CCI_REG8(0xc590), 0x30 }, { CCI_REG8(0xc591), 0x30 }, + { CCI_REG8(0xc592), 0x30 }, { CCI_REG8(0xc598), 0x30 }, + { CCI_REG8(0xc599), 0x30 }, { CCI_REG8(0xc59a), 0x30 }, + { CCI_REG8(0xc59b), 0x30 }, { CCI_REG8(0xc59c), 0x30 }, + { CCI_REG8(0xc59d), 0x30 }, { CCI_REG8(0xc59e), 0x30 }, + { CCI_REG8(0xc59f), 0x30 }, { CCI_REG8(0xc5a0), 0x30 }, + { CCI_REG8(0xc5a1), 0x30 }, { CCI_REG8(0xc5a2), 0x30 }, + { CCI_REG8(0xc5a3), 0x30 }, { CCI_REG8(0xc5a4), 0x30 }, + { CCI_REG8(0xc5a5), 0x30 }, { CCI_REG8(0xc5a6), 0x30 }, + { CCI_REG8(0xc5a7), 0x30 }, { CCI_REG8(0xc5a8), 0x30 }, + { CCI_REG8(0xc5a9), 0x30 }, { CCI_REG8(0xc5aa), 0x30 }, + { CCI_REG8(0xc5ab), 0x30 }, { CCI_REG8(0xc5b1), 0x38 }, + { CCI_REG8(0xc5b2), 0x38 }, { CCI_REG8(0xc5b3), 0x38 }, + { CCI_REG8(0xc5b4), 0x38 }, { CCI_REG8(0xc5b5), 0x38 }, + { CCI_REG8(0xc5b6), 0x38 }, { CCI_REG8(0xc5b7), 0x38 }, + { CCI_REG8(0xc5b8), 0x38 }, { CCI_REG8(0xc5b9), 0x38 }, + { CCI_REG8(0xc5ba), 0x38 }, { CCI_REG8(0xc5bb), 0x38 }, + { CCI_REG8(0xc5bc), 0x38 }, { CCI_REG8(0xc5bd), 0x38 }, + { CCI_REG8(0xc5be), 0x38 }, { CCI_REG8(0xc5bf), 0x38 }, + { CCI_REG8(0xc5c0), 0x38 }, { CCI_REG8(0xc5c1), 0x38 }, + { CCI_REG8(0xc5c2), 0x38 }, { CCI_REG8(0xc5c3), 0x38 }, + { CCI_REG8(0xc5c4), 0x38 }, { CCI_REG8(0xc5ca), 0x38 }, + { CCI_REG8(0xc5cb), 0x38 }, { CCI_REG8(0xc5cc), 0x38 }, + { CCI_REG8(0xc5cd), 0x38 }, { CCI_REG8(0xc5ce), 0x38 }, + { CCI_REG8(0xc5cf), 0x38 }, { CCI_REG8(0xc5d0), 0x38 }, + { CCI_REG8(0xc5d1), 0x38 }, { CCI_REG8(0xc5d2), 0x38 }, + { CCI_REG8(0xc5d3), 0x38 }, { CCI_REG8(0xc5d4), 0x38 }, + { CCI_REG8(0xc5d5), 0x38 }, { CCI_REG8(0xc5d6), 0x38 }, + { CCI_REG8(0xc5d7), 0x38 }, { CCI_REG8(0xc5d8), 0x38 }, + { CCI_REG8(0xc5d9), 0x38 }, { CCI_REG8(0xc5da), 0x38 }, + { CCI_REG8(0xc5db), 0x38 }, { CCI_REG8(0xc5dc), 0x38 }, + { CCI_REG8(0xc5dd), 0x38 }, { CCI_REG8(0x3a60), 0x68 }, + { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc }, + { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3aed), 0x6e }, + { CCI_REG8(0x3af1), 0x73 }, { CCI_REG8(0x3992), 0x02 }, + { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x371d), 0x17 }, + { CCI_REG8(0x371f), 0x08 }, { CCI_REG8(0x3721), 0xc9 }, + { CCI_REG8(0x401e), 0x00 }, { CCI_REG8(0x401f), 0xf8 }, + { CCI_REG8(0x3642), 0x00 }, { CCI_REG8(0x3641), 0x7f }, + { CCI_REG8(0x3ac5), 0x0c }, { CCI_REG8(0x3ac6), 0x09 }, + { CCI_REG8(0x3ac7), 0x06 }, { CCI_REG8(0x3ac8), 0x02 }, + { CCI_REG8(0x3ac9), 0x0c }, { CCI_REG8(0x3aca), 0x09 }, + { CCI_REG8(0x3acb), 0x06 }, { CCI_REG8(0x3acc), 0x02 }, + { CCI_REG8(0x3acd), 0x0c }, { CCI_REG8(0x3ace), 0x09 }, + { CCI_REG8(0x3acf), 0x07 }, { CCI_REG8(0x3ad0), 0x04 }, + { CCI_REG8(0x3ad1), 0x0c }, { CCI_REG8(0x3ad2), 0x09 }, + { CCI_REG8(0x3ad3), 0x07 }, { CCI_REG8(0x3ad4), 0x04 }, + { CCI_REG8(0xc483), 0x0c }, { CCI_REG8(0xc484), 0x0c }, + { CCI_REG8(0xc485), 0x0c }, { CCI_REG8(0xc486), 0x0c }, + { CCI_REG8(0x3a2f), 0x0c }, { CCI_REG8(0x3a30), 0x09 }, + { CCI_REG8(0x3a31), 0x06 }, { CCI_REG8(0x3a32), 0x02 }, + { CCI_REG8(0x3a34), 0x0c }, { CCI_REG8(0x3a35), 0x09 }, + { CCI_REG8(0x3a36), 0x07 }, { CCI_REG8(0x3a37), 0x04 }, + { CCI_REG8(0x3a43), 0x0c }, { CCI_REG8(0x3a44), 0x09 }, + { CCI_REG8(0x3a45), 0x06 }, { CCI_REG8(0x3a46), 0x02 }, + { CCI_REG8(0x3a48), 0x0c }, { CCI_REG8(0x3a49), 0x09 }, + { CCI_REG8(0x3a4a), 0x07 }, { CCI_REG8(0x3a4b), 0x04 }, + { CCI_REG8(0xc487), 0x0c }, { CCI_REG8(0xc488), 0x0c }, + { CCI_REG8(0xc489), 0x0c }, { CCI_REG8(0xc48a), 0x0c }, + { CCI_REG8(0x3645), 0xbd }, { CCI_REG8(0x373f), 0x00 }, + { CCI_REG8(0x374f), 0x10 }, { CCI_REG8(0x3743), 0xc6 }, + { CCI_REG8(0x3717), 0x82 }, { CCI_REG8(0x3732), 0x07 }, + { CCI_REG8(0x3731), 0x16 }, { CCI_REG8(0x3730), 0x16 }, + { CCI_REG8(0x3828), 0x07 }, { CCI_REG8(0x3714), 0x68 }, + { CCI_REG8(0x371d), 0x02 }, { CCI_REG8(0x371f), 0x02 }, + { CCI_REG8(0x37e0), 0x00 }, { CCI_REG8(0x37e1), 0x03 }, + { CCI_REG8(0x37e2), 0x07 }, { CCI_REG8(0x3734), 0x3e }, + { CCI_REG8(0x3736), 0x02 }, { CCI_REG8(0x37e4), 0x36 }, + { CCI_REG8(0x37e9), 0x1c }, { CCI_REG8(0x37ea), 0x01 }, + { CCI_REG8(0x37eb), 0x0a }, { CCI_REG8(0x37ec), 0x1c }, + { CCI_REG8(0x37ed), 0x01 }, { CCI_REG8(0x37ee), 0x36 }, + { CCI_REG8(0x373b), 0x1c }, { CCI_REG8(0x373c), 0x02 }, + { CCI_REG8(0x37bb), 0x1c }, { CCI_REG8(0x37bc), 0x02 }, + { CCI_REG8(0x37b8), 0x0c }, { CCI_REG8(0x371c), 0x01 }, + { CCI_REG8(0x371e), 0x11 }, { CCI_REG8(0x371d), 0x01 }, + { CCI_REG8(0x371f), 0x01 }, { CCI_REG8(0x3721), 0x01 }, + { CCI_REG8(0x3725), 0x12 }, { CCI_REG8(0x37e3), 0x06 }, + { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37db), 0x0a }, + { CCI_REG8(0x37dc), 0x14 }, { CCI_REG8(0x3727), 0x20 }, + { CCI_REG8(0x37b2), 0x80 }, { CCI_REG8(0x37da), 0x04 }, + { CCI_REG8(0x37df), 0x01 }, { CCI_REG8(0x3731), 0x11 }, + { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37df), 0x01 }, + { CCI_REG8(0x37da), 0x03 }, { CCI_REG8(0x37b2), 0x80 }, + { CCI_REG8(0x3727), 0x20 }, { CCI_REG8(0x4883), 0x26 }, + { CCI_REG8(0x488b), 0x88 }, { CCI_REG8(0x3d85), 0x1f }, + { CCI_REG8(0x3d81), 0x01 }, { CCI_REG8(0x3d84), 0x40 }, + { CCI_REG8(0x3d88), 0x00 }, { CCI_REG8(0x3d89), 0x00 }, + { CCI_REG8(0x3d8a), 0x0b }, { CCI_REG8(0x3d8b), 0xff }, + { CCI_REG8(0x4d00), 0x05 }, { CCI_REG8(0x4d01), 0xc4 }, + { CCI_REG8(0x4d02), 0xa3 }, { CCI_REG8(0x4d03), 0x8c }, + { CCI_REG8(0x4d04), 0xfb }, { CCI_REG8(0x4d05), 0xed }, + { CCI_REG8(0x4010), 0x28 }, { CCI_REG8(0x4030), 0x00 }, + { CCI_REG8(0x4031), 0x00 }, { CCI_REG8(0x4032), 0x00 }, + { CCI_REG8(0x4033), 0x00 }, { CCI_REG8(0x4034), 0x00 }, + { CCI_REG8(0x4035), 0x00 }, { CCI_REG8(0x4036), 0x00 }, + { CCI_REG8(0x4037), 0x00 }, { CCI_REG8(0x4040), 0x00 }, + { CCI_REG8(0x4041), 0x00 }, { CCI_REG8(0x4042), 0x00 }, + { CCI_REG8(0x4043), 0x00 }, { CCI_REG8(0x4044), 0x00 }, + { CCI_REG8(0x4045), 0x00 }, { CCI_REG8(0x4046), 0x00 }, + { CCI_REG8(0x4047), 0x00 }, { CCI_REG8(0x3400), 0x00 }, + { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc }, + { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 }, + { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 }, + { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 }, + { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 }, + { CCI_REG8(0x3053), 0x00 }, { CCI_REG8(0x3054), 0x00 }, + { CCI_REG8(0x3055), 0x00 }, { CCI_REG8(0x3056), 0x00 }, + { CCI_REG8(0x3057), 0x00 }, { CCI_REG8(0x3058), 0x00 }, + { CCI_REG8(0x305c), 0x00 }, { CCI_REG8(0x340c), 0x1f }, + { CCI_REG8(0x340d), 0x00 }, { CCI_REG8(0x3501), 0x01 }, + { CCI_REG8(0x3542), 0x48 }, { CCI_REG8(0x3582), 0x24 }, + { CCI_REG8(0x3015), 0xf1 }, { CCI_REG8(0x3018), 0xf2 }, + { CCI_REG8(0x301c), 0xf2 }, { CCI_REG8(0x301d), 0xf6 }, + { CCI_REG8(0x301e), 0xf1 }, { CCI_REG8(0x0100), 0x01 }, + { CCI_REG8(0xfff9), 0x08 }, { CCI_REG8(0x3900), 0xcd }, + { CCI_REG8(0x3901), 0xcd }, { CCI_REG8(0x3902), 0xcd }, + { CCI_REG8(0x3903), 0xcd }, { CCI_REG8(0x3904), 0xcd }, + { CCI_REG8(0x3905), 0xcd }, { CCI_REG8(0x3906), 0xcd }, + { CCI_REG8(0x3907), 0xcd }, { CCI_REG8(0x3908), 0xcd }, + { CCI_REG8(0x3909), 0xcd }, { CCI_REG8(0x390a), 0xcd }, + { CCI_REG8(0x390b), 0xcd }, { CCI_REG8(0x390c), 0xcd }, + { CCI_REG8(0x390d), 0xcd }, { CCI_REG8(0x390e), 0xcd }, + { CCI_REG8(0x390f), 0xcd }, { CCI_REG8(0x3910), 0xcd }, + { CCI_REG8(0x3911), 0xcd }, { CCI_REG8(0x3912), 0xcd }, + { CCI_REG8(0x3913), 0xcd }, { CCI_REG8(0x3914), 0xcd }, + { CCI_REG8(0x3915), 0xcd }, { CCI_REG8(0x3916), 0xcd }, + { CCI_REG8(0x3917), 0xcd }, { CCI_REG8(0x3918), 0xcd }, + { CCI_REG8(0x3919), 0xcd }, { CCI_REG8(0x391a), 0xcd }, + { CCI_REG8(0x391b), 0xcd }, { CCI_REG8(0x391c), 0xcd }, + { CCI_REG8(0x391d), 0xcd }, { CCI_REG8(0x391e), 0xcd }, + { CCI_REG8(0x391f), 0xcd }, { CCI_REG8(0x3920), 0xcd }, + { CCI_REG8(0x3921), 0xcd }, { CCI_REG8(0x3922), 0xcd }, + { CCI_REG8(0x3923), 0xcd }, { CCI_REG8(0x3924), 0xcd }, + { CCI_REG8(0x3925), 0xcd }, { CCI_REG8(0x3926), 0xcd }, + { CCI_REG8(0x3927), 0xcd }, { CCI_REG8(0x3928), 0xcd }, + { CCI_REG8(0x3929), 0xcd }, { CCI_REG8(0x392a), 0xcd }, + { CCI_REG8(0x392b), 0xcd }, { CCI_REG8(0x392c), 0xcd }, + { CCI_REG8(0x392d), 0xcd }, { CCI_REG8(0x392e), 0xcd }, + { CCI_REG8(0x392f), 0xcd }, { CCI_REG8(0x3930), 0xcd }, + { CCI_REG8(0x3931), 0xcd }, { CCI_REG8(0x3932), 0xcd }, + { CCI_REG8(0x3933), 0xcd }, { CCI_REG8(0x3934), 0xcd }, + { CCI_REG8(0x3935), 0xcd }, { CCI_REG8(0x3936), 0xcd }, + { CCI_REG8(0x3937), 0xcd }, { CCI_REG8(0x3938), 0xcd }, + { CCI_REG8(0x3939), 0xcd }, { CCI_REG8(0x393a), 0xcd }, + { CCI_REG8(0x393b), 0xcd }, { CCI_REG8(0x393c), 0xcd }, + { CCI_REG8(0x393d), 0xcd }, { CCI_REG8(0x393e), 0xcd }, + { CCI_REG8(0x393f), 0xcd }, { CCI_REG8(0x3940), 0xcd }, + { CCI_REG8(0x3941), 0xcd }, { CCI_REG8(0x3942), 0xcd }, + { CCI_REG8(0x3943), 0xcd }, { CCI_REG8(0x3944), 0xcd }, + { CCI_REG8(0x3945), 0xcd }, { CCI_REG8(0x3946), 0xcd }, + { CCI_REG8(0x3947), 0xcd }, { CCI_REG8(0x3948), 0xcd }, + { CCI_REG8(0x3949), 0xcd }, { CCI_REG8(0x394a), 0xcd }, + { CCI_REG8(0x394b), 0xcd }, { CCI_REG8(0x394c), 0xcd }, + { CCI_REG8(0x394d), 0xcd }, { CCI_REG8(0x394e), 0xcd }, + { CCI_REG8(0x394f), 0xcd }, { CCI_REG8(0x3950), 0xcd }, + { CCI_REG8(0x3951), 0xcd }, { CCI_REG8(0x3952), 0xcd }, + { CCI_REG8(0x3953), 0xcd }, { CCI_REG8(0x3954), 0xcd }, + { CCI_REG8(0x3955), 0xcd }, { CCI_REG8(0x3956), 0xcd }, + { CCI_REG8(0x3957), 0xcd }, { CCI_REG8(0x3958), 0xcd }, + { CCI_REG8(0x3959), 0xcd }, { CCI_REG8(0x395a), 0xcd }, + { CCI_REG8(0x395b), 0xcd }, { CCI_REG8(0x395c), 0xcd }, + { CCI_REG8(0x395d), 0xcd }, { CCI_REG8(0x395e), 0xcd }, + { CCI_REG8(0x395f), 0xcd }, { CCI_REG8(0x3960), 0xcd }, + { CCI_REG8(0x3961), 0xcd }, { CCI_REG8(0x3962), 0xcd }, + { CCI_REG8(0x3963), 0xcd }, { CCI_REG8(0x3964), 0xcd }, + { CCI_REG8(0x3965), 0xcd }, { CCI_REG8(0x3966), 0xcd }, + { CCI_REG8(0x3967), 0xcd }, { CCI_REG8(0x3968), 0xcd }, + { CCI_REG8(0x3969), 0xcd }, { CCI_REG8(0x396a), 0xcd }, + { CCI_REG8(0x396b), 0xcd }, { CCI_REG8(0x396c), 0xcd }, + { CCI_REG8(0x396d), 0xcd }, { CCI_REG8(0x396e), 0xcd }, + { CCI_REG8(0x396f), 0xcd }, { CCI_REG8(0x3970), 0xcd }, + { CCI_REG8(0x3971), 0xcd }, { CCI_REG8(0x3972), 0xcd }, + { CCI_REG8(0x3973), 0xcd }, { CCI_REG8(0x3974), 0xcd }, + { CCI_REG8(0x3975), 0xcd }, { CCI_REG8(0x3976), 0xcd }, + { CCI_REG8(0x3977), 0xcd }, { CCI_REG8(0x3978), 0xcd }, + { CCI_REG8(0x3979), 0xcd }, { CCI_REG8(0x397a), 0xcd }, + { CCI_REG8(0x397b), 0xcd }, { CCI_REG8(0x397c), 0xcd }, + { CCI_REG8(0x397d), 0xcd }, { CCI_REG8(0x397e), 0xcd }, + { CCI_REG8(0x397f), 0xcd }, { CCI_REG8(0x3980), 0xcd }, + { CCI_REG8(0x3981), 0xcd }, { CCI_REG8(0x3982), 0xcd }, + { CCI_REG8(0x3983), 0xcd }, { CCI_REG8(0x3984), 0xcd }, + { CCI_REG8(0x3985), 0xcd }, { CCI_REG8(0x3986), 0xcd }, + { CCI_REG8(0x3987), 0xcd }, { CCI_REG8(0x3988), 0xcd }, + { CCI_REG8(0x3989), 0xcd }, { CCI_REG8(0x398a), 0xcd }, + { CCI_REG8(0x398b), 0xcd }, { CCI_REG8(0x398c), 0xcd }, + { CCI_REG8(0x398d), 0xcd }, { CCI_REG8(0x398e), 0xcd }, + { CCI_REG8(0x398f), 0xcd }, { CCI_REG8(0x3990), 0xcd }, + { CCI_REG8(0x3991), 0xcd }, { CCI_REG8(0x3992), 0xcd }, + { CCI_REG8(0x3993), 0xcd }, { CCI_REG8(0x3994), 0xcd }, + { CCI_REG8(0x3995), 0xcd }, { CCI_REG8(0x3996), 0xcd }, + { CCI_REG8(0x3997), 0xcd }, { CCI_REG8(0x3998), 0xcd }, + { CCI_REG8(0x3999), 0xcd }, { CCI_REG8(0x399a), 0xcd }, + { CCI_REG8(0x399b), 0xcd }, { CCI_REG8(0x399c), 0xcd }, + { CCI_REG8(0x399d), 0xcd }, { CCI_REG8(0x399e), 0xcd }, + { CCI_REG8(0x399f), 0xcd }, { CCI_REG8(0x39a0), 0xcd }, + { CCI_REG8(0x39a1), 0xcd }, { CCI_REG8(0x39a2), 0xcd }, + { CCI_REG8(0x39a3), 0xcd }, { CCI_REG8(0x39a4), 0xcd }, + { CCI_REG8(0x39a5), 0xcd }, { CCI_REG8(0x39a6), 0xcd }, + { CCI_REG8(0x39a7), 0xcd }, { CCI_REG8(0x39a8), 0xcd }, + { CCI_REG8(0x39a9), 0xcd }, { CCI_REG8(0x39aa), 0xcd }, + { CCI_REG8(0x39ab), 0xcd }, { CCI_REG8(0x39ac), 0xcd }, + { CCI_REG8(0x39ad), 0xcd }, { CCI_REG8(0x39ae), 0xcd }, + { CCI_REG8(0x39af), 0xcd }, { CCI_REG8(0x39b0), 0xcd }, + { CCI_REG8(0x39b1), 0xcd }, { CCI_REG8(0x39b2), 0xcd }, + { CCI_REG8(0x39b3), 0xcd }, { CCI_REG8(0x39b4), 0xcd }, + { CCI_REG8(0x39b5), 0xcd }, { CCI_REG8(0x39b6), 0xcd }, + { CCI_REG8(0x39b7), 0xcd }, { CCI_REG8(0x39b8), 0xcd }, + { CCI_REG8(0x39b9), 0xcd }, { CCI_REG8(0x39ba), 0xcd }, + { CCI_REG8(0x39bb), 0xcd }, { CCI_REG8(0x39bc), 0xcd }, + { CCI_REG8(0x39bd), 0xcd }, { CCI_REG8(0x39be), 0xcd }, + { CCI_REG8(0x39bf), 0xcd }, { CCI_REG8(0x39c0), 0xcd }, + { CCI_REG8(0x39c1), 0xcd }, { CCI_REG8(0x39c2), 0xcd }, + { CCI_REG8(0x39c3), 0xcd }, { CCI_REG8(0x39c4), 0xcd }, + { CCI_REG8(0x39c5), 0xcd }, { CCI_REG8(0x39c6), 0xcd }, + { CCI_REG8(0x39c7), 0xcd }, { CCI_REG8(0x39c8), 0xcd }, + { CCI_REG8(0x39c9), 0xcd }, { CCI_REG8(0x39ca), 0xcd }, + { CCI_REG8(0x39cb), 0xcd }, { CCI_REG8(0x39cc), 0xcd }, + { CCI_REG8(0x39cd), 0xcd }, { CCI_REG8(0x39ce), 0xcd }, + { CCI_REG8(0x39cf), 0xcd }, { CCI_REG8(0x39d0), 0xcd }, + { CCI_REG8(0x39d1), 0xcd }, { CCI_REG8(0x39d2), 0xcd }, + { CCI_REG8(0x39d3), 0xcd }, { CCI_REG8(0x39d4), 0xcd }, + { CCI_REG8(0x39d5), 0xcd }, { CCI_REG8(0x39d6), 0xcd }, + { CCI_REG8(0x39d7), 0xcd }, { CCI_REG8(0x39d8), 0xcd }, + { CCI_REG8(0x39d9), 0xcd }, { CCI_REG8(0x39da), 0xcd }, + { CCI_REG8(0x39db), 0xcd }, { CCI_REG8(0x39dc), 0xcd }, + { CCI_REG8(0x39dd), 0xcd }, { CCI_REG8(0x39de), 0xcd }, + { CCI_REG8(0x39df), 0xcd }, { CCI_REG8(0x39e0), 0xcd }, + { CCI_REG8(0x39e1), 0x40 }, { CCI_REG8(0x39e2), 0x40 }, + { CCI_REG8(0x39e3), 0x40 }, { CCI_REG8(0x39e4), 0x40 }, + { CCI_REG8(0x39e5), 0x40 }, { CCI_REG8(0x39e6), 0x40 }, + { CCI_REG8(0x39e7), 0x40 }, { CCI_REG8(0x39e8), 0x40 }, + { CCI_REG8(0x39e9), 0x40 }, { CCI_REG8(0x39ea), 0x40 }, + { CCI_REG8(0x39eb), 0x40 }, { CCI_REG8(0x39ec), 0x40 }, + { CCI_REG8(0x39ed), 0x40 }, { CCI_REG8(0x39ee), 0x40 }, + { CCI_REG8(0x39ef), 0x40 }, { CCI_REG8(0x39f0), 0x40 }, + { CCI_REG8(0x39f1), 0x40 }, { CCI_REG8(0x39f2), 0x40 }, + { CCI_REG8(0x39f3), 0x40 }, { CCI_REG8(0x39f4), 0x40 }, + { CCI_REG8(0x39f5), 0x40 }, { CCI_REG8(0x39f6), 0x40 }, + { CCI_REG8(0x39f7), 0x40 }, { CCI_REG8(0x39f8), 0x40 }, + { CCI_REG8(0x39f9), 0x40 }, { CCI_REG8(0x39fa), 0x40 }, + { CCI_REG8(0x39fb), 0x40 }, { CCI_REG8(0x39fc), 0x40 }, + { CCI_REG8(0x39fd), 0x40 }, { CCI_REG8(0x39fe), 0x40 }, + { CCI_REG8(0x39ff), 0x40 }, { CCI_REG8(0x3a00), 0x40 }, + { CCI_REG8(0x3a01), 0x40 }, { CCI_REG8(0x3a02), 0x40 }, + { CCI_REG8(0x3a03), 0x40 }, { CCI_REG8(0x3a04), 0x40 }, + { CCI_REG8(0x3a05), 0x40 }, { CCI_REG8(0x3a06), 0x40 }, + { CCI_REG8(0x3a07), 0x40 }, { CCI_REG8(0x3a08), 0x40 }, + { CCI_REG8(0x3a09), 0x40 }, { CCI_REG8(0x3a0a), 0x40 }, + { CCI_REG8(0x3a0b), 0x40 }, { CCI_REG8(0x3a0c), 0x40 }, + { CCI_REG8(0x3a0d), 0x40 }, { CCI_REG8(0x3a0e), 0x40 }, + { CCI_REG8(0x3a0f), 0x40 }, { CCI_REG8(0x3a10), 0x40 }, + { CCI_REG8(0x3a11), 0x40 }, { CCI_REG8(0x3a12), 0x40 }, + { CCI_REG8(0x3a13), 0x40 }, { CCI_REG8(0x3a14), 0x40 }, + { CCI_REG8(0x3a15), 0x40 }, { CCI_REG8(0x3a16), 0x40 }, + { CCI_REG8(0x3a17), 0x40 }, { CCI_REG8(0x3a18), 0x40 }, + { CCI_REG8(0x3a19), 0x40 }, { CCI_REG8(0x3a1a), 0x40 }, + { CCI_REG8(0x3a1b), 0x40 }, { CCI_REG8(0x3a1c), 0x40 }, + { CCI_REG8(0x3a1d), 0x40 }, { CCI_REG8(0x3a1e), 0x40 }, + { CCI_REG8(0x3a1f), 0x40 }, { CCI_REG8(0x3a20), 0x40 }, + { CCI_REG8(0x3a21), 0x40 }, { CCI_REG8(0x3a22), 0x40 }, + { CCI_REG8(0x3a23), 0x40 }, { CCI_REG8(0x3a24), 0x40 }, + { CCI_REG8(0x3a25), 0x40 }, { CCI_REG8(0x3a26), 0x40 }, + { CCI_REG8(0x3a27), 0x40 }, { CCI_REG8(0x3a28), 0x40 }, + { CCI_REG8(0x3a29), 0x40 }, { CCI_REG8(0x3a2a), 0x40 }, + { CCI_REG8(0x3a2b), 0x40 }, { CCI_REG8(0x3a2c), 0x40 }, + { CCI_REG8(0x3a2d), 0x40 }, { CCI_REG8(0x3a2e), 0x40 }, + { CCI_REG8(0x3a2f), 0x40 }, { CCI_REG8(0x3a30), 0x40 }, + { CCI_REG8(0x3a31), 0x40 }, { CCI_REG8(0x3a32), 0x40 }, + { CCI_REG8(0x3a33), 0x40 }, { CCI_REG8(0x3a34), 0x40 }, + { CCI_REG8(0x3a35), 0x40 }, { CCI_REG8(0x3a36), 0x40 }, + { CCI_REG8(0x3a37), 0x40 }, { CCI_REG8(0x3a38), 0x40 }, + { CCI_REG8(0x3a39), 0x40 }, { CCI_REG8(0x3a3a), 0x40 }, + { CCI_REG8(0x3a3b), 0xcd }, { CCI_REG8(0x3a3c), 0xcd }, + { CCI_REG8(0x3a3d), 0xcd }, { CCI_REG8(0x3a3e), 0xcd }, + { CCI_REG8(0x3a3f), 0xcd }, { CCI_REG8(0x3a40), 0xcd }, + { CCI_REG8(0x3a41), 0xcd }, { CCI_REG8(0x3a42), 0xcd }, + { CCI_REG8(0x3a43), 0xcd }, { CCI_REG8(0x3a44), 0xcd }, + { CCI_REG8(0x3a45), 0xcd }, { CCI_REG8(0x3a46), 0xcd }, + { CCI_REG8(0x3a47), 0xcd }, { CCI_REG8(0x3a48), 0xcd }, + { CCI_REG8(0x3a49), 0xcd }, { CCI_REG8(0x3a4a), 0xcd }, + { CCI_REG8(0x3a4b), 0xcd }, { CCI_REG8(0x3a4c), 0xcd }, + { CCI_REG8(0x3a4d), 0xcd }, { CCI_REG8(0x3a4e), 0xcd }, + { CCI_REG8(0x3a4f), 0xcd }, { CCI_REG8(0x3a50), 0xcd }, + { CCI_REG8(0x3a51), 0xcd }, { CCI_REG8(0x3a52), 0xcd }, + { CCI_REG8(0x3a53), 0xcd }, { CCI_REG8(0x3a54), 0xcd }, + { CCI_REG8(0x3a55), 0xcd }, { CCI_REG8(0x3a56), 0xcd }, + { CCI_REG8(0x3a57), 0xcd }, { CCI_REG8(0x3a58), 0xcd }, + { CCI_REG8(0x3a59), 0xcd }, { CCI_REG8(0x3a5a), 0xcd }, + { CCI_REG8(0x3a5b), 0xcd }, { CCI_REG8(0x3a5c), 0xcd }, + { CCI_REG8(0x3a5d), 0xcd }, { CCI_REG8(0x3a5e), 0xcd }, + { CCI_REG8(0x3a5f), 0xcd }, { CCI_REG8(0x3a60), 0xcd }, + { CCI_REG8(0x3a61), 0xcd }, { CCI_REG8(0x3a62), 0xcd }, + { CCI_REG8(0x3a63), 0xcd }, { CCI_REG8(0x3a64), 0xcd }, + { CCI_REG8(0x3a65), 0xcd }, { CCI_REG8(0x3a66), 0xcd }, + { CCI_REG8(0x3a67), 0xcd }, { CCI_REG8(0x3a68), 0xcd }, + { CCI_REG8(0x3a69), 0xcd }, { CCI_REG8(0x3a6a), 0xcd }, + { CCI_REG8(0x3a6b), 0xcd }, { CCI_REG8(0x3a6c), 0xcd }, + { CCI_REG8(0x3a6d), 0xcd }, { CCI_REG8(0x3a6e), 0xcd }, + { CCI_REG8(0x3a6f), 0xcd }, { CCI_REG8(0x3a70), 0xcd }, + { CCI_REG8(0x3a71), 0xcd }, { CCI_REG8(0x3a72), 0xcd }, + { CCI_REG8(0x3a73), 0xcd }, { CCI_REG8(0x3a74), 0xcd }, + { CCI_REG8(0x3a75), 0xcd }, { CCI_REG8(0x3a76), 0xcd }, + { CCI_REG8(0x3a77), 0xcd }, { CCI_REG8(0x3a78), 0xcd }, + { CCI_REG8(0x3a79), 0xcd }, { CCI_REG8(0x3a7a), 0xcd }, + { CCI_REG8(0x3a7b), 0xcd }, { CCI_REG8(0x3a7c), 0xcd }, + { CCI_REG8(0x3a7d), 0xcd }, { CCI_REG8(0x3a7e), 0xcd }, + { CCI_REG8(0x3a7f), 0xcd }, { CCI_REG8(0x3a80), 0xcd }, + { CCI_REG8(0x3a81), 0xcd }, { CCI_REG8(0x3a82), 0xcd }, + { CCI_REG8(0x3a83), 0xcd }, { CCI_REG8(0x3a84), 0xcd }, + { CCI_REG8(0x3a85), 0xcd }, { CCI_REG8(0x3a86), 0xcd }, + { CCI_REG8(0x3a87), 0xcd }, { CCI_REG8(0x3a88), 0xcd }, + { CCI_REG8(0x3a89), 0xcd }, { CCI_REG8(0x3a8a), 0xcd }, + { CCI_REG8(0x3a8b), 0xcd }, { CCI_REG8(0x3a8c), 0xcd }, + { CCI_REG8(0x3a8d), 0xcd }, { CCI_REG8(0x3a8e), 0xcd }, + { CCI_REG8(0x3a8f), 0xcd }, { CCI_REG8(0x3a90), 0xcd }, + { CCI_REG8(0x3a91), 0xcd }, { CCI_REG8(0x3a92), 0xcd }, + { CCI_REG8(0x3a93), 0xcd }, { CCI_REG8(0x3a94), 0xcd }, + { CCI_REG8(0x3a95), 0x40 }, { CCI_REG8(0x3a96), 0x40 }, + { CCI_REG8(0x3a97), 0x40 }, { CCI_REG8(0x3a98), 0x40 }, + { CCI_REG8(0x3a99), 0x40 }, { CCI_REG8(0x3a9a), 0x40 }, + { CCI_REG8(0x3a9b), 0x40 }, { CCI_REG8(0x3a9c), 0x40 }, + { CCI_REG8(0x3a9d), 0x40 }, { CCI_REG8(0x3a9e), 0x40 }, + { CCI_REG8(0x3a9f), 0x40 }, { CCI_REG8(0x3aa0), 0x40 }, + { CCI_REG8(0x3aa1), 0x40 }, { CCI_REG8(0x3aa2), 0x40 }, + { CCI_REG8(0x3aa3), 0x40 }, { CCI_REG8(0x3aa4), 0x40 }, + { CCI_REG8(0x3aa5), 0x40 }, { CCI_REG8(0x3aa6), 0x40 }, + { CCI_REG8(0x3aa7), 0x40 }, { CCI_REG8(0x3aa8), 0x40 }, + { CCI_REG8(0x3aa9), 0x40 }, { CCI_REG8(0x3aaa), 0x40 }, + { CCI_REG8(0x3aab), 0x40 }, { CCI_REG8(0x3aac), 0x40 }, + { CCI_REG8(0x3aad), 0x40 }, { CCI_REG8(0x3aae), 0x40 }, + { CCI_REG8(0x3aaf), 0x40 }, { CCI_REG8(0x3ab0), 0x40 }, + { CCI_REG8(0x3ab1), 0x40 }, { CCI_REG8(0x3ab2), 0x40 }, + { CCI_REG8(0x3ab3), 0x40 }, { CCI_REG8(0x3ab4), 0x40 }, + { CCI_REG8(0x3ab5), 0x40 }, { CCI_REG8(0x3ab6), 0x40 }, + { CCI_REG8(0x3ab7), 0x40 }, { CCI_REG8(0x3ab8), 0x40 }, + { CCI_REG8(0x3ab9), 0x40 }, { CCI_REG8(0x3aba), 0x40 }, + { CCI_REG8(0x3abb), 0x40 }, { CCI_REG8(0x3abc), 0x40 }, + { CCI_REG8(0x3abd), 0x40 }, { CCI_REG8(0x3abe), 0x40 }, + { CCI_REG8(0x3abf), 0x40 }, { CCI_REG8(0x3ac0), 0x40 }, + { CCI_REG8(0x3ac1), 0x40 }, { CCI_REG8(0x3ac2), 0x40 }, + { CCI_REG8(0x3ac3), 0x40 }, { CCI_REG8(0x3ac4), 0x40 }, + { CCI_REG8(0x3ac5), 0x40 }, { CCI_REG8(0x3ac6), 0x40 }, + { CCI_REG8(0x3ac7), 0x40 }, { CCI_REG8(0x3ac8), 0x40 }, + { CCI_REG8(0x3ac9), 0x40 }, { CCI_REG8(0x3aca), 0x40 }, + { CCI_REG8(0x3acb), 0x40 }, { CCI_REG8(0x3acc), 0x40 }, + { CCI_REG8(0x3acd), 0x40 }, { CCI_REG8(0x3ace), 0x40 }, + { CCI_REG8(0x3acf), 0x40 }, { CCI_REG8(0x3ad0), 0x40 }, + { CCI_REG8(0x3ad1), 0x40 }, { CCI_REG8(0x3ad2), 0x40 }, + { CCI_REG8(0x3ad3), 0x40 }, { CCI_REG8(0x3ad4), 0x40 }, + { CCI_REG8(0x3ad5), 0x40 }, { CCI_REG8(0x3ad6), 0x40 }, + { CCI_REG8(0x3ad7), 0x40 }, { CCI_REG8(0x3ad8), 0x40 }, + { CCI_REG8(0x3ad9), 0x40 }, { CCI_REG8(0x3ada), 0x40 }, + { CCI_REG8(0x3adb), 0x40 }, { CCI_REG8(0x3adc), 0x40 }, + { CCI_REG8(0x3add), 0x40 }, { CCI_REG8(0x3ade), 0x40 }, + { CCI_REG8(0x3adf), 0x40 }, { CCI_REG8(0x3ae0), 0x40 }, + { CCI_REG8(0x3ae1), 0x40 }, { CCI_REG8(0x3ae2), 0x40 }, + { CCI_REG8(0x3ae3), 0x40 }, { CCI_REG8(0x3ae4), 0x40 }, + { CCI_REG8(0x3ae5), 0x40 }, { CCI_REG8(0x3ae6), 0x40 }, + { CCI_REG8(0x3ae7), 0x40 }, { CCI_REG8(0x3ae8), 0x40 }, + { CCI_REG8(0x3ae9), 0x40 }, { CCI_REG8(0x3aea), 0x40 }, + { CCI_REG8(0x3aeb), 0x40 }, { CCI_REG8(0x3aec), 0x40 }, + { CCI_REG8(0x3aed), 0x40 }, { CCI_REG8(0x3aee), 0x40 }, + { CCI_REG8(0x3aef), 0xcd }, { CCI_REG8(0x3af0), 0xcd }, + { CCI_REG8(0x3af1), 0xcd }, { CCI_REG8(0x3af2), 0xcd }, + { CCI_REG8(0x3af3), 0xcd }, { CCI_REG8(0x3af4), 0xcd }, + { CCI_REG8(0x3af5), 0xcd }, { CCI_REG8(0x3af6), 0xcd }, + { CCI_REG8(0x3af7), 0xcd }, { CCI_REG8(0x3af8), 0xcd }, + { CCI_REG8(0x3af9), 0xcd }, { CCI_REG8(0x3afa), 0xcd }, + { CCI_REG8(0x3afb), 0xcd }, { CCI_REG8(0x3afc), 0xcd }, + { CCI_REG8(0x3afd), 0xcd }, { CCI_REG8(0x3afe), 0xcd }, + { CCI_REG8(0x3aff), 0xcd }, { CCI_REG8(0x3b00), 0xcd }, + { CCI_REG8(0x3b01), 0xcd }, { CCI_REG8(0x3b02), 0xcd }, + { CCI_REG8(0x3b03), 0xcd }, { CCI_REG8(0x3b04), 0xcd }, + { CCI_REG8(0x3b05), 0xcd }, { CCI_REG8(0x3b06), 0xcd }, + { CCI_REG8(0x3b07), 0xcd }, { CCI_REG8(0x3b08), 0xcd }, + { CCI_REG8(0x3b09), 0xcd }, { CCI_REG8(0x3b0a), 0xcd }, + { CCI_REG8(0x3b0b), 0xcd }, { CCI_REG8(0x3b0c), 0xcd }, + { CCI_REG8(0x3b0d), 0xcd }, { CCI_REG8(0x3b0e), 0xcd }, + { CCI_REG8(0x3b0f), 0xcd }, { CCI_REG8(0x3b10), 0xcd }, + { CCI_REG8(0x3b11), 0xcd }, { CCI_REG8(0x3b12), 0xcd }, + { CCI_REG8(0x3b13), 0xcd }, { CCI_REG8(0x3b14), 0xcd }, + { CCI_REG8(0x3b15), 0xcd }, { CCI_REG8(0x3b16), 0xcd }, + { CCI_REG8(0x3b17), 0xcd }, { CCI_REG8(0x3b18), 0xcd }, + { CCI_REG8(0x3b19), 0xcd }, { CCI_REG8(0x3b1a), 0xcd }, + { CCI_REG8(0x3b1b), 0xcd }, { CCI_REG8(0x3b1c), 0xcd }, + { CCI_REG8(0x3b1d), 0xcd }, { CCI_REG8(0x3b1e), 0xcd }, + { CCI_REG8(0x3b1f), 0xcd }, { CCI_REG8(0x3b20), 0xcd }, + { CCI_REG8(0x3b21), 0xcd }, { CCI_REG8(0x3b22), 0xcd }, + { CCI_REG8(0x3b23), 0xcd }, { CCI_REG8(0x3b24), 0xcd }, + { CCI_REG8(0x3b25), 0xcd }, { CCI_REG8(0x3b26), 0xcd }, + { CCI_REG8(0x3b27), 0xcd }, { CCI_REG8(0x3b28), 0xcd }, + { CCI_REG8(0x3b29), 0xcd }, { CCI_REG8(0x3b2a), 0xcd }, + { CCI_REG8(0x3b2b), 0xcd }, { CCI_REG8(0x3b2c), 0xcd }, + { CCI_REG8(0x3b2d), 0xcd }, { CCI_REG8(0x3b2e), 0xcd }, + { CCI_REG8(0x3b2f), 0xcd }, { CCI_REG8(0x3b30), 0xcd }, + { CCI_REG8(0x3b31), 0xcd }, { CCI_REG8(0x3b32), 0xcd }, + { CCI_REG8(0x3b33), 0xcd }, { CCI_REG8(0x3b34), 0xcd }, + { CCI_REG8(0x3b35), 0xcd }, { CCI_REG8(0x3b36), 0xcd }, + { CCI_REG8(0x3b37), 0xcd }, { CCI_REG8(0x3b38), 0xcd }, + { CCI_REG8(0x3b39), 0xcd }, { CCI_REG8(0x3b3a), 0xcd }, + { CCI_REG8(0x3b3b), 0xcd }, { CCI_REG8(0x3b3c), 0xcd }, + { CCI_REG8(0x3b3d), 0xcd }, { CCI_REG8(0x3b3e), 0xcd }, + { CCI_REG8(0x3b3f), 0xcd }, { CCI_REG8(0x3b40), 0xcd }, + { CCI_REG8(0x3b41), 0xcd }, { CCI_REG8(0x3b42), 0xcd }, + { CCI_REG8(0x3b43), 0xcd }, { CCI_REG8(0x3b44), 0xcd }, + { CCI_REG8(0x3b45), 0xcd }, { CCI_REG8(0x3b46), 0xcd }, + { CCI_REG8(0x3b47), 0xcd }, { CCI_REG8(0x3b48), 0xcd }, + { CCI_REG8(0x3b49), 0xcd }, { CCI_REG8(0x3b4a), 0xcd }, + { CCI_REG8(0x3b4b), 0xcd }, { CCI_REG8(0x3b4c), 0xcd }, + { CCI_REG8(0x3b4d), 0xcd }, { CCI_REG8(0x3b4e), 0xcd }, + { CCI_REG8(0x3b4f), 0xcd }, { CCI_REG8(0x3b50), 0xcd }, + { CCI_REG8(0x3b51), 0xcd }, { CCI_REG8(0x3b52), 0xcd }, + { CCI_REG8(0x3b53), 0xcd }, { CCI_REG8(0x3b54), 0xcd }, + { CCI_REG8(0x3b55), 0xcd }, { CCI_REG8(0x3b56), 0xcd }, + { CCI_REG8(0x3b57), 0xcd }, { CCI_REG8(0x3b58), 0xcd }, + { CCI_REG8(0x3b59), 0xcd }, { CCI_REG8(0x3b5a), 0xcd }, + { CCI_REG8(0x3b5b), 0xcd }, { CCI_REG8(0x3b5c), 0xcd }, + { CCI_REG8(0x3b5d), 0xcd }, { CCI_REG8(0x3b5e), 0xcd }, + { CCI_REG8(0x3b5f), 0xcd }, { CCI_REG8(0x3b60), 0xcd }, + { CCI_REG8(0x3b61), 0xcd }, { CCI_REG8(0x3b62), 0xcd }, + { CCI_REG8(0x3b63), 0xcd }, { CCI_REG8(0x3b64), 0xcd }, + { CCI_REG8(0x3b65), 0xcd }, { CCI_REG8(0x3b66), 0xcd }, + { CCI_REG8(0x3b67), 0xcd }, { CCI_REG8(0x3b68), 0xcd }, + { CCI_REG8(0x3b69), 0xcd }, { CCI_REG8(0x3b6a), 0xcd }, + { CCI_REG8(0x3b6b), 0xcd }, { CCI_REG8(0x3b6c), 0xcd }, + { CCI_REG8(0x3b6d), 0xcd }, { CCI_REG8(0x3b6e), 0xcd }, + { CCI_REG8(0x3b6f), 0xcd }, { CCI_REG8(0x3b70), 0xcd }, + { CCI_REG8(0x3b71), 0xcd }, { CCI_REG8(0x3b72), 0xcd }, + { CCI_REG8(0x3b73), 0xcd }, { CCI_REG8(0x3b74), 0xcd }, + { CCI_REG8(0x3b75), 0xcd }, { CCI_REG8(0x3b76), 0xcd }, + { CCI_REG8(0x3b77), 0xcd }, { CCI_REG8(0x3b78), 0xcd }, + { CCI_REG8(0x3b79), 0xcd }, { CCI_REG8(0x3b7a), 0xcd }, + { CCI_REG8(0x3b7b), 0xcd }, { CCI_REG8(0x3b7c), 0xcd }, + { CCI_REG8(0x3b7d), 0xcd }, { CCI_REG8(0x3b7e), 0xcd }, + { CCI_REG8(0x3b7f), 0xcd }, { CCI_REG8(0x3b80), 0xcd }, + { CCI_REG8(0x3b81), 0xcd }, { CCI_REG8(0x3b82), 0xcd }, + { CCI_REG8(0x3b83), 0xcd }, { CCI_REG8(0x3b84), 0xcd }, + { CCI_REG8(0x3b85), 0xcd }, { CCI_REG8(0x3b86), 0xcd }, + { CCI_REG8(0x3b87), 0xcd }, { CCI_REG8(0x3b88), 0xcd }, + { CCI_REG8(0x3b89), 0xcd }, { CCI_REG8(0x3b8a), 0xcd }, + { CCI_REG8(0x3b8b), 0xcd }, { CCI_REG8(0x3b8c), 0xcd }, + { CCI_REG8(0x3b8d), 0xcd }, { CCI_REG8(0x3b8e), 0xcd }, + { CCI_REG8(0x3b8f), 0xcd }, { CCI_REG8(0x3b90), 0xcd }, + { CCI_REG8(0x3b91), 0xcd }, { CCI_REG8(0x3b92), 0xcd }, + { CCI_REG8(0x3b93), 0xcd }, { CCI_REG8(0x3b94), 0xcd }, + { CCI_REG8(0x3b95), 0xcd }, { CCI_REG8(0x3b96), 0xcd }, + { CCI_REG8(0x3b97), 0xcd }, { CCI_REG8(0x3b98), 0xcd }, + { CCI_REG8(0x3b99), 0xcd }, { CCI_REG8(0x3b9a), 0xcd }, + { CCI_REG8(0x3b9b), 0xcd }, { CCI_REG8(0x3b9c), 0xcd }, + { CCI_REG8(0x3b9d), 0xcd }, { CCI_REG8(0x3b9e), 0xcd }, + { CCI_REG8(0x3b9f), 0xcd }, { CCI_REG8(0x3ba0), 0xcd }, + { CCI_REG8(0x3ba1), 0xcd }, { CCI_REG8(0x3ba2), 0xcd }, + { CCI_REG8(0x3ba3), 0xcd }, { CCI_REG8(0x3ba4), 0xcd }, + { CCI_REG8(0x3ba5), 0xcd }, { CCI_REG8(0x3ba6), 0xcd }, + { CCI_REG8(0x3ba7), 0xcd }, { CCI_REG8(0x3ba8), 0xcd }, + { CCI_REG8(0x3ba9), 0xcd }, { CCI_REG8(0x3baa), 0xcd }, + { CCI_REG8(0x3bab), 0xcd }, { CCI_REG8(0x3bac), 0xcd }, + { CCI_REG8(0x3bad), 0xcd }, { CCI_REG8(0x3bae), 0xcd }, + { CCI_REG8(0x3baf), 0xcd }, { CCI_REG8(0x3bb0), 0xcd }, + { CCI_REG8(0x3bb1), 0xcd }, { CCI_REG8(0x3bb2), 0xcd }, + { CCI_REG8(0x3bb3), 0xcd }, { CCI_REG8(0x3bb4), 0xcd }, + { CCI_REG8(0x3bb5), 0xcd }, { CCI_REG8(0x3bb6), 0xcd }, + { CCI_REG8(0x3bb7), 0xcd }, { CCI_REG8(0x3bb8), 0xcd }, + { CCI_REG8(0x3bb9), 0xcd }, { CCI_REG8(0x3bba), 0xcd }, + { CCI_REG8(0x3bbb), 0xcd }, { CCI_REG8(0x3bbc), 0xcd }, + { CCI_REG8(0x3bbd), 0xcd }, { CCI_REG8(0x3bbe), 0xcd }, + { CCI_REG8(0x3bbf), 0xcd }, { CCI_REG8(0x3bc0), 0xcd }, + { CCI_REG8(0x3bc1), 0xcd }, { CCI_REG8(0x3bc2), 0xcd }, + { CCI_REG8(0x3bc3), 0xcd }, { CCI_REG8(0x3bc4), 0xcd }, + { CCI_REG8(0x3bc5), 0xcd }, { CCI_REG8(0x3bc6), 0xcd }, + { CCI_REG8(0x3bc7), 0xcd }, { CCI_REG8(0x3bc8), 0xcd }, + { CCI_REG8(0x3bc9), 0xcd }, { CCI_REG8(0x3bca), 0xcd }, + { CCI_REG8(0x3bcb), 0xcd }, { CCI_REG8(0x3bcc), 0xcd }, + { CCI_REG8(0x3bcd), 0xcd }, { CCI_REG8(0x3bce), 0xcd }, + { CCI_REG8(0x3bcf), 0xcd }, { CCI_REG8(0x3bd0), 0xcd }, + { CCI_REG8(0x3bd1), 0xcd }, { CCI_REG8(0x3bd2), 0xcd }, + { CCI_REG8(0x3bd3), 0xcd }, { CCI_REG8(0x3bd4), 0xcd }, + { CCI_REG8(0x3bd5), 0xcd }, { CCI_REG8(0x3bd6), 0xcd }, + { CCI_REG8(0x3bd7), 0xcd }, { CCI_REG8(0x3bd8), 0xcd }, + { CCI_REG8(0x3bd9), 0xcd }, { CCI_REG8(0x3bda), 0xcd }, + { CCI_REG8(0x3bdb), 0xcd }, { CCI_REG8(0x3bdc), 0xcd }, + { CCI_REG8(0x3bdd), 0xcd }, { CCI_REG8(0x3bde), 0xcd }, + { CCI_REG8(0x3bdf), 0xcd }, { CCI_REG8(0x3be0), 0xcd }, + { CCI_REG8(0x3be1), 0xcd }, { CCI_REG8(0x3be2), 0xcd }, + { CCI_REG8(0x3be3), 0xcd }, { CCI_REG8(0x3be4), 0xcd }, + { CCI_REG8(0x3be5), 0xcd }, { CCI_REG8(0x3be6), 0xcd }, + { CCI_REG8(0x3be7), 0xcd }, { CCI_REG8(0x3be8), 0xcd }, + { CCI_REG8(0x3be9), 0xcd }, { CCI_REG8(0x3bea), 0xcd }, + { CCI_REG8(0x3beb), 0xcd }, { CCI_REG8(0x3bec), 0xcd }, + { CCI_REG8(0x3bed), 0xcd }, { CCI_REG8(0x3bee), 0xcd }, + { CCI_REG8(0x3bef), 0xcd }, { CCI_REG8(0x3bf0), 0xcd }, + { CCI_REG8(0x3bf1), 0xcd }, { CCI_REG8(0x3bf2), 0xcd }, + { CCI_REG8(0x3bf3), 0xcd }, { CCI_REG8(0x3bf4), 0xcd }, + { CCI_REG8(0x3bf5), 0xcd }, { CCI_REG8(0x3bf6), 0xcd }, + { CCI_REG8(0x3bf7), 0xcd }, { CCI_REG8(0x3bf8), 0xcd }, + { CCI_REG8(0x3bf9), 0xcd }, { CCI_REG8(0x3bfa), 0xcd }, + { CCI_REG8(0x3bfb), 0xcd }, { CCI_REG8(0x3bfc), 0xcd }, + { CCI_REG8(0x3bfd), 0xcd }, { CCI_REG8(0x3bfe), 0xcd }, + { CCI_REG8(0x3bff), 0xcd }, { CCI_REG8(0x3c00), 0xcd }, + { CCI_REG8(0x3c01), 0xcd }, { CCI_REG8(0x3c02), 0xcd }, + { CCI_REG8(0x3c03), 0xcd }, { CCI_REG8(0x3c04), 0xcd }, + { CCI_REG8(0x3c05), 0xcd }, { CCI_REG8(0x3c06), 0xcd }, + { CCI_REG8(0x3c07), 0xcd }, { CCI_REG8(0x3c08), 0xcd }, + { CCI_REG8(0x3c09), 0xcd }, { CCI_REG8(0x3c0a), 0xcd }, + { CCI_REG8(0x3c0b), 0xcd }, { CCI_REG8(0x3c0c), 0xcd }, + { CCI_REG8(0x3c0d), 0xcd }, { CCI_REG8(0x3c0e), 0xcd }, + { CCI_REG8(0x3c0f), 0xcd }, { CCI_REG8(0x3c10), 0xcd }, + { CCI_REG8(0x3c11), 0xcd }, { CCI_REG8(0x3c12), 0xcd }, + { CCI_REG8(0x3c13), 0xcd }, { CCI_REG8(0x3c14), 0xcd }, + { CCI_REG8(0x3c15), 0xcd }, { CCI_REG8(0x3c16), 0xcd }, + { CCI_REG8(0x3c17), 0xcd }, { CCI_REG8(0x3c18), 0xcd }, + { CCI_REG8(0x3c19), 0xcd }, { CCI_REG8(0x3c1a), 0xcd }, + { CCI_REG8(0x3c1b), 0xcd }, { CCI_REG8(0x3c1c), 0xcd }, + { CCI_REG8(0x3c1d), 0xcd }, { CCI_REG8(0x3c1e), 0xcd }, + { CCI_REG8(0x3c1f), 0xcd }, { CCI_REG8(0x3c20), 0xcd }, + { CCI_REG8(0x3c21), 0xcd }, { CCI_REG8(0x3c22), 0xcd }, + { CCI_REG8(0x3c23), 0xcd }, { CCI_REG8(0x3c24), 0xcd }, + { CCI_REG8(0x3c25), 0xcd }, { CCI_REG8(0x3c26), 0xcd }, + { CCI_REG8(0x3c27), 0xcd }, { CCI_REG8(0x3c28), 0xcd }, + { CCI_REG8(0x3c29), 0xcd }, { CCI_REG8(0x3c2a), 0xcd }, + { CCI_REG8(0x3c2b), 0xcd }, { CCI_REG8(0x3c2c), 0xcd }, + { CCI_REG8(0x3c2d), 0xcd }, { CCI_REG8(0x3c2e), 0xcd }, + { CCI_REG8(0x3c2f), 0xcd }, { CCI_REG8(0x3c30), 0xcd }, + { CCI_REG8(0x3c31), 0xcd }, { CCI_REG8(0x3c32), 0xcd }, + { CCI_REG8(0x3c33), 0xcd }, { CCI_REG8(0x3c34), 0xcd }, + { CCI_REG8(0x3c35), 0xcd }, { CCI_REG8(0x3c36), 0xcd }, + { CCI_REG8(0x3c37), 0xcd }, { CCI_REG8(0x3c38), 0xcd }, + { CCI_REG8(0x3c39), 0xcd }, { CCI_REG8(0x3c3a), 0xcd }, + { CCI_REG8(0x3c3b), 0xcd }, { CCI_REG8(0x3c3c), 0xcd }, + { CCI_REG8(0x3c3d), 0xcd }, { CCI_REG8(0x3c3e), 0xcd }, + { CCI_REG8(0x3c3f), 0xcd }, { CCI_REG8(0x3c40), 0xcd }, + { CCI_REG8(0x3c41), 0xcd }, { CCI_REG8(0x3c42), 0xcd }, + { CCI_REG8(0x3c43), 0xcd }, { CCI_REG8(0x3c44), 0xcd }, + { CCI_REG8(0x3c45), 0xcd }, { CCI_REG8(0x3c46), 0xcd }, + { CCI_REG8(0x3c47), 0xcd }, { CCI_REG8(0x3c48), 0xcd }, + { CCI_REG8(0x3c49), 0xcd }, { CCI_REG8(0x3c4a), 0xcd }, + { CCI_REG8(0x3c4b), 0xcd }, { CCI_REG8(0x3c4c), 0xcd }, + { CCI_REG8(0x3c4d), 0xcd }, { CCI_REG8(0x3c4e), 0xcd }, + { CCI_REG8(0x3c4f), 0xcd }, { CCI_REG8(0x3c50), 0xcd }, + { CCI_REG8(0x3c51), 0xcd }, { CCI_REG8(0x3c52), 0xcd }, + { CCI_REG8(0x3c53), 0xcd }, { CCI_REG8(0x3c54), 0xcd }, + { CCI_REG8(0x3c55), 0xcd }, { CCI_REG8(0x3c56), 0xcd }, + { CCI_REG8(0x3c57), 0xcd }, { CCI_REG8(0x3c58), 0xcd }, + { CCI_REG8(0x3c59), 0xcd }, { CCI_REG8(0x3c5a), 0xcd }, + { CCI_REG8(0x3c5b), 0xcd }, { CCI_REG8(0x3c5c), 0xcd }, + { CCI_REG8(0x3c5d), 0xcd }, { CCI_REG8(0x3c5e), 0xcd }, + { CCI_REG8(0x3c5f), 0xcd }, { CCI_REG8(0x3c60), 0xcd }, + { CCI_REG8(0x3c61), 0xcd }, { CCI_REG8(0x3c62), 0xcd }, + { CCI_REG8(0x3c63), 0xcd }, { CCI_REG8(0x3c64), 0xcd }, + { CCI_REG8(0x3c65), 0xcd }, { CCI_REG8(0x3c66), 0xcd }, + { CCI_REG8(0x3c67), 0xcd }, { CCI_REG8(0x3c68), 0xcd }, + { CCI_REG8(0x3c69), 0xcd }, { CCI_REG8(0x3c6a), 0xcd }, + { CCI_REG8(0x3c6b), 0xcd }, { CCI_REG8(0x3c6c), 0xcd }, + { CCI_REG8(0x3c6d), 0xcd }, { CCI_REG8(0x3c6e), 0xcd }, + { CCI_REG8(0x3c6f), 0xcd }, { CCI_REG8(0x3c70), 0xcd }, + { CCI_REG8(0x3c71), 0xcd }, { CCI_REG8(0x3c72), 0xcd }, + { CCI_REG8(0x3c73), 0xcd }, { CCI_REG8(0x3c74), 0xcd }, + { CCI_REG8(0x3c75), 0xcd }, { CCI_REG8(0x3c76), 0xcd }, + { CCI_REG8(0x3c77), 0xcd }, { CCI_REG8(0x3c78), 0xcd }, + { CCI_REG8(0x3c79), 0xcd }, { CCI_REG8(0x3c7a), 0xcd }, + { CCI_REG8(0x3c7b), 0xcd }, { CCI_REG8(0x3c7c), 0xcd }, + { CCI_REG8(0x3c7d), 0xcd }, { CCI_REG8(0x3c7e), 0xcd }, + { CCI_REG8(0x3c7f), 0xcd }, { CCI_REG8(0x3c80), 0xcd }, + { CCI_REG8(0x3c81), 0xcd }, { CCI_REG8(0x3c82), 0xcd }, + { CCI_REG8(0x3c83), 0xcd }, { CCI_REG8(0x3c84), 0xcd }, + { CCI_REG8(0x3c85), 0xcd }, { CCI_REG8(0x3c86), 0xcd }, + { CCI_REG8(0x3c87), 0xcd }, { CCI_REG8(0x3c88), 0xcd }, + { CCI_REG8(0x3c89), 0xcd }, { CCI_REG8(0x3c8a), 0xcd }, + { CCI_REG8(0x3c8b), 0xcd }, { CCI_REG8(0x3c8c), 0xcd }, + { CCI_REG8(0x3c8d), 0xcd }, { CCI_REG8(0x3c8e), 0xcd }, + { CCI_REG8(0x3c8f), 0xcd }, { CCI_REG8(0x3c90), 0xcd }, + { CCI_REG8(0x3c91), 0xcd }, { CCI_REG8(0x3c92), 0xcd }, + { CCI_REG8(0x3c93), 0xcd }, { CCI_REG8(0x3c94), 0xcd }, + { CCI_REG8(0x3c95), 0xcd }, { CCI_REG8(0x3c96), 0xcd }, + { CCI_REG8(0x3c97), 0xcd }, { CCI_REG8(0x3c98), 0xcd }, + { CCI_REG8(0x3c99), 0xcd }, { CCI_REG8(0x3c9a), 0xcd }, + { CCI_REG8(0x3c9b), 0xcd }, { CCI_REG8(0x3c9c), 0xcd }, + { CCI_REG8(0x3c9d), 0xcd }, { CCI_REG8(0x3c9e), 0xcd }, + { CCI_REG8(0x3c9f), 0xcd }, { CCI_REG8(0x3ca0), 0xcd }, + { CCI_REG8(0x3ca1), 0xcd }, { CCI_REG8(0x3ca2), 0xcd }, + { CCI_REG8(0x3ca3), 0xcd }, { CCI_REG8(0x3ca4), 0xcd }, + { CCI_REG8(0x3ca5), 0xcd }, { CCI_REG8(0x3ca6), 0xcd }, + { CCI_REG8(0x3ca7), 0xcd }, { CCI_REG8(0x3ca8), 0xcd }, + { CCI_REG8(0x3ca9), 0xcd }, { CCI_REG8(0x3caa), 0xcd }, + { CCI_REG8(0x3cab), 0xcd }, { CCI_REG8(0x3cac), 0xcd }, + { CCI_REG8(0x3cad), 0xcd }, { CCI_REG8(0x3cae), 0xcd }, + { CCI_REG8(0x3caf), 0xcd }, { CCI_REG8(0x3cb0), 0xcd }, + { CCI_REG8(0x3cb1), 0x40 }, { CCI_REG8(0x3cb2), 0x40 }, + { CCI_REG8(0x3cb3), 0x40 }, { CCI_REG8(0x3cb4), 0x40 }, + { CCI_REG8(0x3cb5), 0x40 }, { CCI_REG8(0x3cb6), 0x40 }, + { CCI_REG8(0x3cb7), 0x40 }, { CCI_REG8(0x3cb8), 0x40 }, + { CCI_REG8(0x3cb9), 0x40 }, { CCI_REG8(0x3cba), 0x40 }, + { CCI_REG8(0x3cbb), 0x40 }, { CCI_REG8(0x3cbc), 0x40 }, + { CCI_REG8(0x3cbd), 0x40 }, { CCI_REG8(0x3cbe), 0x40 }, + { CCI_REG8(0x3cbf), 0x40 }, { CCI_REG8(0x3cc0), 0x40 }, + { CCI_REG8(0x3cc1), 0x40 }, { CCI_REG8(0x3cc2), 0x40 }, + { CCI_REG8(0x3cc3), 0x40 }, { CCI_REG8(0x3cc4), 0x40 }, + { CCI_REG8(0x3cc5), 0x40 }, { CCI_REG8(0x3cc6), 0x40 }, + { CCI_REG8(0x3cc7), 0x40 }, { CCI_REG8(0x3cc8), 0x40 }, + { CCI_REG8(0x3cc9), 0x40 }, { CCI_REG8(0x3cca), 0x40 }, + { CCI_REG8(0x3ccb), 0x40 }, { CCI_REG8(0x3ccc), 0x40 }, + { CCI_REG8(0x3ccd), 0x40 }, { CCI_REG8(0x3cce), 0x40 }, + { CCI_REG8(0x3ccf), 0x40 }, { CCI_REG8(0x3cd0), 0x40 }, + { CCI_REG8(0x3cd1), 0x40 }, { CCI_REG8(0x3cd2), 0x40 }, + { CCI_REG8(0x3cd3), 0x40 }, { CCI_REG8(0x3cd4), 0x40 }, + { CCI_REG8(0x3cd5), 0x40 }, { CCI_REG8(0x3cd6), 0x40 }, + { CCI_REG8(0x3cd7), 0x40 }, { CCI_REG8(0x3cd8), 0x40 }, + { CCI_REG8(0x3cd9), 0x40 }, { CCI_REG8(0x3cda), 0x40 }, + { CCI_REG8(0x3cdb), 0x40 }, { CCI_REG8(0x3cdc), 0x40 }, + { CCI_REG8(0x3cdd), 0x40 }, { CCI_REG8(0x3cde), 0x40 }, + { CCI_REG8(0x3cdf), 0x40 }, { CCI_REG8(0x3ce0), 0x40 }, + { CCI_REG8(0x3ce1), 0x40 }, { CCI_REG8(0x3ce2), 0x40 }, + { CCI_REG8(0x3ce3), 0x40 }, { CCI_REG8(0x3ce4), 0x40 }, + { CCI_REG8(0x3ce5), 0x40 }, { CCI_REG8(0x3ce6), 0x40 }, + { CCI_REG8(0x3ce7), 0x40 }, { CCI_REG8(0x3ce8), 0x40 }, + { CCI_REG8(0x3ce9), 0x40 }, { CCI_REG8(0x3cea), 0x40 }, + { CCI_REG8(0x3ceb), 0x40 }, { CCI_REG8(0x3cec), 0x40 }, + { CCI_REG8(0x3ced), 0x40 }, { CCI_REG8(0x3cee), 0x40 }, + { CCI_REG8(0x3cef), 0x40 }, { CCI_REG8(0x3cf0), 0x40 }, + { CCI_REG8(0x3cf1), 0x40 }, { CCI_REG8(0x3cf2), 0x40 }, + { CCI_REG8(0x3cf3), 0x40 }, { CCI_REG8(0x3cf4), 0x40 }, + { CCI_REG8(0x3cf5), 0x40 }, { CCI_REG8(0x3cf6), 0x40 }, + { CCI_REG8(0x3cf7), 0x40 }, { CCI_REG8(0x3cf8), 0x40 }, + { CCI_REG8(0x3cf9), 0x40 }, { CCI_REG8(0x3cfa), 0x40 }, + { CCI_REG8(0x3cfb), 0x40 }, { CCI_REG8(0x3cfc), 0x40 }, + { CCI_REG8(0x3cfd), 0x40 }, { CCI_REG8(0x3cfe), 0x40 }, + { CCI_REG8(0x3cff), 0x40 }, { CCI_REG8(0x3d00), 0x40 }, + { CCI_REG8(0x3d01), 0x40 }, { CCI_REG8(0x3d02), 0x40 }, + { CCI_REG8(0x3d03), 0x40 }, { CCI_REG8(0x3d04), 0x40 }, + { CCI_REG8(0x3d05), 0x40 }, { CCI_REG8(0x3d06), 0x40 }, + { CCI_REG8(0x3d07), 0x40 }, { CCI_REG8(0x3d08), 0x40 }, + { CCI_REG8(0x3d09), 0x40 }, { CCI_REG8(0x3d0a), 0x40 }, + { CCI_REG8(0x3d0b), 0xcd }, { CCI_REG8(0x3d0c), 0xcd }, + { CCI_REG8(0x3d0d), 0xcd }, { CCI_REG8(0x3d0e), 0xcd }, + { CCI_REG8(0x3d0f), 0xcd }, { CCI_REG8(0x3d10), 0xcd }, + { CCI_REG8(0x3d11), 0xcd }, { CCI_REG8(0x3d12), 0xcd }, + { CCI_REG8(0x3d13), 0xcd }, { CCI_REG8(0x3d14), 0xcd }, + { CCI_REG8(0x3d15), 0xcd }, { CCI_REG8(0x3d16), 0xcd }, + { CCI_REG8(0x3d17), 0xcd }, { CCI_REG8(0x3d18), 0xcd }, + { CCI_REG8(0x3d19), 0xcd }, { CCI_REG8(0x3d1a), 0xcd }, + { CCI_REG8(0x3d1b), 0xcd }, { CCI_REG8(0x3d1c), 0xcd }, + { CCI_REG8(0x3d1d), 0xcd }, { CCI_REG8(0x3d1e), 0xcd }, + { CCI_REG8(0x3d1f), 0xcd }, { CCI_REG8(0x3d20), 0xcd }, + { CCI_REG8(0x3d21), 0xcd }, { CCI_REG8(0x3d22), 0xcd }, + { CCI_REG8(0x3d23), 0xcd }, { CCI_REG8(0x3d24), 0xcd }, + { CCI_REG8(0x3d25), 0xcd }, { CCI_REG8(0x3d26), 0xcd }, + { CCI_REG8(0x3d27), 0xcd }, { CCI_REG8(0x3d28), 0xcd }, + { CCI_REG8(0x3d29), 0xcd }, { CCI_REG8(0x3d2a), 0xcd }, + { CCI_REG8(0x3d2b), 0xcd }, { CCI_REG8(0x3d2c), 0xcd }, + { CCI_REG8(0x3d2d), 0xcd }, { CCI_REG8(0x3d2e), 0xcd }, + { CCI_REG8(0x3d2f), 0xcd }, { CCI_REG8(0x3d30), 0xcd }, + { CCI_REG8(0x3d31), 0xcd }, { CCI_REG8(0x3d32), 0xcd }, + { CCI_REG8(0x3d33), 0xcd }, { CCI_REG8(0x3d34), 0xcd }, + { CCI_REG8(0x3d35), 0xcd }, { CCI_REG8(0x3d36), 0xcd }, + { CCI_REG8(0x3d37), 0xcd }, { CCI_REG8(0x3d38), 0xcd }, + { CCI_REG8(0x3d39), 0xcd }, { CCI_REG8(0x3d3a), 0xcd }, + { CCI_REG8(0x3d3b), 0xcd }, { CCI_REG8(0x3d3c), 0xcd }, + { CCI_REG8(0x3d3d), 0xcd }, { CCI_REG8(0x3d3e), 0xcd }, + { CCI_REG8(0x3d3f), 0xcd }, { CCI_REG8(0x3d40), 0xcd }, + { CCI_REG8(0x3d41), 0xcd }, { CCI_REG8(0x3d42), 0xcd }, + { CCI_REG8(0x3d43), 0xcd }, { CCI_REG8(0x3d44), 0xcd }, + { CCI_REG8(0x3d45), 0xcd }, { CCI_REG8(0x3d46), 0xcd }, + { CCI_REG8(0x3d47), 0xcd }, { CCI_REG8(0x3d48), 0xcd }, + { CCI_REG8(0x3d49), 0xcd }, { CCI_REG8(0x3d4a), 0xcd }, + { CCI_REG8(0x3d4b), 0xcd }, { CCI_REG8(0x3d4c), 0xcd }, + { CCI_REG8(0x3d4d), 0xcd }, { CCI_REG8(0x3d4e), 0xcd }, + { CCI_REG8(0x3d4f), 0xcd }, { CCI_REG8(0x3d50), 0xcd }, + { CCI_REG8(0x3d51), 0xcd }, { CCI_REG8(0x3d52), 0xcd }, + { CCI_REG8(0x3d53), 0xcd }, { CCI_REG8(0x3d54), 0xcd }, + { CCI_REG8(0x3d55), 0xcd }, { CCI_REG8(0x3d56), 0xcd }, + { CCI_REG8(0x3d57), 0xcd }, { CCI_REG8(0x3d58), 0xcd }, + { CCI_REG8(0x3d59), 0xcd }, { CCI_REG8(0x3d5a), 0xcd }, + { CCI_REG8(0x3d5b), 0xcd }, { CCI_REG8(0x3d5c), 0xcd }, + { CCI_REG8(0x3d5d), 0xcd }, { CCI_REG8(0x3d5e), 0xcd }, + { CCI_REG8(0x3d5f), 0xcd }, { CCI_REG8(0x3d60), 0xcd }, + { CCI_REG8(0x3d61), 0xcd }, { CCI_REG8(0x3d62), 0xcd }, + { CCI_REG8(0x3d63), 0xcd }, { CCI_REG8(0x3d64), 0xcd }, + { CCI_REG8(0x3d65), 0x40 }, { CCI_REG8(0x3d66), 0x40 }, + { CCI_REG8(0x3d67), 0x40 }, { CCI_REG8(0x3d68), 0x40 }, + { CCI_REG8(0x3d69), 0x40 }, { CCI_REG8(0x3d6a), 0x40 }, + { CCI_REG8(0x3d6b), 0x40 }, { CCI_REG8(0x3d6c), 0x40 }, + { CCI_REG8(0x3d6d), 0x40 }, { CCI_REG8(0x3d6e), 0x40 }, + { CCI_REG8(0x3d6f), 0x40 }, { CCI_REG8(0x3d70), 0x40 }, + { CCI_REG8(0x3d71), 0x40 }, { CCI_REG8(0x3d72), 0x40 }, + { CCI_REG8(0x3d73), 0x40 }, { CCI_REG8(0x3d74), 0x40 }, + { CCI_REG8(0x3d75), 0x40 }, { CCI_REG8(0x3d76), 0x40 }, + { CCI_REG8(0x3d77), 0x40 }, { CCI_REG8(0x3d78), 0x40 }, + { CCI_REG8(0x3d79), 0x40 }, { CCI_REG8(0x3d7a), 0x40 }, + { CCI_REG8(0x3d7b), 0x40 }, { CCI_REG8(0x3d7c), 0x40 }, + { CCI_REG8(0x3d7d), 0x40 }, { CCI_REG8(0x3d7e), 0x40 }, + { CCI_REG8(0x3d7f), 0x40 }, { CCI_REG8(0x3d80), 0x40 }, + { CCI_REG8(0x3d81), 0x40 }, { CCI_REG8(0x3d82), 0x40 }, + { CCI_REG8(0x3d83), 0x40 }, { CCI_REG8(0x3d84), 0x40 }, + { CCI_REG8(0x3d85), 0x40 }, { CCI_REG8(0x3d86), 0x40 }, + { CCI_REG8(0x3d87), 0x40 }, { CCI_REG8(0x3d88), 0x40 }, + { CCI_REG8(0x3d89), 0x40 }, { CCI_REG8(0x3d8a), 0x40 }, + { CCI_REG8(0x3d8b), 0x40 }, { CCI_REG8(0x3d8c), 0x40 }, + { CCI_REG8(0x3d8d), 0x40 }, { CCI_REG8(0x3d8e), 0x40 }, + { CCI_REG8(0x3d8f), 0x40 }, { CCI_REG8(0x3d90), 0x40 }, + { CCI_REG8(0x3d91), 0x40 }, { CCI_REG8(0x3d92), 0x40 }, + { CCI_REG8(0x3d93), 0x40 }, { CCI_REG8(0x3d94), 0x40 }, + { CCI_REG8(0x3d95), 0x40 }, { CCI_REG8(0x3d96), 0x40 }, + { CCI_REG8(0x3d97), 0x40 }, { CCI_REG8(0x3d98), 0x40 }, + { CCI_REG8(0x3d99), 0x40 }, { CCI_REG8(0x3d9a), 0x40 }, + { CCI_REG8(0x3d9b), 0x40 }, { CCI_REG8(0x3d9c), 0x40 }, + { CCI_REG8(0x3d9d), 0x40 }, { CCI_REG8(0x3d9e), 0x40 }, + { CCI_REG8(0x3d9f), 0x40 }, { CCI_REG8(0x3da0), 0x40 }, + { CCI_REG8(0x3da1), 0x40 }, { CCI_REG8(0x3da2), 0x40 }, + { CCI_REG8(0x3da3), 0x40 }, { CCI_REG8(0x3da4), 0x40 }, + { CCI_REG8(0x3da5), 0x40 }, { CCI_REG8(0x3da6), 0x40 }, + { CCI_REG8(0x3da7), 0x40 }, { CCI_REG8(0x3da8), 0x40 }, + { CCI_REG8(0x3da9), 0x40 }, { CCI_REG8(0x3daa), 0x40 }, + { CCI_REG8(0x3dab), 0x40 }, { CCI_REG8(0x3dac), 0x40 }, + { CCI_REG8(0x3dad), 0x40 }, { CCI_REG8(0x3dae), 0x40 }, + { CCI_REG8(0x3daf), 0x40 }, { CCI_REG8(0x3db0), 0x40 }, + { CCI_REG8(0x3db1), 0x40 }, { CCI_REG8(0x3db2), 0x40 }, + { CCI_REG8(0x3db3), 0x40 }, { CCI_REG8(0x3db4), 0x40 }, + { CCI_REG8(0x3db5), 0x40 }, { CCI_REG8(0x3db6), 0x40 }, + { CCI_REG8(0x3db7), 0x40 }, { CCI_REG8(0x3db8), 0x40 }, + { CCI_REG8(0x3db9), 0x40 }, { CCI_REG8(0x3dba), 0x40 }, + { CCI_REG8(0x3dbb), 0x40 }, { CCI_REG8(0x3dbc), 0x40 }, + { CCI_REG8(0x3dbd), 0x40 }, { CCI_REG8(0x3dbe), 0x40 }, + { CCI_REG8(0x3dbf), 0xcd }, { CCI_REG8(0x3dc0), 0xcd }, + { CCI_REG8(0x3dc1), 0xcd }, { CCI_REG8(0x3dc2), 0xcd }, + { CCI_REG8(0x3dc3), 0xcd }, { CCI_REG8(0x3dc4), 0xcd }, + { CCI_REG8(0x3dc5), 0xcd }, { CCI_REG8(0x3dc6), 0xcd }, + { CCI_REG8(0x3dc7), 0xcd }, { CCI_REG8(0x3dc8), 0xcd }, + { CCI_REG8(0x3dc9), 0xcd }, { CCI_REG8(0x3dca), 0xcd }, + { CCI_REG8(0x3dcb), 0xcd }, { CCI_REG8(0x3dcc), 0xcd }, + { CCI_REG8(0x3dcd), 0xcd }, { CCI_REG8(0x3dce), 0xcd }, + { CCI_REG8(0x3dcf), 0xcd }, { CCI_REG8(0x3dd0), 0xcd }, + { CCI_REG8(0x3dd1), 0xcd }, { CCI_REG8(0x3dd2), 0xcd }, + { CCI_REG8(0x3dd3), 0xcd }, { CCI_REG8(0x3dd4), 0xcd }, + { CCI_REG8(0x3dd5), 0xcd }, { CCI_REG8(0x3dd6), 0xcd }, + { CCI_REG8(0x3dd7), 0xcd }, { CCI_REG8(0x3dd8), 0xcd }, + { CCI_REG8(0x3dd9), 0xcd }, { CCI_REG8(0x3dda), 0xcd }, + { CCI_REG8(0x3ddb), 0xcd }, { CCI_REG8(0x3ddc), 0xcd }, + { CCI_REG8(0x3ddd), 0xcd }, { CCI_REG8(0x3dde), 0xcd }, + { CCI_REG8(0x3ddf), 0xcd }, { CCI_REG8(0x3de0), 0xcd }, + { CCI_REG8(0x3de1), 0xcd }, { CCI_REG8(0x3de2), 0xcd }, + { CCI_REG8(0x3de3), 0xcd }, { CCI_REG8(0x3de4), 0xcd }, + { CCI_REG8(0x3de5), 0xcd }, { CCI_REG8(0x3de6), 0xcd }, + { CCI_REG8(0x3de7), 0xcd }, { CCI_REG8(0x3de8), 0xcd }, + { CCI_REG8(0x3de9), 0xcd }, { CCI_REG8(0x3dea), 0xcd }, + { CCI_REG8(0x3deb), 0xcd }, { CCI_REG8(0x3dec), 0xcd }, + { CCI_REG8(0x3ded), 0xcd }, { CCI_REG8(0x3dee), 0xcd }, + { CCI_REG8(0x3def), 0xcd }, { CCI_REG8(0x3df0), 0xcd }, + { CCI_REG8(0x3df1), 0xcd }, { CCI_REG8(0x3df2), 0xcd }, + { CCI_REG8(0x3df3), 0xcd }, { CCI_REG8(0x3df4), 0xcd }, + { CCI_REG8(0x3df5), 0xcd }, { CCI_REG8(0x3df6), 0xcd }, + { CCI_REG8(0x3df7), 0xcd }, { CCI_REG8(0x3df8), 0xcd }, + { CCI_REG8(0x3df9), 0xcd }, { CCI_REG8(0x3dfa), 0xcd }, + { CCI_REG8(0x3dfb), 0xcd }, { CCI_REG8(0x3dfc), 0xcd }, + { CCI_REG8(0x3dfd), 0xcd }, { CCI_REG8(0x3dfe), 0xcd }, + { CCI_REG8(0x3dff), 0xcd }, { CCI_REG8(0x3e00), 0xcd }, + { CCI_REG8(0x3e01), 0xcd }, { CCI_REG8(0x3e02), 0xcd }, + { CCI_REG8(0x3e03), 0xcd }, { CCI_REG8(0x3e04), 0xcd }, + { CCI_REG8(0x3e05), 0xcd }, { CCI_REG8(0x3e06), 0xcd }, + { CCI_REG8(0x3e07), 0xcd }, { CCI_REG8(0x3e08), 0xcd }, + { CCI_REG8(0x3e09), 0xcd }, { CCI_REG8(0x3e0a), 0xcd }, + { CCI_REG8(0x3e0b), 0xcd }, { CCI_REG8(0x3e0c), 0xcd }, + { CCI_REG8(0x3e0d), 0xcd }, { CCI_REG8(0x3e0e), 0xcd }, + { CCI_REG8(0x3e0f), 0xcd }, { CCI_REG8(0x3e10), 0xcd }, + { CCI_REG8(0x3e11), 0xcd }, { CCI_REG8(0x3e12), 0xcd }, + { CCI_REG8(0x3e13), 0xcd }, { CCI_REG8(0x3e14), 0xcd }, + { CCI_REG8(0x3e15), 0xcd }, { CCI_REG8(0x3e16), 0xcd }, + { CCI_REG8(0x3e17), 0xcd }, { CCI_REG8(0x3e18), 0xcd }, + { CCI_REG8(0x3e19), 0xcd }, { CCI_REG8(0x3e1a), 0xcd }, + { CCI_REG8(0x3e1b), 0xcd }, { CCI_REG8(0x3e1c), 0xcd }, + { CCI_REG8(0x3e1d), 0xcd }, { CCI_REG8(0x3e1e), 0xcd }, + { CCI_REG8(0x3e1f), 0xcd }, { CCI_REG8(0x3e20), 0xcd }, + { CCI_REG8(0x3e21), 0xcd }, { CCI_REG8(0x3e22), 0xcd }, + { CCI_REG8(0x3e23), 0xcd }, { CCI_REG8(0x3e24), 0xcd }, + { CCI_REG8(0x3e25), 0xcd }, { CCI_REG8(0x3e26), 0xcd }, + { CCI_REG8(0x3e27), 0xcd }, { CCI_REG8(0x3e28), 0xcd }, + { CCI_REG8(0x3e29), 0xcd }, { CCI_REG8(0x3e2a), 0xcd }, + { CCI_REG8(0x3e2b), 0xcd }, { CCI_REG8(0x3e2c), 0xcd }, + { CCI_REG8(0x3e2d), 0xcd }, { CCI_REG8(0x3e2e), 0xcd }, + { CCI_REG8(0x3e2f), 0xcd }, { CCI_REG8(0x3e30), 0xcd }, + { CCI_REG8(0x3e31), 0xcd }, { CCI_REG8(0x3e32), 0xcd }, + { CCI_REG8(0x3e33), 0xcd }, { CCI_REG8(0x3e34), 0xcd }, + { CCI_REG8(0x3e35), 0xcd }, { CCI_REG8(0x3e36), 0xcd }, + { CCI_REG8(0x3e37), 0xcd }, { CCI_REG8(0x3e38), 0xcd }, + { CCI_REG8(0x3e39), 0xcd }, { CCI_REG8(0x3e3a), 0xcd }, + { CCI_REG8(0x3e3b), 0xcd }, { CCI_REG8(0x3e3c), 0xcd }, + { CCI_REG8(0x3e3d), 0xcd }, { CCI_REG8(0x3e3e), 0xcd }, + { CCI_REG8(0x3e3f), 0xcd }, { CCI_REG8(0x3e40), 0xcd }, + { CCI_REG8(0x3e41), 0xcd }, { CCI_REG8(0x3e42), 0xcd }, + { CCI_REG8(0x3e43), 0xcd }, { CCI_REG8(0x3e44), 0xcd }, + { CCI_REG8(0x3e45), 0xcd }, { CCI_REG8(0x3e46), 0xcd }, + { CCI_REG8(0x3e47), 0xcd }, { CCI_REG8(0x3e48), 0xcd }, + { CCI_REG8(0x3e49), 0xcd }, { CCI_REG8(0x3e4a), 0xcd }, + { CCI_REG8(0x3e4b), 0xcd }, { CCI_REG8(0x3e4c), 0xcd }, + { CCI_REG8(0x3e4d), 0xcd }, { CCI_REG8(0x3e4e), 0xcd }, + { CCI_REG8(0x3e4f), 0xcd }, { CCI_REG8(0x3e50), 0xcd }, + { CCI_REG8(0x3e51), 0xcd }, { CCI_REG8(0x3e52), 0xcd }, + { CCI_REG8(0x3e53), 0xcd }, { CCI_REG8(0x3e54), 0xcd }, + { CCI_REG8(0x3e55), 0xcd }, { CCI_REG8(0x3e56), 0xcd }, + { CCI_REG8(0x3e57), 0xcd }, { CCI_REG8(0x3e58), 0xcd }, + { CCI_REG8(0x3e59), 0xcd }, { CCI_REG8(0x3e5a), 0xcd }, + { CCI_REG8(0x3e5b), 0xcd }, { CCI_REG8(0x3e5c), 0xcd }, + { CCI_REG8(0x3e5d), 0xcd }, { CCI_REG8(0x3e5e), 0xcd }, + { CCI_REG8(0x3e5f), 0xcd }, { CCI_REG8(0x3e60), 0xcd }, + { CCI_REG8(0x3e61), 0xcd }, { CCI_REG8(0x3e62), 0xcd }, + { CCI_REG8(0x3e63), 0xcd }, { CCI_REG8(0x3e64), 0xcd }, + { CCI_REG8(0x3e65), 0xcd }, { CCI_REG8(0x3e66), 0xcd }, + { CCI_REG8(0x3e67), 0xcd }, { CCI_REG8(0x3e68), 0xcd }, + { CCI_REG8(0x3e69), 0xcd }, { CCI_REG8(0x3e6a), 0xcd }, + { CCI_REG8(0x3e6b), 0xcd }, { CCI_REG8(0x3e6c), 0xcd }, + { CCI_REG8(0x3e6d), 0xcd }, { CCI_REG8(0x3e6e), 0xcd }, + { CCI_REG8(0x3e6f), 0xcd }, { CCI_REG8(0x3e70), 0xcd }, + { CCI_REG8(0x3e71), 0xcd }, { CCI_REG8(0x3e72), 0xcd }, + { CCI_REG8(0x3e73), 0xcd }, { CCI_REG8(0x3e74), 0xcd }, + { CCI_REG8(0x3e75), 0xcd }, { CCI_REG8(0x3e76), 0xcd }, + { CCI_REG8(0x3e77), 0xcd }, { CCI_REG8(0x3e78), 0xcd }, + { CCI_REG8(0x3e79), 0xcd }, { CCI_REG8(0x3e7a), 0xcd }, + { CCI_REG8(0x3e7b), 0xcd }, { CCI_REG8(0x3e7c), 0xcd }, + { CCI_REG8(0x3e7d), 0xcd }, { CCI_REG8(0x3e7e), 0xcd }, + { CCI_REG8(0x3e7f), 0xcd }, { CCI_REG8(0x3e80), 0xcd }, + { CCI_REG8(0x3e81), 0xcd }, { CCI_REG8(0x3e82), 0xcd }, + { CCI_REG8(0x3e83), 0xcd }, { CCI_REG8(0x3e84), 0xcd }, + { CCI_REG8(0x3e85), 0xcd }, { CCI_REG8(0x3e86), 0xcd }, + { CCI_REG8(0x3e87), 0xcd }, { CCI_REG8(0x3e88), 0xcd }, + { CCI_REG8(0x3e89), 0xcd }, { CCI_REG8(0x3e8a), 0xcd }, + { CCI_REG8(0x3e8b), 0xcd }, { CCI_REG8(0x3e8c), 0xcd }, + { CCI_REG8(0x3e8d), 0xcd }, { CCI_REG8(0x3e8e), 0xcd }, + { CCI_REG8(0x3e8f), 0xcd }, { CCI_REG8(0x3e90), 0xcd }, + { CCI_REG8(0x3e91), 0xcd }, { CCI_REG8(0x3e92), 0xcd }, + { CCI_REG8(0x3e93), 0xcd }, { CCI_REG8(0x3e94), 0xcd }, + { CCI_REG8(0x3e95), 0xcd }, { CCI_REG8(0x3e96), 0xcd }, + { CCI_REG8(0x3e97), 0xcd }, { CCI_REG8(0x3e98), 0xcd }, + { CCI_REG8(0x3e99), 0xcd }, { CCI_REG8(0x3e9a), 0xcd }, + { CCI_REG8(0x3e9b), 0xcd }, { CCI_REG8(0x3e9c), 0xcd }, + { CCI_REG8(0x3e9d), 0xcd }, { CCI_REG8(0x3e9e), 0xcd }, + { CCI_REG8(0x3e9f), 0xcd }, { CCI_REG8(0xfff9), 0x06 }, + { CCI_REG8(0xc03f), 0x01 }, { CCI_REG8(0xc03e), 0x08 }, + { CCI_REG8(0xc02c), 0xff }, { CCI_REG8(0xc005), 0x06 }, + { CCI_REG8(0xc006), 0x30 }, { CCI_REG8(0xc007), 0xc0 }, + { CCI_REG8(0xc027), 0x01 }, { CCI_REG8(0x30c0), 0x05 }, + { CCI_REG8(0x30c1), 0x9f }, { CCI_REG8(0x30c2), 0x06 }, + { CCI_REG8(0x30c3), 0x5f }, { CCI_REG8(0x30c4), 0x80 }, + { CCI_REG8(0x30c5), 0x08 }, { CCI_REG8(0x30c6), 0x39 }, + { CCI_REG8(0x30c7), 0x00 }, { CCI_REG8(0xc046), 0x20 }, + { CCI_REG8(0xc043), 0x01 }, { CCI_REG8(0xc04b), 0x01 }, + { CCI_REG8(0x0102), 0x01 }, { CCI_REG8(0x0100), 0x00 }, + { CCI_REG8(0x0102), 0x00 }, { CCI_REG8(0x3015), 0xf0 }, + { CCI_REG8(0x3018), 0xf0 }, { CCI_REG8(0x301c), 0xf0 }, + { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 } +}; + +static const struct cci_reg_sequence ov64a40_9248x6944[] = { + { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 }, + { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a }, + { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 }, + { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 }, + { CCI_REG8(0x5001), 0x21 } +}; + +static const struct cci_reg_sequence ov64a40_8000x6000[] = { + { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 }, + { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a }, + { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 }, + { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 }, + { CCI_REG8(0x5001), 0x21 } +}; + +static const struct cci_reg_sequence ov64a40_4624_3472[] = { + { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 }, + { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e }, + { CCI_REG8(0x3712), 0x50 }, { CCI_REG8(0x3822), 0x00 }, + { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x08 }, + { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x02 }, + { CCI_REG8(0x384d), 0xba }, { CCI_REG8(0x3852), 0x00 }, + { CCI_REG8(0x3856), 0x08 }, { CCI_REG8(0x3857), 0x08 }, + { CCI_REG8(0x3858), 0x10 }, { CCI_REG8(0x3859), 0x10 }, + { CCI_REG8(0x4016), 0x0f }, { CCI_REG8(0x4018), 0x03 }, + { CCI_REG8(0x4504), 0x1e }, { CCI_REG8(0x4523), 0x41 }, + { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x12 }, + { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4915), 0x02 }, + { CCI_REG8(0x4916), 0x1d }, { CCI_REG8(0x4a15), 0x02 }, + { CCI_REG8(0x4a16), 0x1d }, { CCI_REG8(0x3703), 0x72 }, + { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a60), 0x68 }, + { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc }, + { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3721), 0xc9 }, + { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 }, + { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 }, + { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 }, + { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b }, + { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 }, + { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b }, + { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 }, + { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b }, + { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x481b), 0x35 }, + { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x3400), 0x00 }, + { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc }, + { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 }, + { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 }, + { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 }, + { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 }, + { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x0305), 0x98 }, + { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 }, + { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 }, + { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 }, + { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 } +}; + +static const struct cci_reg_sequence ov64a40_3840x2160[] = { + { CCI_REG8(0x034a), 0x05 }, { CCI_REG8(0x034b), 0x05 }, + { CCI_REG8(0x3504), 0x08 }, { CCI_REG8(0x360d), 0x82 }, + { CCI_REG8(0x368a), 0x2e }, { CCI_REG8(0x3712), 0x50 }, + { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3827), 0x40 }, + { CCI_REG8(0x383d), 0x08 }, { CCI_REG8(0x383f), 0x00 }, + { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0xba }, + { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x08 }, + { CCI_REG8(0x3857), 0x08 }, { CCI_REG8(0x3858), 0x10 }, + { CCI_REG8(0x3859), 0x10 }, { CCI_REG8(0x4016), 0x0f }, + { CCI_REG8(0x4018), 0x03 }, { CCI_REG8(0x4504), 0x1e }, + { CCI_REG8(0x4523), 0x41 }, { CCI_REG8(0x45c0), 0x01 }, + { CCI_REG8(0x4641), 0x12 }, { CCI_REG8(0x4643), 0x0c }, + { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d }, + { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d }, + { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3709), 0xe6 }, + { CCI_REG8(0x3a60), 0x68 }, { CCI_REG8(0x3a6f), 0x68 }, + { CCI_REG8(0x3a5e), 0xdc }, { CCI_REG8(0x3a6d), 0xdc }, + { CCI_REG8(0x3721), 0xc9 }, { CCI_REG8(0x5250), 0x06 }, + { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x65 }, + { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x82 }, + { CCI_REG8(0x5280), 0x24 }, { CCI_REG8(0x5281), 0x40 }, + { CCI_REG8(0x5282), 0x1b }, { CCI_REG8(0x5283), 0x40 }, + { CCI_REG8(0x5284), 0x24 }, { CCI_REG8(0x5285), 0x40 }, + { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 }, + { CCI_REG8(0x5200), 0x24 }, { CCI_REG8(0x5201), 0x40 }, + { CCI_REG8(0x5202), 0x1b }, { CCI_REG8(0x5203), 0x40 }, + { CCI_REG8(0x481b), 0x35 }, { CCI_REG8(0x4862), 0x25 }, + { CCI_REG8(0x3400), 0x00 }, { CCI_REG8(0x3421), 0x23 }, + { CCI_REG8(0x3422), 0xfc }, { CCI_REG8(0x3423), 0x07 }, + { CCI_REG8(0x3424), 0x01 }, { CCI_REG8(0x3425), 0x04 }, + { CCI_REG8(0x3426), 0x50 }, { CCI_REG8(0x3427), 0x55 }, + { CCI_REG8(0x3428), 0x15 }, { CCI_REG8(0x3429), 0x00 }, + { CCI_REG8(0x3025), 0x03 }, { CCI_REG8(0x5250), 0x06 }, + { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 }, + { CCI_REG8(0x0345), 0x90 }, { CCI_REG8(0x0307), 0x01 }, + { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 }, + { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 }, + { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 }, + { CCI_REG8(0x5000), 0x01 } +}; + +static const struct cci_reg_sequence ov64a40_2312_1736[] = { + { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 }, + { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e }, + { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 }, + { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 }, + { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 }, + { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 }, + { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 }, + { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 }, + { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 }, + { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 }, + { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 }, + { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b }, + { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d }, + { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d }, + { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 }, + { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 }, + { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a }, + { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 }, + { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 }, + { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 }, + { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 }, + { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 }, + { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 }, + { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b }, + { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 }, + { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b }, + { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 }, + { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b }, + { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 }, + { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 }, + { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 }, + { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 }, + { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 }, + { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 }, + { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 }, + { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 }, + { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 }, + { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 }, + { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 }, + { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e }, + { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 }, + { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 }, + { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 }, + { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 }, + { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 }, + { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 }, + { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 }, + { CCI_REG8(0x480C), 0x92 } +}; + +static const struct cci_reg_sequence ov64a40_1920x1080[] = { + { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 }, + { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e }, + { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 }, + { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 }, + { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 }, + { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 }, + { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 }, + { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 }, + { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 }, + { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 }, + { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 }, + { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b }, + { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d }, + { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d }, + { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 }, + { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 }, + { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a }, + { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 }, + { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 }, + { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 }, + { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 }, + { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 }, + { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 }, + { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b }, + { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 }, + { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b }, + { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 }, + { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b }, + { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 }, + { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 }, + { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 }, + { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 }, + { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 }, + { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 }, + { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 }, + { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 }, + { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 }, + { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 }, + { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 }, + { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e }, + { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 }, + { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 }, + { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 }, + { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 }, + { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 }, + { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 }, + { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 }, + { CCI_REG8(0x480C), 0x92 } +}; + +/* 456MHz MIPI link frequency with 24MHz input clock. */ +static const struct cci_reg_sequence ov64a40_pll_config[] = { + { OV64A40_PLL1_PRE_DIV0, 0x88 }, + { OV64A40_PLL1_PRE_DIV, 0x02 }, + { OV64A40_PLL1_MULTIPLIER, 0x0098 }, + { OV64A40_PLL1_M_DIV, 0x01 }, + { OV64A40_PLL2_SEL_BAK_SA1, 0x00 }, + { OV64A40_PLL2_PRE_DIV, 0x12 }, + { OV64A40_PLL2_MULTIPLIER, 0x0190 }, + { OV64A40_PLL2_PRE_DIV0, 0xd7 }, + { OV64A40_PLL2_DIVSP, 0x00 }, + { OV64A40_PLL2_DIVDAC, 0x00 }, + { OV64A40_PLL2_DACPREDIV, 0x00 } +}; + +struct ov64a40_reglist { + unsigned int num_regs; + const struct cci_reg_sequence *regvals; +}; + +struct ov64a40_subsampling { + unsigned int x_odd_inc; + unsigned int x_even_inc; + unsigned int y_odd_inc; + unsigned int y_even_inc; + bool vbin; + bool hbin; +}; + +static struct ov64a40_mode { + unsigned int width; + unsigned int height; + struct ov64a40_timings { + unsigned int vts; + unsigned int ppl; + } timings_default[OV64A40_NUM_LINK_FREQ]; + const struct ov64a40_reglist reglist; + struct v4l2_rect analogue_crop; + struct v4l2_rect digital_crop; + struct ov64a40_subsampling subsampling; +} ov64a40_modes[] = { + /* Full resolution */ + { + .width = 9248, + .height = 6944, + .timings_default = { + /* 2.6 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 7072, + .ppl = 4072, + }, + /* 2 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 7072, + .ppl = 5248, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_9248x6944), + .regvals = ov64a40_9248x6944, + }, + .analogue_crop = { + .left = 0, + .top = 0, + .width = 9280, + .height = 6976, + }, + .digital_crop = { + .left = 17, + .top = 16, + .width = 9248, + .height = 6944, + }, + .subsampling = { + .x_odd_inc = 1, + .x_even_inc = 1, + .y_odd_inc = 1, + .y_even_inc = 1, + .vbin = false, + .hbin = false, + }, + }, + /* Analogue crop + digital crop */ + { + .width = 8000, + .height = 6000, + .timings_default = { + /* 3.0 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 6400, + .ppl = 3848, + }, + /* 2.5 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 6304, + .ppl = 4736, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_8000x6000), + .regvals = ov64a40_8000x6000, + }, + .analogue_crop = { + .left = 624, + .top = 472, + .width = 8048, + .height = 6032, + }, + .digital_crop = { + .left = 17, + .top = 16, + .width = 8000, + .height = 6000, + }, + .subsampling = { + .x_odd_inc = 1, + .x_even_inc = 1, + .y_odd_inc = 1, + .y_even_inc = 1, + .vbin = false, + .hbin = false, + }, + }, + /* 2x2 downscaled */ + { + .width = 4624, + .height = 3472, + .timings_default = { + /* 10 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 3533, + .ppl = 2112, + }, + /* 7 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 3939, + .ppl = 2720, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_4624_3472), + .regvals = ov64a40_4624_3472, + }, + .analogue_crop = { + .left = 0, + .top = 0, + .width = 9280, + .height = 6976, + }, + .digital_crop = { + .left = 9, + .top = 8, + .width = 4624, + .height = 3472, + }, + .subsampling = { + .x_odd_inc = 3, + .x_even_inc = 1, + .y_odd_inc = 1, + .y_even_inc = 1, + .vbin = true, + .hbin = false, + }, + }, + /* Analogue crop + 2x2 downscale + digital crop */ + { + .width = 3840, + .height = 2160, + .timings_default = { + /* 20 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 2218, + .ppl = 1690, + }, + /* 15 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 2270, + .ppl = 2202, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_3840x2160), + .regvals = ov64a40_3840x2160, + }, + .analogue_crop = { + .left = 784, + .top = 1312, + .width = 7712, + .height = 4352, + }, + .digital_crop = { + .left = 9, + .top = 8, + .width = 3840, + .height = 2160, + }, + .subsampling = { + .x_odd_inc = 3, + .x_even_inc = 1, + .y_odd_inc = 1, + .y_even_inc = 1, + .vbin = true, + .hbin = false, + }, + }, + /* 4x4 downscaled */ + { + .width = 2312, + .height = 1736, + .timings_default = { + /* 30 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 1998, + .ppl = 1248, + }, + /* 25 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 1994, + .ppl = 1504, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_2312_1736), + .regvals = ov64a40_2312_1736, + }, + .analogue_crop = { + .left = 0, + .top = 0, + .width = 9280, + .height = 6976, + }, + .digital_crop = { + .left = 5, + .top = 4, + .width = 2312, + .height = 1736, + }, + .subsampling = { + .x_odd_inc = 3, + .x_even_inc = 1, + .y_odd_inc = 3, + .y_even_inc = 1, + .vbin = true, + .hbin = true, + }, + }, + /* Analogue crop + 4x4 downscale + digital crop */ + { + .width = 1920, + .height = 1080, + .timings_default = { + /* 60 FPS */ + [OV64A40_LINK_FREQ_456M_ID] = { + .vts = 1397, + .ppl = 880, + }, + /* 45 FPS */ + [OV64A40_LINK_FREQ_360M_ID] = { + .vts = 1216, + .ppl = 1360, + }, + }, + .reglist = { + .num_regs = ARRAY_SIZE(ov64a40_1920x1080), + .regvals = ov64a40_1920x1080, + }, + .analogue_crop = { + .left = 784, + .top = 1312, + .width = 7712, + .height = 4352, + }, + .digital_crop = { + .left = 7, + .top = 6, + .width = 1920, + .height = 1080, + }, + .subsampling = { + .x_odd_inc = 3, + .x_even_inc = 1, + .y_odd_inc = 3, + .y_even_inc = 1, + .vbin = true, + .hbin = true, + }, + }, +}; + +struct ov64a40 { + struct device *dev; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct regmap *cci; + + struct ov64a40_mode *mode; + + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov64a40_supply_names)]; + + s64 *link_frequencies; + unsigned int num_link_frequencies; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; +}; + +static inline struct ov64a40 *sd_to_ov64a40(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct ov64a40, sd); +} + +static const struct ov64a40_timings * +ov64a40_get_timings(struct ov64a40 *ov64a40, unsigned int link_freq_index) +{ + s64 link_freq = ov64a40->link_frequencies[link_freq_index]; + unsigned int timings_index = link_freq == OV64A40_LINK_FREQ_360M + ? OV64A40_LINK_FREQ_360M_ID + : OV64A40_LINK_FREQ_456M_ID; + + return &ov64a40->mode->timings_default[timings_index]; +} + +static int ov64a40_program_geometry(struct ov64a40 *ov64a40) +{ + struct ov64a40_mode *mode = ov64a40->mode; + struct v4l2_rect *anacrop = &mode->analogue_crop; + struct v4l2_rect *digicrop = &mode->digital_crop; + const struct ov64a40_timings *timings; + int ret = 0; + + /* Analogue crop. */ + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL0, + anacrop->left, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL2, + anacrop->top, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL4, + anacrop->width + anacrop->left - 1, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL6, + anacrop->height + anacrop->top - 1, &ret); + + /* ISP windowing. */ + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL10, + digicrop->left, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL12, + digicrop->top, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL8, + digicrop->width, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLA, + digicrop->height, &ret); + + /* Total timings. */ + timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLC, timings->ppl, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLE, timings->vts, &ret); + + return ret; +} + +static int ov64a40_program_subsampling(struct ov64a40 *ov64a40) +{ + struct ov64a40_subsampling *subsampling = &ov64a40->mode->subsampling; + int ret = 0; + + /* Skipping configuration */ + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL14, + OV64A40_SKIPPING_CONFIG(subsampling->x_odd_inc, + subsampling->x_even_inc), &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL15, + OV64A40_SKIPPING_CONFIG(subsampling->y_odd_inc, + subsampling->y_even_inc), &ret); + + /* Binning configuration */ + cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20, + OV64A40_TIMING_CTRL_20_VBIN, + subsampling->vbin ? OV64A40_TIMING_CTRL_20_VBIN : 0, + &ret); + cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21, + OV64A40_TIMING_CTRL_21_HBIN_CONF, + subsampling->hbin ? + OV64A40_TIMING_CTRL_21_HBIN_CONF : 0, &ret); + + return ret; +} + +static int ov64a40_start_streaming(struct ov64a40 *ov64a40, + struct v4l2_subdev_state *state) +{ + const struct ov64a40_reglist *reglist = &ov64a40->mode->reglist; + const struct ov64a40_timings *timings; + unsigned long delay; + int ret; + + ret = pm_runtime_resume_and_get(ov64a40->dev); + if (ret < 0) + return ret; + + ret = cci_multi_reg_write(ov64a40->cci, ov64a40_init, + ARRAY_SIZE(ov64a40_init), NULL); + if (ret) + goto error_power_off; + + ret = cci_multi_reg_write(ov64a40->cci, reglist->regvals, + reglist->num_regs, NULL); + if (ret) + goto error_power_off; + + ret = ov64a40_program_geometry(ov64a40); + if (ret) + goto error_power_off; + + ret = ov64a40_program_subsampling(ov64a40); + if (ret) + goto error_power_off; + + ret = __v4l2_ctrl_handler_setup(&ov64a40->ctrl_handler); + if (ret) + goto error_power_off; + + ret = cci_write(ov64a40->cci, OV64A40_REG_SMIA, + OV64A40_REG_SMIA_STREAMING, NULL); + if (ret) + goto error_power_off; + + /* Link frequency and flips cannot change while streaming. */ + __v4l2_ctrl_grab(ov64a40->link_freq, true); + __v4l2_ctrl_grab(ov64a40->vflip, true); + __v4l2_ctrl_grab(ov64a40->hflip, true); + + /* delay: max(4096 xclk pulses, 150usec) + exposure time */ + timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val); + delay = DIV_ROUND_UP(4096, OV64A40_XCLK_FREQ / 1000 / 1000); + delay = max(delay, 150ul); + + /* The sensor has an internal x4 multiplier on the line length. */ + delay += DIV_ROUND_UP(timings->ppl * 4 * ov64a40->exposure->cur.val, + OV64A40_PIXEL_RATE / 1000 / 1000); + fsleep(delay); + + return 0; + +error_power_off: + pm_runtime_mark_last_busy(ov64a40->dev); + pm_runtime_put_autosuspend(ov64a40->dev); + + return ret; +} + +static int ov64a40_stop_streaming(struct ov64a40 *ov64a40, + struct v4l2_subdev_state *state) +{ + cci_update_bits(ov64a40->cci, OV64A40_REG_SMIA, BIT(0), 0, NULL); + pm_runtime_mark_last_busy(ov64a40->dev); + pm_runtime_put_autosuspend(ov64a40->dev); + + __v4l2_ctrl_grab(ov64a40->link_freq, false); + __v4l2_ctrl_grab(ov64a40->vflip, false); + __v4l2_ctrl_grab(ov64a40->hflip, false); + + return 0; +} + +static int ov64a40_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (enable) + ret = ov64a40_start_streaming(ov64a40, state); + else + ret = ov64a40_stop_streaming(ov64a40, state); + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov64a40_video_ops = { + .s_stream = ov64a40_set_stream, +}; + +static u32 ov64a40_mbus_code(struct ov64a40 *ov64a40) +{ + unsigned int index = ov64a40->hflip->val << 1 | ov64a40->vflip->val; + + return ov64a40_mbus_codes[index]; +} + +static void ov64a40_update_pad_fmt(struct ov64a40 *ov64a40, + struct ov64a40_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = ov64a40_mbus_code(ov64a40); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; +} + +static int ov64a40_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + format = v4l2_subdev_state_get_format(state, 0); + ov64a40_update_pad_fmt(ov64a40, &ov64a40_modes[0], format); + + crop = v4l2_subdev_state_get_crop(state, 0); + crop->top = OV64A40_PIXEL_ARRAY_TOP; + crop->left = OV64A40_PIXEL_ARRAY_LEFT; + crop->width = OV64A40_PIXEL_ARRAY_WIDTH; + crop->height = OV64A40_PIXEL_ARRAY_HEIGHT; + + return 0; +} + +static int ov64a40_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + + if (code->index) + return -EINVAL; + + code->code = ov64a40_mbus_code(ov64a40); + + return 0; +} + +static int ov64a40_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + struct ov64a40_mode *mode; + u32 code; + + if (fse->index >= ARRAY_SIZE(ov64a40_modes)) + return -EINVAL; + + code = ov64a40_mbus_code(ov64a40); + if (fse->code != code) + return -EINVAL; + + mode = &ov64a40_modes[fse->index]; + fse->min_width = mode->width; + fse->max_width = mode->width; + fse->min_height = mode->height; + fse->max_height = mode->height; + + return 0; +} + +static int ov64a40_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: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV64A40_NATIVE_WIDTH; + sel->r.height = OV64A40_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV64A40_PIXEL_ARRAY_TOP; + sel->r.left = OV64A40_PIXEL_ARRAY_LEFT; + sel->r.width = OV64A40_PIXEL_ARRAY_WIDTH; + sel->r.height = OV64A40_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int ov64a40_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + struct v4l2_mbus_framefmt *format; + struct ov64a40_mode *mode; + + mode = v4l2_find_nearest_size(ov64a40_modes, + ARRAY_SIZE(ov64a40_modes), + width, height, + fmt->format.width, fmt->format.height); + + ov64a40_update_pad_fmt(ov64a40, mode, &fmt->format); + + format = v4l2_subdev_state_get_format(state, 0); + if (ov64a40->mode == mode && format->code == fmt->format.code) + return 0; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + const struct ov64a40_timings *timings; + int vblank_max, vblank_def; + int hblank_val; + int exp_max; + + ov64a40->mode = mode; + *v4l2_subdev_state_get_crop(state, 0) = mode->analogue_crop; + + /* Update control limits according to the new mode. */ + timings = ov64a40_get_timings(ov64a40, + ov64a40->link_freq->cur.val); + vblank_max = OV64A40_VTS_MAX - mode->height; + vblank_def = timings->vts - mode->height; + __v4l2_ctrl_modify_range(ov64a40->vblank, OV64A40_VBLANK_MIN, + vblank_max, 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov64a40->vblank, vblank_def); + + exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(ov64a40->exposure, + OV64A40_EXPOSURE_MIN, exp_max, + 1, OV64A40_EXPOSURE_MIN); + + hblank_val = timings->ppl * 4 - mode->width; + __v4l2_ctrl_modify_range(ov64a40->hblank, + hblank_val, hblank_val, 1, hblank_val); + } + + *format = fmt->format; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ov64a40_pad_ops = { + .enum_mbus_code = ov64a40_enum_mbus_code, + .enum_frame_size = ov64a40_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ov64a40_set_format, + .get_selection = ov64a40_get_selection, +}; + +static const struct v4l2_subdev_core_ops ov64a40_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ov64a40_subdev_ops = { + .core = &ov64a40_core_ops, + .video = &ov64a40_video_ops, + .pad = &ov64a40_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ov64a40_internal_ops = { + .init_state = ov64a40_init_state, +}; + +static int ov64a40_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + int ret; + + ret = clk_prepare_enable(ov64a40->xclk); + if (ret) + return ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ov64a40_supply_names), + ov64a40->supplies); + if (ret) { + clk_disable_unprepare(ov64a40->xclk); + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(ov64a40->reset_gpio, 0); + + fsleep(5000); + + return 0; +} + +static int ov64a40_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov64a40 *ov64a40 = sd_to_ov64a40(sd); + + gpiod_set_value_cansleep(ov64a40->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ov64a40_supply_names), + ov64a40->supplies); + clk_disable_unprepare(ov64a40->xclk); + + return 0; +} + +static int ov64a40_link_freq_config(struct ov64a40 *ov64a40, int link_freq_id) +{ + s64 link_frequency; + int ret = 0; + + /* Default 456MHz with 24MHz input clock. */ + cci_multi_reg_write(ov64a40->cci, ov64a40_pll_config, + ARRAY_SIZE(ov64a40_pll_config), &ret); + + /* Decrease the PLL1 multiplier to obtain 360MHz mipi link frequency. */ + link_frequency = ov64a40->link_frequencies[link_freq_id]; + if (link_frequency == OV64A40_LINK_FREQ_360M) + cci_write(ov64a40->cci, OV64A40_PLL1_MULTIPLIER, 0x0078, &ret); + + return ret; +} + +static int ov64a40_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov64a40 *ov64a40 = container_of(ctrl->handler, struct ov64a40, + ctrl_handler); + int pm_status; + int ret = 0; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exp_max = ov64a40->mode->height + ctrl->val + - OV64A40_EXPOSURE_MARGIN; + int exp_val = min(ov64a40->exposure->cur.val, exp_max); + + __v4l2_ctrl_modify_range(ov64a40->exposure, + ov64a40->exposure->minimum, + exp_max, 1, exp_val); + } + + pm_status = pm_runtime_get_if_active(ov64a40->dev, true); + if (!pm_status) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_EXPO, + ctrl->val, NULL); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_GAIN, + ctrl->val << 1, NULL); + break; + case V4L2_CID_VBLANK: { + int vts = ctrl->val + ov64a40->mode->height; + + cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_LOW, vts, &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_MID, + (vts >> 8), &ret); + cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_HIGH, + (vts >> 16), &ret); + break; + } + case V4L2_CID_VFLIP: + ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20, + OV64A40_TIMING_CTRL_20_VFLIP, + ctrl->val << 2, + NULL); + break; + case V4L2_CID_HFLIP: + ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21, + OV64A40_TIMING_CTRL_21_HFLIP, + ctrl->val ? 0 + : OV64A40_TIMING_CTRL_21_HFLIP, + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(ov64a40->cci, OV64A40_REG_TEST_PATTERN, + ov64a40_test_pattern_val[ctrl->val], NULL); + break; + case V4L2_CID_LINK_FREQ: + ret = ov64a40_link_freq_config(ov64a40, ctrl->val); + break; + default: + dev_err(ov64a40->dev, "Unhandled control: %#x\n", ctrl->id); + ret = -EINVAL; + break; + } + + if (pm_status > 0) { + pm_runtime_mark_last_busy(ov64a40->dev); + pm_runtime_put_autosuspend(ov64a40->dev); + } + + return ret; +} + +static const struct v4l2_ctrl_ops ov64a40_ctrl_ops = { + .s_ctrl = ov64a40_set_ctrl, +}; + +static int ov64a40_init_controls(struct ov64a40 *ov64a40) +{ + int exp_max, hblank_val, vblank_max, vblank_def; + struct v4l2_ctrl_handler *hdlr = &ov64a40->ctrl_handler; + struct v4l2_fwnode_device_properties props; + const struct ov64a40_timings *timings; + int ret; + + ret = v4l2_ctrl_handler_init(hdlr, 11); + if (ret) + return ret; + + v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_PIXEL_RATE, + OV64A40_PIXEL_RATE, OV64A40_PIXEL_RATE, 1, + OV64A40_PIXEL_RATE); + + ov64a40->link_freq = + v4l2_ctrl_new_int_menu(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov64a40->num_link_frequencies - 1, + 0, ov64a40->link_frequencies); + + v4l2_ctrl_new_std_menu_items(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov64a40_test_pattern_menu) - 1, + 0, 0, ov64a40_test_pattern_menu); + + timings = ov64a40_get_timings(ov64a40, 0); + exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN; + ov64a40->exposure = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_EXPOSURE, + OV64A40_EXPOSURE_MIN, exp_max, 1, + OV64A40_EXPOSURE_MIN); + + hblank_val = timings->ppl * 4 - ov64a40->mode->width; + ov64a40->hblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_HBLANK, hblank_val, + hblank_val, 1, hblank_val); + if (ov64a40->hblank) + ov64a40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = timings->vts - ov64a40->mode->height; + vblank_max = OV64A40_VTS_MAX - ov64a40->mode->height; + ov64a40->vblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_VBLANK, OV64A40_VBLANK_MIN, + vblank_max, 1, vblank_def); + + v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV64A40_ANA_GAIN_MIN, OV64A40_ANA_GAIN_MAX, 1, + OV64A40_ANA_GAIN_DEFAULT); + + ov64a40->hflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (ov64a40->hflip) + ov64a40->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ov64a40->vflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ov64a40->vflip) + ov64a40->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + if (hdlr->error) { + ret = hdlr->error; + dev_err(ov64a40->dev, "control init failed: %d\n", ret); + goto error_free_hdlr; + } + + ret = v4l2_fwnode_device_parse(ov64a40->dev, &props); + if (ret) + goto error_free_hdlr; + + ret = v4l2_ctrl_new_fwnode_properties(hdlr, &ov64a40_ctrl_ops, + &props); + if (ret) + goto error_free_hdlr; + + ov64a40->sd.ctrl_handler = hdlr; + + return 0; + +error_free_hdlr: + v4l2_ctrl_handler_free(hdlr); + return ret; +} + +static int ov64a40_identify(struct ov64a40 *ov64a40) +{ + int ret; + u64 id; + + ret = cci_read(ov64a40->cci, OV64A40_REG_CHIP_ID, &id, NULL); + if (ret) { + dev_err(ov64a40->dev, "Failed to read chip id: %d\n", ret); + return ret; + } + + if (id != OV64A40_CHIP_ID) { + dev_err(ov64a40->dev, "chip id mismatch: %#llx\n", id); + return -ENODEV; + } + + dev_dbg(ov64a40->dev, "OV64A40 chip identified: %#llx\n", id); + + return 0; +} + +static int ov64a40_parse_dt(struct ov64a40 *ov64a40) +{ + struct v4l2_fwnode_endpoint v4l2_fwnode = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *endpoint; + unsigned int i; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(ov64a40->dev), + NULL); + if (!endpoint) { + dev_err(ov64a40->dev, "Failed to find endpoint\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &v4l2_fwnode); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(ov64a40->dev, "Failed to parse endpoint\n"); + return ret; + } + + if (v4l2_fwnode.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(ov64a40->dev, "Unsupported number of data lanes: %u\n", + v4l2_fwnode.bus.mipi_csi2.num_data_lanes); + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + return -EINVAL; + } + + if (!v4l2_fwnode.nr_of_link_frequencies) { + dev_warn(ov64a40->dev, "no link frequencies defined\n"); + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + return -EINVAL; + } + + if (v4l2_fwnode.nr_of_link_frequencies > 2) { + dev_warn(ov64a40->dev, + "Unsupported number of link frequencies\n"); + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + return -EINVAL; + } + + ov64a40->link_frequencies = + devm_kcalloc(ov64a40->dev, v4l2_fwnode.nr_of_link_frequencies, + sizeof(v4l2_fwnode.link_frequencies[0]), + GFP_KERNEL); + if (!ov64a40->link_frequencies) { + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + return -ENOMEM; + } + ov64a40->num_link_frequencies = v4l2_fwnode.nr_of_link_frequencies; + + for (i = 0; i < v4l2_fwnode.nr_of_link_frequencies; ++i) { + if (v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_360M && + v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_456M) { + dev_err(ov64a40->dev, + "Unsupported link frequency %lld\n", + v4l2_fwnode.link_frequencies[i]); + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + return -EINVAL; + } + + ov64a40->link_frequencies[i] = v4l2_fwnode.link_frequencies[i]; + } + + v4l2_fwnode_endpoint_free(&v4l2_fwnode); + + return 0; +} + +static int ov64a40_get_regulators(struct ov64a40 *ov64a40) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov64a40->sd); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov64a40_supply_names); i++) + ov64a40->supplies[i].supply = ov64a40_supply_names[i]; + + return devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(ov64a40_supply_names), + ov64a40->supplies); +} + +static int ov64a40_probe(struct i2c_client *client) +{ + struct ov64a40 *ov64a40; + u32 xclk_freq; + int ret; + + ov64a40 = devm_kzalloc(&client->dev, sizeof(*ov64a40), GFP_KERNEL); + if (!ov64a40) + return -ENOMEM; + + ov64a40->dev = &client->dev; + v4l2_i2c_subdev_init(&ov64a40->sd, client, &ov64a40_subdev_ops); + + ov64a40->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov64a40->cci)) { + dev_err(&client->dev, "Failed to initialize CCI\n"); + return PTR_ERR(ov64a40->cci); + } + + ov64a40->xclk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(ov64a40->xclk)) + return dev_err_probe(&client->dev, PTR_ERR(ov64a40->xclk), + "Failed to get clock\n"); + + xclk_freq = clk_get_rate(ov64a40->xclk); + if (xclk_freq != OV64A40_XCLK_FREQ) { + dev_err(&client->dev, "Unsupported xclk frequency %u\n", + xclk_freq); + return -EINVAL; + } + + ret = ov64a40_get_regulators(ov64a40); + if (ret) + return ret; + + ov64a40->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov64a40->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(ov64a40->reset_gpio), + "Failed to get reset gpio\n"); + + ret = ov64a40_parse_dt(ov64a40); + if (ret) + return ret; + + ret = ov64a40_power_on(&client->dev); + if (ret) + return ret; + + ret = ov64a40_identify(ov64a40); + if (ret) + goto error_poweroff; + + ov64a40->mode = &ov64a40_modes[0]; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + + ret = ov64a40_init_controls(ov64a40); + if (ret) + goto error_poweroff; + + /* Initialize subdev */ + ov64a40->sd.internal_ops = &ov64a40_internal_ops; + ov64a40->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE + | V4L2_SUBDEV_FL_HAS_EVENTS; + ov64a40->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ov64a40->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov64a40->sd.entity, 1, &ov64a40->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + ov64a40->sd.state_lock = ov64a40->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov64a40->sd); + if (ret < 0) { + dev_err(&client->dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&ov64a40->sd); + if (ret < 0) { + dev_err(&client->dev, + "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&ov64a40->sd); +error_media_entity: + media_entity_cleanup(&ov64a40->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(ov64a40->sd.ctrl_handler); +error_poweroff: + ov64a40_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return ret; +} + +static void ov64a40_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov64a40_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id ov64a40_of_ids[] = { + { .compatible = "ovti,ov64a40" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov64a40_of_ids); + +static const struct dev_pm_ops ov64a40_pm_ops = { + SET_RUNTIME_PM_OPS(ov64a40_power_off, ov64a40_power_on, NULL) +}; + +static struct i2c_driver ov64a40_i2c_driver = { + .driver = { + .name = "ov64a40", + .of_match_table = ov64a40_of_ids, + .pm = &ov64a40_pm_ops, + }, + .probe = ov64a40_probe, + .remove = ov64a40_remove, +}; + +module_i2c_driver(ov64a40_i2c_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("OmniVision OV64A40 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 1ad07935f..b65befb22 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -197,7 +197,7 @@ struct ov6650 { struct clk *clk; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ - struct v4l2_fract tpf; /* as requested with s_frame_interval */ + struct v4l2_fract tpf; /* as requested with set_frame_interval */ u32 code; }; @@ -476,7 +476,7 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { /* pre-select try crop rectangle */ - rect = &sd_state->pads->try_crop; + rect = v4l2_subdev_state_get_crop(sd_state, 0); } else { /* pre-select active crop rectangle */ @@ -531,8 +531,10 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, ov6650_bind_align_crop_rectangle(&sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_rect *crop = &sd_state->pads->try_crop; - struct v4l2_mbus_framefmt *mf = &sd_state->pads->try_fmt; + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(sd_state, 0); + struct v4l2_mbus_framefmt *mf = + v4l2_subdev_state_get_format(sd_state, 0); /* detect current pad config scaling factor */ bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); @@ -588,9 +590,12 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, /* update media bus format code and frame size */ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_state_get_format(sd_state, 0); + + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { mf->width = priv->rect.width >> priv->half_scale; @@ -717,23 +722,26 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, } if (format->which == V4L2_SUBDEV_FORMAT_TRY) - crop = &sd_state->pads->try_crop; + crop = v4l2_subdev_state_get_crop(sd_state, 0); else crop = &priv->rect; half_scale = !is_unscaled_ok(mf->width, mf->height, crop); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_state_get_format(sd_state, 0); + /* store new mbus frame format code and size in pad config */ - sd_state->pads->try_fmt.width = crop->width >> half_scale; - sd_state->pads->try_fmt.height = crop->height >> half_scale; - sd_state->pads->try_fmt.code = mf->code; + try_fmt->width = crop->width >> half_scale; + try_fmt->height = crop->height >> half_scale; + try_fmt->code = mf->code; /* return default mbus frame format updated with pad config */ *mf = ov6650_def_fmt; - mf->width = sd_state->pads->try_fmt.width; - mf->height = sd_state->pads->try_fmt.height; - mf->code = sd_state->pads->try_fmt.code; + mf->width = try_fmt->width; + mf->height = try_fmt->height; + mf->code = try_fmt->code; } else { int ret = 0; @@ -791,12 +799,20 @@ static int ov6650_enum_frame_interval(struct v4l2_subdev *sd, return 0; } -static int ov6650_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov6650_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + ival->interval = priv->tpf; dev_dbg(&client->dev, "Frame interval: %u/%u s\n", @@ -805,14 +821,22 @@ static int ov6650_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int ov6650_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov6650_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); struct v4l2_fract *tpf = &ival->interval; int div, ret; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ else @@ -998,8 +1022,6 @@ static int ov6650_get_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, - .g_frame_interval = ov6650_g_frame_interval, - .s_frame_interval = ov6650_s_frame_interval, }; static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { @@ -1009,6 +1031,8 @@ static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { .set_selection = ov6650_set_selection, .get_fmt = ov6650_get_fmt, .set_fmt = ov6650_set_fmt, + .get_frame_interval = ov6650_get_frame_interval, + .set_frame_interval = ov6650_set_frame_interval, .get_mbus_config = ov6650_get_mbus_config, }; diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 6582cc0e2..30f61e04e 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1139,7 +1139,7 @@ __ov7251_get_pad_format(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->fmt; default: @@ -1169,7 +1169,7 @@ __ov7251_get_pad_crop(struct ov7251 *ov7251, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov7251->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov7251->crop; default: @@ -1282,8 +1282,8 @@ exit: return ret; } -static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int ov7251_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY @@ -1386,10 +1386,18 @@ err_power_down: } static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(subdev); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&ov7251->lock); fi->interval = ov7251->current_mode->timeperframe; mutex_unlock(&ov7251->lock); @@ -1398,12 +1406,20 @@ static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, } static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *fi) { struct ov7251 *ov7251 = to_ov7251(subdev); const struct ov7251_mode_info *new_mode; int ret = 0; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&ov7251->lock); new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); @@ -1436,18 +1452,17 @@ exit: static const struct v4l2_subdev_video_ops ov7251_video_ops = { .s_stream = ov7251_s_stream, - .g_frame_interval = ov7251_get_frame_interval, - .s_frame_interval = ov7251_set_frame_interval, }; static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { - .init_cfg = ov7251_entity_init_cfg, .enum_mbus_code = ov7251_enum_mbus_code, .enum_frame_size = ov7251_enum_frame_size, .enum_frame_interval = ov7251_enum_frame_ival, .get_fmt = ov7251_get_format, .set_fmt = ov7251_set_format, .get_selection = ov7251_get_selection, + .get_frame_interval = ov7251_get_frame_interval, + .set_frame_interval = ov7251_set_frame_interval, }; static const struct v4l2_subdev_ops ov7251_subdev_ops = { @@ -1455,6 +1470,10 @@ static const struct v4l2_subdev_ops ov7251_subdev_ops = { .pad = &ov7251_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov7251_internal_ops = { + .init_state = ov7251_init_state, +}; + static int ov7251_check_hwcfg(struct ov7251 *ov7251) { struct fwnode_handle *fwnode = dev_fwnode(ov7251->dev); @@ -1693,6 +1712,7 @@ static int ov7251_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.internal_ops = &ov7251_internal_ops; ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; ov7251->sd.dev = &client->dev; @@ -1750,7 +1770,7 @@ static int ov7251_probe(struct i2c_client *client) goto free_entity; } - ov7251_entity_init_cfg(&ov7251->sd, NULL); + ov7251_init_state(&ov7251->sd, NULL); return 0; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 172483597..0cb96b6c9 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1112,8 +1112,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); if (ret) return ret; - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; return 0; } @@ -1141,7 +1140,7 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mbus_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; return 0; } else { @@ -1155,23 +1154,37 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, * Implement G/S_PARM. There is a "high quality" mode we could try * to do someday; for now, we just do the frame rate tweak. */ -static int ov7670_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov7670_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct ov7670_info *info = to_state(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; info->devtype->get_framerate(sd, &ival->interval); return 0; } -static int ov7670_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov7670_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct v4l2_fract *tpf = &ival->interval; struct ov7670_info *info = to_state(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; return info->devtype->set_framerate(sd, tpf); } @@ -1707,7 +1720,7 @@ static void ov7670_get_default_format(struct v4l2_subdev *sd, static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov7670_get_default_format(sd, format); @@ -1729,22 +1742,18 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { #endif }; -static const struct v4l2_subdev_video_ops ov7670_video_ops = { - .s_frame_interval = ov7670_s_frame_interval, - .g_frame_interval = ov7670_g_frame_interval, -}; - static const struct v4l2_subdev_pad_ops ov7670_pad_ops = { .enum_frame_interval = ov7670_enum_frame_interval, .enum_frame_size = ov7670_enum_frame_size, .enum_mbus_code = ov7670_enum_mbus_code, .get_fmt = ov7670_get_fmt, .set_fmt = ov7670_set_fmt, + .get_frame_interval = ov7670_get_frame_interval, + .set_frame_interval = ov7670_set_frame_interval, }; static const struct v4l2_subdev_ops ov7670_ops = { .core = &ov7670_core_ops, - .video = &ov7670_video_ops, .pad = &ov7670_pad_ops, }; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 7618b58a7..3e36a5527 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -717,26 +717,42 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, return 0; } -static int ov772x_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov772x_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_fract *tpf = &ival->interval; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + tpf->numerator = 1; tpf->denominator = priv->fps; return 0; } -static int ov772x_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +static int ov772x_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_fract *tpf = &ival->interval; unsigned int fps; int ret = 0; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&priv->lock); if (priv->streaming) { @@ -1220,7 +1236,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + *v4l2_subdev_state_get_format(sd_state, 0) = *mf; return 0; } @@ -1349,8 +1365,6 @@ static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .s_stream = ov772x_s_stream, - .s_frame_interval = ov772x_s_frame_interval, - .g_frame_interval = ov772x_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { @@ -1359,6 +1373,8 @@ static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { .get_selection = ov772x_get_selection, .get_fmt = ov772x_get_fmt, .set_fmt = ov772x_set_fmt, + .get_frame_interval = ov772x_get_frame_interval, + .set_frame_interval = ov772x_set_frame_interval, }; static const struct v4l2_subdev_ops ov772x_subdev_ops = { diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 356a45e65..47b1b14d8 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -638,34 +638,8 @@ err_unlock: return ret; } -static int ov7740_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) -{ - struct v4l2_fract *tpf = &ival->interval; - - - tpf->numerator = 1; - tpf->denominator = 60; - - return 0; -} - -static int ov7740_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) -{ - struct v4l2_fract *tpf = &ival->interval; - - - tpf->numerator = 1; - tpf->denominator = 60; - - return 0; -} - static const struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { .s_stream = ov7740_set_stream, - .s_frame_interval = ov7740_s_frame_interval, - .g_frame_interval = ov7740_g_frame_interval, }; static const struct reg_sequence ov7740_format_yuyv[] = { @@ -812,8 +786,7 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, if (ret) goto error; - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *mbus_fmt = format->format; mutex_unlock(&ov7740->mutex); return 0; @@ -843,7 +816,7 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, mutex_lock(&ov7740->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); format->format = *mbus_fmt; } else { format->format = ov7740->format; @@ -853,12 +826,26 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, return 0; } +static int ov7740_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) +{ + struct v4l2_fract *tpf = &ival->interval; + + tpf->numerator = 1; + tpf->denominator = 60; + + return 0; +} + static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = { .enum_frame_interval = ov7740_enum_frame_interval, .enum_frame_size = ov7740_enum_frame_size, .enum_mbus_code = ov7740_enum_mbus_code, .get_fmt = ov7740_get_fmt, .set_fmt = ov7740_set_fmt, + .get_frame_interval = ov7740_get_frame_interval, + .set_frame_interval = ov7740_get_frame_interval, }; static const struct v4l2_subdev_ops ov7740_subdev_ops = { @@ -883,7 +870,7 @@ static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); struct v4l2_mbus_framefmt *format = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); mutex_lock(&ov7740->mutex); ov7740_get_default_format(sd, format); diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index a0f673a24..6ffe10e57 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -2134,7 +2134,7 @@ static int ov8856_set_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov8856->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); @@ -2172,9 +2172,8 @@ static int ov8856_get_format(struct v4l2_subdev *sd, mutex_lock(&ov8856->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov8856_update_pad_format(ov8856, ov8856->cur_mode, &fmt->format); @@ -2225,7 +2224,7 @@ static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov8856->mutex); ov8856_update_pad_format(ov8856, &ov8856->priv_lane->supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov8856->mutex); return 0; diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c index 4d9fd76e2..174c65f76 100644 --- a/drivers/media/i2c/ov8858.c +++ b/drivers/media/i2c/ov8858.c @@ -1333,7 +1333,7 @@ static int ov8858_start_stream(struct ov8858 *ov8858, if (ret) return ret; - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); mode = v4l2_find_nearest_size(ov8858_modes, ARRAY_SIZE(ov8858_modes), width, height, format->width, format->height); @@ -1428,7 +1428,7 @@ static int ov8858_set_fmt(struct v4l2_subdev *sd, fmt->format.field = V4L2_FIELD_NONE; /* Store the format in the current subdev state. */ - *v4l2_subdev_get_pad_format(sd, state, 0) = fmt->format; + *v4l2_subdev_state_get_format(state, 0) = fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) return 0; @@ -1476,8 +1476,8 @@ static int ov8858_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov8858_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov8858_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct ov8858_mode *def_mode = &ov8858_modes[0]; struct v4l2_subdev_format fmt = { @@ -1494,7 +1494,6 @@ static int ov8858_init_cfg(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov8858_pad_ops = { - .init_cfg = ov8858_init_cfg, .enum_mbus_code = ov8858_enum_mbus_code, .enum_frame_size = ov8858_enum_frame_sizes, .get_fmt = v4l2_subdev_get_fmt, @@ -1512,6 +1511,10 @@ static const struct v4l2_subdev_ops ov8858_subdev_ops = { .pad = &ov8858_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov8858_internal_ops = { + .init_state = ov8858_init_state, +}; + /* ---------------------------------------------------------------------------- * Controls handling */ @@ -1547,7 +1550,7 @@ static int ov8858_set_ctrl(struct v4l2_ctrl *ctrl) * - by the driver when s_ctrl is called in the s_stream(1) call path */ state = v4l2_subdev_get_locked_active_state(&ov8858->subdev); - format = v4l2_subdev_get_pad_format(&ov8858->subdev, state, 0); + format = v4l2_subdev_state_get_format(state, 0); /* Propagate change of current control to all related controls */ switch (ctrl->id) { @@ -1899,6 +1902,7 @@ static int ov8858_probe(struct i2c_client *client) "Failed to get powerdown gpio\n"); v4l2_i2c_subdev_init(&ov8858->subdev, client, &ov8858_subdev_ops); + ov8858->subdev.internal_ops = &ov8858_internal_ops; ret = ov8858_configure_regulators(ov8858); if (ret) diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index f2213c615..95ffe7536 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2640,33 +2640,8 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } -static int ov8865_g_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *interval) -{ - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - const struct ov8865_mode *mode; - unsigned int framesize; - unsigned int fps; - - mutex_lock(&sensor->mutex); - - mode = sensor->state.mode; - framesize = mode->hts * (mode->output_size_y + - sensor->ctrls.vblank->val); - fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize); - - interval->interval.numerator = 1; - interval->interval.denominator = fps; - - mutex_unlock(&sensor->mutex); - - return 0; -} - static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = { .s_stream = ov8865_s_stream, - .g_frame_interval = ov8865_g_frame_interval, - .s_frame_interval = ov8865_g_frame_interval, }; /* Subdev Pad Operations */ @@ -2710,8 +2685,8 @@ static int ov8865_get_fmt(struct v4l2_subdev *subdev, mutex_lock(&sensor->mutex); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(sd_state, + format->pad); else ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code, sensor->state.mode); @@ -2765,7 +2740,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, ov8865_mbus_format_fill(mbus_format, mbus_code, mode); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, sd_state, format->pad) = + *v4l2_subdev_state_get_format(sd_state, format->pad) = *mbus_format; else if (sensor->state.mode != mode || sensor->state.mbus_code != mbus_code) @@ -2818,7 +2793,7 @@ __ov8865_get_pad_crop(struct ov8865_sensor *sensor, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); + *r = *v4l2_subdev_state_get_crop(state, pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: r->height = mode->output_size_y; @@ -2862,6 +2837,37 @@ static int ov8865_get_selection(struct v4l2_subdev *subdev, return 0; } +static int ov8865_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + const struct ov8865_mode *mode; + unsigned int framesize; + unsigned int fps; + + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + mutex_lock(&sensor->mutex); + + mode = sensor->state.mode; + framesize = mode->hts * (mode->output_size_y + + sensor->ctrls.vblank->val); + fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize); + + interval->interval.numerator = 1; + interval->interval.denominator = fps; + + mutex_unlock(&sensor->mutex); + + return 0; +} + static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { .enum_mbus_code = ov8865_enum_mbus_code, .get_fmt = ov8865_get_fmt, @@ -2869,6 +2875,8 @@ static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { .enum_frame_size = ov8865_enum_frame_size, .get_selection = ov8865_get_selection, .set_selection = ov8865_get_selection, + .get_frame_interval = ov8865_get_frame_interval, + .set_frame_interval = ov8865_get_frame_interval, }; static const struct v4l2_subdev_ops ov8865_subdev_ops = { diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index bf6dfce1b..251a4b534 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -814,7 +814,7 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *framefmt; } else { ov9282_fill_pad_format(ov9282, ov9282->cur_mode, ov9282->code, @@ -860,7 +860,7 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; } else { ret = ov9282_update_controls(ov9282, mode, fmt); @@ -876,14 +876,14 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, } /** - * ov9282_init_pad_cfg() - Initialize sub-device pad configuration + * ov9282_init_state() - Initialize sub-device state * @sd: pointer to ov9282 V4L2 sub-device structure * @sd_state: V4L2 sub-device configuration * * Return: 0 if successful, error code otherwise. */ -static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int ov9282_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov9282 *ov9282 = to_ov9282(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -902,7 +902,7 @@ __ov9282_get_pad_crop(struct ov9282 *ov9282, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&ov9282->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &ov9282->cur_mode->crop; } @@ -1192,7 +1192,6 @@ static const struct v4l2_subdev_video_ops ov9282_video_ops = { }; static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { - .init_cfg = ov9282_init_pad_cfg, .enum_mbus_code = ov9282_enum_mbus_code, .enum_frame_size = ov9282_enum_frame_size, .get_fmt = ov9282_get_pad_format, @@ -1206,6 +1205,10 @@ static const struct v4l2_subdev_ops ov9282_subdev_ops = { .pad = &ov9282_pad_ops, }; +static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { + .init_state = ov9282_init_state, +}; + /** * ov9282_power_on() - Sensor power on sequence * @dev: pointer to i2c device @@ -1394,6 +1397,7 @@ static int ov9282_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov9282->sd, client, &ov9282_subdev_ops); + ov9282->sd.internal_ops = &ov9282_internal_ops; v4l2_i2c_subdev_set_name(&ov9282->sd, client, device_get_match_data(ov9282->dev), NULL); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index cbaea0495..e9a52a8a9 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -547,8 +547,6 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ov9640_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; - return 0; } diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index da1ab5135..66cd0e9dd 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1101,11 +1101,19 @@ static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, return 0; } -static int ov965x_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int ov965x_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct ov965x *ov965x = to_ov965x(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&ov965x->lock); fi->interval = ov965x->fiv->interval; mutex_unlock(&ov965x->lock); @@ -1148,12 +1156,20 @@ static int __ov965x_set_frame_interval(struct ov965x *ov965x, return 0; } -static int ov965x_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int ov965x_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct ov965x *ov965x = to_ov965x(sd); int ret; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", fi->interval.numerator, fi->interval.denominator); @@ -1172,7 +1188,7 @@ static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); fmt->format = *mf; return 0; } @@ -1233,8 +1249,7 @@ static int ov965x_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { if (sd_state) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } } else { @@ -1363,7 +1378,7 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on) static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_try_format(sd, fh->state, 0); + v4l2_subdev_state_get_format(fh->state, 0); ov965x_get_default_format(mf); return 0; @@ -1374,12 +1389,12 @@ static const struct v4l2_subdev_pad_ops ov965x_pad_ops = { .enum_frame_size = ov965x_enum_frame_sizes, .get_fmt = ov965x_get_fmt, .set_fmt = ov965x_set_fmt, + .get_frame_interval = ov965x_get_frame_interval, + .set_frame_interval = ov965x_set_frame_interval, }; static const struct v4l2_subdev_video_ops ov965x_video_ops = { .s_stream = ov965x_s_stream, - .g_frame_interval = ov965x_g_frame_interval, - .s_frame_interval = ov965x_s_frame_interval, }; diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c index f1377c305..d99728597 100644 --- a/drivers/media/i2c/ov9734.c +++ b/drivers/media/i2c/ov9734.c @@ -697,7 +697,7 @@ static int ov9734_set_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); ov9734_update_pad_format(mode, &fmt->format); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; } else { ov9734->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov9734->link_freq, mode->link_freq_index); @@ -730,9 +730,8 @@ static int ov9734_get_format(struct v4l2_subdev *sd, mutex_lock(&ov9734->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov9734->sd, - sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else ov9734_update_pad_format(ov9734->cur_mode, &fmt->format); @@ -777,7 +776,7 @@ static int ov9734_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&ov9734->mutex); ov9734_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); + v4l2_subdev_state_get_format(fh->state, 0)); mutex_unlock(&ov9734->mutex); return 0; diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index b430046f9..a59db1015 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1008,10 +1008,8 @@ static int rj54n1_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* * Verify if the sensor has just been powered on. TODO: replace this diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ed5b10731..af8d01f78 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -819,7 +819,6 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, struct v4l2_subdev_format *fmt, const struct s5c73m3_frame_size **fs) { - struct v4l2_subdev *sd = &state->sensor_sd; u32 code; switch (fmt->pad) { @@ -841,10 +840,8 @@ static void s5c73m3_oif_try_format(struct s5c73m3 *state, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) *fs = state->oif_pix_size[RES_ISP]; else - *fs = s5c73m3_find_frame_size( - v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD), - RES_ISP); + *fs = s5c73m3_find_frame_size(v4l2_subdev_state_get_format(sd_state, OIF_ISP_PAD), + RES_ISP); break; } @@ -869,11 +866,19 @@ static void s5c73m3_try_format(struct s5c73m3 *state, s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); } -static int s5c73m3_oif_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int s5c73m3_oif_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (fi->pad != OIF_SOURCE_PAD) return -EINVAL; @@ -918,12 +923,20 @@ static int __s5c73m3_set_frame_interval(struct s5c73m3 *state, return 0; } -static int s5c73m3_oif_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int s5c73m3_oif_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); int ret; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + if (fi->pad != OIF_SOURCE_PAD) return -EINVAL; @@ -990,8 +1003,8 @@ static int s5c73m3_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1025,8 +1038,8 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, u32 code; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -1069,7 +1082,7 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd, s5c73m3_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; } else { switch (fmt->pad) { @@ -1108,11 +1121,11 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, s5c73m3_oif_try_format(state, sd_state, fmt, &frame_size); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; if (fmt->pad == OIF_ISP_PAD) { - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_SOURCE_PAD); mf->width = fmt->format.width; mf->height = fmt->format.height; } @@ -1260,8 +1273,8 @@ static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, if (fse->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, - OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(sd_state, + OIF_ISP_PAD); w = mf->width; h = mf->height; @@ -1316,11 +1329,11 @@ static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, S5C73M3_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, S5C73M3_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); @@ -1331,15 +1344,15 @@ static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_ISP_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_ISP_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_JPEG_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_JPEG_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], S5C73M3_JPEG_FMT); - mf = v4l2_subdev_get_try_format(sd, fh->state, OIF_SOURCE_PAD); + mf = v4l2_subdev_state_get_format(fh->state, OIF_SOURCE_PAD); s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], S5C73M3_ISP_FMT); return 0; @@ -1500,6 +1513,8 @@ static const struct v4l2_subdev_pad_ops s5c73m3_oif_pad_ops = { .enum_frame_interval = s5c73m3_oif_enum_frame_interval, .get_fmt = s5c73m3_oif_get_fmt, .set_fmt = s5c73m3_oif_set_fmt, + .get_frame_interval = s5c73m3_oif_get_frame_interval, + .set_frame_interval = s5c73m3_oif_set_frame_interval, .get_frame_desc = s5c73m3_oif_get_frame_desc, .set_frame_desc = s5c73m3_oif_set_frame_desc, }; @@ -1511,8 +1526,6 @@ static const struct v4l2_subdev_core_ops s5c73m3_oif_core_ops = { static const struct v4l2_subdev_video_ops s5c73m3_oif_video_ops = { .s_stream = s5c73m3_oif_s_stream, - .g_frame_interval = s5c73m3_oif_g_frame_interval, - .s_frame_interval = s5c73m3_oif_s_frame_interval, }; static const struct v4l2_subdev_ops oif_subdev_ops = { diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 67da2045f..de079d2c9 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1118,11 +1118,19 @@ out: return ret; } -static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int s5k5baf_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct s5k5baf *state = to_s5k5baf(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&state->lock); fi->interval.numerator = state->fiv; fi->interval.denominator = 10000; @@ -1131,8 +1139,8 @@ static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static void s5k5baf_set_frame_interval(struct s5k5baf *state, - struct v4l2_subdev_frame_interval *fi) +static void __s5k5baf_set_frame_interval(struct s5k5baf *state, + struct v4l2_subdev_frame_interval *fi) { struct v4l2_fract *i = &fi->interval; @@ -1155,13 +1163,21 @@ static void s5k5baf_set_frame_interval(struct s5k5baf *state, state->fiv); } -static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) +static int s5k5baf_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) { struct s5k5baf *state = to_s5k5baf(sd); + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mutex_lock(&state->lock); - s5k5baf_set_frame_interval(state, fi); + __s5k5baf_set_frame_interval(state, fi); mutex_unlock(&state->lock); return 0; } @@ -1273,7 +1289,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1307,7 +1323,7 @@ static int s5k5baf_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = *mf; + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = *mf; return 0; } @@ -1379,11 +1395,11 @@ static int s5k5baf_get_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { if (rtype == R_COMPOSE) - sel->r = *v4l2_subdev_get_try_compose(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_compose(sd_state, + sel->pad); else - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, - sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + sel->pad); return 0; } @@ -1472,14 +1488,11 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { rects = (struct v4l2_rect * []) { - &s5k5baf_cis_rect, - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_compose(sd, sd_state, - PAD_CIS), - v4l2_subdev_get_try_crop(sd, sd_state, - PAD_OUT) - }; + &s5k5baf_cis_rect, + v4l2_subdev_state_get_crop(sd_state, PAD_CIS), + v4l2_subdev_state_get_compose(sd_state, PAD_CIS), + v4l2_subdev_state_get_crop(sd_state, PAD_OUT) + }; s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); return 0; } @@ -1529,11 +1542,11 @@ static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { .set_fmt = s5k5baf_set_fmt, .get_selection = s5k5baf_get_selection, .set_selection = s5k5baf_set_selection, + .get_frame_interval = s5k5baf_get_frame_interval, + .set_frame_interval = s5k5baf_set_frame_interval, }; static const struct v4l2_subdev_video_ops s5k5baf_video_ops = { - .g_frame_interval = s5k5baf_g_frame_interval, - .s_frame_interval = s5k5baf_s_frame_interval, .s_stream = s5k5baf_s_stream, }; @@ -1696,22 +1709,22 @@ static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_CIS); + mf = v4l2_subdev_state_get_format(fh->state, PAD_CIS); s5k5baf_try_cis_format(mf); if (s5k5baf_is_cis_subdev(sd)) return 0; - mf = v4l2_subdev_get_try_format(sd, fh->state, PAD_OUT); + mf = v4l2_subdev_state_get_format(fh->state, PAD_OUT); mf->colorspace = s5k5baf_formats[0].colorspace; mf->code = s5k5baf_formats[0].code; mf->width = s5k5baf_cis_rect.width; mf->height = s5k5baf_cis_rect.height; mf->field = V4L2_FIELD_NONE; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_compose(sd, fh->state, PAD_CIS) = s5k5baf_cis_rect; - *v4l2_subdev_get_try_crop(sd, fh->state, PAD_OUT) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_compose(fh->state, PAD_CIS) = s5k5baf_cis_rect; + *v4l2_subdev_state_get_crop(fh->state, PAD_OUT) = s5k5baf_cis_rect; return 0; } diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index b3560c8f8..0c2674115 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -127,8 +127,7 @@ static struct v4l2_mbus_framefmt *__s5k6a3_get_format( u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&sensor->subdev, - sd_state, pad) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL; return &sensor->format; } @@ -174,9 +173,8 @@ static const struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(fh->state, + 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index c106e7a7d..897eaa669 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -594,10 +594,8 @@ static int saa6752hs_set_fmt(struct v4l2_subdev *sd, f->field = V4L2_FIELD_INTERLACED; f->colorspace = V4L2_COLORSPACE_SMPTE170M; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *f; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - } /* FIXME: translate and round width/height into EMPRESS diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index dab147871..f25064072 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -16,25 +16,27 @@ #include #include #include +#include #include +#include #include #include #include #include -#define MIPID02_CLK_LANE_WR_REG1 0x01 -#define MIPID02_CLK_LANE_REG1 0x02 -#define MIPID02_CLK_LANE_REG3 0x04 -#define MIPID02_DATA_LANE0_REG1 0x05 -#define MIPID02_DATA_LANE0_REG2 0x06 -#define MIPID02_DATA_LANE1_REG1 0x09 -#define MIPID02_DATA_LANE1_REG2 0x0a -#define MIPID02_MODE_REG1 0x14 -#define MIPID02_MODE_REG2 0x15 -#define MIPID02_DATA_ID_RREG 0x17 -#define MIPID02_DATA_SELECTION_CTRL 0x19 -#define MIPID02_PIX_WIDTH_CTRL 0x1e -#define MIPID02_PIX_WIDTH_CTRL_EMB 0x1f +#define MIPID02_CLK_LANE_WR_REG1 CCI_REG8(0x01) +#define MIPID02_CLK_LANE_REG1 CCI_REG8(0x02) +#define MIPID02_CLK_LANE_REG3 CCI_REG8(0x04) +#define MIPID02_DATA_LANE0_REG1 CCI_REG8(0x05) +#define MIPID02_DATA_LANE0_REG2 CCI_REG8(0x06) +#define MIPID02_DATA_LANE1_REG1 CCI_REG8(0x09) +#define MIPID02_DATA_LANE1_REG2 CCI_REG8(0x0a) +#define MIPID02_MODE_REG1 CCI_REG8(0x14) +#define MIPID02_MODE_REG2 CCI_REG8(0x15) +#define MIPID02_DATA_ID_RREG CCI_REG8(0x17) +#define MIPID02_DATA_SELECTION_CTRL CCI_REG8(0x19) +#define MIPID02_PIX_WIDTH_CTRL CCI_REG8(0x1e) +#define MIPID02_PIX_WIDTH_CTRL_EMB CCI_REG8(0x1f) /* Bits definition for MIPID02_CLK_LANE_REG1 */ #define CLK_ENABLE BIT(0) @@ -68,7 +70,7 @@ static const u32 mipid02_supported_fmt_codes[] = { MEDIA_BUS_FMT_RGB565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YVYU8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8, - MEDIA_BUS_FMT_JPEG_1X8 + MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_JPEG_1X8 }; /* regulator supplies */ @@ -88,12 +90,12 @@ struct mipid02_dev { struct i2c_client *i2c_client; struct regulator_bulk_data supplies[MIPID02_NUM_SUPPLIES]; struct v4l2_subdev sd; + struct regmap *regmap; struct media_pad pad[MIPID02_PAD_NB]; struct clk *xclk; struct gpio_desc *reset_gpio; /* endpoints info */ struct v4l2_fwnode_endpoint rx; - u64 link_frequency; struct v4l2_fwnode_endpoint tx; /* remote source */ struct v4l2_async_notifier notifier; @@ -110,10 +112,6 @@ struct mipid02_dev { u8 pix_width_ctrl; u8 pix_width_ctrl_emb; } r; - /* lock to protect all members below */ - struct mutex lock; - bool streaming; - struct v4l2_mbus_framefmt fmt; }; static int bpp_from_code(__u32 code) @@ -123,6 +121,7 @@ static int bpp_from_code(__u32 code) case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_Y8_1X8: return 8; case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: @@ -160,17 +159,18 @@ static u8 data_type_from_code(__u32 code) case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: - return 0x2a; + case MEDIA_BUS_FMT_Y8_1X8: + return MIPI_CSI2_DT_RAW8; case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: - return 0x2b; + return MIPI_CSI2_DT_RAW10; case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SRGGB12_1X12: - return 0x2c; + return MIPI_CSI2_DT_RAW12; case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YVYU8_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: @@ -179,30 +179,18 @@ static u8 data_type_from_code(__u32 code) case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: - return 0x1e; + return MIPI_CSI2_DT_YUV422_8B; case MEDIA_BUS_FMT_BGR888_1X24: - return 0x24; + return MIPI_CSI2_DT_RGB888; case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB565_2X8_BE: - return 0x22; + return MIPI_CSI2_DT_RGB565; default: return 0; } } -static void init_format(struct v4l2_mbus_framefmt *fmt) -{ - fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB); - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB); - fmt->width = 640; - fmt->height = 480; -} - static __u32 get_fmt_code(__u32 code) { unsigned int i; @@ -238,62 +226,6 @@ static inline struct mipid02_dev *to_mipid02_dev(struct v4l2_subdev *sd) return container_of(sd, struct mipid02_dev, sd); } -static int mipid02_read_reg(struct mipid02_dev *bridge, u16 reg, u8 *val) -{ - struct i2c_client *client = bridge->i2c_client; - struct i2c_msg msg[2]; - u8 buf[2]; - int ret; - - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - - msg[0].addr = client->addr; - msg[0].flags = client->flags; - msg[0].buf = buf; - msg[0].len = sizeof(buf); - - msg[1].addr = client->addr; - msg[1].flags = client->flags | I2C_M_RD; - msg[1].buf = val; - msg[1].len = 1; - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) { - dev_dbg(&client->dev, "%s: %x i2c_transfer, reg: %x => %d\n", - __func__, client->addr, reg, ret); - return ret; - } - - return 0; -} - -static int mipid02_write_reg(struct mipid02_dev *bridge, u16 reg, u8 val) -{ - struct i2c_client *client = bridge->i2c_client; - struct i2c_msg msg; - u8 buf[3]; - int ret; - - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - buf[2] = val; - - msg.addr = client->addr; - msg.flags = client->flags; - msg.buf = buf; - msg.len = sizeof(buf); - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c_transfer, reg: %x => %d\n", - __func__, reg, ret); - return ret; - } - - return 0; -} - static int mipid02_get_regulators(struct mipid02_dev *bridge) { unsigned int i; @@ -358,73 +290,44 @@ static void mipid02_set_power_off(struct mipid02_dev *bridge) static int mipid02_detect(struct mipid02_dev *bridge) { - u8 reg; + u64 reg; /* * There is no version registers. Just try to read register * MIPID02_CLK_LANE_WR_REG1. */ - return mipid02_read_reg(bridge, MIPID02_CLK_LANE_WR_REG1, ®); -} - -static u32 mipid02_get_link_freq_from_cid_link_freq(struct mipid02_dev *bridge, - struct v4l2_subdev *subdev) -{ - struct v4l2_querymenu qm = {.id = V4L2_CID_LINK_FREQ, }; - struct v4l2_ctrl *ctrl; - int ret; - - ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ); - if (!ctrl) - return 0; - qm.index = v4l2_ctrl_g_ctrl(ctrl); - - ret = v4l2_querymenu(subdev->ctrl_handler, &qm); - if (ret) - return 0; - - return qm.value; -} - -static u32 mipid02_get_link_freq_from_cid_pixel_rate(struct mipid02_dev *bridge, - struct v4l2_subdev *subdev) -{ - struct v4l2_fwnode_endpoint *ep = &bridge->rx; - struct v4l2_ctrl *ctrl; - u32 pixel_clock; - u32 bpp = bpp_from_code(bridge->fmt.code); - - ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) - return 0; - pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); - - return pixel_clock * bpp / (2 * ep->bus.mipi_csi2.num_data_lanes); + return cci_read(bridge->regmap, MIPID02_CLK_LANE_WR_REG1, ®, NULL); } /* * We need to know link frequency to setup clk_lane_reg1 timings. Link frequency - * will be computed using connected device V4L2_CID_PIXEL_RATE, bit per pixel + * will be retrieve from connected device via v4l2_get_link_freq, bit per pixel * and number of lanes. */ -static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge) +static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, + struct v4l2_mbus_framefmt *fmt) { struct i2c_client *client = bridge->i2c_client; struct v4l2_subdev *subdev = bridge->s_subdev; - u32 link_freq; - - link_freq = mipid02_get_link_freq_from_cid_link_freq(bridge, subdev); - if (!link_freq) { - link_freq = mipid02_get_link_freq_from_cid_pixel_rate(bridge, - subdev); - if (!link_freq) { - dev_err(&client->dev, "Failed to get link frequency"); - return -EINVAL; - } + struct v4l2_fwnode_endpoint *ep = &bridge->rx; + u32 bpp = bpp_from_code(fmt->code); + /* + * clk_lane_reg1 requires 4 times the unit interval time, and bitrate + * is twice the link frequency, hence ui_4 = 1000000000 * 4 / 2 + */ + u64 ui_4 = 2000000000; + s64 link_freq; + + link_freq = v4l2_get_link_freq(subdev->ctrl_handler, bpp, + 2 * ep->bus.mipi_csi2.num_data_lanes); + if (link_freq < 0) { + dev_err(&client->dev, "Failed to get link frequency"); + return -EINVAL; } - dev_dbg(&client->dev, "detect link_freq = %d Hz", link_freq); - bridge->r.clk_lane_reg1 |= (2000000000 / link_freq) << 2; + dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq); + do_div(ui_4, link_freq); + bridge->r.clk_lane_reg1 |= ui_4 << 2; return 0; } @@ -479,7 +382,8 @@ static int mipid02_configure_data1_lane(struct mipid02_dev *bridge, int nb, return 0; } -static int mipid02_configure_from_rx(struct mipid02_dev *bridge) +static int mipid02_configure_from_rx(struct mipid02_dev *bridge, + struct v4l2_mbus_framefmt *fmt) { struct v4l2_fwnode_endpoint *ep = &bridge->rx; bool are_lanes_swap = ep->bus.mipi_csi2.data_lanes[0] == 2; @@ -504,7 +408,7 @@ static int mipid02_configure_from_rx(struct mipid02_dev *bridge) bridge->r.mode_reg1 |= are_lanes_swap ? MODE_DATA_SWAP : 0; bridge->r.mode_reg1 |= (nb - 1) << 1; - return mipid02_configure_from_rx_speed(bridge); + return mipid02_configure_from_rx_speed(bridge, fmt); } static int mipid02_configure_from_tx(struct mipid02_dev *bridge) @@ -524,16 +428,17 @@ static int mipid02_configure_from_tx(struct mipid02_dev *bridge) return 0; } -static int mipid02_configure_from_code(struct mipid02_dev *bridge) +static int mipid02_configure_from_code(struct mipid02_dev *bridge, + struct v4l2_mbus_framefmt *fmt) { u8 data_type; bridge->r.data_id_rreg = 0; - if (bridge->fmt.code != MEDIA_BUS_FMT_JPEG_1X8) { + if (fmt->code != MEDIA_BUS_FMT_JPEG_1X8) { bridge->r.data_selection_ctrl |= SELECTION_MANUAL_DATA; - data_type = data_type_from_code(bridge->fmt.code); + data_type = data_type_from_code(fmt->code); if (!data_type) return -EINVAL; bridge->r.data_id_rreg = data_type; @@ -555,13 +460,9 @@ static int mipid02_stream_disable(struct mipid02_dev *bridge) goto error; /* Disable all lanes */ - ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, 0); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG1, 0); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG1, 0); + cci_write(bridge->regmap, MIPID02_CLK_LANE_REG1, 0, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE0_REG1, 0, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE1_REG1, 0, &ret); if (ret) goto error; error: @@ -574,69 +475,52 @@ error: static int mipid02_stream_enable(struct mipid02_dev *bridge) { struct i2c_client *client = bridge->i2c_client; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; int ret = -EINVAL; if (!bridge->s_subdev) goto error; memset(&bridge->r, 0, sizeof(bridge->r)); + + state = v4l2_subdev_lock_and_get_active_state(&bridge->sd); + fmt = v4l2_subdev_state_get_format(state, MIPID02_SINK_0); + /* build registers content */ - ret = mipid02_configure_from_rx(bridge); + ret = mipid02_configure_from_rx(bridge, fmt); if (ret) goto error; ret = mipid02_configure_from_tx(bridge); if (ret) goto error; - ret = mipid02_configure_from_code(bridge); + ret = mipid02_configure_from_code(bridge, fmt); if (ret) goto error; + v4l2_subdev_unlock_state(state); + /* write mipi registers */ - ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, - bridge->r.clk_lane_reg1); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG3, CLK_MIPI_CSI); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG1, - bridge->r.data_lane0_reg1); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE0_REG2, - DATA_MIPI_CSI); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG1, - bridge->r.data_lane1_reg1); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_LANE1_REG2, - DATA_MIPI_CSI); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_MODE_REG1, - MODE_NO_BYPASS | bridge->r.mode_reg1); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_MODE_REG2, - bridge->r.mode_reg2); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_ID_RREG, - bridge->r.data_id_rreg); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_DATA_SELECTION_CTRL, - bridge->r.data_selection_ctrl); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_PIX_WIDTH_CTRL, - bridge->r.pix_width_ctrl); - if (ret) - goto error; - ret = mipid02_write_reg(bridge, MIPID02_PIX_WIDTH_CTRL_EMB, - bridge->r.pix_width_ctrl_emb); + cci_write(bridge->regmap, MIPID02_CLK_LANE_REG1, + bridge->r.clk_lane_reg1, &ret); + cci_write(bridge->regmap, MIPID02_CLK_LANE_REG3, CLK_MIPI_CSI, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE0_REG1, + bridge->r.data_lane0_reg1, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE0_REG2, DATA_MIPI_CSI, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE1_REG1, + bridge->r.data_lane1_reg1, &ret); + cci_write(bridge->regmap, MIPID02_DATA_LANE1_REG2, DATA_MIPI_CSI, &ret); + cci_write(bridge->regmap, MIPID02_MODE_REG1, + MODE_NO_BYPASS | bridge->r.mode_reg1, &ret); + cci_write(bridge->regmap, MIPID02_MODE_REG2, bridge->r.mode_reg2, &ret); + cci_write(bridge->regmap, MIPID02_DATA_ID_RREG, bridge->r.data_id_rreg, + &ret); + cci_write(bridge->regmap, MIPID02_DATA_SELECTION_CTRL, + bridge->r.data_selection_ctrl, &ret); + cci_write(bridge->regmap, MIPID02_PIX_WIDTH_CTRL, + bridge->r.pix_width_ctrl, &ret); + cci_write(bridge->regmap, MIPID02_PIX_WIDTH_CTRL_EMB, + bridge->r.pix_width_ctrl_emb, &ret); if (ret) goto error; @@ -659,31 +543,43 @@ static int mipid02_s_stream(struct v4l2_subdev *sd, int enable) struct i2c_client *client = bridge->i2c_client; int ret = 0; - dev_dbg(&client->dev, "%s : requested %d / current = %d", __func__, - enable, bridge->streaming); - mutex_lock(&bridge->lock); - - if (bridge->streaming == enable) - goto out; + dev_dbg(&client->dev, "%s : requested %d\n", __func__, enable); ret = enable ? mipid02_stream_enable(bridge) : mipid02_stream_disable(bridge); - if (!ret) - bridge->streaming = enable; - -out: - dev_dbg(&client->dev, "%s current now = %d / %d", __func__, - bridge->streaming, ret); - mutex_unlock(&bridge->lock); + if (ret) + dev_err(&client->dev, "failed to stream %s (%d)\n", + enable ? "enable" : "disable", ret); return ret; } +static const struct v4l2_mbus_framefmt default_fmt = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + .width = 640, + .height = 480, +}; + +static int mipid02_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + *v4l2_subdev_state_get_format(state, MIPID02_SINK_0) = default_fmt; + /* MIPID02_SINK_1 isn't supported yet */ + *v4l2_subdev_state_get_format(state, MIPID02_SOURCE) = default_fmt; + + return 0; +} + static int mipid02_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - struct mipid02_dev *bridge = to_mipid02_dev(sd); + struct v4l2_mbus_framefmt *sink_fmt; int ret = 0; switch (code->pad) { @@ -694,10 +590,13 @@ static int mipid02_enum_mbus_code(struct v4l2_subdev *sd, code->code = mipid02_supported_fmt_codes[code->index]; break; case MIPID02_SOURCE: - if (code->index == 0) - code->code = serial_to_parallel_code(bridge->fmt.code); - else + if (code->index == 0) { + sink_fmt = v4l2_subdev_state_get_format(sd_state, + MIPID02_SINK_0); + code->code = serial_to_parallel_code(sink_fmt->code); + } else { ret = -EINVAL; + } break; default: ret = -EINVAL; @@ -706,122 +605,38 @@ static int mipid02_enum_mbus_code(struct v4l2_subdev *sd, return ret; } -static int mipid02_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mbus_fmt = &format->format; - struct mipid02_dev *bridge = to_mipid02_dev(sd); - struct i2c_client *client = bridge->i2c_client; - struct v4l2_mbus_framefmt *fmt; - - dev_dbg(&client->dev, "%s probe %d", __func__, format->pad); - - if (format->pad >= MIPID02_PAD_NB) - return -EINVAL; - /* second CSI-2 pad not yet supported */ - if (format->pad == MIPID02_SINK_1) - return -EINVAL; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&bridge->sd, sd_state, - format->pad); - else - fmt = &bridge->fmt; - - mutex_lock(&bridge->lock); - - *mbus_fmt = *fmt; - /* code may need to be converted for source */ - if (format->pad == MIPID02_SOURCE) - mbus_fmt->code = serial_to_parallel_code(mbus_fmt->code); - - mutex_unlock(&bridge->lock); - - return 0; -} - -static void mipid02_set_fmt_source(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct mipid02_dev *bridge = to_mipid02_dev(sd); - - /* source pad mirror sink pad */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - format->format = bridge->fmt; - else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, - MIPID02_SINK_0); - - /* but code may need to be converted */ - format->format.code = serial_to_parallel_code(format->format.code); - - /* only apply format for V4L2_SUBDEV_FORMAT_TRY case */ - if (format->which != V4L2_SUBDEV_FORMAT_TRY) - return; - - *v4l2_subdev_get_try_format(sd, sd_state, MIPID02_SOURCE) = - format->format; -} - -static void mipid02_set_fmt_sink(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct mipid02_dev *bridge = to_mipid02_dev(sd); - struct v4l2_subdev_format source_fmt; - struct v4l2_mbus_framefmt *fmt; - - format->format.code = get_fmt_code(format->format.code); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); - else - fmt = &bridge->fmt; - - *fmt = format->format; - - /* - * Propagate the format change to the source pad, taking - * care not to update the format pointer given back to user - */ - source_fmt = *format; - mipid02_set_fmt_source(sd, sd_state, &source_fmt); -} - static int mipid02_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) + struct v4l2_subdev_format *fmt) { struct mipid02_dev *bridge = to_mipid02_dev(sd); struct i2c_client *client = bridge->i2c_client; - int ret = 0; + struct v4l2_mbus_framefmt *pad_fmt; - dev_dbg(&client->dev, "%s for %d", __func__, format->pad); + dev_dbg(&client->dev, "%s for %d", __func__, fmt->pad); - if (format->pad >= MIPID02_PAD_NB) - return -EINVAL; /* second CSI-2 pad not yet supported */ - if (format->pad == MIPID02_SINK_1) + if (fmt->pad == MIPID02_SINK_1) return -EINVAL; - mutex_lock(&bridge->lock); + pad_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + fmt->format.code = get_fmt_code(fmt->format.code); - if (bridge->streaming) { - ret = -EBUSY; - goto error; - } + /* code may need to be converted */ + if (fmt->pad == MIPID02_SOURCE) + fmt->format.code = serial_to_parallel_code(fmt->format.code); - if (format->pad == MIPID02_SOURCE) - mipid02_set_fmt_source(sd, sd_state, format); - else - mipid02_set_fmt_sink(sd, sd_state, format); + *pad_fmt = fmt->format; -error: - mutex_unlock(&bridge->lock); + /* Propagate the format to the source pad in case of sink pad update */ + if (fmt->pad == MIPID02_SINK_0) { + pad_fmt = v4l2_subdev_state_get_format(sd_state, + MIPID02_SOURCE); + *pad_fmt = fmt->format; + pad_fmt->code = serial_to_parallel_code(fmt->format.code); + } - return ret; + return 0; } static const struct v4l2_subdev_video_ops mipid02_video_ops = { @@ -830,7 +645,7 @@ static const struct v4l2_subdev_video_ops mipid02_video_ops = { static const struct v4l2_subdev_pad_ops mipid02_pad_ops = { .enum_mbus_code = mipid02_enum_mbus_code, - .get_fmt = mipid02_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mipid02_set_fmt, }; @@ -839,6 +654,10 @@ static const struct v4l2_subdev_ops mipid02_subdev_ops = { .pad = &mipid02_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipid02_subdev_internal_ops = { + .init_state = mipid02_init_state, +}; + static const struct media_entity_operations mipid02_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -998,8 +817,6 @@ static int mipid02_probe(struct i2c_client *client) if (!bridge) return -ENOMEM; - init_format(&bridge->fmt); - bridge->i2c_client = client; v4l2_i2c_subdev_init(&bridge->sd, client, &mipid02_subdev_ops); @@ -1031,9 +848,15 @@ static int mipid02_probe(struct i2c_client *client) return ret; } - mutex_init(&bridge->lock); + /* Initialise the regmap for further cci access */ + bridge->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(bridge->regmap)) + return dev_err_probe(dev, PTR_ERR(bridge->regmap), + "failed to get cci regmap\n"); + bridge->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; bridge->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + bridge->sd.internal_ops = &mipid02_subdev_internal_ops; bridge->sd.entity.ops = &mipid02_subdev_entity_ops; bridge->pad[0].flags = MEDIA_PAD_FL_SINK; bridge->pad[1].flags = MEDIA_PAD_FL_SINK; @@ -1042,7 +865,13 @@ static int mipid02_probe(struct i2c_client *client) bridge->pad); if (ret) { dev_err(&client->dev, "pads init failed %d", ret); - goto mutex_cleanup; + return ret; + } + + ret = v4l2_subdev_init_finalize(&bridge->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto entity_cleanup; } /* enable clock, power and reset device if available */ @@ -1086,8 +915,6 @@ power_off: mipid02_set_power_off(bridge); entity_cleanup: media_entity_cleanup(&bridge->sd.entity); -mutex_cleanup: - mutex_destroy(&bridge->lock); return ret; } @@ -1102,7 +929,6 @@ static void mipid02_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&bridge->sd); mipid02_set_power_off(bridge); media_entity_cleanup(&bridge->sd.entity); - mutex_destroy(&bridge->lock); } static const struct of_device_id mipid02_dt_ids[] = { diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c index 5dbfb04b3..e4d37a197 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/st-vgxy61.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -780,8 +781,7 @@ static int vgxy61_get_fmt(struct v4l2_subdev *sd, mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); else fmt = &sensor->fmt; @@ -1289,7 +1289,7 @@ static int vgxy61_set_fmt(struct v4l2_subdev *sd, goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + fmt = v4l2_subdev_state_get_format(sd_state, 0); *fmt = format->format; } else if (sensor->current_mode != new_mode || sensor->fmt.code != format->format.code) { @@ -1323,8 +1323,8 @@ out: return ret; } -static int vgxy61_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vgxy61_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vgxy61_dev *sensor = to_vgxy61_dev(sd); struct v4l2_subdev_format fmt = { 0 }; @@ -1403,6 +1403,7 @@ static int vgxy61_init_controls(struct vgxy61_dev *sensor) const struct v4l2_ctrl_ops *ops = &vgxy61_ctrl_ops; struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; const struct vgxy61_mode_info *cur_mode = sensor->current_mode; + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl *ctrl; int ret; @@ -1457,6 +1458,14 @@ static int vgxy61_init_controls(struct vgxy61_dev *sensor) goto free_ctrls; } + ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + if (ret) + goto free_ctrls; + sensor->sd.ctrl_handler = hdl; return 0; @@ -1465,12 +1474,16 @@ free_ctrls: return ret; } +static const struct v4l2_subdev_core_ops vgxy61_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops vgxy61_video_ops = { .s_stream = vgxy61_s_stream, }; static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { - .init_cfg = vgxy61_init_cfg, .enum_mbus_code = vgxy61_enum_mbus_code, .get_fmt = vgxy61_get_fmt, .set_fmt = vgxy61_set_fmt, @@ -1479,10 +1492,15 @@ static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { }; static const struct v4l2_subdev_ops vgxy61_subdev_ops = { + .core = &vgxy61_core_ops, .video = &vgxy61_video_ops, .pad = &vgxy61_pad_ops, }; +static const struct v4l2_subdev_internal_ops vgxy61_internal_ops = { + .init_state = vgxy61_init_state, +}; + static const struct media_entity_operations vgxy61_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -1843,7 +1861,9 @@ static int vgxy61_probe(struct i2c_client *client) device_property_read_bool(dev, "st,strobe-gpios-polarity"); v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops); - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.internal_ops = &vgxy61_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.ops = &vgxy61_subdev_entity_ops; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index ce612a47b..106de4271 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -427,7 +427,7 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); /* Self defined CSI user data type id's are not supported yet */ @@ -740,15 +740,15 @@ err_out: return v4l2_subdev_call(src, video, s_stream, 0); } -static int tc358746_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int tc358746_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SINK); + fmt = v4l2_subdev_state_get_format(state, TC358746_SINK); *fmt = tc358746_def_fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SOURCE); + fmt = v4l2_subdev_state_get_format(state, TC358746_SOURCE); *fmt = tc358746_def_fmt; fmt->code = tc358746_src_mbus_code(tc358746_def_fmt.code); @@ -781,7 +781,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, if (format->pad == TC358746_SOURCE) return v4l2_subdev_get_fmt(sd, sd_state, format); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SINK); fmt = tc358746_get_format_by_code(format->pad, format->format.code); if (IS_ERR(fmt)) { @@ -800,7 +800,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, *sink_fmt = format->format; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SOURCE); + src_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SOURCE); *src_fmt = *sink_fmt; src_fmt->code = tc358746_src_mbus_code(sink_fmt->code); @@ -905,7 +905,7 @@ tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, return err; sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); /* Check the FIFO settings */ fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); @@ -1038,7 +1038,6 @@ static const struct v4l2_subdev_video_ops tc358746_video_ops = { }; static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { - .init_cfg = tc358746_init_cfg, .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, @@ -1052,6 +1051,10 @@ static const struct v4l2_subdev_ops tc358746_ops = { .pad = &tc358746_pad_ops, }; +static const struct v4l2_subdev_internal_ops tc358746_internal_ops = { + .init_state = tc358746_init_state, +}; + static const struct media_entity_operations tc358746_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -1282,6 +1285,7 @@ tc358746_init_subdev(struct tc358746 *tc358746, struct i2c_client *client) int err; v4l2_i2c_subdev_init(sd, client, &tc358746_ops); + sd->internal_ops = &tc358746_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->entity.ops = &tc358746_entity_ops; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 325e99125..1ea703a99 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1734,13 +1734,13 @@ static const struct v4l2_subdev_video_ops tda1997x_video_ops = { * v4l2_subdev_pad_ops */ -static int tda1997x_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tda1997x_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tda1997x_state *state = to_state(sd); struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); mf->code = state->mbus_codes[0]; return 0; @@ -1792,7 +1792,7 @@ static int tda1997x_get_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); format->format.code = fmt->code; } else format->format.code = state->mbus_code; @@ -1826,7 +1826,7 @@ static int tda1997x_set_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(sd, sd_state, format->pad); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); *fmt = format->format; } else { int ret = tda1997x_setup_format(state, format->format.code); @@ -1925,7 +1925,6 @@ static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { - .init_cfg = tda1997x_init_cfg, .enum_mbus_code = tda1997x_enum_mbus_code, .get_fmt = tda1997x_get_format, .set_fmt = tda1997x_set_format, @@ -2047,6 +2046,10 @@ static const struct v4l2_subdev_ops tda1997x_subdev_ops = { .pad = &tda1997x_pad_ops, }; +static const struct v4l2_subdev_internal_ops tda1997x_internal_ops = { + .init_state = tda1997x_init_state, +}; + /* ----------------------------------------------------------------------------- * v4l2_controls */ @@ -2588,6 +2591,7 @@ static int tda1997x_probe(struct i2c_client *client) /* initialize subdev */ sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); + sd->internal_ops = &tda1997x_internal_ops; snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c new file mode 100644 index 000000000..280688751 --- /dev/null +++ b/drivers/media/i2c/thp7312.c @@ -0,0 +1,2256 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 THine Electronics, Inc. + * Copyright (C) 2023 Ideas on Board Oy + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ISP registers */ + +#define THP7312_REG_FIRMWARE_VERSION_1 CCI_REG8(0xf000) +#define THP7312_REG_CAMERA_STATUS CCI_REG8(0xf001) +#define THP7312_REG_FIRMWARE_VERSION_2 CCI_REG8(0xf005) +#define THP7312_REG_SET_OUTPUT_ENABLE CCI_REG8(0xf008) +#define THP7312_OUTPUT_ENABLE 0x01 +#define THP7312_OUTPUT_DISABLE 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION CCI_REG8(0xf009) +#define THP7312_REG_SET_OUTPUT_COLOR_UYVY 0x00 +#define THP7312_REG_SET_OUTPUT_COLOR_YUY2 0x04 +#define THP7312_REG_FLIP_MIRROR CCI_REG8(0xf00c) +#define THP7312_REG_FLIP_MIRROR_FLIP BIT(0) +#define THP7312_REG_FLIP_MIRROR_MIRROR BIT(1) +#define THP7312_REG_VIDEO_IMAGE_SIZE CCI_REG8(0xf00d) +#define THP7312_VIDEO_IMAGE_SIZE_640x360 0x52 +#define THP7312_VIDEO_IMAGE_SIZE_640x460 0x03 +#define THP7312_VIDEO_IMAGE_SIZE_1280x720 0x0a +#define THP7312_VIDEO_IMAGE_SIZE_1920x1080 0x0b +#define THP7312_VIDEO_IMAGE_SIZE_3840x2160 0x0d +#define THP7312_VIDEO_IMAGE_SIZE_4160x3120 0x14 +#define THP7312_VIDEO_IMAGE_SIZE_2016x1512 0x20 +#define THP7312_VIDEO_IMAGE_SIZE_2048x1536 0x21 +#define THP7312_REG_VIDEO_FRAME_RATE_MODE CCI_REG8(0xf00f) +#define THP7312_VIDEO_FRAME_RATE_MODE1 0x80 +#define THP7312_VIDEO_FRAME_RATE_MODE2 0x81 +#define THP7312_VIDEO_FRAME_RATE_MODE3 0x82 +#define THP7312_REG_SET_DRIVING_MODE CCI_REG8(0xf010) +#define THP7312_REG_DRIVING_MODE_STATUS CCI_REG8(0xf011) +#define THP7312_REG_JPEG_COMPRESSION_FACTOR CCI_REG8(0xf01b) +#define THP7312_REG_AE_EXPOSURE_COMPENSATION CCI_REG8(0xf022) +#define THP7312_REG_AE_FLICKER_MODE CCI_REG8(0xf023) +#define THP7312_AE_FLICKER_MODE_50 0x00 +#define THP7312_AE_FLICKER_MODE_60 0x01 +#define THP7312_AE_FLICKER_MODE_DISABLE 0x80 +#define THP7312_REG_AE_FIX_FRAME_RATE CCI_REG8(0xf02e) +#define THP7312_REG_MANUAL_WB_RED_GAIN CCI_REG8(0xf036) +#define THP7312_REG_MANUAL_WB_BLUE_GAIN CCI_REG8(0xf037) +#define THP7312_REG_WB_MODE CCI_REG8(0xf039) +#define THP7312_WB_MODE_AUTO 0x00 +#define THP7312_WB_MODE_MANUAL 0x11 +#define THP7312_REG_MANUAL_FOCUS_POSITION CCI_REG16(0xf03c) +#define THP7312_REG_AF_CONTROL CCI_REG8(0xf040) +#define THP7312_REG_AF_CONTROL_AF 0x01 +#define THP7312_REG_AF_CONTROL_MANUAL 0x10 +#define THP7312_REG_AF_CONTROL_LOCK 0x80 +#define THP7312_REG_AF_SETTING CCI_REG8(0xf041) +#define THP7312_REG_AF_SETTING_ONESHOT_CONTRAST 0x00 +#define THP7312_REG_AF_SETTING_ONESHOT_PDAF 0x40 +#define THP7312_REG_AF_SETTING_ONESHOT_HYBRID 0x80 +#define THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST 0x30 +#define THP7312_REG_AF_SETTING_CONTINUOUS_PDAF 0x70 +#define THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID 0xf0 +#define THP7312_REG_AF_SUPPORT CCI_REG8(0xf043) +#define THP7312_AF_SUPPORT_PDAF BIT(1) +#define THP7312_AF_SUPPORT_CONTRAST BIT(0) +#define THP7312_REG_SATURATION CCI_REG8(0xf052) +#define THP7312_REG_SHARPNESS CCI_REG8(0xf053) +#define THP7312_REG_BRIGHTNESS CCI_REG8(0xf056) +#define THP7312_REG_CONTRAST CCI_REG8(0xf057) +#define THP7312_REG_NOISE_REDUCTION CCI_REG8(0xf059) +#define THP7312_REG_NOISE_REDUCTION_FIXED BIT(7) + +#define TH7312_REG_CUSTOM_MIPI_SET CCI_REG8(0xf0f6) +#define TH7312_REG_CUSTOM_MIPI_STATUS CCI_REG8(0xf0f7) +#define TH7312_REG_CUSTOM_MIPI_RD CCI_REG8(0xf0f8) +#define TH7312_REG_CUSTOM_MIPI_TD CCI_REG8(0xf0f9) + +/* + * Firmware update registers. Those use a different address space than the + * normal operation ISP registers. + */ + +#define THP7312_REG_FW_DRIVABILITY CCI_REG32(0xd65c) +#define THP7312_REG_FW_DEST_BANK_ADDR CCI_REG32(0xff08) +#define THP7312_REG_FW_VERIFY_RESULT CCI_REG8(0xff60) +#define THP7312_REG_FW_RESET_FLASH CCI_REG8(0xff61) +#define THP7312_REG_FW_MEMORY_IO_SETTING CCI_REG8(0xff62) +#define THP7312_FW_MEMORY_IO_GPIO0 1 +#define THP7312_FW_MEMORY_IO_GPIO1 0 +#define THP7312_REG_FW_CRC_RESULT CCI_REG32(0xff64) +#define THP7312_REG_FW_STATUS CCI_REG8(0xfffc) + +#define THP7312_FW_VERSION(major, minor) (((major) << 8) | (minor)) +#define THP7312_FW_VERSION_MAJOR(v) ((v) >> 8) +#define THP7312_FW_VERSION_MINOR(v) ((v) & 0xff) + +enum thp7312_focus_method { + THP7312_FOCUS_METHOD_CONTRAST, + THP7312_FOCUS_METHOD_PDAF, + THP7312_FOCUS_METHOD_HYBRID, +}; + +/* + * enum thp7312_focus_state - State of the focus handler + * + * @THP7312_FOCUS_STATE_MANUAL: Manual focus, controlled through the + * V4L2_CID_FOCUS_ABSOLUTE control + * @THP7312_FOCUS_STATE_AUTO: Continuous auto-focus + * @THP7312_FOCUS_STATE_LOCKED: Lock the focus to a fixed position. This state + * is entered when switching from auto to manual mode. + * @THP7312_FOCUS_STATE_ONESHOT: One-shot auto-focus + * + * Valid transitions are as follow: + * + * digraph fsm { + * node [shape=circle]; + * + * manual [label="MANUAL"]; + * auto [label="AUTO"]; + * locked [label="LOCKED"]; + * oneshot [label="ONESHOT"]; + * + * manual -> auto [label="FOCUS_AUTO <- true"] + * locked -> auto [label="FOCUS_AUTO <- true"] + * oneshot -> auto [label="FOCUS_AUTO <- true"] + * auto -> locked [label="FOCUS_AUTO <- false"] + * + * locked -> manual [label="FOCUS_ABSOLUTE <- *"] + * oneshot -> manual [label="FOCUS_ABSOLUTE <- *"] + * + * manual -> oneshot [label="FOCUS_START <- *"] + * locked -> oneshot [label="FOCUS_START <- *"] + * } + */ +enum thp7312_focus_state { + THP7312_FOCUS_STATE_MANUAL, + THP7312_FOCUS_STATE_AUTO, + THP7312_FOCUS_STATE_LOCKED, + THP7312_FOCUS_STATE_ONESHOT, +}; + +enum thp7312_boot_mode { + THP7312_BOOT_MODE_2WIRE_SLAVE = 0, + THP7312_BOOT_MODE_SPI_MASTER = 1, +}; + +struct thp7312_frame_rate { + u32 fps; + u32 link_freq; + u8 reg_frame_rate_mode; +}; + +struct thp7312_mode_info { + u32 width; + u32 height; + u8 reg_image_size; + const struct thp7312_frame_rate *rates; +}; + +static const u32 thp7312_colour_fmts[] = { + MEDIA_BUS_FMT_YUYV8_1X16, +}; + +/* regulator supplies */ +static const char * const thp7312_supply_name[] = { + "vddcore", + "vhtermrx", + "vddtx", + "vddhost", + "vddcmos", + "vddgpio-0", + "vddgpio-1", +}; + +static const struct thp7312_mode_info thp7312_mode_info_data[] = { + { + .width = 1920, + .height = 1080, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_1920x1080, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 60, 387500000, 0x82 }, + { 0 } + }, + }, { + .width = 2048, + .height = 1536, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_2048x1536, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 300000000, 0x81 }, + { 0 } + } + }, { + .width = 3840, + .height = 2160, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_3840x2160, + .rates = (const struct thp7312_frame_rate[]) { + { 30, 600000000, 0x81 }, + { 0 } + }, + }, { + .width = 4160, + .height = 3120, + .reg_image_size = THP7312_VIDEO_IMAGE_SIZE_4160x3120, + .rates = (const struct thp7312_frame_rate[]) { + { 20, 600000000, 0x81 }, + { 0 } + }, + }, +}; + +struct thp7312_device; + +struct thp7312_sensor_info { + const char *model; +}; + +struct thp7312_sensor { + const struct thp7312_sensor_info *info; + u8 lane_remap; +}; + +struct thp7312_device { + struct device *dev; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(thp7312_supply_name)]; + struct clk *iclk; + + u8 lane_remap; + + struct thp7312_sensor sensors[1]; + + enum thp7312_boot_mode boot_mode; + + struct v4l2_ctrl_handler ctrl_handler; + bool ctrls_applied; + + s64 link_freq; + + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct { + struct v4l2_ctrl *focus_auto; + struct v4l2_ctrl *focus_absolute; + struct v4l2_ctrl *focus_start; + struct v4l2_ctrl *focus_method; + }; + + enum thp7312_focus_state focus_state; + + struct { + struct v4l2_ctrl *noise_reduction_auto; + struct v4l2_ctrl *noise_reduction_absolute; + }; + + /* Lock to protect fw_cancel */ + struct mutex fw_lock; + struct fw_upload *fwl; + u8 *fw_write_buf; + bool fw_cancel; + + u16 fw_version; +}; + +static const struct thp7312_sensor_info thp7312_sensor_info[] = { + { + .model = "sony,imx258", + }, +}; + +static inline struct thp7312_device *to_thp7312_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct thp7312_device, sd); +} + +static const struct thp7312_mode_info * +thp7312_find_mode(unsigned int width, unsigned int height, bool nearest) +{ + const struct thp7312_mode_info *mode; + + mode = v4l2_find_nearest_size(thp7312_mode_info_data, + ARRAY_SIZE(thp7312_mode_info_data), + width, height, width, height); + + if (!nearest && (mode->width != width || mode->height != height)) + return NULL; + + return mode; +} + +static const struct thp7312_frame_rate * +thp7312_find_rate(const struct thp7312_mode_info *mode, unsigned int fps, + bool nearest) +{ + const struct thp7312_frame_rate *best_rate = NULL; + const struct thp7312_frame_rate *rate; + unsigned int best_delta = UINT_MAX; + + if (!mode) + return NULL; + + for (rate = mode->rates; rate->fps && best_delta; ++rate) { + unsigned int delta = abs(rate->fps - fps); + + if (delta <= best_delta) { + best_delta = delta; + best_rate = rate; + } + } + + if (!nearest && best_delta) + return NULL; + + return best_rate; +} + +/* ----------------------------------------------------------------------------- + * Device Access & Configuration + */ + +#define thp7312_read_poll_timeout(dev, addr, val, cond, sleep_us, timeout_us) \ +({ \ + int __ret, __err; \ + __ret = read_poll_timeout(cci_read, __err, __err || (cond), sleep_us, \ + timeout_us, false, (dev)->regmap, addr, \ + &(val), NULL); \ + __ret ? : __err; \ +}) + +static int thp7312_map_data_lanes(u8 *lane_remap, const u8 *lanes, u8 num_lanes) +{ + u8 used_lanes = 0; + u8 val = 0; + unsigned int i; + + /* + * The value that we write to the register is the index in the + * data-lanes array, so we need to do a conversion. Do this in the same + * pass as validating data-lanes. + */ + for (i = 0; i < num_lanes; i++) { + if (lanes[i] < 1 || lanes[i] > 4) + return -EINVAL; + + if (used_lanes & (BIT(lanes[i]))) + return -EINVAL; + + used_lanes |= BIT(lanes[i]); + + /* + * data-lanes is 1-indexed while the field position in the + * register is 0-indexed. + */ + val |= i << ((lanes[i] - 1) * 2); + } + + *lane_remap = val; + + return 0; +} + +static int thp7312_set_mipi_lanes(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret = 0; + u64 val; + + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_RD, + thp7312->sensors[0].lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_TD, + thp7312->lane_remap, &ret); + cci_write(thp7312->regmap, TH7312_REG_CUSTOM_MIPI_SET, 1, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, TH7312_REG_CUSTOM_MIPI_STATUS, + val, val == 0x00, 100000, 2000000); + if (ret) { + dev_err(dev, "Failed to poll MIPI lane status: %d\n", ret); + return ret; + } + + return 0; +} + +static int thp7312_change_mode(struct thp7312_device *thp7312, + const struct thp7312_mode_info *mode, + const struct thp7312_frame_rate *rate) +{ + struct device *dev = thp7312->dev; + u64 val = 0; + int ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_CAMERA_STATUS, val, + val == 0x80, 20000, 200000); + if (ret < 0) { + dev_err(dev, "%s(): failed to poll ISP: %d\n", __func__, ret); + return ret; + } + + cci_write(thp7312->regmap, THP7312_REG_VIDEO_IMAGE_SIZE, + mode->reg_image_size, &ret); + cci_write(thp7312->regmap, THP7312_REG_VIDEO_FRAME_RATE_MODE, + rate->reg_frame_rate_mode, &ret); + cci_write(thp7312->regmap, THP7312_REG_JPEG_COMPRESSION_FACTOR, 0x5e, + &ret); + cci_write(thp7312->regmap, THP7312_REG_SET_DRIVING_MODE, 0x01, &ret); + + if (ret) + return ret; + + ret = thp7312_read_poll_timeout(thp7312, THP7312_REG_DRIVING_MODE_STATUS, + val, val == 0x01, 20000, 100000); + if (ret < 0) { + dev_err(dev, "%s(): failed\n", __func__); + return ret; + } + + return 0; +} + +static int thp7312_set_framefmt(struct thp7312_device *thp7312, + struct v4l2_mbus_framefmt *format) +{ + u8 val; + + switch (format->code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + /* YUV422, UYVY */ + val = THP7312_REG_SET_OUTPUT_COLOR_UYVY; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + /* YUV422, YUYV */ + val = THP7312_REG_SET_OUTPUT_COLOR_YUY2; + break; + default: + /* Should never happen */ + return -EINVAL; + } + + return cci_write(thp7312->regmap, + THP7312_REG_SET_OUTPUT_COLOR_COMPRESSION, val, NULL); +} + +static int thp7312_init_mode(struct thp7312_device *thp7312, + struct v4l2_subdev_state *sd_state) +{ + const struct thp7312_mode_info *mode; + const struct thp7312_frame_rate *rate; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *interval; + int ret; + + /* + * TODO: The mode and rate should be cached in the subdev state, once + * support for extending states will be available. + */ + fmt = v4l2_subdev_state_get_format(sd_state, 0); + interval = v4l2_subdev_state_get_interval(sd_state, 0); + + mode = thp7312_find_mode(fmt->width, fmt->height, false); + rate = thp7312_find_rate(mode, interval->denominator, false); + + if (WARN_ON(!mode || !rate)) + return -EINVAL; + + ret = thp7312_set_framefmt(thp7312, fmt); + if (ret) + return ret; + + return thp7312_change_mode(thp7312, mode, rate); +} + +static int thp7312_stream_enable(struct thp7312_device *thp7312, bool enable) +{ + return cci_write(thp7312->regmap, THP7312_REG_SET_OUTPUT_ENABLE, + enable ? THP7312_OUTPUT_ENABLE : THP7312_OUTPUT_DISABLE, + NULL); +} + +static int thp7312_check_status_stream_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u64 status = 0; + int ret; + + while (status != 0x80) { + ret = cci_read(thp7312->regmap, THP7312_REG_CAMERA_STATUS, + &status, NULL); + if (ret) + return ret; + + if (status == 0x80) { + dev_dbg(dev, "Camera initialization done\n"); + return 0; + } + + if (status != 0x00) { + dev_err(dev, "Invalid camera status %llx\n", status); + return -EINVAL; + } + + dev_dbg(dev, "Camera initializing...\n"); + usleep_range(70000, 80000); + } + + return 0; +} + +static void thp7312_reset(struct thp7312_device *thp7312) +{ + unsigned long rate; + + gpiod_set_value_cansleep(thp7312->reset_gpio, 1); + + /* + * The minimum reset duration is 8 clock cycles, make it 10 to provide + * a safety margin. + */ + rate = clk_get_rate(thp7312->iclk); + fsleep(DIV_ROUND_UP(10 * USEC_PER_SEC, rate)); + + gpiod_set_value_cansleep(thp7312->reset_gpio, 0); + + /* + * TODO: The documentation states that the device needs 2ms to + * initialize after reset is deasserted. It then proceeds to load the + * firmware from the flash memory, which takes an unspecified amount of + * time. Check if this delay could be reduced. + */ + fsleep(300000); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static void __thp7312_power_off(struct thp7312_device *thp7312) +{ + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), thp7312->supplies); + clk_disable_unprepare(thp7312->iclk); +} + +static void thp7312_power_off(struct thp7312_device *thp7312) +{ + __thp7312_power_off(thp7312); +} + +static int __thp7312_power_on(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(thp7312->iclk); + if (ret < 0) { + dev_err(dev, "clk prepare enable failed\n"); + regulator_bulk_disable(ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); + return ret; + } + + /* + * We cannot assume that turning off and on again will reset, so do a + * software reset on power up. + */ + thp7312_reset(thp7312); + + return 0; +} + +static int thp7312_power_on(struct thp7312_device *thp7312) +{ + int ret; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_check_status_stream_mode(thp7312); + if (ret < 0) + goto error; + + ret = thp7312_set_mipi_lanes(thp7312); + if (ret) + goto error; + + return 0; + +error: + thp7312_power_off(thp7312); + return ret; +} + +static int __maybe_unused thp7312_pm_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + thp7312_power_off(thp7312); + + thp7312->ctrls_applied = false; + + return 0; +} + +static int __maybe_unused thp7312_pm_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + return thp7312_power_on(thp7312); +} + +static const struct dev_pm_ops thp7312_pm_ops = { + SET_RUNTIME_PM_OPS(thp7312_pm_runtime_suspend, + thp7312_pm_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev Operations + */ + +static bool thp7312_find_bus_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312_colour_fmts); ++i) { + if (thp7312_colour_fmts[i] == code) + return true; + } + + return false; +} + +static int thp7312_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(thp7312_colour_fmts)) + return -EINVAL; + + code->code = thp7312_colour_fmts[code->index]; + + return 0; +} + +static int thp7312_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (!thp7312_find_bus_code(fse->code)) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(thp7312_mode_info_data)) + return -EINVAL; + + fse->min_width = thp7312_mode_info_data[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = thp7312_mode_info_data[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int thp7312_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + const struct thp7312_frame_rate *rate; + const struct thp7312_mode_info *mode; + unsigned int index = fie->index; + + if (!thp7312_find_bus_code(fie->code)) + return -EINVAL; + + mode = thp7312_find_mode(fie->width, fie->height, false); + if (!mode) + return -EINVAL; + + for (rate = mode->rates; rate->fps; ++rate, --index) { + if (!index) { + fie->interval.numerator = 1; + fie->interval.denominator = rate->fps; + + return 0; + } + } + + return -EINVAL; +} + +static int thp7312_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *interval; + const struct thp7312_mode_info *mode; + + if (!thp7312_find_bus_code(mbus_fmt->code)) + mbus_fmt->code = thp7312_colour_fmts[0]; + + mode = thp7312_find_mode(mbus_fmt->width, mbus_fmt->height, true); + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + + fmt->code = mbus_fmt->code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + + *mbus_fmt = *fmt; + + interval = v4l2_subdev_state_get_interval(sd_state, 0); + interval->numerator = 1; + interval->denominator = mode->rates[0].fps; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + thp7312->link_freq = mode->rates[0].link_freq; + + return 0; +} + +static int thp7312_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *fi) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + const struct thp7312_mode_info *mode; + const struct thp7312_frame_rate *rate; + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *interval; + unsigned int fps; + + /* Avoid divisions by 0, pick the highest frame if the interval is 0. */ + fps = fi->interval.numerator + ? DIV_ROUND_CLOSEST(fi->interval.denominator, fi->interval.numerator) + : UINT_MAX; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + mode = thp7312_find_mode(fmt->width, fmt->height, false); + rate = thp7312_find_rate(mode, fps, true); + + interval = v4l2_subdev_state_get_interval(sd_state, 0); + interval->numerator = 1; + interval->denominator = rate->fps; + + if (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE) + thp7312->link_freq = rate->link_freq; + + fi->interval = *interval; + + return 0; +} + +static int thp7312_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + struct v4l2_subdev_state *sd_state; + int ret; + + sd_state = v4l2_subdev_lock_and_get_active_state(sd); + + if (!enable) { + thp7312_stream_enable(thp7312, false); + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + v4l2_subdev_unlock_state(sd_state); + + return 0; + } + + ret = pm_runtime_resume_and_get(thp7312->dev); + if (ret) + goto finish_unlock; + + ret = thp7312_init_mode(thp7312, sd_state); + if (ret) + goto finish_pm; + + if (!thp7312->ctrls_applied) { + ret = __v4l2_ctrl_handler_setup(&thp7312->ctrl_handler); + if (ret) + goto finish_pm; + + thp7312->ctrls_applied = true; + } + + ret = thp7312_stream_enable(thp7312, true); + if (ret) + goto finish_pm; + + goto finish_unlock; + +finish_pm: + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); +finish_unlock: + v4l2_subdev_unlock_state(sd_state); + + return ret; +} + +static int thp7312_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + const struct thp7312_mode_info *default_mode = &thp7312_mode_info_data[0]; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *interval; + + fmt = v4l2_subdev_state_get_format(sd_state, 0); + interval = v4l2_subdev_state_get_interval(sd_state, 0); + + /* + * default init sequence initialize thp7312 to + * YUV422 YUYV VGA@30fps + */ + fmt->code = MEDIA_BUS_FMT_YUYV8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->width = default_mode->width; + fmt->height = default_mode->height; + fmt->field = V4L2_FIELD_NONE; + + interval->numerator = 1; + interval->denominator = default_mode->rates[0].fps; + + return 0; +} + +static const struct v4l2_subdev_core_ops thp7312_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops thp7312_video_ops = { + .s_stream = thp7312_s_stream, +}; + +static const struct v4l2_subdev_pad_ops thp7312_pad_ops = { + .enum_mbus_code = thp7312_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = thp7312_set_fmt, + .get_frame_interval = v4l2_subdev_get_frame_interval, + .set_frame_interval = thp7312_set_frame_interval, + .enum_frame_size = thp7312_enum_frame_size, + .enum_frame_interval = thp7312_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops thp7312_subdev_ops = { + .core = &thp7312_core_ops, + .video = &thp7312_video_ops, + .pad = &thp7312_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops thp7312_internal_ops = { + .init_state = thp7312_init_state, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Control Operations + */ + +static inline struct thp7312_device *to_thp7312_from_ctrl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct thp7312_device, ctrl_handler); +} + +/* 0: 3000cm, 18: 8cm */ +static const u16 thp7312_focus_values[] = { + 3000, 1000, 600, 450, 350, + 290, 240, 200, 170, 150, + 140, 130, 120, 110, 100, + 93, 87, 83, 80, +}; + +static int thp7312_set_focus(struct thp7312_device *thp7312) +{ + enum thp7312_focus_state new_state = thp7312->focus_state; + bool continuous; + u8 af_control; + u8 af_setting; + int ret = 0; + + /* Start by programming the manual focus position if it has changed. */ + if (thp7312->focus_absolute->is_new) { + unsigned int value; + + value = thp7312_focus_values[thp7312->focus_absolute->val]; + + ret = cci_write(thp7312->regmap, + THP7312_REG_MANUAL_FOCUS_POSITION, value, NULL); + if (ret) + return ret; + } + + /* Calculate the new focus state. */ + switch (thp7312->focus_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + break; + + case THP7312_FOCUS_STATE_AUTO: + if (!thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_LOCKED; + break; + + case THP7312_FOCUS_STATE_LOCKED: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + + case THP7312_FOCUS_STATE_ONESHOT: + if (thp7312->focus_auto->val) + new_state = THP7312_FOCUS_STATE_AUTO; + else if (thp7312->focus_start->is_new) + new_state = THP7312_FOCUS_STATE_ONESHOT; + else if (thp7312->focus_absolute->is_new) + new_state = THP7312_FOCUS_STATE_MANUAL; + break; + } + + /* + * If neither the state nor the focus method has changed, and no new + * one-shot focus is requested, there's nothing new to program to the + * hardware. + */ + if (thp7312->focus_state == new_state && + !thp7312->focus_method->is_new && !thp7312->focus_start->is_new) + return 0; + + continuous = new_state == THP7312_FOCUS_STATE_MANUAL || + new_state == THP7312_FOCUS_STATE_ONESHOT; + + switch (thp7312->focus_method->val) { + case THP7312_FOCUS_METHOD_CONTRAST: + default: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_CONTRAST + : THP7312_REG_AF_SETTING_ONESHOT_CONTRAST; + break; + case THP7312_FOCUS_METHOD_PDAF: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_PDAF + : THP7312_REG_AF_SETTING_ONESHOT_PDAF; + break; + case THP7312_FOCUS_METHOD_HYBRID: + af_setting = continuous + ? THP7312_REG_AF_SETTING_CONTINUOUS_HYBRID + : THP7312_REG_AF_SETTING_ONESHOT_HYBRID; + break; + } + + switch (new_state) { + case THP7312_FOCUS_STATE_MANUAL: + default: + af_control = THP7312_REG_AF_CONTROL_MANUAL; + break; + case THP7312_FOCUS_STATE_AUTO: + case THP7312_FOCUS_STATE_ONESHOT: + af_control = THP7312_REG_AF_CONTROL_AF; + break; + case THP7312_FOCUS_STATE_LOCKED: + af_control = THP7312_REG_AF_CONTROL_LOCK; + break; + } + + cci_write(thp7312->regmap, THP7312_REG_AF_SETTING, af_setting, &ret); + + if (new_state == THP7312_FOCUS_STATE_MANUAL && + (thp7312->focus_state == THP7312_FOCUS_STATE_AUTO || + thp7312->focus_state == THP7312_FOCUS_STATE_ONESHOT)) { + /* When switching to manual state, lock AF first. */ + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, + THP7312_REG_AF_CONTROL_LOCK, &ret); + } + + cci_write(thp7312->regmap, THP7312_REG_AF_CONTROL, af_control, &ret); + + if (ret) + return ret; + + thp7312->focus_state = new_state; + + return 0; +} + +static int thp7312_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct thp7312_device *thp7312 = to_thp7312_from_ctrl(ctrl); + int ret = 0; + u8 value; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return -EINVAL; + + if (!pm_runtime_get_if_active(thp7312->dev, true)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cci_write(thp7312->regmap, THP7312_REG_BRIGHTNESS, + ctrl->val + 10, &ret); + break; + + case V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION: + /* 0 = Auto adjust frame rate, 1 = Fix frame rate */ + cci_write(thp7312->regmap, THP7312_REG_AE_FIX_FRAME_RATE, + ctrl->val ? 0 : 1, &ret); + break; + + case V4L2_CID_FOCUS_AUTO: + case V4L2_CID_FOCUS_ABSOLUTE: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_THP7312_AUTO_FOCUS_METHOD: + ret = thp7312_set_focus(thp7312); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + value = (thp7312->hflip->val ? THP7312_REG_FLIP_MIRROR_MIRROR : 0) + | (thp7312->vflip->val ? THP7312_REG_FLIP_MIRROR_FLIP : 0); + + cci_write(thp7312->regmap, THP7312_REG_FLIP_MIRROR, value, &ret); + break; + + case V4L2_CID_THP7312_NOISE_REDUCTION_AUTO: + case V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE: + value = thp7312->noise_reduction_auto->val ? 0 + : THP7312_REG_NOISE_REDUCTION_FIXED | + thp7312->noise_reduction_absolute->val; + + cci_write(thp7312->regmap, THP7312_REG_NOISE_REDUCTION, value, + &ret); + break; + + case V4L2_CID_AUTO_WHITE_BALANCE: + value = ctrl->val ? THP7312_WB_MODE_AUTO : THP7312_WB_MODE_MANUAL; + + cci_write(thp7312->regmap, THP7312_REG_WB_MODE, value, &ret); + break; + + case V4L2_CID_RED_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_RED_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_BLUE_BALANCE: + cci_write(thp7312->regmap, THP7312_REG_MANUAL_WB_BLUE_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_AUTO_EXPOSURE_BIAS: + cci_write(thp7312->regmap, THP7312_REG_AE_EXPOSURE_COMPENSATION, + ctrl->val, &ret); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) { + value = THP7312_AE_FLICKER_MODE_60; + } else if (ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) { + value = THP7312_AE_FLICKER_MODE_50; + } else { + if (thp7312->fw_version == THP7312_FW_VERSION(40, 3)) { + /* THP7312_AE_FLICKER_MODE_DISABLE is not supported */ + value = THP7312_AE_FLICKER_MODE_50; + } else { + value = THP7312_AE_FLICKER_MODE_DISABLE; + } + } + + cci_write(thp7312->regmap, THP7312_REG_AE_FLICKER_MODE, + value, &ret); + break; + + case V4L2_CID_SATURATION: + cci_write(thp7312->regmap, THP7312_REG_SATURATION, + ctrl->val, &ret); + break; + + case V4L2_CID_CONTRAST: + cci_write(thp7312->regmap, THP7312_REG_CONTRAST, + ctrl->val, &ret); + break; + + case V4L2_CID_SHARPNESS: + cci_write(thp7312->regmap, THP7312_REG_SHARPNESS, + ctrl->val, &ret); + break; + + default: + break; + } + + pm_runtime_mark_last_busy(thp7312->dev); + pm_runtime_put_autosuspend(thp7312->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops thp7312_ctrl_ops = { + .s_ctrl = thp7312_s_ctrl, +}; + +/* + * Refer to Documentation/userspace-api/media/drivers/thp7312.rst for details. + */ +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_cdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_CONTRAST, + .max = THP7312_FOCUS_METHOD_CONTRAST, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_ctrl_focus_method_pdaf = { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_AUTO_FOCUS_METHOD, + .name = "Auto-Focus Method", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = THP7312_FOCUS_METHOD_CONTRAST, + .def = THP7312_FOCUS_METHOD_HYBRID, + .max = THP7312_FOCUS_METHOD_HYBRID, + .step = 1, +}; + +static const struct v4l2_ctrl_config thp7312_v4l2_ctrls_custom[] = { + { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_LOW_LIGHT_COMPENSATION, + .name = "Low Light Compensation", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_AUTO, + .name = "Noise Reduction Auto", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .def = 1, + .max = 1, + .step = 1, + }, { + .ops = &thp7312_ctrl_ops, + .id = V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE, + .name = "Noise Reduction Level", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .def = 0, + .max = 10, + .step = 1, + }, +}; + +static const s64 exp_bias_qmenu[] = { + -2000, -1667, -1333, -1000, -667, -333, 0, 333, 667, 1000, 1333, 1667, 2000 +}; + +static int thp7312_init_controls(struct thp7312_device *thp7312) +{ + struct v4l2_ctrl_handler *hdl = &thp7312->ctrl_handler; + struct device *dev = thp7312->dev; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + unsigned int num_controls; + unsigned int i; + u8 af_support; + int ret; + + /* + * Check what auto-focus methods the connected sensor supports, if any. + * Firmwares before v90.03 didn't expose the AF_SUPPORT register, + * consider both CDAF and PDAF as supported in that case. + */ + if (thp7312->fw_version >= THP7312_FW_VERSION(90, 3)) { + u64 val; + + ret = cci_read(thp7312->regmap, THP7312_REG_AF_SUPPORT, &val, + NULL); + if (ret) + return ret; + + af_support = val & (THP7312_AF_SUPPORT_PDAF | + THP7312_AF_SUPPORT_CONTRAST); + } else { + af_support = THP7312_AF_SUPPORT_PDAF + | THP7312_AF_SUPPORT_CONTRAST; + } + + num_controls = 14 + ARRAY_SIZE(thp7312_v4l2_ctrls_custom) + + (af_support ? 4 : 0); + + v4l2_ctrl_handler_init(hdl, num_controls); + + if (af_support) { + const struct v4l2_ctrl_config *af_method; + + af_method = af_support & THP7312_AF_SUPPORT_PDAF + ? &thp7312_ctrl_focus_method_pdaf + : &thp7312_ctrl_focus_method_cdaf; + + thp7312->focus_state = THP7312_FOCUS_STATE_MANUAL; + + thp7312->focus_auto = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_AUTO, + 0, 1, 1, 1); + thp7312->focus_absolute = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, + 0, ARRAY_SIZE(thp7312_focus_values), + 1, 0); + thp7312->focus_method = + v4l2_ctrl_new_custom(hdl, af_method, NULL); + thp7312->focus_start = + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_FOCUS_START, + 1, 1, 1, 1); + + v4l2_ctrl_cluster(4, &thp7312->focus_auto); + } + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_RED_BALANCE, + 32, 255, 1, 64); + /* 32: 1x, 255: 7.95x */ + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BLUE_BALANCE, + 32, 255, 1, 50); + + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_BRIGHTNESS, + -10, 10, 1, 0); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SATURATION, + 0, 31, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_CONTRAST, + 0, 20, 1, 10); + v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, V4L2_CID_SHARPNESS, + 0, 31, 1, 8); + + thp7312->hflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + thp7312->vflip = v4l2_ctrl_new_std(hdl, &thp7312_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, &thp7312->hflip); + + v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(exp_bias_qmenu) - 1, + ARRAY_SIZE(exp_bias_qmenu) / 2, exp_bias_qmenu); + + v4l2_ctrl_new_std_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + thp7312->link_freq = thp7312_mode_info_data[0].rates[0].link_freq; + + link_freq = v4l2_ctrl_new_int_menu(hdl, &thp7312_ctrl_ops, + V4L2_CID_LINK_FREQ, 0, 0, + &thp7312->link_freq); + + /* Set properties from fwnode (e.g. rotation, orientation). */ + ret = v4l2_fwnode_device_parse(dev, &props); + if (ret) { + dev_err(dev, "Failed to parse fwnode: %d\n", ret); + goto error; + } + + ret = v4l2_ctrl_new_fwnode_properties(hdl, &thp7312_ctrl_ops, &props); + if (ret) { + dev_err(dev, "Failed to create new v4l2 ctrl for fwnode properties: %d\n", ret); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_v4l2_ctrls_custom); i++) { + const struct v4l2_ctrl_config *ctrl_cfg = + &thp7312_v4l2_ctrls_custom[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(hdl, ctrl_cfg, NULL); + + if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_AUTO) + thp7312->noise_reduction_auto = ctrl; + else if (ctrl_cfg->id == V4L2_CID_THP7312_NOISE_REDUCTION_ABSOLUTE) + thp7312->noise_reduction_absolute = ctrl; + } + + v4l2_ctrl_cluster(2, &thp7312->noise_reduction_auto); + + if (hdl->error) { + dev_err(dev, "v4l2_ctrl_handler error\n"); + ret = hdl->error; + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + return ret; + +error: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Firmware Update + */ + +/* + * The firmware data is made of 128kB of RAM firmware, followed by a + * variable-size "header". Both are stored in flash memory. + */ +#define THP7312_FW_RAM_SIZE (128 * 1024) +#define THP7312_FW_MIN_SIZE (THP7312_FW_RAM_SIZE + 4) +#define THP7312_FW_MAX_SIZE (THP7312_FW_RAM_SIZE + 64 * 1024) + +/* + * Data is first uploaded to the THP7312 128kB SRAM, and then written to flash. + * The SRAM is exposed over I2C as 32kB banks, and up to 4kB of data can be + * transferred in a single I2C write. + */ +#define THP7312_RAM_BANK_SIZE (32 * 1024) +#define THP7312_FW_DOWNLOAD_UNIT (4 * 1024) + +#define THP7312_FLASH_MEMORY_ERASE_TIMEOUT 40 + +#define THP7312_FLASH_MAX_REG_READ_SIZE 10 +#define THP7312_FLASH_MAX_REG_DATA_SIZE 10 + +static const u8 thp7312_cmd_config_flash_mem_if[] = { + 0xd5, 0x18, 0x00, 0x00, 0x00, 0x80 +}; + +static const u8 thp7312_cmd_write_to_reg[] = { + 0xd5, 0x0c, 0x80, 0x00, 0x00, 0x00 +}; + +static const u8 thp7312_cmd_read_reg[] = { + 0xd5, 0x04 +}; + +/* + * THP7312 Write data from RAM to Flash Memory + * Command ID FF700F + * Format: FF700F AA AA AA BB BB BB + * AA AA AA: destination start address + * BB BB BB: (write size - 1) + * Source address always starts from 0 + */ +static const u8 thp7312_cmd_write_ram_to_flash[] = { 0xff, 0x70, 0x0f }; + +/* + * THP7312 Calculate CRC command + * Command ID: FF70 09 + * Format: FF70 09 AA AA AA BB BB BB + * AA AA AA: Start address of calculation + * BB BB BB: (calculate size - 1) + */ +static const u8 thp7312_cmd_calc_crc[] = { 0xff, 0x70, 0x09 }; + +static const u8 thp7312_jedec_rdid[] = { SPINOR_OP_RDID, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_rdsr[] = { SPINOR_OP_RDSR, 0x00, 0x00, 0x00 }; +static const u8 thp7312_jedec_wen[] = { SPINOR_OP_WREN }; + +static int thp7312_read_firmware_version(struct thp7312_device *thp7312) +{ + u64 val = 0; + int ret = 0; + u8 major; + u8 minor; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_1, &val, &ret); + major = val; + + cci_read(thp7312->regmap, THP7312_REG_FIRMWARE_VERSION_2, &val, &ret); + minor = val; + + thp7312->fw_version = THP7312_FW_VERSION(major, minor); + return ret; +} + +static int thp7312_write_buf(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + int ret; + + ret = i2c_master_send(client, write_buf, write_size); + return ret >= 0 ? 0 : ret; +} + +static int __thp7312_flash_reg_write(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size) +{ + struct device *dev = thp7312->dev; + u8 temp_write_buf[THP7312_FLASH_MAX_REG_DATA_SIZE + 2]; + int ret; + + if (write_size > THP7312_FLASH_MAX_REG_DATA_SIZE) { + dev_err(dev, "%s: Write size error size = %d\n", + __func__, write_size); + return -EINVAL; + } + + ret = thp7312_write_buf(thp7312, thp7312_cmd_config_flash_mem_if, + sizeof(thp7312_cmd_config_flash_mem_if)); + if (ret < 0) { + dev_err(dev, "%s: Failed to config flash memory IF: %d\n", + __func__, ret); + return ret; + } + + temp_write_buf[0] = 0xd5; + temp_write_buf[1] = 0x00; + memcpy((temp_write_buf + 2), write_buf, write_size); + ret = thp7312_write_buf(thp7312, temp_write_buf, write_size + 2); + if (ret < 0) + return ret; + + thp7312_write_buf(thp7312, thp7312_cmd_write_to_reg, + sizeof(thp7312_cmd_write_to_reg)); + + return 0; +} + +static int __thp7312_flash_reg_read(struct thp7312_device *thp7312, + const u8 *write_buf, u16 write_size, + u8 *read_buf, u16 read_size) +{ + struct i2c_client *client = to_i2c_client(thp7312->dev); + struct i2c_msg msgs[2]; + int ret; + + ret = __thp7312_flash_reg_write(thp7312, write_buf, write_size); + if (ret) + return ret; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(thp7312_cmd_read_reg), + msgs[0].buf = (u8 *)thp7312_cmd_read_reg; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = read_size; + msgs[1].buf = read_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + return ret >= 0 ? 0 : ret; +} + +#define thp7312_flash_reg_write(thp7312, wrbuf) \ + __thp7312_flash_reg_write(thp7312, wrbuf, sizeof(wrbuf)) + +#define thp7312_flash_reg_read(thp7312, wrbuf, rdbuf) \ + __thp7312_flash_reg_read(thp7312, wrbuf, sizeof(wrbuf), \ + rdbuf, sizeof(rdbuf)) + +static enum fw_upload_err thp7312_fw_prepare_config(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_MEMORY_IO_SETTING, + THP7312_FW_MEMORY_IO_GPIO0, NULL); + if (ret) { + dev_err(dev, "Failed to set flash memory I/O\n"); + return FW_UPLOAD_ERR_HW_ERROR; + } + + /* Set max drivability. */ + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DRIVABILITY, 0x00777777, + NULL); + if (ret) { + dev_err(dev, "Failed to set drivability: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_check(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[3] = { 0 }; + int ret; + + /* Get JEDEC ID */ + ret = thp7312_flash_reg_read(thp7312, thp7312_jedec_rdid, read_buf); + if (ret) { + dev_err(dev, "Failed to get JEDEC ID: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + dev_dbg(dev, "Flash Memory: JEDEC ID = 0x%x 0x%x 0x%x\n", + read_buf[0], read_buf[1], read_buf[2]); + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare_reset(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + int ret; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_RESET_FLASH, 0x81, NULL); + if (ret) { + dev_err(dev, "Failed to reset flash memory: %d\n", ret); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +/* TODO: Erase only the amount of blocks necessary */ +static enum fw_upload_err thp7312_flash_erase(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + u8 read_buf[1] = { 0 }; + unsigned int i; + u8 block; + int ret; + + for (block = 0; block < 3; block++) { + const u8 jedec_se[] = { SPINOR_OP_SE, block, 0x00, 0x00 }; + + ret = thp7312_flash_reg_write(thp7312, thp7312_jedec_wen); + if (ret < 0) { + dev_err(dev, "Failed to enable flash for writing\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + ret = thp7312_flash_reg_write(thp7312, jedec_se); + if (ret < 0) { + dev_err(dev, "Failed to erase flash sector\n"); + return FW_UPLOAD_ERR_RW_ERROR; + } + + for (i = 0; i < THP7312_FLASH_MEMORY_ERASE_TIMEOUT; i++) { + usleep_range(100000, 101000); + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, + read_buf); + + /* Check Busy bit. Busy == 0x0 means erase complete. */ + if (!(read_buf[0] & SR_WIP)) + break; + } + + if (i == THP7312_FLASH_MEMORY_ERASE_TIMEOUT) + return FW_UPLOAD_ERR_TIMEOUT; + } + + thp7312_flash_reg_read(thp7312, thp7312_jedec_rdsr, read_buf); + + /* Check WEL bit. */ + if (read_buf[0] & SR_WEL) + return FW_UPLOAD_ERR_HW_ERROR; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err +thp7312_write_download_data_by_unit(struct thp7312_device *thp7312, + unsigned int addr, const u8 *data, + unsigned int size) +{ + struct device *dev = thp7312->dev; + u8 *write_buf = thp7312->fw_write_buf; + int ret; + + dev_dbg(dev, "%s: addr = 0x%04x, data = 0x%p, size = %u\n", + __func__, addr, data, size); + + write_buf[0] = (addr >> 8) & 0xff; + write_buf[1] = (addr >> 0) & 0xff; + memcpy(&write_buf[2], data, size); + + /* + * THP7312 Firmware download to RAM + * Command ID (address to download): 0x0000 - 0x7fff + * Format:: 0000 XX XX XX ........ XX + */ + ret = thp7312_write_buf(thp7312, write_buf, size + 2); + if (ret < 0) + dev_err(dev, "Unit transfer ERROR %s(): ret = %d\n", __func__, ret); + + return ret >= 0 ? FW_UPLOAD_ERR_NONE : FW_UPLOAD_ERR_RW_ERROR; +} + +static enum fw_upload_err thp7312_fw_load_to_ram(struct thp7312_device *thp7312, + const u8 *data, u32 size) +{ + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + unsigned int num_banks; + unsigned int i, j; + + num_banks = DIV_ROUND_UP(size, THP7312_RAM_BANK_SIZE); + + dev_dbg(dev, "%s: loading %u bytes in SRAM (%u banks)\n", __func__, + size, num_banks); + + for (i = 0; i < num_banks; i++) { + const u32 bank_addr = 0x10000000 | (i * THP7312_RAM_BANK_SIZE); + unsigned int bank_size; + unsigned int num_chunks; + + ret = cci_write(thp7312->regmap, THP7312_REG_FW_DEST_BANK_ADDR, + bank_addr, NULL); + if (ret) + return FW_UPLOAD_ERR_HW_ERROR; + + bank_size = min_t(u32, size, THP7312_RAM_BANK_SIZE); + num_chunks = DIV_ROUND_UP(bank_size, THP7312_FW_DOWNLOAD_UNIT); + + dev_dbg(dev, "%s: loading %u bytes in SRAM bank %u (%u chunks)\n", + __func__, bank_size, i, num_chunks); + + for (j = 0 ; j < num_chunks; j++) { + unsigned int chunk_addr; + unsigned int chunk_size; + + chunk_addr = j * THP7312_FW_DOWNLOAD_UNIT; + chunk_size = min_t(u32, size, THP7312_FW_DOWNLOAD_UNIT); + + ret = thp7312_write_download_data_by_unit(thp7312, chunk_addr, + data, chunk_size); + if (ret != FW_UPLOAD_ERR_NONE) { + dev_err(dev, "Unit transfer ERROR at bank transfer %s(): %d\n", + __func__, j); + return ret; + } + + data += chunk_size; + size -= chunk_size; + } + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_write_to_flash(struct thp7312_device *thp7312, + u32 dest, u32 write_size) +{ + u8 command[sizeof(thp7312_cmd_write_ram_to_flash) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_write_ram_to_flash); + u64 val; + int ret; + + memcpy(command, thp7312_cmd_write_ram_to_flash, cmd_size); + + command[cmd_size] = (dest & 0xff0000) >> 16; + command[cmd_size + 1] = (dest & 0x00ff00) >> 8; + command[cmd_size + 2] = (dest & 0x0000ff); + command[cmd_size + 3] = ((write_size - 1) & 0xff0000) >> 16; + command[cmd_size + 4] = ((write_size - 1) & 0x00ff00) >> 8; + command[cmd_size + 5] = ((write_size - 1) & 0x0000ff); + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(8000000, 8100000); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_VERIFY_RESULT, &val, + NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + return val ? FW_UPLOAD_ERR_HW_ERROR : FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_check_crc(struct thp7312_device *thp7312, + const u8 *fw_data, u32 fw_size) +{ + struct device *dev = thp7312->dev; + u16 header_size = fw_size - THP7312_FW_RAM_SIZE; + u8 command[sizeof(thp7312_cmd_calc_crc) + 6]; + static const u32 cmd_size = sizeof(thp7312_cmd_calc_crc); + u32 size = THP7312_FW_RAM_SIZE - 4; + u32 fw_crc; + u64 crc; + int ret; + + memcpy(command, thp7312_cmd_calc_crc, cmd_size); + + command[cmd_size] = 0; + command[cmd_size + 1] = (header_size >> 8) & 0xff; + command[cmd_size + 2] = header_size & 0xff; + + command[cmd_size + 3] = (size >> 16) & 0xff; + command[cmd_size + 4] = (size >> 8) & 0xff; + command[cmd_size + 5] = size & 0xff; + + ret = thp7312_write_buf(thp7312, command, sizeof(command)); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + usleep_range(2000000, 2100000); + + fw_crc = get_unaligned_be32(&fw_data[fw_size - 4]); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_CRC_RESULT, &crc, NULL); + if (ret < 0) + return FW_UPLOAD_ERR_RW_ERROR; + + if (fw_crc != crc) { + dev_err(dev, "CRC mismatch: firmware 0x%08x, flash 0x%08llx\n", + fw_crc, crc); + return FW_UPLOAD_ERR_HW_ERROR; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_prepare(struct fw_upload *fw_upload, + const u8 *data, u32 size) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + enum fw_upload_err ret; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = false; + mutex_unlock(&thp7312->fw_lock); + + if (size < THP7312_FW_MIN_SIZE || size > THP7312_FW_MAX_SIZE) { + dev_err(dev, "%s: Invalid firmware size %d; must be between %d and %d\n", + __func__, size, THP7312_FW_MIN_SIZE, THP7312_FW_MAX_SIZE); + return FW_UPLOAD_ERR_INVALID_SIZE; + } + + ret = thp7312_fw_prepare_config(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_check(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_prepare_reset(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + mutex_lock(&thp7312->fw_lock); + ret = thp7312->fw_cancel ? FW_UPLOAD_ERR_CANCELED : FW_UPLOAD_ERR_NONE; + mutex_unlock(&thp7312->fw_lock); + + return ret; +} + +static enum fw_upload_err thp7312_fw_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, + u32 size, u32 *written) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + struct device *dev = thp7312->dev; + u16 header_size = size - THP7312_FW_RAM_SIZE; + enum fw_upload_err ret; + bool cancel; + + mutex_lock(&thp7312->fw_lock); + cancel = thp7312->fw_cancel; + mutex_unlock(&thp7312->fw_lock); + + if (cancel) + return FW_UPLOAD_ERR_CANCELED; + + ret = thp7312_flash_erase(thp7312); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data, THP7312_FW_RAM_SIZE); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0, 0x1ffff); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_load_to_ram(thp7312, data + THP7312_FW_RAM_SIZE, header_size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_write_to_flash(thp7312, 0x20000, header_size - 1); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + ret = thp7312_fw_check_crc(thp7312, data, size); + if (ret != FW_UPLOAD_ERR_NONE) + return ret; + + dev_info(dev, "Successfully wrote firmware\n"); + + *written = size; + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err thp7312_fw_poll_complete(struct fw_upload *fw_upload) +{ + return FW_UPLOAD_ERR_NONE; +} + +/* + * This may be called asynchronously with an on-going update. All other + * functions are called sequentially in a single thread. To avoid contention on + * register accesses, only update the cancel_request flag. Other functions will + * check this flag and handle the cancel request synchronously. + */ +static void thp7312_fw_cancel(struct fw_upload *fw_upload) +{ + struct thp7312_device *thp7312 = fw_upload->dd_handle; + + mutex_lock(&thp7312->fw_lock); + thp7312->fw_cancel = true; + mutex_unlock(&thp7312->fw_lock); +} + +static const struct fw_upload_ops thp7312_fw_upload_ops = { + .prepare = thp7312_fw_prepare, + .write = thp7312_fw_write, + .poll_complete = thp7312_fw_poll_complete, + .cancel = thp7312_fw_cancel, +}; + +static int thp7312_register_flash_mode(struct thp7312_device *thp7312) +{ + struct device *dev = thp7312->dev; + struct fw_upload *fwl; + u64 val; + int ret; + + dev_info(dev, "booted in flash mode\n"); + + mutex_init(&thp7312->fw_lock); + + thp7312->fw_write_buf = devm_kzalloc(dev, THP7312_FW_DOWNLOAD_UNIT + 2, + GFP_KERNEL); + if (!thp7312->fw_write_buf) + return -ENOMEM; + + ret = __thp7312_power_on(thp7312); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to power on\n"); + + ret = cci_read(thp7312->regmap, THP7312_REG_FW_STATUS, &val, NULL); + if (ret) { + dev_err_probe(dev, ret, "Camera status read failed\n"); + goto error; + } + + fwl = firmware_upload_register(THIS_MODULE, dev, "thp7312-firmware", + &thp7312_fw_upload_ops, thp7312); + if (IS_ERR(fwl)) { + ret = PTR_ERR(fwl); + dev_err_probe(dev, ret, "Failed to register firmware upload\n"); + goto error; + } + + thp7312->fwl = fwl; + return 0; + +error: + __thp7312_power_off(thp7312); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int thp7312_get_regulators(struct thp7312_device *thp7312) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(thp7312->supplies); i++) + thp7312->supplies[i].supply = thp7312_supply_name[i]; + + return devm_regulator_bulk_get(thp7312->dev, + ARRAY_SIZE(thp7312->supplies), + thp7312->supplies); +} + +static int thp7312_sensor_parse_dt(struct thp7312_device *thp7312, + struct fwnode_handle *node) +{ + struct device *dev = thp7312->dev; + struct thp7312_sensor *sensor; + const char *model; + u8 data_lanes[4]; + u32 values[4]; + unsigned int i; + u32 reg; + int ret; + + /* Retrieve the sensor index from the reg property. */ + ret = fwnode_property_read_u32(node, "reg", ®); + if (ret < 0) { + dev_err(dev, "'reg' property missing in sensor node\n"); + return -EINVAL; + } + + if (reg >= ARRAY_SIZE(thp7312->sensors)) { + dev_err(dev, "Out-of-bounds 'reg' value %u\n", reg); + return -EINVAL; + } + + sensor = &thp7312->sensors[reg]; + if (sensor->info) { + dev_err(dev, "Duplicate entry for sensor %u\n", reg); + return -EINVAL; + } + + ret = fwnode_property_read_string(node, "thine,model", &model); + if (ret < 0) { + dev_err(dev, "'thine,model' property missing in sensor node\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(thp7312_sensor_info); i++) { + const struct thp7312_sensor_info *info = + &thp7312_sensor_info[i]; + + if (!strcmp(info->model, model)) { + sensor->info = info; + break; + } + } + + if (!sensor->info) { + dev_err(dev, "Unsupported sensor model %s\n", model); + return -EINVAL; + } + + ret = fwnode_property_read_u32_array(node, "data-lanes", values, + ARRAY_SIZE(values)); + if (ret < 0) { + dev_err(dev, "Failed to read property data-lanes: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data_lanes); ++i) + data_lanes[i] = values[i]; + + ret = thp7312_map_data_lanes(&sensor->lane_remap, data_lanes, + ARRAY_SIZE(data_lanes)); + if (ret) { + dev_err(dev, "Invalid sensor@%u data-lanes value\n", reg); + return ret; + } + + return 0; +} + +static int thp7312_parse_dt(struct thp7312_device *thp7312) +{ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = thp7312->dev; + struct fwnode_handle *endpoint; + struct fwnode_handle *sensors; + unsigned int num_sensors = 0; + struct fwnode_handle *node; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "Endpoint node not found\n"); + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) + return dev_err_probe(dev, ret, "Could not parse endpoint\n"); + + ret = thp7312_map_data_lanes(&thp7312->lane_remap, + ep.bus.mipi_csi2.data_lanes, + ep.bus.mipi_csi2.num_data_lanes); + if (ret) { + dev_err(dev, "Invalid data-lanes value\n"); + return ret; + } + + /* + * The thine,boot-mode property is optional and default to + * THP7312_BOOT_MODE_SPI_MASTER (1). + */ + thp7312->boot_mode = THP7312_BOOT_MODE_SPI_MASTER; + ret = device_property_read_u32(dev, "thine,boot-mode", + &thp7312->boot_mode); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Property '%s' is invalid\n", + "thine,boot-mode"); + + if (thp7312->boot_mode != THP7312_BOOT_MODE_2WIRE_SLAVE && + thp7312->boot_mode != THP7312_BOOT_MODE_SPI_MASTER) + return dev_err_probe(dev, -EINVAL, "Invalid '%s' value %u\n", + "thine,boot-mode", thp7312->boot_mode); + + /* Sensors */ + sensors = device_get_named_child_node(dev, "sensors"); + if (!sensors) { + dev_err(dev, "'sensors' child node not found\n"); + return -EINVAL; + } + + fwnode_for_each_available_child_node(sensors, node) { + if (fwnode_name_eq(node, "sensor")) { + if (!thp7312_sensor_parse_dt(thp7312, node)) + num_sensors++; + } + } + + fwnode_handle_put(sensors); + + if (!num_sensors) { + dev_err(dev, "No sensor found\n"); + return -EINVAL; + } + + return 0; +} + +static int thp7312_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct thp7312_device *thp7312; + int ret; + + thp7312 = devm_kzalloc(dev, sizeof(*thp7312), GFP_KERNEL); + if (!thp7312) + return -ENOMEM; + + thp7312->dev = dev; + + thp7312->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(thp7312->regmap)) + return dev_err_probe(dev, PTR_ERR(thp7312->regmap), + "Unable to initialize I2C\n"); + + ret = thp7312_parse_dt(thp7312); + if (ret < 0) + return ret; + + ret = thp7312_get_regulators(thp7312); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + thp7312->iclk = devm_clk_get(dev, NULL); + if (IS_ERR(thp7312->iclk)) + return dev_err_probe(dev, PTR_ERR(thp7312->iclk), + "Failed to get iclk\n"); + + thp7312->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(thp7312->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(thp7312->reset_gpio), + "Failed to get reset gpio\n"); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) + return thp7312_register_flash_mode(thp7312); + + v4l2_i2c_subdev_init(&thp7312->sd, client, &thp7312_subdev_ops); + thp7312->sd.internal_ops = &thp7312_internal_ops; + thp7312->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + thp7312->pad.flags = MEDIA_PAD_FL_SOURCE; + thp7312->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&thp7312->sd.entity, 1, &thp7312->pad); + if (ret) + return ret; + + /* + * Enable power management. The driver supports runtime PM, but needs to + * work when runtime PM is disabled in the kernel. To that end, power + * the device manually here. + */ + ret = thp7312_power_on(thp7312); + if (ret) + goto err_entity_cleanup; + + ret = thp7312_read_firmware_version(thp7312); + if (ret < 0) { + dev_err(dev, "Camera is not found\n"); + goto err_power_off; + } + + ret = thp7312_init_controls(thp7312); + if (ret) { + dev_err(dev, "Failed to initialize controls\n"); + goto err_power_off; + } + + thp7312->sd.ctrl_handler = &thp7312->ctrl_handler; + thp7312->sd.state_lock = thp7312->ctrl_handler.lock; + + ret = v4l2_subdev_init_finalize(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev active state initialization failed\n"); + goto err_free_ctrls; + } + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = v4l2_async_register_subdev(&thp7312->sd); + if (ret < 0) { + dev_err(dev, "Subdev registration failed\n"); + goto err_pm; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + dev_info(dev, "THP7312 firmware version %02u.%02u\n", + THP7312_FW_VERSION_MAJOR(thp7312->fw_version), + THP7312_FW_VERSION_MINOR(thp7312->fw_version)); + + return 0; + +err_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + v4l2_subdev_cleanup(&thp7312->sd); +err_free_ctrls: + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); +err_power_off: + thp7312_power_off(thp7312); +err_entity_cleanup: + media_entity_cleanup(&thp7312->sd.entity); + return ret; +} + +static void thp7312_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct thp7312_device *thp7312 = to_thp7312_dev(sd); + + if (thp7312->boot_mode == THP7312_BOOT_MODE_2WIRE_SLAVE) { + firmware_upload_unregister(thp7312->fwl); + __thp7312_power_off(thp7312); + return; + } + + v4l2_async_unregister_subdev(&thp7312->sd); + v4l2_subdev_cleanup(&thp7312->sd); + media_entity_cleanup(&thp7312->sd.entity); + v4l2_ctrl_handler_free(&thp7312->ctrl_handler); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(thp7312->dev); + if (!pm_runtime_status_suspended(thp7312->dev)) + thp7312_power_off(thp7312); + pm_runtime_set_suspended(thp7312->dev); +} + +static const struct of_device_id thp7312_dt_ids[] = { + { .compatible = "thine,thp7312" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, thp7312_dt_ids); + +static struct i2c_driver thp7312_i2c_driver = { + .driver = { + .name = "thp7312", + .pm = &thp7312_pm_ops, + .of_match_table = thp7312_dt_ids, + }, + .probe = thp7312_probe, + .remove = thp7312_remove, +}; + +module_i2c_driver(thp7312_i2c_driver); + +MODULE_DESCRIPTION("THP7312 MIPI Camera Subdev Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index c37f605cb..5a561e5bf 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -738,20 +738,20 @@ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) return err; } -/** - * tvp514x_g_frame_interval() - V4L2 decoder interface handler - * @sd: pointer to standard V4L2 sub-device structure - * @ival: pointer to a v4l2_subdev_frame_interval structure - * - * Returns the decoder's video CAPTURE parameters. - */ static int -tvp514x_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +tvp514x_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct tvp514x_decoder *decoder = to_decoder(sd); enum tvp514x_std current_std; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; /* get the current standard */ current_std = decoder->current_std; @@ -762,22 +762,21 @@ tvp514x_g_frame_interval(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_frame_interval() - V4L2 decoder interface handler - * @sd: pointer to standard V4L2 sub-device structure - * @ival: pointer to a v4l2_subdev_frame_interval structure - * - * Configures the decoder to use the input parameters, if possible. If - * not possible, returns the appropriate error code. - */ static int -tvp514x_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) +tvp514x_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval *ival) { struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_fract *timeperframe; enum tvp514x_std current_std; + /* + * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 + * subdev active state API. + */ + if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; timeperframe = &ival->interval; @@ -940,8 +939,6 @@ static const struct v4l2_subdev_video_ops tvp514x_video_ops = { .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, .querystd = tvp514x_querystd, - .g_frame_interval = tvp514x_g_frame_interval, - .s_frame_interval = tvp514x_s_frame_interval, .s_stream = tvp514x_s_stream, }; @@ -949,6 +946,8 @@ static const struct v4l2_subdev_pad_ops tvp514x_pad_ops = { .enum_mbus_code = tvp514x_enum_mbus_code, .get_fmt = tvp514x_get_pad_format, .set_fmt = tvp514x_set_pad_format, + .get_frame_interval = tvp514x_get_frame_interval, + .set_frame_interval = tvp514x_set_frame_interval, }; static const struct v4l2_subdev_ops tvp514x_ops = { diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e543b3f7a..9fc586cfd 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1035,7 +1035,7 @@ tvp5150_get_pad_crop(struct tvp5150 *decoder, return &decoder->rect; case V4L2_SUBDEV_FORMAT_TRY: #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - return v4l2_subdev_get_try_crop(&decoder->sd, sd_state, pad); + return v4l2_subdev_state_get_crop(sd_state, pad); #else return ERR_PTR(-EINVAL); #endif @@ -1209,8 +1209,8 @@ static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, /**************************************************************************** V4L2 subdev pad ops ****************************************************************************/ -static int tvp5150_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int tvp5150_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; @@ -1722,7 +1722,6 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { }; static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { - .init_cfg = tvp5150_init_cfg, .enum_mbus_code = tvp5150_enum_mbus_code, .enum_frame_size = tvp5150_enum_frame_size, .set_fmt = tvp5150_fill_fmt, @@ -1741,6 +1740,7 @@ static const struct v4l2_subdev_ops tvp5150_ops = { }; static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { + .init_state = tvp5150_init_state, .registered = tvp5150_registered, .open = tvp5150_open, .close = tvp5150_close, diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index a2d7bc799..30831b4b5 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -791,7 +791,7 @@ static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { /* * tvp7002_enum_mbus_code() - Enum supported digital video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to subdev enum mbus code struct * * Enumerate supported digital video formats for pad. @@ -813,7 +813,7 @@ tvp7002_enum_mbus_code(struct v4l2_subdev *sd, /* * tvp7002_get_pad_format() - get video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * get video format for pad. @@ -837,7 +837,7 @@ tvp7002_get_pad_format(struct v4l2_subdev *sd, /* * tvp7002_set_pad_format() - set video format on pad * @sd: pointer to standard V4L2 sub-device structure - * @cfg: pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to subdev format struct * * set video format for pad. diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c new file mode 100644 index 000000000..bc7623ec4 --- /dev/null +++ b/drivers/media/i2c/tw9900.c @@ -0,0 +1,781 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Techwell TW9900 multi-standard video decoder. + * + * Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd. + * Copyright (C) 2020 Maxime Chevallier + * Copyright (C) 2023 Mehdi Djait + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TW9900_REG_CHIP_ID 0x00 +#define TW9900_REG_CHIP_STATUS 0x01 +#define TW9900_REG_CHIP_STATUS_VDLOSS BIT(7) +#define TW9900_REG_CHIP_STATUS_HLOCK BIT(6) +#define TW9900_REG_OUT_FMT_CTL 0x03 +#define TW9900_REG_OUT_FMT_CTL_STANDBY 0xA7 +#define TW9900_REG_OUT_FMT_CTL_STREAMING 0xA0 +#define TW9900_REG_CKHY_HSDLY 0x04 +#define TW9900_REG_OUT_CTRL_I 0x05 +#define TW9900_REG_ANALOG_CTL 0x06 +#define TW9900_REG_CROP_HI 0x07 +#define TW9900_REG_VDELAY_LO 0x08 +#define TW9900_REG_VACTIVE_LO 0x09 +#define TW9900_REG_HACTIVE_LO 0x0B +#define TW9900_REG_CNTRL1 0x0C +#define TW9900_REG_BRIGHT_CTL 0x10 +#define TW9900_REG_CONTRAST_CTL 0x11 +#define TW9900_REG_VBI_CNTL 0x19 +#define TW9900_REG_ANAL_CTL_II 0x1A +#define TW9900_REG_OUT_CTRL_II 0x1B +#define TW9900_REG_STD 0x1C +#define TW9900_REG_STD_AUTO_PROGRESS BIT(7) +#define TW9900_STDNOW_MASK GENMASK(6, 4) +#define TW9900_REG_STDR 0x1D +#define TW9900_REG_MISSCNT 0x26 +#define TW9900_REG_MISC_CTL_II 0x2F +#define TW9900_REG_VVBI 0x55 + +#define TW9900_CHIP_ID 0x00 +#define TW9900_STD_NTSC_M 0 +#define TW9900_STD_PAL_BDGHI 1 +#define TW9900_STD_AUTO 7 + +#define TW9900_VIDEO_POLL_TRIES 20 + +struct regval { + u8 addr; + u8 val; +}; + +struct tw9900_mode { + u32 width; + u32 height; + u32 std; + const struct regval *reg_list; + int n_regs; +}; + +struct tw9900 { + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator *regulator; + + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; + + /* Serialize access to hardware and global state. */ + struct mutex mutex; + + bool streaming; + const struct tw9900_mode *cur_mode; +}; + +#define to_tw9900(sd) container_of(sd, struct tw9900, subdev) + +static const struct regval tw9900_init_regs[] = { + { TW9900_REG_MISC_CTL_II, 0xE6 }, + { TW9900_REG_MISSCNT, 0x24 }, + { TW9900_REG_OUT_FMT_CTL, 0xA7 }, + { TW9900_REG_ANAL_CTL_II, 0x0A }, + { TW9900_REG_VDELAY_LO, 0x19 }, + { TW9900_REG_STD, 0x00 }, + { TW9900_REG_VACTIVE_LO, 0xF0 }, + { TW9900_REG_STD, 0x07 }, + { TW9900_REG_CKHY_HSDLY, 0x00 }, + { TW9900_REG_ANALOG_CTL, 0x80 }, + { TW9900_REG_CNTRL1, 0xDC }, + { TW9900_REG_OUT_CTRL_I, 0x98 }, +}; + +static const struct regval tw9900_pal_regs[] = { + { TW9900_REG_STD, 0x01 }, +}; + +static const struct regval tw9900_ntsc_regs[] = { + { TW9900_REG_OUT_FMT_CTL, 0xA4 }, + { TW9900_REG_VDELAY_LO, 0x12 }, + { TW9900_REG_VACTIVE_LO, 0xF0 }, + { TW9900_REG_CROP_HI, 0x02 }, + { TW9900_REG_HACTIVE_LO, 0xD0 }, + { TW9900_REG_VBI_CNTL, 0x01 }, + { TW9900_REG_STD, 0x00 }, +}; + +static const struct tw9900_mode supported_modes[] = { + { + .width = 720, + .height = 480, + .std = V4L2_STD_NTSC, + .reg_list = tw9900_ntsc_regs, + .n_regs = ARRAY_SIZE(tw9900_ntsc_regs), + }, + { + .width = 720, + .height = 576, + .std = V4L2_STD_PAL, + .reg_list = tw9900_pal_regs, + .n_regs = ARRAY_SIZE(tw9900_pal_regs), + }, +}; + +static int tw9900_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) + dev_err(&client->dev, "write reg error: %d\n", ret); + + return ret; +} + +static int tw9900_write_array(struct i2c_client *client, + const struct regval *regs, int n_regs) +{ + int i, ret = 0; + + for (i = 0; i < n_regs; i++) { + ret = tw9900_write_reg(client, regs[i].addr, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +static int tw9900_read_reg(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, "read reg error: %d\n", ret); + + return ret; +} + +static void tw9900_fill_fmt(const struct tw9900_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SMPTE170M); + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SMPTE170M); +} + +static int tw9900_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + + mutex_lock(&tw9900->mutex); + tw9900_fill_fmt(tw9900->cur_mode, mbus_fmt); + mutex_unlock(&tw9900->mutex); + + return 0; +} + +static int tw9900_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + + mutex_lock(&tw9900->mutex); + + if (tw9900->streaming) { + mutex_unlock(&tw9900->mutex); + return -EBUSY; + } + + tw9900_fill_fmt(tw9900->cur_mode, mbus_fmt); + + mutex_unlock(&tw9900->mutex); + + return 0; +} + +static int tw9900_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int tw9900_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw9900 *tw9900 = container_of(ctrl->handler, struct tw9900, hdl); + int ret; + + if (pm_runtime_suspended(&tw9900->client->dev)) + return 0; + + /* v4l2_ctrl_lock() locks tw9900->mutex. */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ret = tw9900_write_reg(tw9900->client, TW9900_REG_BRIGHT_CTL, + (u8)ctrl->val); + break; + case V4L2_CID_CONTRAST: + ret = tw9900_write_reg(tw9900->client, TW9900_REG_CONTRAST_CTL, + (u8)ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int tw9900_s_stream(struct v4l2_subdev *sd, int on) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + struct i2c_client *client = tw9900->client; + int ret; + + mutex_lock(&tw9900->mutex); + + if (tw9900->streaming == on) { + mutex_unlock(&tw9900->mutex); + return 0; + } + + mutex_unlock(&tw9900->mutex); + + if (on) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + mutex_lock(&tw9900->mutex); + + ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler); + if (ret) + goto err_unlock; + + ret = tw9900_write_array(tw9900->client, + tw9900->cur_mode->reg_list, + tw9900->cur_mode->n_regs); + if (ret) + goto err_unlock; + + ret = tw9900_write_reg(client, TW9900_REG_OUT_FMT_CTL, + TW9900_REG_OUT_FMT_CTL_STREAMING); + if (ret) + goto err_unlock; + + tw9900->streaming = on; + + mutex_unlock(&tw9900->mutex); + + } else { + mutex_lock(&tw9900->mutex); + + ret = tw9900_write_reg(client, TW9900_REG_OUT_FMT_CTL, + TW9900_REG_OUT_FMT_CTL_STANDBY); + if (ret) + goto err_unlock; + + tw9900->streaming = on; + + mutex_unlock(&tw9900->mutex); + + pm_runtime_put(&client->dev); + } + + return 0; + +err_unlock: + mutex_unlock(&tw9900->mutex); + pm_runtime_put(&client->dev); + + return ret; +} + +static int tw9900_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + +static int tw9900_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + const struct tw9900_mode *mode = NULL; + int i; + + if (!(std & (V4L2_STD_NTSC | V4L2_STD_PAL))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) + if (supported_modes[i].std & std) + mode = &supported_modes[i]; + if (!mode) + return -EINVAL; + + mutex_lock(&tw9900->mutex); + tw9900->cur_mode = mode; + mutex_unlock(&tw9900->mutex); + + return 0; +} + +static int tw9900_get_stream_std(struct tw9900 *tw9900, + v4l2_std_id *std) +{ + int cur_std, ret; + + lockdep_assert_held(&tw9900->mutex); + + ret = tw9900_read_reg(tw9900->client, TW9900_REG_STD); + if (ret < 0) { + *std = V4L2_STD_UNKNOWN; + return ret; + } + + cur_std = FIELD_GET(TW9900_STDNOW_MASK, ret); + switch (cur_std) { + case TW9900_STD_NTSC_M: + *std = V4L2_STD_NTSC; + break; + case TW9900_STD_PAL_BDGHI: + *std = V4L2_STD_PAL; + break; + case TW9900_STD_AUTO: + *std = V4L2_STD_UNKNOWN; + break; + default: + *std = V4L2_STD_UNKNOWN; + break; + } + + return 0; +} + +static int tw9900_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + + mutex_lock(&tw9900->mutex); + *std = tw9900->cur_mode->std; + mutex_unlock(&tw9900->mutex); + + return 0; +} + +static int tw9900_start_autodetect(struct tw9900 *tw9900) +{ + int ret; + + lockdep_assert_held(&tw9900->mutex); + + ret = tw9900_write_reg(tw9900->client, TW9900_REG_STDR, + BIT(TW9900_STD_NTSC_M) | + BIT(TW9900_STD_PAL_BDGHI)); + if (ret) + return ret; + + ret = tw9900_write_reg(tw9900->client, TW9900_REG_STD, + TW9900_STD_AUTO); + if (ret) + return ret; + + ret = tw9900_write_reg(tw9900->client, TW9900_REG_STDR, + BIT(TW9900_STD_NTSC_M) | + BIT(TW9900_STD_PAL_BDGHI) | + BIT(TW9900_STD_AUTO)); + if (ret) + return ret; + + /* + * Autodetect takes a while to start, and during the starting sequence + * the autodetection status is reported as done. + */ + msleep(30); + + return 0; +} + +static int tw9900_detect_done(struct tw9900 *tw9900, bool *done) +{ + int ret; + + lockdep_assert_held(&tw9900->mutex); + + ret = tw9900_read_reg(tw9900->client, TW9900_REG_STD); + if (ret < 0) + return ret; + + *done = !(ret & TW9900_REG_STD_AUTO_PROGRESS); + + return 0; +} + +static int tw9900_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + bool done = false; + int i, ret; + + mutex_lock(&tw9900->mutex); + + if (tw9900->streaming) { + mutex_unlock(&tw9900->mutex); + return -EBUSY; + } + + mutex_unlock(&tw9900->mutex); + + ret = pm_runtime_resume_and_get(&tw9900->client->dev); + if (ret < 0) + return ret; + + mutex_lock(&tw9900->mutex); + + ret = tw9900_start_autodetect(tw9900); + if (ret) + goto out_unlock; + + for (i = 0; i < TW9900_VIDEO_POLL_TRIES; i++) { + ret = tw9900_detect_done(tw9900, &done); + if (ret) + goto out_unlock; + + if (done) + break; + + msleep(20); + } + + if (!done) { + ret = -ETIMEDOUT; + goto out_unlock; + } + + ret = tw9900_get_stream_std(tw9900, std); + +out_unlock: + mutex_unlock(&tw9900->mutex); + pm_runtime_put(&tw9900->client->dev); + + return ret; +} + +static int tw9900_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + *std = V4L2_STD_NTSC | V4L2_STD_PAL; + + return 0; +} + +static int tw9900_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct tw9900 *tw9900 = to_tw9900(sd); + int ret; + + mutex_lock(&tw9900->mutex); + + if (tw9900->streaming) { + mutex_unlock(&tw9900->mutex); + return -EBUSY; + } + + mutex_unlock(&tw9900->mutex); + + *status = V4L2_IN_ST_NO_SIGNAL; + + ret = pm_runtime_resume_and_get(&tw9900->client->dev); + if (ret < 0) + return ret; + + mutex_lock(&tw9900->mutex); + ret = tw9900_read_reg(tw9900->client, TW9900_REG_CHIP_STATUS); + mutex_unlock(&tw9900->mutex); + + pm_runtime_put(&tw9900->client->dev); + + if (ret < 0) + return ret; + + *status = ret & TW9900_REG_CHIP_STATUS_HLOCK ? 0 : V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static const struct v4l2_subdev_core_ops tw9900_core_ops = { + .subscribe_event = tw9900_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops tw9900_video_ops = { + .s_std = tw9900_s_std, + .g_std = tw9900_g_std, + .querystd = tw9900_querystd, + .g_tvnorms = tw9900_g_tvnorms, + .g_input_status = tw9900_g_input_status, + .s_stream = tw9900_s_stream, +}; + +static const struct v4l2_subdev_pad_ops tw9900_pad_ops = { + .enum_mbus_code = tw9900_enum_mbus_code, + .get_fmt = tw9900_get_fmt, + .set_fmt = tw9900_set_fmt, +}; + +static const struct v4l2_subdev_ops tw9900_subdev_ops = { + .core = &tw9900_core_ops, + .video = &tw9900_video_ops, + .pad = &tw9900_pad_ops, +}; + +static const struct v4l2_ctrl_ops tw9900_ctrl_ops = { + .s_ctrl = tw9900_s_ctrl, +}; + +static int tw9900_check_id(struct tw9900 *tw9900, + struct i2c_client *client) +{ + struct device *dev = &tw9900->client->dev; + int ret; + + ret = pm_runtime_resume_and_get(&tw9900->client->dev); + if (ret < 0) + return ret; + + mutex_lock(&tw9900->mutex); + ret = tw9900_read_reg(client, TW9900_CHIP_ID); + mutex_unlock(&tw9900->mutex); + + pm_runtime_put(&tw9900->client->dev); + + if (ret < 0) + return ret; + + if (ret != TW9900_CHIP_ID) { + dev_err(dev, "Unexpected decoder id %#x\n", ret); + return -ENODEV; + } + + return 0; +} + +static int tw9900_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tw9900 *tw9900 = to_tw9900(sd); + int ret; + + mutex_lock(&tw9900->mutex); + + if (tw9900->reset_gpio) + gpiod_set_value_cansleep(tw9900->reset_gpio, 1); + + ret = regulator_enable(tw9900->regulator); + if (ret < 0) { + mutex_unlock(&tw9900->mutex); + return ret; + } + + usleep_range(50000, 52000); + + if (tw9900->reset_gpio) + gpiod_set_value_cansleep(tw9900->reset_gpio, 0); + + usleep_range(1000, 2000); + + ret = tw9900_write_array(tw9900->client, tw9900_init_regs, + ARRAY_SIZE(tw9900_init_regs)); + + mutex_unlock(&tw9900->mutex); + + /* This sleep is needed for the Horizontal Sync PLL to lock. */ + msleep(300); + + return ret; +} + +static int tw9900_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tw9900 *tw9900 = to_tw9900(sd); + + mutex_lock(&tw9900->mutex); + + if (tw9900->reset_gpio) + gpiod_set_value_cansleep(tw9900->reset_gpio, 1); + + regulator_disable(tw9900->regulator); + + mutex_unlock(&tw9900->mutex); + + return 0; +} + +static int tw9900_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_ctrl_handler *hdl; + struct tw9900 *tw9900; + int ret = 0; + + tw9900 = devm_kzalloc(dev, sizeof(*tw9900), GFP_KERNEL); + if (!tw9900) + return -ENOMEM; + + tw9900->client = client; + tw9900->cur_mode = &supported_modes[0]; + + tw9900->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(tw9900->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(tw9900->reset_gpio), + "Failed to get reset gpio\n"); + + tw9900->regulator = devm_regulator_get(&tw9900->client->dev, "vdd"); + if (IS_ERR(tw9900->regulator)) + return dev_err_probe(dev, PTR_ERR(tw9900->regulator), + "Failed to get power regulator\n"); + + v4l2_i2c_subdev_init(&tw9900->subdev, client, &tw9900_subdev_ops); + tw9900->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + + mutex_init(&tw9900->mutex); + + hdl = &tw9900->hdl; + + ret = v4l2_ctrl_handler_init(hdl, 2); + if (ret) + goto err_destory_mutex; + + hdl->lock = &tw9900->mutex; + + v4l2_ctrl_new_std(hdl, &tw9900_ctrl_ops, V4L2_CID_BRIGHTNESS, + -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw9900_ctrl_ops, V4L2_CID_CONTRAST, + 0, 255, 1, 0x60); + + tw9900->subdev.ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + goto err_free_handler; + } + + tw9900->pad.flags = MEDIA_PAD_FL_SOURCE; + tw9900->subdev.entity.function = MEDIA_ENT_F_DV_DECODER; + + ret = media_entity_pads_init(&tw9900->subdev.entity, 1, &tw9900->pad); + if (ret < 0) + goto err_free_handler; + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + ret = tw9900_check_id(tw9900, client); + if (ret) + goto err_disable_pm; + + ret = v4l2_async_register_subdev(&tw9900->subdev); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_disable_pm; + } + + return 0; + +err_disable_pm: + pm_runtime_disable(dev); + media_entity_cleanup(&tw9900->subdev.entity); +err_free_handler: + v4l2_ctrl_handler_free(hdl); +err_destory_mutex: + mutex_destroy(&tw9900->mutex); + + return ret; +} + +static void tw9900_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tw9900 *tw9900 = to_tw9900(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + + mutex_destroy(&tw9900->mutex); +} + +static const struct dev_pm_ops tw9900_pm_ops = { + .runtime_suspend = tw9900_runtime_suspend, + .runtime_resume = tw9900_runtime_resume, +}; + +static const struct i2c_device_id tw9900_id[] = { + { "tw9900", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9900_id); + +static const struct of_device_id tw9900_of_match[] = { + { .compatible = "techwell,tw9900" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tw9900_of_match); + +static struct i2c_driver tw9900_i2c_driver = { + .driver = { + .name = "tw9900", + .pm = &tw9900_pm_ops, + .of_match_table = tw9900_of_match, + }, + .probe = tw9900_probe, + .remove = tw9900_remove, + .id_table = tw9900_id, +}; + +module_i2c_driver(tw9900_i2c_driver); + +MODULE_DESCRIPTION("tw9900 decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 477a64d8f..905af98c7 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -829,8 +829,6 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return tw9910_s_fmt(sd, mf); - sd_state->pads->try_fmt = *mf; - return 0; } diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 178bd06cc..56dbe07a1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -405,9 +405,10 @@ static int queue_setup(struct vb2_queue *vq, { struct video_i2c_data *data = vb2_get_drv_priv(vq); unsigned int size = data->chip->buffer_size; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2; + if (q_num_bufs + *nbuffers < 2) + *nbuffers = 2 - q_num_bufs; if (*nplanes) return sizes[0] < size ? -EINVAL : 0; @@ -794,7 +795,7 @@ static int video_i2c_probe(struct i2c_client *client) queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; queue->drv_priv = data; queue->buf_struct_size = sizeof(struct video_i2c_buffer); - queue->min_buffers_needed = 1; + queue->min_queued_buffers = 1; queue->ops = &video_i2c_video_qops; queue->mem_ops = &vb2_vmalloc_memops; diff --git a/drivers/media/mc/Kconfig b/drivers/media/mc/Kconfig index 375b09612..c82b07d2e 100644 --- a/drivers/media/mc/Kconfig +++ b/drivers/media/mc/Kconfig @@ -11,10 +11,3 @@ config MEDIA_CONTROLLER_DVB Enable the media controller API support for DVB. This is currently experimental. - -config MEDIA_CONTROLLER_REQUEST_API - bool - depends on MEDIA_CONTROLLER - help - This option enables the Request API for the Media controller and V4L2 - interfaces. It is currently needed by a few stateless codec drivers. diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index 8cee956e3..c0dd4ae57 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -372,16 +372,12 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) static long media_device_request_alloc(struct media_device *mdev, void *arg) { -#ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API int *alloc_fd = arg; if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue) return -ENOTTY; return media_request_alloc(mdev, alloc_fd); -#else - return -ENOTTY; -#endif } static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 49a3dd70e..511f013cc 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3113,7 +3113,7 @@ static int vdev_init(struct bttv *btv, struct video_device *vfd, q->gfp_flags = __GFP_DMA32; q->buf_struct_size = sizeof(struct bttv_buffer); q->lock = &btv->lock; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->dev = &btv->c.pci->dev; err = vb2_queue_init(q); if (err) diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index 26bf58d17..77ba08ace 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -1260,7 +1260,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) q->ops = &cobalt_qops; q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &s->lock; q->dev = &cobalt->pci_dev->dev; vdev->queue = q; diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index 597472754..acc6418db 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -104,6 +104,7 @@ static int cx18_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { + unsigned int q_num_bufs = vb2_get_num_buffers(vq); struct cx18_stream *s = vb2_get_drv_priv(vq); struct cx18 *cx = s->cx; unsigned int szimage; @@ -121,8 +122,8 @@ static int cx18_queue_setup(struct vb2_queue *vq, * Let's request at least three buffers: two for the * DMA engine and one for userspace. */ - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (*nplanes != 1 || sizes[0] < szimage) @@ -286,7 +287,7 @@ static int cx18_stream_init(struct cx18 *cx, int type) s->vidq.ops = &cx18_vb2_qops; s->vidq.mem_ops = &vb2_vmalloc_memops; s->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - s->vidq.min_buffers_needed = 2; + s->vidq.min_queued_buffers = 2; s->vidq.gfp_flags = GFP_DMA32; s->vidq.dev = &cx->pci_dev->dev; s->vidq.lock = &cx->serialize_lock; diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 434677bd4..fdb96f80c 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1525,7 +1525,7 @@ int cx23885_417_register(struct cx23885_dev *dev) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx23885_buffer); q->ops = &cx23885_qops; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 7551ca4a3..3d01cdc4c 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2667,7 +2667,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = port; q->buf_struct_size = sizeof(struct cx23885_buffer); q->ops = &dvb_qops; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 9af2c5596..42fdcf992 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1321,7 +1321,7 @@ int cx23885_video_register(struct cx23885_dev *dev) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx23885_buffer); q->ops = &cx23885_video_qops; @@ -1338,7 +1338,7 @@ int cx23885_video_register(struct cx23885_dev *dev) q->type = V4L2_BUF_TYPE_VBI_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx23885_buffer); q->ops = &cx23885_vbi_qops; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 1b80c990c..0bee4b728 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -730,7 +730,7 @@ int cx25821_video_register(struct cx25821_dev *dev) q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->io_modes |= is_output ? VB2_WRITE : VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = chan; q->buf_struct_size = sizeof(struct cx25821_buffer); q->ops = &cx25821_video_qops; diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index c1b41a928..d55df8fdb 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1195,7 +1195,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx88_buffer); q->ops = &blackbird_qops; diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 2087f2491..b33b3a5e3 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1776,7 +1776,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx88_buffer); q->ops = &dvb_qops; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index c0ef03ed7..cefb6b25e 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1411,7 +1411,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx88_buffer); q->ops = &cx8800_video_qops; @@ -1428,7 +1428,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, q->type = V4L2_BUF_TYPE_VBI_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->gfp_flags = GFP_DMA32; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->drv_priv = dev; q->buf_struct_size = sizeof(struct cx88_buffer); q->ops = &cx8800_vbi_qops; diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 548156b19..dff853e73 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -128,8 +128,6 @@ dt3155_queue_setup(struct vb2_queue *vq, struct dt3155_priv *pd = vb2_get_drv_priv(vq); unsigned size = pd->width * pd->height; - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; *num_planes = 1; @@ -519,7 +517,7 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) pd->vidq.ops = &q_ops; pd->vidq.mem_ops = &vb2_dma_contig_memops; pd->vidq.drv_priv = pd; - pd->vidq.min_buffers_needed = 2; + pd->vidq.min_queued_buffers = 2; pd->vidq.gfp_flags = GFP_DMA32; pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */ pd->vidq.dev = &pdev->dev; diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index e38198e25..f980e3125 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -53,7 +53,7 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { /* Omnivision ov8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Omnivision ov2740 */ - IPU_SENSOR_CONFIG("INT3474", 1, 360000000), + IPU_SENSOR_CONFIG("INT3474", 1, 180000000), /* Hynix hi556 */ IPU_SENSOR_CONFIG("INT3537", 1, 437000000), /* Omnivision ov13b10 */ diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 5dd69a251..ed08bf417 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1205,23 +1205,16 @@ static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) }; /* Initialize try_fmt */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SINK); *format = fmt_default; /* same as sink */ - format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SOURCE); + format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SOURCE); *format = fmt_default; return 0; } -/* - * cio2_subdev_get_fmt - Handle get format by pads subdev method - * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad config - * @fmt: pointer to v4l2 subdev format structure - * return -EINVAL or zero on success - */ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1231,8 +1224,8 @@ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, mutex_lock(&q->subdev_lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, - fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); else fmt->format = q->subdev_fmt; @@ -1241,13 +1234,6 @@ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, return 0; } -/* - * cio2_subdev_set_fmt - Handle set format by pads subdev method - * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad config - * @fmt: pointer to v4l2 subdev format structure - * return -EINVAL or zero on success - */ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1265,7 +1251,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, return cio2_subdev_get_fmt(sd, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mbus = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mbus = v4l2_subdev_state_get_format(sd_state, fmt->pad); else mbus = &q->subdev_fmt; @@ -1603,7 +1589,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) vbq->mem_ops = &vb2_dma_sg_memops; vbq->buf_struct_size = sizeof(struct cio2_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = 1; + vbq->min_queued_buffers = 1; vbq->drv_priv = cio2; vbq->lock = &q->lock; r = vb2_queue_init(vbq); diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index e111fd6ff..3c74d06a2 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -338,7 +338,7 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &csi->format_mbus[pad]; default: @@ -346,8 +346,8 @@ mei_csi_get_pad_format(struct v4l2_subdev *sd, } } -static int mei_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mei_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mbusformat; struct mei_csi *csi = sd_to_csi(sd); @@ -356,7 +356,7 @@ static int mei_csi_init_cfg(struct v4l2_subdev *sd, mutex_lock(&csi->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = mei_csi_format_mbus_default; } @@ -554,7 +554,6 @@ static const struct v4l2_subdev_video_ops mei_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = { - .init_cfg = mei_csi_init_cfg, .get_fmt = mei_csi_get_fmt, .set_fmt = mei_csi_set_fmt, }; @@ -564,6 +563,10 @@ static const struct v4l2_subdev_ops mei_csi_subdev_ops = { .pad = &mei_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops mei_csi_internal_ops = { + .init_state = mei_csi_init_state, +}; + static const struct media_entity_operations mei_csi_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -645,47 +648,66 @@ static int mei_csi_parse_firmware(struct mei_csi *csi) }; struct device *dev = &csi->cldev->dev; struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode; - struct fwnode_handle *ep; + struct fwnode_handle *sink_ep, *source_ep; int ret; - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); - if (!ep) { - dev_err(dev, "not connected to subdevice\n"); + sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!sink_ep) { + dev_err(dev, "can't obtain sink endpoint\n"); return -EINVAL; } - ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + v4l2_async_subdev_nf_init(&csi->notifier, &csi->subdev); + csi->notifier.ops = &mei_csi_notify_ops; + + ret = v4l2_fwnode_endpoint_parse(sink_ep, &v4l2_ep); if (ret) { - dev_err(dev, "could not parse v4l2 endpoint\n"); - fwnode_handle_put(ep); - return -EINVAL; + dev_err(dev, "could not parse v4l2 sink endpoint\n"); + goto out_nf_cleanup; } - fwnode = fwnode_graph_get_remote_endpoint(ep); - fwnode_handle_put(ep); + csi->nr_of_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; - v4l2_async_subdev_nf_init(&csi->notifier, &csi->subdev); - csi->notifier.ops = &mei_csi_notify_ops; + source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0); + if (!source_ep) { + ret = -ENOTCONN; + dev_err(dev, "can't obtain source endpoint\n"); + goto out_nf_cleanup; + } - asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode, - struct v4l2_async_connection); - if (IS_ERR(asd)) { - fwnode_handle_put(fwnode); - return PTR_ERR(asd); + ret = v4l2_fwnode_endpoint_parse(source_ep, &v4l2_ep); + fwnode_handle_put(source_ep); + if (ret) { + dev_err(dev, "could not parse v4l2 source endpoint\n"); + goto out_nf_cleanup; } - ret = v4l2_fwnode_endpoint_alloc_parse(fwnode, &v4l2_ep); - fwnode_handle_put(fwnode); - if (ret) - return ret; - csi->nr_of_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csi->nr_of_lanes != v4l2_ep.bus.mipi_csi2.num_data_lanes) { + ret = -EINVAL; + dev_err(dev, + "the number of lanes does not match (%u vs. %u)\n", + csi->nr_of_lanes, v4l2_ep.bus.mipi_csi2.num_data_lanes); + goto out_nf_cleanup; + } + + asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, sink_ep, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out_nf_cleanup; + } ret = v4l2_async_nf_register(&csi->notifier); if (ret) - v4l2_async_nf_cleanup(&csi->notifier); + goto out_nf_cleanup; + + fwnode_handle_put(sink_ep); - v4l2_fwnode_endpoint_free(&v4l2_ep); + return 0; + +out_nf_cleanup: + v4l2_async_nf_cleanup(&csi->notifier); + fwnode_handle_put(sink_ep); return ret; } @@ -728,6 +750,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, csi->subdev.dev = &cldev->dev; v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops); + csi->subdev.internal_ops = &mei_csi_internal_ops; v4l2_set_subdevdata(&csi->subdev, csi); csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig index 9be52101b..2498f9079 100644 --- a/drivers/media/pci/ivtv/Kconfig +++ b/drivers/media/pci/ivtv/Kconfig @@ -48,9 +48,7 @@ config VIDEO_IVTV_ALSA config VIDEO_FB_IVTV tristate "Conexant cx23415 framebuffer support" depends on VIDEO_IVTV && FB - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT + select FB_IOMEM_HELPERS help This is a framebuffer driver for the Conexant cx23415 MPEG encoder/decoder. diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h index ce3a7ca51..a6ffa99e1 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.h +++ b/drivers/media/pci/ivtv/ivtv-driver.h @@ -619,6 +619,7 @@ struct ivtv { u32 hw_flags; /* hardware description of the board */ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */ + bool sd_video_is_streaming; /* is video already streaming? */ struct v4l2_subdev *sd_audio; /* controlling audio subdev */ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */ resource_size_t base_addr; /* PCI resource base address */ diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index 13d7d55e6..af9e6235c 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -623,10 +623,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) /* Avoid tinny audio problem - ensure audio clocks are going */ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); /* Avoid unpredictable PCI bus hang - disable video clocks */ - v4l2_subdev_call(itv->sd_video, video, s_stream, 0); + if (itv->sd_video_is_streaming) + v4l2_subdev_call(itv->sd_video, video, s_stream, 0); ivtv_msleep_timeout(300, 0); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); v4l2_subdev_call(itv->sd_video, video, s_stream, 1); + itv->sd_video_is_streaming = true; } /* begin_capture */ diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 23c8c094e..410477e3e 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -927,17 +927,17 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) static const struct fb_ops ivtvfb_ops = { .owner = THIS_MODULE, + .fb_read = fb_io_read, .fb_write = ivtvfb_write, .fb_check_var = ivtvfb_check_var, .fb_set_par = ivtvfb_set_par, .fb_setcolreg = ivtvfb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, + __FB_DEFAULT_IOMEM_OPS_DRAW, .fb_cursor = NULL, .fb_ioctl = ivtvfb_ioctl, .fb_pan_display = ivtvfb_pan_display, .fb_blank = ivtvfb_blank, + __FB_DEFAULT_IOMEM_OPS_MMAP, }; /* Restore hardware after firmware restart */ diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index d72b07b87..2cd78c539 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -849,7 +849,7 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) vindev->queue.mem_ops = &vb2_dma_sg_memops; vindev->queue.gfp_flags = GFP_DMA32; vindev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vindev->queue.min_buffers_needed = 2; + vindev->queue.min_queued_buffers = 2; vindev->queue.drv_priv = vindev; vindev->queue.lock = &vindev->lock; vindev->queue.dev = dev; diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 857fc7bbd..241353ee7 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -523,7 +523,7 @@ struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id) voutdev->queue.mem_ops = &vb2_dma_sg_memops; voutdev->queue.gfp_flags = GFP_DMA32; voutdev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - voutdev->queue.min_buffers_needed = 2; + voutdev->queue.min_queued_buffers = 2; voutdev->queue.drv_priv = voutdev; voutdev->queue.lock = &voutdev->lock; voutdev->queue.dev = dev; diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index d85bfbb77..557985ba2 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -293,12 +293,13 @@ static int netup_unidvb_queue_setup(struct vb2_queue *vq, struct device *alloc_devs[]) { struct netup_dma *dma = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); *nplanes = 1; - if (vq->num_buffers + *nbuffers < VIDEO_MAX_FRAME) - *nbuffers = VIDEO_MAX_FRAME - vq->num_buffers; + if (q_num_bufs + *nbuffers < VIDEO_MAX_FRAME) + *nbuffers = VIDEO_MAX_FRAME - q_num_bufs; sizes[0] = PAGE_ALIGN(NETUP_DMA_PACKETS_COUNT * 188); dev_dbg(&dma->ndev->pci_dev->dev, "%s() nbuffers=%d sizes[0]=%d\n", __func__, *nbuffers, sizes[0]); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c index bd38ce444..46676f2c8 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -289,7 +289,7 @@ static const struct i2c_algorithm netup_i2c_algorithm = { static const struct i2c_adapter netup_i2c_adapter = { .owner = THIS_MODULE, .name = NETUP_UNIDVB_NAME, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &netup_i2c_algorithm, }; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index e4cf9d63e..364ce9e57 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -757,7 +757,7 @@ static const struct video_device video_dev_template = { /** * vip_irq - interrupt routine * @irq: Number of interrupt ( not used, correct number is assumed ) - * @vip: local data structure containing all information + * @data: local data structure containing all information * * check for both frame interrupts set ( top and bottom ). * check FIFO overflow, but limit number of log messages after open. @@ -767,8 +767,9 @@ static const struct video_device video_dev_template = { * * IRQ_HANDLED, interrupt done. */ -static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) +static irqreturn_t vip_irq(int irq, void *data) { + struct sta2x11_vip *vip = data; unsigned int status; status = reg_read(vip, DVP_ITS); @@ -1053,9 +1054,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, spin_lock_init(&vip->slock); - ret = request_irq(pdev->irq, - (irq_handler_t) vip_irq, - IRQF_SHARED, KBUILD_MODNAME, vip); + ret = request_irq(pdev->irq, vip_irq, IRQF_SHARED, KBUILD_MODNAME, vip); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); ret = -ENODEV; diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 197ed8978..8b1aae4b6 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -1114,7 +1114,7 @@ static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) input->vidq.gfp_flags = 0; input->vidq.buf_struct_size = sizeof(struct tw5864_buf); input->vidq.lock = &input->lock; - input->vidq.min_buffers_needed = 2; + input->vidq.min_queued_buffers = 2; input->vidq.dev = &input->root->pci->dev; ret = vb2_queue_init(&input->vidq); if (ret) diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 773a18702..cdf5d733b 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -360,13 +360,14 @@ static int tw68_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct tw68_dev *dev = vb2_get_drv_priv(q); - unsigned tot_bufs = q->num_buffers + *num_buffers; + unsigned int q_num_bufs = vb2_get_num_buffers(q); + unsigned int tot_bufs = q_num_bufs + *num_buffers; unsigned size = (dev->fmt->depth * dev->width * dev->height) >> 3; if (tot_bufs < 2) tot_bufs = 2; tot_bufs = tw68_buffer_count(size, tot_bufs); - *num_buffers = tot_bufs - q->num_buffers; + *num_buffers = tot_bufs - q_num_bufs; /* * We allow create_bufs, but only if the sizeimage is >= as the * current sizeimage. The tw68_buffer_count calculation becomes quite @@ -951,7 +952,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr) dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM; dev->vidq.buf_struct_size = sizeof(struct tw68_buf); dev->vidq.lock = &dev->lock; - dev->vidq.min_buffers_needed = 2; + dev->vidq.min_queued_buffers = 2; dev->vidq.dev = &dev->pci->dev; ret = vb2_queue_init(&dev->vidq); if (ret) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 3ebf7a2c9..63be95fce 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -423,6 +423,7 @@ static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); unsigned int szimage = (vc->width * vc->height * vc->format->depth) >> 3; @@ -430,8 +431,8 @@ static int tw686x_queue_setup(struct vb2_queue *vq, * Let's request at least three buffers: two for the * DMA engine and one for userspace. */ - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (*nplanes != 1 || sizes[0] < szimage) @@ -1221,7 +1222,7 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->vidq.ops = &tw686x_video_qops; vc->vidq.mem_ops = dev->dma_ops->mem_ops; vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vc->vidq.min_buffers_needed = 2; + vc->vidq.min_queued_buffers = 2; vc->vidq.lock = &vc->vb_mutex; vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ? GFP_DMA32 : 0; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index fa672cc8b..5c05e64c7 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -749,8 +749,8 @@ static int zr_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsi zr->buf_in_reserve = 0; - if (*nbuffers < vq->min_buffers_needed) - *nbuffers = vq->min_buffers_needed; + if (*nbuffers < vq->min_queued_buffers) + *nbuffers = vq->min_queued_buffers; if (*nplanes) { if (sizes[0] < size) @@ -971,7 +971,7 @@ int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) vq->mem_ops = &vb2_dma_contig_memops; vq->gfp_flags = GFP_DMA32; vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vq->min_buffers_needed = 9; + vq->min_queued_buffers = 9; vq->lock = &zr->lock; err = vb2_queue_init(vq); if (err) diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index 982c2c777..940e5bda5 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -87,7 +87,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", vb2_is_streaming(vq), - vq->num_buffers, + vb2_get_num_buffers(vq), inst->out_format.pixfmt, inst->out_format.pixfmt >> 8, inst->out_format.pixfmt >> 16, @@ -111,7 +111,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", vb2_is_streaming(vq), - vq->num_buffers, + vb2_get_num_buffers(vq), inst->cap_format.pixfmt, inst->cap_format.pixfmt >> 8, inst->cap_format.pixfmt >> 16, @@ -139,12 +139,19 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) return 0; vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); - for (i = 0; i < vq->num_buffers; i++) { - struct vb2_buffer *vb = vq->bufs[i]; - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + for (i = 0; i < vb2_get_num_buffers(vq); i++) { + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + + vb = vb2_get_buffer(vq, i); + if (!vb) + continue; if (vb->state == VB2_BUF_STATE_DEQUEUED) continue; + + vbuf = to_vb2_v4l2_buffer(vb); + num = scnprintf(str, sizeof(str), "output [%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], @@ -154,12 +161,19 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) } vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); - for (i = 0; i < vq->num_buffers; i++) { - struct vb2_buffer *vb = vq->bufs[i]; - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + for (i = 0; i < vb2_get_num_buffers(vq); i++) { + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + + vb = vb2_get_buffer(vq, i); + if (!vb) + continue; if (vb->state == VB2_BUF_STATE_DEQUEUED) continue; + + vbuf = to_vb2_v4l2_buffer(vb); + num = scnprintf(str, sizeof(str), "capture[%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index d7e0de49b..c88738e8f 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -439,7 +439,7 @@ int vpu_get_num_buffers(struct vpu_inst *inst, u32 type) else q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); - return q->num_buffers; + return vb2_get_num_buffers(q); } static void vpu_m2m_device_run(void *priv) @@ -587,7 +587,7 @@ static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count) fmt->sizeimage[0], fmt->bytesperline[0], fmt->sizeimage[1], fmt->bytesperline[1], fmt->sizeimage[2], fmt->bytesperline[2], - q->num_buffers); + vb2_get_num_buffers(q)); vb2_clear_last_buffer_dequeued(q); ret = call_vop(inst, start, q->type); if (ret) @@ -649,7 +649,7 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->dev = inst->vpu->dev; src_vq->lock = &inst->lock; ret = vb2_queue_init(src_vq); @@ -666,7 +666,7 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - dst_vq->min_buffers_needed = 1; + dst_vq->min_queued_buffers = 1; dst_vq->dev = inst->vpu->dev; dst_vq->lock = &inst->lock; ret = vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index d08aa7f73..fc6050e3b 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2034,7 +2034,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video) vbq->drv_priv = video; vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; + vbq->min_queued_buffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; rc = vb2_queue_init(vbq); if (rc) { diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 4046212d4..f8450a8cc 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -559,11 +559,13 @@ static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, struct v4l2_subdev_state *sd_state) { - int ret; + struct v4l2_rect *try_crop = + v4l2_subdev_state_get_crop(sd_state, 0); struct v4l2_subdev_frame_size_enum fse = { .code = isi_fmt->mbus_code, .which = V4L2_SUBDEV_FORMAT_TRY, }; + int ret; ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, sd_state, &fse); @@ -572,11 +574,11 @@ static void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, * just use the maximum ISI can receive. */ if (ret) { - sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; - sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; + try_crop->width = MAX_SUPPORT_WIDTH; + try_crop->height = MAX_SUPPORT_HEIGHT; } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; + try_crop->width = fse.max_width; + try_crop->height = fse.max_height; } } @@ -587,6 +589,7 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, struct v4l2_pix_format *pixfmt = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg = {}; struct v4l2_subdev_state pad_state = { + .sd = isi->entity.subdev, .pads = &pad_cfg, }; struct v4l2_subdev_format format = { @@ -1242,7 +1245,7 @@ static int atmel_isi_probe(struct platform_device *pdev) q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->dev = &pdev->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index c2a979397..0ea5fa956 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -406,20 +406,20 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_get_pad_format(subdev, state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad); *fmt = format->format; /* Propagate to source formats */ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_get_pad_format(subdev, state, i); + fmt = v4l2_subdev_state_get_format(state, i); *fmt = format->format; } return 0; } -static int csi2rx_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int csi2rx_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .pad = CSI2RX_PAD_SINK, @@ -441,7 +441,6 @@ static int csi2rx_init_cfg(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2rx_set_fmt, - .init_cfg = csi2rx_init_cfg, }; static const struct v4l2_subdev_video_ops csi2rx_video_ops = { @@ -453,6 +452,10 @@ static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .pad = &csi2rx_pad_ops, }; +static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { + .init_state = csi2rx_init_state, +}; + static const struct media_entity_operations csi2rx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -663,6 +666,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.owner = THIS_MODULE; csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); + csi2rx->subdev.internal_ops = &csi2rx_internal_ops; v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); snprintf(csi2rx->subdev.name, sizeof(csi2rx->subdev.name), "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index c115742f3..3d98f91f1 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -176,8 +176,7 @@ __csi2tx_get_pad_format(struct v4l2_subdev *subdev, struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + return v4l2_subdev_state_get_format(sd_state, fmt->pad); return &csi2tx->pad_fmts[fmt->pad]; } diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig index 57f8f8a22..ad350eb6b 100644 --- a/drivers/media/platform/chips-media/Kconfig +++ b/drivers/media/platform/chips-media/Kconfig @@ -2,19 +2,5 @@ comment "Chips&Media media platform drivers" -config VIDEO_CODA - tristate "Chips&Media Coda multi-standard codec IP" - depends on V4L_MEM2MEM_DRIVERS - depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST) - select SRAM - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_JPEG_HELPER - select V4L2_MEM2MEM_DEV - select GENERIC_ALLOCATOR - help - Coda is a range of video codec IPs that supports - H.264, MPEG-4, and other video formats. - -config VIDEO_IMX_VDOA - def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST +source "drivers/media/platform/chips-media/coda/Kconfig" +source "drivers/media/platform/chips-media/wave5/Kconfig" diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile index bbb16425a..6b5d99de8 100644 --- a/drivers/media/platform/chips-media/Makefile +++ b/drivers/media/platform/chips-media/Makefile @@ -1,6 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o - -obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o -obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o +obj-y += coda/ +obj-y += wave5/ diff --git a/drivers/media/platform/chips-media/coda-bit.c b/drivers/media/platform/chips-media/coda-bit.c deleted file mode 100644 index ed47d5bd8..000000000 --- a/drivers/media/platform/chips-media/coda-bit.c +++ /dev/null @@ -1,2666 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - BIT processor functions - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "imx-vdoa.h" -#define CREATE_TRACE_POINTS -#include "trace.h" - -#define CODA_PARA_BUF_SIZE (10 * 1024) -#define CODA7_PS_BUF_SIZE 0x28000 -#define CODA9_PS_SAVE_SIZE (512 * 1024) - -#define CODA_DEFAULT_GAMMA 4096 -#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ - -static void coda_free_bitstream_buffer(struct coda_ctx *ctx); - -static inline int coda_is_initialized(struct coda_dev *dev) -{ - return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; -} - -static inline unsigned long coda_isbusy(struct coda_dev *dev) -{ - return coda_read(dev, CODA_REG_BIT_BUSY); -} - -static int coda_wait_timeout(struct coda_dev *dev) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while (coda_isbusy(dev)) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - } - return 0; -} - -static void coda_command_async(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541 || - dev->devtype->product == CODA_960) { - /* Restore context related registers to CODA */ - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - coda_write(dev, ctx->frame_mem_ctrl, - CODA_REG_BIT_FRAME_MEM_CTRL); - coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); - } - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - } - - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - - coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); - - trace_coda_bit_run(ctx, cmd); - - coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); -} - -static int coda_command_sync(struct coda_ctx *ctx, int cmd) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - coda_command_async(ctx, cmd); - ret = coda_wait_timeout(dev); - trace_coda_bit_done(ctx); - - return ret; -} - -int coda_hw_reset(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - unsigned long timeout; - unsigned int idx; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - if (!dev->rstc) - return -ENOENT; - - idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); - - if (dev->devtype->product == CODA_960) { - timeout = jiffies + msecs_to_jiffies(100); - coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); - while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - } - - ret = reset_control_reset(dev->rstc); - if (ret < 0) - return ret; - - if (dev->devtype->product == CODA_960) - coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - ret = coda_wait_timeout(dev); - coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); - - return ret; -} - -static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr; - - rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - kfifo->out = (kfifo->in & ~kfifo->mask) | - (rd_ptr - ctx->bitstream.paddr); - if (kfifo->out > kfifo->in) - kfifo->out -= kfifo->mask + 1; -} - -static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 rd_ptr, wr_ptr; - - rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); - coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) -{ - struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr; - - wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); - coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); -} - -static int coda_h264_bitstream_pad(struct coda_ctx *ctx, u32 size) -{ - unsigned char *buf; - u32 n; - - if (size < 6) - size = 6; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - coda_h264_filler_nal(size, buf); - n = kfifo_in(&ctx->bitstream_fifo, buf, size); - kfree(buf); - - return (n < size) ? -ENOSPC : 0; -} - -int coda_bitstream_flush(struct coda_ctx *ctx) -{ - int ret; - - if (ctx->inst_type != CODA_INST_DECODER || !ctx->use_bit) - return 0; - - ret = coda_command_sync(ctx, CODA_COMMAND_DEC_BUF_FLUSH); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to flush bitstream\n"); - return ret; - } - - kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, - ctx->bitstream.size); - coda_kfifo_sync_to_device_full(ctx); - - return 0; -} - -static int coda_bitstream_queue(struct coda_ctx *ctx, const u8 *buf, u32 size) -{ - u32 n = kfifo_in(&ctx->bitstream_fifo, buf, size); - - return (n < size) ? -ENOSPC : 0; -} - -static u32 coda_buffer_parse_headers(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *src_buf, - u32 payload) -{ - u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - u32 size = 0; - - switch (ctx->codec->src_fourcc) { - case V4L2_PIX_FMT_MPEG2: - size = coda_mpeg2_parse_headers(ctx, vaddr, payload); - break; - case V4L2_PIX_FMT_MPEG4: - size = coda_mpeg4_parse_headers(ctx, vaddr, payload); - break; - default: - break; - } - - return size; -} - -static bool coda_bitstream_try_queue(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *src_buf) -{ - unsigned long payload = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - int ret; - int i; - - if (coda_get_bitstream_payload(ctx) + payload + 512 >= - ctx->bitstream.size) - return false; - - if (!vaddr) { - v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); - return true; - } - - if (ctx->qsequence == 0 && payload < 512) { - /* - * Add padding after the first buffer, if it is too small to be - * fetched by the CODA, by repeating the headers. Without - * repeated headers, or the first frame already queued, decoder - * sequence initialization fails with error code 0x2000 on i.MX6 - * or error code 0x1 on i.MX51. - */ - u32 header_size = coda_buffer_parse_headers(ctx, src_buf, - payload); - - if (header_size) { - coda_dbg(1, ctx, "pad with %u-byte header\n", - header_size); - for (i = payload; i < 512; i += header_size) { - ret = coda_bitstream_queue(ctx, vaddr, - header_size); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, - "bitstream buffer overflow\n"); - return false; - } - if (ctx->dev->devtype->product == CODA_960) - break; - } - } else { - coda_dbg(1, ctx, - "could not parse header, sequence initialization might fail\n"); - } - - /* Add padding before the first buffer, if it is too small */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_h264_bitstream_pad(ctx, 512 - payload); - } - - ret = coda_bitstream_queue(ctx, vaddr, payload); - if (ret < 0) { - v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); - return false; - } - - src_buf->sequence = ctx->qsequence++; - - /* Sync read pointer to device */ - if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) - coda_kfifo_sync_to_device_write(ctx); - - /* Set the stream-end flag after the last buffer is queued */ - if (src_buf->flags & V4L2_BUF_FLAG_LAST) - coda_bit_stream_end_flag(ctx); - ctx->hold = false; - - return true; -} - -void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) -{ - struct vb2_v4l2_buffer *src_buf; - struct coda_buffer_meta *meta; - u32 start; - - lockdep_assert_held(&ctx->bitstream_mutex); - - if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) - return; - - while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { - /* - * Only queue two JPEGs into the bitstream buffer to keep - * latency low. We need at least one complete buffer and the - * header of another buffer (for prescan) in the bitstream. - */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - ctx->num_metas > 1) - break; - - if (ctx->num_internal_frames && - ctx->num_metas >= ctx->num_internal_frames) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - - /* - * If we managed to fill in at least a full reorder - * window of buffers (num_internal_frames is a - * conservative estimate for this) and the bitstream - * prefetcher has at least 2 256 bytes periods beyond - * the first buffer to fetch, we can safely stop queuing - * in order to limit the decoder drain latency. - */ - if (coda_bitstream_can_fetch_past(ctx, meta->end)) - break; - } - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - - /* Drop frames that do not start/end with a SOI/EOI markers */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - !coda_jpeg_check_buffer(ctx, &src_buf->vb2_buf)) { - v4l2_err(&ctx->dev->v4l2_dev, - "dropping invalid JPEG frame %d\n", - ctx->qsequence); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - if (buffer_list) { - struct v4l2_m2m_buffer *m2m_buf; - - m2m_buf = container_of(src_buf, - struct v4l2_m2m_buffer, - vb); - list_add_tail(&m2m_buf->list, buffer_list); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } - continue; - } - - /* Dump empty buffers */ - if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) { - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - continue; - } - - /* Buffer start position */ - start = ctx->bitstream_fifo.kfifo.in; - - if (coda_bitstream_try_queue(ctx, src_buf)) { - /* - * Source buffer is queued in the bitstream ringbuffer; - * queue the timestamp and mark source buffer as done - */ - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - - meta = kmalloc(sizeof(*meta), GFP_KERNEL); - if (meta) { - meta->sequence = src_buf->sequence; - meta->timecode = src_buf->timecode; - meta->timestamp = src_buf->vb2_buf.timestamp; - meta->start = start; - meta->end = ctx->bitstream_fifo.kfifo.in; - meta->last = src_buf->flags & V4L2_BUF_FLAG_LAST; - if (meta->last) - coda_dbg(1, ctx, "marking last meta"); - spin_lock(&ctx->buffer_meta_lock); - list_add_tail(&meta->list, - &ctx->buffer_meta_list); - ctx->num_metas++; - spin_unlock(&ctx->buffer_meta_lock); - - trace_coda_bit_queue(ctx, src_buf, meta); - } - - if (buffer_list) { - struct v4l2_m2m_buffer *m2m_buf; - - m2m_buf = container_of(src_buf, - struct v4l2_m2m_buffer, - vb); - list_add_tail(&m2m_buf->list, buffer_list); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } - } else { - break; - } - } -} - -void coda_bit_stream_end_flag(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - /* If this context is currently running, update the hardware flag */ - if ((dev->devtype->product == CODA_960) && - coda_isbusy(dev) && - (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - } -} - -static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) -{ - struct coda_dev *dev = ctx->dev; - u32 *p = ctx->parabuf.vaddr; - - if (dev->devtype->product == CODA_DX6) - p[index] = value; - else - p[index ^ 1] = value; -} - -static inline int coda_alloc_context_buf(struct coda_ctx *ctx, - struct coda_aux_buf *buf, size_t size, - const char *name) -{ - return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); -} - - -static void coda_free_framebuffers(struct coda_ctx *ctx) -{ - int i; - - for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) - coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i].buf); -} - -static int coda_alloc_framebuffers(struct coda_ctx *ctx, - struct coda_q_data *q_data, u32 fourcc) -{ - struct coda_dev *dev = ctx->dev; - unsigned int ysize, ycbcr_size; - int ret; - int i; - - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) - ysize = round_up(q_data->rect.width, 16) * - round_up(q_data->rect.height, 16); - else - ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; - - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ycbcr_size = round_up(ysize, 4096) + ysize / 2; - else - ycbcr_size = ysize + ysize / 2; - - /* Allocate frame buffers */ - for (i = 0; i < ctx->num_internal_frames; i++) { - size_t size = ycbcr_size; - char *name; - - /* Add space for mvcol buffers */ - if (dev->devtype->product != CODA_DX6 && - (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0))) - size += ysize / 4; - name = kasprintf(GFP_KERNEL, "fb%d", i); - if (!name) { - coda_free_framebuffers(ctx); - return -ENOMEM; - } - ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i].buf, - size, name); - kfree(name); - if (ret < 0) { - coda_free_framebuffers(ctx); - return ret; - } - } - - /* Register frame buffers in the parameter buffer */ - for (i = 0; i < ctx->num_internal_frames; i++) { - u32 y, cb, cr, mvcol; - - /* Start addresses of Y, Cb, Cr planes */ - y = ctx->internal_frames[i].buf.paddr; - cb = y + ysize; - cr = y + ysize + ysize/4; - mvcol = y + ysize + ysize/4 + ysize/4; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { - cb = round_up(cb, 4096); - mvcol = cb + ysize/2; - cr = 0; - /* Packed 20-bit MSB of base addresses */ - /* YYYYYCCC, CCyyyyyc, cccc.... */ - y = (y & 0xfffff000) | cb >> 20; - cb = (cb & 0x000ff000) << 12; - } - coda_parabuf_write(ctx, i * 3 + 0, y); - coda_parabuf_write(ctx, i * 3 + 1, cb); - coda_parabuf_write(ctx, i * 3 + 2, cr); - - if (dev->devtype->product == CODA_DX6) - continue; - - /* mvcol buffer for h.264 and mpeg4 */ - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_parabuf_write(ctx, 96 + i, mvcol); - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0) - coda_parabuf_write(ctx, 97, mvcol); - } - - return 0; -} - -static void coda_free_context_buffers(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - - coda_free_aux_buf(dev, &ctx->slicebuf); - coda_free_aux_buf(dev, &ctx->psbuf); - if (dev->devtype->product != CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - coda_free_aux_buf(dev, &ctx->parabuf); -} - -static int coda_alloc_context_buffers(struct coda_ctx *ctx, - struct coda_q_data *q_data) -{ - struct coda_dev *dev = ctx->dev; - size_t size; - int ret; - - if (!ctx->parabuf.vaddr) { - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, - CODA_PARA_BUF_SIZE, "parabuf"); - if (ret < 0) - return ret; - } - - if (dev->devtype->product == CODA_DX6) - return 0; - - if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { - /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->rect.width, 16) * - DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; - ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, - "slicebuf"); - if (ret < 0) - goto err; - } - - if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541)) { - ret = coda_alloc_context_buf(ctx, &ctx->psbuf, - CODA7_PS_BUF_SIZE, "psbuf"); - if (ret < 0) - goto err; - } - - if (!ctx->workbuf.vaddr) { - size = dev->devtype->workbuf_size; - if (dev->devtype->product == CODA_960 && - q_data->fourcc == V4L2_PIX_FMT_H264) - size += CODA9_PS_SAVE_SIZE; - ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, - "workbuf"); - if (ret < 0) - goto err; - } - - return 0; - -err: - coda_free_context_buffers(ctx); - return ret; -} - -static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - int header_code, u8 *header, int *size) -{ - struct vb2_buffer *vb = &buf->vb2_buf; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src; - struct v4l2_rect *r; - size_t bufsize; - int ret; - int i; - - if (dev->devtype->product == CODA_960) - memset(vb2_plane_vaddr(vb, 0), 0, 64); - - coda_write(dev, vb2_dma_contig_plane_dma_addr(vb, 0), - CODA_CMD_ENC_HEADER_BB_START); - bufsize = vb2_plane_size(vb, 0); - if (dev->devtype->product == CODA_960) - bufsize /= 1024; - coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); - if (dev->devtype->product == CODA_960 && - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && - header_code == CODA_HEADER_H264_SPS) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - r = &q_data_src->rect; - - if (r->width % 16 || r->height % 16) { - u32 crop_right = round_up(r->width, 16) - r->width; - u32 crop_bottom = round_up(r->height, 16) - r->height; - - coda_write(dev, crop_right, - CODA9_CMD_ENC_HEADER_FRAME_CROP_H); - coda_write(dev, crop_bottom, - CODA9_CMD_ENC_HEADER_FRAME_CROP_V); - header_code |= CODA9_HEADER_FRAME_CROP; - } - } - coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); - ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return ret; - } - - if (dev->devtype->product == CODA_960) { - for (i = 63; i > 0; i--) - if (((char *)vb2_plane_vaddr(vb, 0))[i] != 0) - break; - *size = i + 1; - } else { - *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - } - memcpy(header, vb2_plane_vaddr(vb, 0), *size); - - return 0; -} - -static u32 coda_slice_mode(struct coda_ctx *ctx) -{ - int size, unit; - - switch (ctx->params.slice_mode) { - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: - default: - return 0; - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: - size = ctx->params.slice_max_mb; - unit = 1; - break; - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: - size = ctx->params.slice_max_bits; - unit = 0; - break; - } - - return ((size & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET) | - ((unit & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET) | - ((1 & CODA_SLICING_MODE_MASK) << CODA_SLICING_MODE_OFFSET); -} - -static int coda_enc_param_change(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 change_enable = 0; - u32 success; - int ret; - - if (ctx->params.gop_size_changed) { - change_enable |= CODA_PARAM_CHANGE_RC_GOP; - coda_write(dev, ctx->params.gop_size, - CODA_CMD_ENC_PARAM_RC_GOP); - ctx->gopcounter = ctx->params.gop_size - 1; - ctx->params.gop_size_changed = false; - } - if (ctx->params.h264_intra_qp_changed) { - coda_dbg(1, ctx, "parameter change: intra Qp %u\n", - ctx->params.h264_intra_qp); - - if (ctx->params.bitrate) { - change_enable |= CODA_PARAM_CHANGE_RC_INTRA_QP; - coda_write(dev, ctx->params.h264_intra_qp, - CODA_CMD_ENC_PARAM_RC_INTRA_QP); - } - ctx->params.h264_intra_qp_changed = false; - } - if (ctx->params.bitrate_changed) { - coda_dbg(1, ctx, "parameter change: bitrate %u kbit/s\n", - ctx->params.bitrate); - change_enable |= CODA_PARAM_CHANGE_RC_BITRATE; - coda_write(dev, ctx->params.bitrate, - CODA_CMD_ENC_PARAM_RC_BITRATE); - ctx->params.bitrate_changed = false; - } - if (ctx->params.framerate_changed) { - coda_dbg(1, ctx, "parameter change: frame rate %u/%u Hz\n", - ctx->params.framerate & 0xffff, - (ctx->params.framerate >> 16) + 1); - change_enable |= CODA_PARAM_CHANGE_RC_FRAME_RATE; - coda_write(dev, ctx->params.framerate, - CODA_CMD_ENC_PARAM_RC_FRAME_RATE); - ctx->params.framerate_changed = false; - } - if (ctx->params.intra_refresh_changed) { - coda_dbg(1, ctx, "parameter change: intra refresh MBs %u\n", - ctx->params.intra_refresh); - change_enable |= CODA_PARAM_CHANGE_INTRA_MB_NUM; - coda_write(dev, ctx->params.intra_refresh, - CODA_CMD_ENC_PARAM_INTRA_MB_NUM); - ctx->params.intra_refresh_changed = false; - } - if (ctx->params.slice_mode_changed) { - change_enable |= CODA_PARAM_CHANGE_SLICE_MODE; - coda_write(dev, coda_slice_mode(ctx), - CODA_CMD_ENC_PARAM_SLICE_MODE); - ctx->params.slice_mode_changed = false; - } - - if (!change_enable) - return 0; - - coda_write(dev, change_enable, CODA_CMD_ENC_PARAM_CHANGE_ENABLE); - - ret = coda_command_sync(ctx, CODA_COMMAND_RC_CHANGE_PARAMETER); - if (ret < 0) - return ret; - - success = coda_read(dev, CODA_RET_ENC_PARAM_CHANGE_SUCCESS); - if (success != 1) - coda_dbg(1, ctx, "parameter change failed: %u\n", success); - - return 0; -} - -static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) -{ - phys_addr_t ret; - - size = round_up(size, 1024); - if (size > iram->remaining) - return 0; - iram->remaining -= size; - - ret = iram->next_paddr; - iram->next_paddr += size; - - return ret; -} - -static void coda_setup_iram(struct coda_ctx *ctx) -{ - struct coda_iram_info *iram_info = &ctx->iram_info; - struct coda_dev *dev = ctx->dev; - int w64, w128; - int mb_width; - int dbk_bits; - int bit_bits; - int ip_bits; - int me_bits; - - memset(iram_info, 0, sizeof(*iram_info)); - iram_info->next_paddr = dev->iram.paddr; - iram_info->remaining = dev->iram.size; - - if (!dev->iram.vaddr) - return; - - switch (dev->devtype->product) { - case CODA_HX4: - dbk_bits = CODA7_USE_HOST_DBK_ENABLE; - bit_bits = CODA7_USE_HOST_BIT_ENABLE; - ip_bits = CODA7_USE_HOST_IP_ENABLE; - me_bits = CODA7_USE_HOST_ME_ENABLE; - break; - case CODA_7541: - dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; - bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE; - break; - case CODA_960: - dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; - bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; - ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; - me_bits = 0; - break; - default: /* CODA_DX6 */ - return; - } - - if (ctx->inst_type == CODA_INST_ENCODER) { - struct coda_q_data *q_data_src; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); - w128 = mb_width * 128; - w64 = mb_width * 64; - - /* Prioritize in case IRAM is too small for everything */ - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - iram_info->search_ram_size = round_up(mb_width * 16 * - 36 + 2048, 1024); - iram_info->search_ram_paddr = coda_iram_alloc(iram_info, - iram_info->search_ram_size); - if (!iram_info->search_ram_paddr) { - pr_err("IRAM is smaller than the search ram size\n"); - goto out; - } - iram_info->axi_sram_use |= me_bits; - } - - /* Only H.264BP and H.263P3 are considered */ - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64); - if (!iram_info->buf_dbk_y_use || !iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP disabled for encoder */ - } else if (ctx->inst_type == CODA_INST_DECODER) { - struct coda_q_data *q_data_dst; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - mb_width = DIV_ROUND_UP(q_data_dst->width, 16); - w128 = mb_width * 128; - - iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128); - iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_dbk_y_use || !iram_info->buf_dbk_c_use) - goto out; - iram_info->axi_sram_use |= dbk_bits; - - iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_bit_use) - goto out; - iram_info->axi_sram_use |= bit_bits; - - iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); - if (!iram_info->buf_ip_ac_dc_use) - goto out; - iram_info->axi_sram_use |= ip_bits; - - /* OVL and BTP unused as there is no VC1 support yet */ - } - -out: - if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) - coda_dbg(1, ctx, "IRAM smaller than needed\n"); - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - /* TODO - Enabling these causes picture errors on CODA7541 */ - if (ctx->inst_type == CODA_INST_DECODER) { - /* fw 1.4.50 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_IP_ENABLE); - } else { - /* fw 13.4.29 */ - iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | - CODA7_USE_HOST_DBK_ENABLE | - CODA7_USE_IP_ENABLE | - CODA7_USE_DBK_ENABLE); - } - } -} - -static u32 coda_supported_firmwares[] = { - CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), - CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), - CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), - CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), - CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), -}; - -static bool coda_firmware_supported(u32 vernum) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) - if (vernum == coda_supported_firmwares[i]) - return true; - return false; -} - -int coda_check_firmware(struct coda_dev *dev) -{ - u16 product, major, minor, release; - u32 data; - int ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); - coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); - coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); - coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); - coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); - if (coda_wait_timeout(dev)) { - v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); - ret = -EIO; - goto err_run_cmd; - } - - if (dev->devtype->product == CODA_960) { - data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); - v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", - data); - } - - /* Check we are compatible with the loaded firmware */ - data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); - product = CODA_FIRMWARE_PRODUCT(data); - major = CODA_FIRMWARE_MAJOR(data); - minor = CODA_FIRMWARE_MINOR(data); - release = CODA_FIRMWARE_RELEASE(data); - - clk_disable_unprepare(dev->clk_per); - clk_disable_unprepare(dev->clk_ahb); - - if (product != dev->devtype->product) { - v4l2_err(&dev->v4l2_dev, - "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n", - coda_product_name(dev->devtype->product), - coda_product_name(product), major, minor, release); - return -EINVAL; - } - - v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", - coda_product_name(product)); - - if (coda_firmware_supported(data)) { - v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", - major, minor, release); - } else { - v4l2_warn(&dev->v4l2_dev, - "Unsupported firmware version: %u.%u.%u\n", - major, minor, release); - } - - return 0; - -err_run_cmd: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) -{ - u32 cache_size, cache_config; - - if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { - /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ - cache_size = 0x20262024; - cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; - } else { - /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ - cache_size = 0x02440243; - cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; - } - coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); - if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_YUYV) { - cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | - 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; - } else { - cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | - 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; - } - coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); -} - -/* - * Encoder context operations - */ - -static int coda_encoder_reqbufs(struct coda_ctx *ctx, - struct v4l2_requestbuffers *rb) -{ - struct coda_q_data *q_data_src; - int ret; - - if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return 0; - - if (rb->count) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - } else { - coda_free_context_buffers(ctx); - } - - return 0; -} - -static int coda_start_encoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct vb2_v4l2_buffer *buf; - int gamma, ret, value; - u32 dst_fourcc; - int num_fb; - u32 stride; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - bitstream_buf = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); - bitstream_size = q_data_dst->sizeimage; - - if (!coda_is_initialized(dev)) { - v4l2_err(v4l2_dev, "coda is not initialized.\n"); - return -EFAULT; - } - - if (dst_fourcc == V4L2_PIX_FMT_JPEG) { - if (!ctx->params.jpeg_qmat_tab[0]) { - ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[0]) - return -ENOMEM; - } - if (!ctx->params.jpeg_qmat_tab[1]) { - ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[1]) - return -ENOMEM; - } - coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); - } - - mutex_lock(&dev->coda_mutex); - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); - coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | - CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - case CODA_960: - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - fallthrough; - case CODA_HX4: - case CODA_7541: - coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | - CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); - break; - } - - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; - coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product == CODA_DX6) { - /* Configure the coda */ - coda_write(dev, dev->iram.paddr, - CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); - } - - /* Could set rotation here if needed */ - value = 0; - switch (dev->devtype->product) { - case CODA_DX6: - value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) - << CODADX6_PICWIDTH_OFFSET; - value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) - << CODA_PICHEIGHT_OFFSET; - break; - case CODA_HX4: - case CODA_7541: - if (dst_fourcc == V4L2_PIX_FMT_H264) { - value = (round_up(q_data_src->rect.width, 16) & - CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (round_up(q_data_src->rect.height, 16) & - CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; - break; - } - fallthrough; - case CODA_960: - value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) - << CODA7_PICWIDTH_OFFSET; - value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) - << CODA_PICHEIGHT_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); - if (dst_fourcc == V4L2_PIX_FMT_JPEG) - ctx->params.framerate = 0; - coda_write(dev, ctx->params.framerate, - CODA_CMD_ENC_SEQ_SRC_F_RATE); - - ctx->params.codec_mode = ctx->codec->mode; - switch (dst_fourcc) { - case V4L2_PIX_FMT_MPEG4: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_MPEG4, - CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_MPEG4, - CODA_CMD_ENC_SEQ_COD_STD); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); - break; - case V4L2_PIX_FMT_H264: - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_STD_H264, - CODA_CMD_ENC_SEQ_COD_STD); - else - coda_write(dev, CODA_STD_H264, - CODA_CMD_ENC_SEQ_COD_STD); - value = ((ctx->params.h264_disable_deblocking_filter_idc & - CODA_264PARAM_DISABLEDEBLK_MASK) << - CODA_264PARAM_DISABLEDEBLK_OFFSET) | - ((ctx->params.h264_slice_alpha_c0_offset_div2 & - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | - ((ctx->params.h264_slice_beta_offset_div2 & - CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | - (ctx->params.h264_constrained_intra_pred_flag << - CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | - (ctx->params.h264_chroma_qp_index_offset & - CODA_264PARAM_CHROMAQPOFFSET_MASK); - coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); - break; - case V4L2_PIX_FMT_JPEG: - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_PARA); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_EN); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET); - - coda_jpeg_write_tables(ctx); - break; - default: - v4l2_err(v4l2_dev, - "dst format (0x%08x) invalid.\n", dst_fourcc); - ret = -EINVAL; - goto out; - } - - /* - * slice mode and GOP size registers are used for thumb size/offset - * in JPEG mode - */ - if (dst_fourcc != V4L2_PIX_FMT_JPEG) { - value = coda_slice_mode(ctx); - coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); - value = ctx->params.gop_size; - coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); - } - - if (ctx->params.bitrate && (ctx->params.frame_rc_enable || - ctx->params.mb_rc_enable)) { - ctx->params.bitrate_changed = false; - ctx->params.h264_intra_qp_changed = false; - - /* Rate control enabled */ - value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) - << CODA_RATECONTROL_BITRATE_OFFSET; - value |= 1 & CODA_RATECONTROL_ENABLE_MASK; - value |= (ctx->params.vbv_delay & - CODA_RATECONTROL_INITIALDELAY_MASK) - << CODA_RATECONTROL_INITIALDELAY_OFFSET; - if (dev->devtype->product == CODA_960) - value |= BIT(31); /* disable autoskip */ - } else { - value = 0; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); - - coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); - coda_write(dev, ctx->params.intra_refresh, - CODA_CMD_ENC_SEQ_INTRA_REFRESH); - - coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); - - - value = 0; - if (dev->devtype->product == CODA_960) - gamma = CODA9_DEFAULT_GAMMA; - else - gamma = CODA_DEFAULT_GAMMA; - if (gamma > 0) { - coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, - CODA_CMD_ENC_SEQ_RC_GAMMA); - } - - if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { - coda_write(dev, - ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | - ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, - CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); - } - if (dev->devtype->product == CODA_960) { - if (ctx->params.h264_max_qp) - value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; - if (CODA_DEFAULT_GAMMA > 0) - value |= 1 << CODA9_OPTION_GAMMA_OFFSET; - } else { - if (CODA_DEFAULT_GAMMA > 0) { - if (dev->devtype->product == CODA_DX6) - value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; - else - value |= 1 << CODA7_OPTION_GAMMA_OFFSET; - } - if (ctx->params.h264_min_qp) - value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; - if (ctx->params.h264_max_qp) - value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; - } - coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); - - if (ctx->params.frame_rc_enable && !ctx->params.mb_rc_enable) - value = 1; - else - value = 0; - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); - - coda_setup_iram(ctx); - - if (dst_fourcc == V4L2_PIX_FMT_H264) { - switch (dev->devtype->product) { - case CODA_DX6: - value = FMO_SLICE_SAVE_BUF_SIZE << 7; - coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); - break; - case CODA_HX4: - case CODA_7541: - coda_write(dev, ctx->iram_info.search_ram_paddr, - CODA7_CMD_ENC_SEQ_SEARCH_BASE); - coda_write(dev, ctx->iram_info.search_ram_size, - CODA7_CMD_ENC_SEQ_SEARCH_SIZE); - break; - case CODA_960: - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); - coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - goto out; - } - - if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); - ret = -EFAULT; - goto out; - } - ctx->initialized = 1; - - if (dst_fourcc != V4L2_PIX_FMT_JPEG) { - if (dev->devtype->product == CODA_960) - ctx->num_internal_frames = 4; - else - ctx->num_internal_frames = 2; - ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); - if (ret < 0) { - v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); - goto out; - } - num_fb = 2; - stride = q_data_src->bytesperline; - } else { - ctx->num_internal_frames = 0; - num_fb = 0; - stride = 0; - } - coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - coda_write(dev, q_data_src->bytesperline, - CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); - } - if (dev->devtype->product != CODA_DX6) { - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) { - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - - coda9_set_frame_cache(ctx, q_data_src->fourcc); - - /* FIXME */ - coda_write(dev, ctx->internal_frames[2].buf.paddr, - CODA9_CMD_SET_FRAME_SUBSAMP_A); - coda_write(dev, ctx->internal_frames[3].buf.paddr, - CODA9_CMD_SET_FRAME_SUBSAMP_B); - } - } - - ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); - if (ret < 0) { - v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - goto out; - } - - coda_dbg(1, ctx, "start encoding %dx%d %4.4s->%4.4s @ %d/%d Hz\n", - q_data_src->rect.width, q_data_src->rect.height, - (char *)&ctx->codec->src_fourcc, (char *)&dst_fourcc, - ctx->params.framerate & 0xffff, - (ctx->params.framerate >> 16) + 1); - - /* Save stream headers */ - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - /* - * Get SPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - /* - * If visible width or height are not aligned to macroblock - * size, the crop_right and crop_bottom SPS fields must be set - * to the difference between visible and coded size. This is - * only supported by CODA960 firmware. All others do not allow - * writing frame cropping parameters, so we have to manually - * fix up the SPS RBSP (Sequence Parameter Set Raw Byte - * Sequence Payload) ourselves. - */ - if (ctx->dev->devtype->product != CODA_960 && - ((q_data_src->rect.width % 16) || - (q_data_src->rect.height % 16))) { - ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, - q_data_src->rect.height, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0], - sizeof(ctx->vpu_header[0])); - if (ret < 0) - goto out; - } - - /* - * Get PPS in the first frame and copy it to an - * intermediate buffer. - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - /* - * Length of H.264 headers is variable and thus it might not be - * aligned for the coda to append the encoded frame. In that is - * the case a filler NAL must be added to header 2. - */ - ctx->vpu_header_size[2] = coda_h264_padding( - (ctx->vpu_header_size[0] + - ctx->vpu_header_size[1]), - ctx->vpu_header[2]); - break; - case V4L2_PIX_FMT_MPEG4: - /* - * Get VOS in the first frame and copy it to an - * intermediate buffer - */ - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, - &ctx->vpu_header[0][0], - &ctx->vpu_header_size[0]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, - &ctx->vpu_header[1][0], - &ctx->vpu_header_size[1]); - if (ret < 0) - goto out; - - ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, - &ctx->vpu_header[2][0], - &ctx->vpu_header_size[2]); - if (ret < 0) - goto out; - break; - default: - /* No more formats need to save headers at the moment */ - break; - } - -out: - mutex_unlock(&dev->coda_mutex); - return ret; -} - -static int coda_prepare_encode(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - int force_ipicture; - int quant_param = 0; - u32 pic_stream_buffer_addr, pic_stream_buffer_size; - u32 rot_mode = 0; - u32 dst_fourcc; - u32 reg; - int ret; - - ret = coda_enc_param_change(ctx); - if (ret < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "parameter change failed: %d\n", - ret); - } - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - src_buf->sequence = ctx->osequence; - dst_buf->sequence = ctx->osequence; - ctx->osequence++; - - force_ipicture = ctx->params.force_ipicture; - if (force_ipicture) - ctx->params.force_ipicture = false; - else if (ctx->params.gop_size != 0 && - (src_buf->sequence % ctx->params.gop_size) == 0) - force_ipicture = 1; - - /* - * Workaround coda firmware BUG that only marks the first - * frame as IDR. This is a problem for some decoders that can't - * recover when a frame is lost. - */ - if (!force_ipicture) { - src_buf->flags |= V4L2_BUF_FLAG_PFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } else { - src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - /* - * Copy headers in front of the first frame and forced I frames for - * H.264 only. In MPEG4 they are already copied by the CODA. - */ - if (src_buf->sequence == 0 || force_ipicture) { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]; - pic_stream_buffer_size = q_data_dst->sizeimage - - ctx->vpu_header_size[0] - - ctx->vpu_header_size[1] - - ctx->vpu_header_size[2]; - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0), - &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) - + ctx->vpu_header_size[0], &ctx->vpu_header[1][0], - ctx->vpu_header_size[1]); - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) - + ctx->vpu_header_size[0] + ctx->vpu_header_size[1], - &ctx->vpu_header[2][0], ctx->vpu_header_size[2]); - } else { - pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - pic_stream_buffer_size = q_data_dst->sizeimage; - } - - if (force_ipicture) { - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_intra_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_intra_qp; - break; - case V4L2_PIX_FMT_JPEG: - quant_param = 30; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set intra qp, fmt not supported\n"); - break; - } - } else { - switch (dst_fourcc) { - case V4L2_PIX_FMT_H264: - quant_param = ctx->params.h264_inter_qp; - break; - case V4L2_PIX_FMT_MPEG4: - quant_param = ctx->params.mpeg4_inter_qp; - break; - default: - v4l2_warn(&ctx->dev->v4l2_dev, - "cannot set inter qp, fmt not supported\n"); - break; - } - } - - /* submit */ - if (ctx->params.rot_mode) - rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; - coda_write(dev, rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); - coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); - - if (dev->devtype->product == CODA_960) { - coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); - coda_write(dev, q_data_src->bytesperline, - CODA9_CMD_ENC_PIC_SRC_STRIDE); - coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); - - reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; - } else { - reg = CODA_CMD_ENC_PIC_SRC_ADDR_Y; - } - coda_write_base(ctx, q_data_src, src_buf, reg); - - coda_write(dev, force_ipicture << 1 & 0x2, - CODA_CMD_ENC_PIC_OPTION); - - coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); - coda_write(dev, pic_stream_buffer_size / 1024, - CODA_CMD_ENC_PIC_BB_SIZE); - - if (!ctx->streamon_out) { - /* After streamoff on the output side, set stream end flag */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - coda_write(dev, ctx->bit_stream_param, - CODA_REG_BIT_BIT_STREAM_PARAM); - } - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, ctx->iram_info.axi_sram_use, - CODA7_REG_BIT_AXI_SRAM_USE); - - trace_coda_enc_pic_run(ctx, src_buf); - - coda_command_async(ctx, CODA_COMMAND_PIC_RUN); - - return 0; -} - -static char coda_frame_type_char(u32 flags) -{ - return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : - (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : - (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; -} - -static void coda_finish_encode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr, start_ptr; - - if (ctx->aborting) - return; - - /* - * Lock to make sure that an encoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - trace_coda_enc_pic_done(ctx, dst_buf); - - /* Get results from the coda */ - start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); - wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); - - /* Calculate bytesused field */ - if (dst_buf->sequence == 0 || - src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]); - } else { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); - } - - coda_dbg(1, ctx, "frame size = %u\n", wr_ptr - start_ptr); - - coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); - coda_read(dev, CODA_RET_ENC_PIC_FLAG); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_LAST); - if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - mutex_unlock(&ctx->wakeup_mutex); - - ctx->gopcounter--; - if (ctx->gopcounter < 0) - ctx->gopcounter = ctx->params.gop_size - 1; - - coda_dbg(1, ctx, "job finished: encoded %c frame (%d)%s\n", - coda_frame_type_char(dst_buf->flags), dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); -} - -static void coda_seq_end_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); - struct coda_dev *dev = ctx->dev; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - if (ctx->initialized == 0) - goto out; - - coda_dbg(1, ctx, "%s: sent command 'SEQ_END' to coda\n", __func__); - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_END failed\n"); - } - - /* - * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing - * from the output stream after the h.264 decoder has run. Resetting the - * hardware after the decoder has finished seems to help. - */ - if (dev->devtype->product == CODA_960) - coda_hw_reset(ctx); - - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - coda_free_framebuffers(ctx); - - ctx->initialized = 0; - -out: - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); -} - -static void coda_bit_release(struct coda_ctx *ctx) -{ - mutex_lock(&ctx->buffer_mutex); - coda_free_framebuffers(ctx); - coda_free_context_buffers(ctx); - coda_free_bitstream_buffer(ctx); - mutex_unlock(&ctx->buffer_mutex); -} - -const struct coda_context_ops coda_bit_encode_ops = { - .queue_init = coda_encoder_queue_init, - .reqbufs = coda_encoder_reqbufs, - .start_streaming = coda_start_encoding, - .prepare_run = coda_prepare_encode, - .finish_run = coda_finish_encode, - .seq_end_work = coda_seq_end_work, - .release = coda_bit_release, -}; - -/* - * Decoder context operations - */ - -static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, - struct coda_q_data *q_data) -{ - if (ctx->bitstream.vaddr) - return 0; - - ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); - ctx->bitstream.vaddr = dma_alloc_wc(ctx->dev->dev, ctx->bitstream.size, - &ctx->bitstream.paddr, GFP_KERNEL); - if (!ctx->bitstream.vaddr) { - v4l2_err(&ctx->dev->v4l2_dev, - "failed to allocate bitstream ringbuffer"); - return -ENOMEM; - } - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - return 0; -} - -static void coda_free_bitstream_buffer(struct coda_ctx *ctx) -{ - if (ctx->bitstream.vaddr == NULL) - return; - - dma_free_wc(ctx->dev->dev, ctx->bitstream.size, ctx->bitstream.vaddr, - ctx->bitstream.paddr); - ctx->bitstream.vaddr = NULL; - kfifo_init(&ctx->bitstream_fifo, NULL, 0); -} - -static int coda_decoder_reqbufs(struct coda_ctx *ctx, - struct v4l2_requestbuffers *rb) -{ - struct coda_q_data *q_data_src; - int ret; - - if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return 0; - - if (rb->count) { - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - ret = coda_alloc_bitstream_buffer(ctx, q_data_src); - if (ret < 0) { - coda_free_context_buffers(ctx); - return ret; - } - } else { - coda_free_bitstream_buffer(ctx); - coda_free_context_buffers(ctx); - } - - return 0; -} - -static bool coda_reorder_enable(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int profile; - - if (dev->devtype->product != CODA_HX4 && - dev->devtype->product != CODA_7541 && - dev->devtype->product != CODA_960) - return false; - - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) - return false; - - if (ctx->codec->src_fourcc != V4L2_PIX_FMT_H264) - return true; - - profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) - v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", - ctx->params.h264_profile_idc); - - /* Baseline profile does not support reordering */ - return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; -} - -static void coda_decoder_drop_used_metas(struct coda_ctx *ctx) -{ - struct coda_buffer_meta *meta, *tmp; - - /* - * All metas that end at or before the RD pointer (fifo out), - * are now consumed by the VPU and should be released. - */ - spin_lock(&ctx->buffer_meta_lock); - list_for_each_entry_safe(meta, tmp, &ctx->buffer_meta_list, list) { - if (ctx->bitstream_fifo.kfifo.out >= meta->end) { - coda_dbg(2, ctx, "releasing meta: seq=%d start=%d end=%d\n", - meta->sequence, meta->start, meta->end); - - list_del(&meta->list); - ctx->num_metas--; - ctx->first_frame_sequence++; - kfree(meta); - } - } - spin_unlock(&ctx->buffer_meta_lock); -} - -static int __coda_decoder_seq_init(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - u32 bitstream_buf, bitstream_size; - struct coda_dev *dev = ctx->dev; - int width, height; - u32 src_fourcc, dst_fourcc; - u32 val; - int ret; - - lockdep_assert_held(&dev->coda_mutex); - - coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", - ctx->use_vdoa ? "Enabled" : "Disabled"); - - /* Start decoding */ - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - bitstream_buf = ctx->bitstream.paddr; - bitstream_size = ctx->bitstream.size; - src_fourcc = q_data_src->fourcc; - dst_fourcc = q_data_dst->fourcc; - - /* Update coda bitstream read and write pointers from kfifo */ - coda_kfifo_sync_to_device_full(ctx); - - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | - ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); - coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); - - ctx->display_idx = -1; - ctx->frm_dis_flg = 0; - coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); - coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); - val = 0; - if (coda_reorder_enable(ctx)) - val |= CODA_REORDER_ENABLE; - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) - val |= CODA_NO_INT_ENABLE; - coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); - - ctx->params.codec_mode = ctx->codec->mode; - if (dev->devtype->product == CODA_960 && - src_fourcc == V4L2_PIX_FMT_MPEG4) - ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; - else - ctx->params.codec_mode_aux = 0; - if (src_fourcc == V4L2_PIX_FMT_MPEG4) { - coda_write(dev, CODA_MP4_CLASS_MPEG4, - CODA_CMD_DEC_SEQ_MP4_ASP_CLASS); - } - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - coda_write(dev, ctx->psbuf.paddr, - CODA_CMD_DEC_SEQ_PS_BB_START); - coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), - CODA_CMD_DEC_SEQ_PS_BB_SIZE); - } - if (dev->devtype->product == CODA_960) { - coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); - coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); - } - } - if (src_fourcc == V4L2_PIX_FMT_JPEG) - coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); - if (dev->devtype->product != CODA_960) - coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); - - ctx->bit_stream_param = CODA_BIT_DEC_SEQ_INIT_ESCAPE; - ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); - ctx->bit_stream_param = 0; - if (ret) { - v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - return ret; - } - ctx->sequence_offset = ~0U; - ctx->initialized = 1; - ctx->first_frame_sequence = 0; - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - /* - * After updating the read pointer, we need to check if - * any metas are consumed and should be released. - */ - coda_decoder_drop_used_metas(ctx); - - if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_INIT failed, error code = 0x%x\n", - coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); - return -EAGAIN; - } - - val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); - if (dev->devtype->product == CODA_DX6) { - width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; - height = val & CODADX6_PICHEIGHT_MASK; - } else { - width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; - height = val & CODA7_PICHEIGHT_MASK; - } - - if (width > q_data_dst->bytesperline || height > q_data_dst->height) { - v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", - width, height, q_data_dst->bytesperline, - q_data_dst->height); - return -EINVAL; - } - - width = round_up(width, 16); - height = round_up(height, 16); - - coda_dbg(1, ctx, "start decoding: %dx%d\n", width, height); - - ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); - /* - * If the VDOA is used, the decoder needs one additional frame, - * because the frames are freed when the next frame is decoded. - * Otherwise there are visible errors in the decoded frames (green - * regions in displayed frames) and a broken order of frames (earlier - * frames are sporadically displayed after later frames). - */ - if (ctx->use_vdoa) - ctx->num_internal_frames += 1; - if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { - v4l2_err(&dev->v4l2_dev, - "not enough framebuffers to decode (%d < %d)\n", - CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); - return -EINVAL; - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); - - q_data_dst->rect.left = (left_right >> 10) & 0x3ff; - q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; - q_data_dst->rect.width = width - q_data_dst->rect.left - - (left_right & 0x3ff); - q_data_dst->rect.height = height - q_data_dst->rect.top - - (top_bottom & 0x3ff); - } - - if (dev->devtype->product != CODA_DX6) { - u8 profile, level; - - val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); - profile = val & 0xff; - level = (val >> 8) & 0x7f; - - if (profile || level) - coda_update_profile_level_ctrls(ctx, profile, level); - } - - return 0; -} - -static void coda_dec_seq_init_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, - struct coda_ctx, seq_init_work); - struct coda_dev *dev = ctx->dev; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - if (!ctx->initialized) - __coda_decoder_seq_init(ctx); - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); -} - -static int __coda_start_decoding(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src, *q_data_dst; - struct coda_dev *dev = ctx->dev; - u32 src_fourcc, dst_fourcc; - int ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - src_fourcc = q_data_src->fourcc; - dst_fourcc = q_data_dst->fourcc; - - if (!ctx->initialized) { - ret = __coda_decoder_seq_init(ctx); - if (ret < 0) - return ret; - } else { - ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | - CODA9_FRAME_TILED2LINEAR); - if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) - ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - ctx->frame_mem_ctrl |= (0x3 << 9) | - ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); - } - - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - - ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); - return ret; - } - - /* Tell the decoder how many frame buffers we allocated. */ - coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, round_up(q_data_dst->rect.width, 16), - CODA_CMD_SET_FRAME_BUF_STRIDE); - - if (dev->devtype->product != CODA_DX6) { - /* Set secondary AXI IRAM */ - coda_setup_iram(ctx); - - coda_write(dev, ctx->iram_info.buf_bit_use, - CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, - CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_y_use, - CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, ctx->iram_info.buf_dbk_c_use, - CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, ctx->iram_info.buf_ovl_use, - CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) { - coda_write(dev, ctx->iram_info.buf_btp_use, - CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - - coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); - coda9_set_frame_cache(ctx, dst_fourcc); - } - } - - if (src_fourcc == V4L2_PIX_FMT_H264) { - coda_write(dev, ctx->slicebuf.paddr, - CODA_CMD_SET_FRAME_SLICE_BB_START); - coda_write(dev, ctx->slicebuf.size / 1024, - CODA_CMD_SET_FRAME_SLICE_BB_SIZE); - } - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); - } else if (dev->devtype->product == CODA_960) { - int max_mb_x = 1920 / 16; - int max_mb_y = 1088 / 16; - int max_mb_num = max_mb_x * max_mb_y; - - coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, - CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); - } - - if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { - v4l2_err(&ctx->dev->v4l2_dev, - "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int coda_start_decoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - mutex_lock(&dev->coda_mutex); - ret = __coda_start_decoding(ctx); - mutex_unlock(&dev->coda_mutex); - - return ret; -} - -static int coda_prepare_decode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *dst_buf; - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_dst; - struct coda_buffer_meta *meta; - u32 rot_mode = 0; - u32 reg_addr, reg_stride; - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - /* Try to copy source buffer contents into the bitstream ringbuffer */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - - if (coda_get_bitstream_payload(ctx) < 512 && - (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { - coda_dbg(1, ctx, "bitstream payload: %d, skipping\n", - coda_get_bitstream_payload(ctx)); - return -EAGAIN; - } - - /* Run coda_start_decoding (again) if not yet initialized */ - if (!ctx->initialized) { - int ret = __coda_start_decoding(ctx); - - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); - return -EAGAIN; - } else { - ctx->initialized = 1; - } - } - - if (dev->devtype->product == CODA_960) - coda_set_gdi_regs(ctx); - - if (ctx->use_vdoa && - ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - vdoa_device_run(ctx->vdoa, - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), - ctx->internal_frames[ctx->display_idx].buf.paddr); - } else { - if (dev->devtype->product == CODA_960) { - /* - * It was previously assumed that the CODA960 has an - * internal list of 64 buffer entries that contains - * both the registered internal frame buffers as well - * as the rotator buffer output, and that the ROT_INDEX - * register must be set to a value between the last - * internal frame buffers' index and 64. - * At least on firmware version 3.1.1 it turns out that - * setting ROT_INDEX to any value >= 32 causes CODA - * hangups that it can not recover from with the SRC VPU - * reset. - * It does appear to work however, to just set it to a - * fixed value in the [ctx->num_internal_frames, 31] - * range, for example CODA_MAX_FRAMEBUFFERS. - */ - coda_write(dev, CODA_MAX_FRAMEBUFFERS, - CODA9_CMD_DEC_PIC_ROT_INDEX); - - reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y; - reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE; - } else { - reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y; - reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE; - } - coda_write_base(ctx, q_data_dst, dst_buf, reg_addr); - coda_write(dev, q_data_dst->bytesperline, reg_stride); - - rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; - } - - coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE); - - switch (dev->devtype->product) { - case CODA_DX6: - /* TBD */ - case CODA_HX4: - case CODA_7541: - coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); - break; - case CODA_960: - /* 'hardcode to use interrupt disable mode'? */ - coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); - break; - } - - coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); - - coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); - coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, ctx->iram_info.axi_sram_use, - CODA7_REG_BIT_AXI_SRAM_USE); - - spin_lock(&ctx->buffer_meta_lock); - meta = list_first_entry_or_null(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - - if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { - - /* If this is the last buffer in the bitstream, add padding */ - if (meta->end == ctx->bitstream_fifo.kfifo.in) { - static unsigned char buf[512]; - unsigned int pad; - - /* Pad to multiple of 256 and then add 256 more */ - pad = ((0 - meta->end) & 0xff) + 256; - - memset(buf, 0xff, sizeof(buf)); - - kfifo_in(&ctx->bitstream_fifo, buf, pad); - } - } - spin_unlock(&ctx->buffer_meta_lock); - - coda_kfifo_sync_to_device_full(ctx); - - /* Clear decode success flag */ - coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); - - /* Clear error return value */ - coda_write(dev, 0, CODA_RET_DEC_PIC_ERR_MB); - - trace_coda_dec_pic_run(ctx, meta); - - coda_command_async(ctx, CODA_COMMAND_PIC_RUN); - - return 0; -} - -static void coda_finish_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct coda_q_data *q_data_src; - struct coda_q_data *q_data_dst; - struct vb2_v4l2_buffer *dst_buf; - struct coda_buffer_meta *meta; - int width, height; - int decoded_idx; - int display_idx; - struct coda_internal_frame *decoded_frame = NULL; - u32 src_fourcc; - int success; - u32 err_mb; - int err_vdoa = 0; - u32 val; - - if (ctx->aborting) - return; - - /* Update kfifo out pointer from coda bitstream read pointer */ - coda_kfifo_sync_from_device(ctx); - - /* - * in stream-end mode, the read pointer can overshoot the write pointer - * by up to 512 bytes - */ - if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { - if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512) - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - } - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - src_fourcc = q_data_src->fourcc; - - val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); - if (val != 1) - pr_err("DEC_PIC_SUCCESS = %d\n", val); - - success = val & 0x1; - if (!success) - v4l2_err(&dev->v4l2_dev, "decode failed\n"); - - if (src_fourcc == V4L2_PIX_FMT_H264) { - if (val & (1 << 3)) - v4l2_err(&dev->v4l2_dev, - "insufficient PS buffer space (%d bytes)\n", - ctx->psbuf.size); - if (val & (1 << 2)) - v4l2_err(&dev->v4l2_dev, - "insufficient slice buffer space (%d bytes)\n", - ctx->slicebuf.size); - } - - val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); - width = (val >> 16) & 0xffff; - height = val & 0xffff; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - /* frame crop information */ - if (src_fourcc == V4L2_PIX_FMT_H264) { - u32 left_right; - u32 top_bottom; - - left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); - top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); - - if (left_right == 0xffffffff && top_bottom == 0xffffffff) { - /* Keep current crop information */ - } else { - struct v4l2_rect *rect = &q_data_dst->rect; - - rect->left = left_right >> 16 & 0xffff; - rect->top = top_bottom >> 16 & 0xffff; - rect->width = width - rect->left - - (left_right & 0xffff); - rect->height = height - rect->top - - (top_bottom & 0xffff); - } - } else { - /* no cropping */ - } - - err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); - if (err_mb > 0) { - if (__ratelimit(&dev->mb_err_rs)) - coda_dbg(1, ctx, "errors in %d macroblocks\n", err_mb); - v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, - v4l2_ctrl_g_ctrl(ctx->mb_err_cnt_ctrl) + err_mb); - } - - if (dev->devtype->product == CODA_HX4 || - dev->devtype->product == CODA_7541) { - val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); - if (val == 0) { - /* not enough bitstream data */ - coda_dbg(1, ctx, "prescan failed: %d\n", val); - ctx->hold = true; - return; - } - } - - /* Wait until the VDOA finished writing the previous display frame */ - if (ctx->use_vdoa && - ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - err_vdoa = vdoa_wait_for_completion(ctx->vdoa); - } - - ctx->frm_dis_flg = coda_read(dev, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - - /* The previous display frame was copied out and can be overwritten */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - ctx->frm_dis_flg &= ~(1 << ctx->display_idx); - coda_write(dev, ctx->frm_dis_flg, - CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); - } - - /* - * The index of the last decoded frame, not necessarily in - * display order, and the index of the next display frame. - * The latter could have been decoded in a previous run. - */ - decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); - display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); - - if (decoded_idx == -1) { - /* no frame was decoded, but we might have a display frame */ - if (display_idx >= 0 && display_idx < ctx->num_internal_frames) - ctx->sequence_offset++; - else if (ctx->display_idx < 0) - ctx->hold = true; - } else if (decoded_idx == -2) { - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) - ctx->sequence_offset++; - /* no frame was decoded, we still return remaining buffers */ - } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "decoded frame index out of range: %d\n", decoded_idx); - } else { - int sequence; - - decoded_frame = &ctx->internal_frames[decoded_idx]; - - val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); - if (ctx->sequence_offset == -1) - ctx->sequence_offset = val; - - sequence = val + ctx->first_frame_sequence - - ctx->sequence_offset; - spin_lock(&ctx->buffer_meta_lock); - if (!list_empty(&ctx->buffer_meta_list)) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - list_del(&meta->list); - ctx->num_metas--; - spin_unlock(&ctx->buffer_meta_lock); - /* - * Clamp counters to 16 bits for comparison, as the HW - * counter rolls over at this point for h.264. This - * may be different for other formats, but using 16 bits - * should be enough to detect most errors and saves us - * from doing different things based on the format. - */ - if ((sequence & 0xffff) != (meta->sequence & 0xffff)) { - v4l2_err(&dev->v4l2_dev, - "sequence number mismatch (%d(%d) != %d)\n", - sequence, ctx->sequence_offset, - meta->sequence); - } - decoded_frame->meta = *meta; - kfree(meta); - } else { - spin_unlock(&ctx->buffer_meta_lock); - v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); - memset(&decoded_frame->meta, 0, - sizeof(struct coda_buffer_meta)); - decoded_frame->meta.sequence = sequence; - decoded_frame->meta.last = false; - ctx->sequence_offset++; - } - - trace_coda_dec_pic_done(ctx, &decoded_frame->meta); - - val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; - decoded_frame->type = (val == 0) ? V4L2_BUF_FLAG_KEYFRAME : - (val == 1) ? V4L2_BUF_FLAG_PFRAME : - V4L2_BUF_FLAG_BFRAME; - - decoded_frame->error = err_mb; - } - - if (display_idx == -1) { - /* - * no more frames to be decoded, but there could still - * be rotator output to dequeue - */ - ctx->hold = true; - } else if (display_idx == -3) { - /* possibly prescan failure */ - } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { - v4l2_err(&dev->v4l2_dev, - "presentation frame index out of range: %d\n", - display_idx); - } - - /* If a frame was copied out, return it */ - if (ctx->display_idx >= 0 && - ctx->display_idx < ctx->num_internal_frames) { - struct coda_internal_frame *ready_frame; - - ready_frame = &ctx->internal_frames[ctx->display_idx]; - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->osequence++; - - dst_buf->field = V4L2_FIELD_NONE; - dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME); - dst_buf->flags |= ready_frame->type; - meta = &ready_frame->meta; - if (meta->last && !coda_reorder_enable(ctx)) { - /* - * If this was the last decoded frame, and reordering - * is disabled, this will be the last display frame. - */ - coda_dbg(1, ctx, "last meta, marking as last frame\n"); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - } else if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG && - display_idx == -1) { - /* - * If there is no designated presentation frame anymore, - * this frame has to be the last one. - */ - coda_dbg(1, ctx, - "no more frames to return, marking as last frame\n"); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - } - dst_buf->timecode = meta->timecode; - dst_buf->vb2_buf.timestamp = meta->timestamp; - - trace_coda_dec_rot_done(ctx, dst_buf, meta); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - q_data_dst->sizeimage); - - if (ready_frame->error || err_vdoa) - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); - else - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - - if (decoded_frame) { - coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", - coda_frame_type_char(decoded_frame->type), - decoded_frame->meta.sequence, - coda_frame_type_char(dst_buf->flags), - ready_frame->meta.sequence, - dst_buf->sequence, ctx->qsequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? - " (last)" : ""); - } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", - decoded_idx, - coda_frame_type_char(dst_buf->flags), - ready_frame->meta.sequence, - dst_buf->sequence, ctx->qsequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? - " (last)" : ""); - } - } else { - if (decoded_frame) { - coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", - coda_frame_type_char(decoded_frame->type), - decoded_frame->meta.sequence, - ctx->display_idx); - } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", - decoded_idx, ctx->display_idx); - } - } - - /* The rotator will copy the current display frame next time */ - ctx->display_idx = display_idx; - - /* - * The current decode run might have brought the bitstream fill level - * below the size where we can start the next decode run. As userspace - * might have filled the output queue completely and might thus be - * blocked, we can't rely on the next qbuf to trigger the bitstream - * refill. Check if we have data to refill the bitstream now. - */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); -} - -static void coda_decode_timeout(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *dst_buf; - - /* - * For now this only handles the case where we would deadlock with - * userspace, i.e. userspace issued DEC_CMD_STOP and waits for EOS, - * but after a failed decode run we would hold the context and wait for - * userspace to queue more buffers. - */ - if (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG)) - return; - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->qsequence - 1; - - coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); -} - -const struct coda_context_ops coda_bit_decode_ops = { - .queue_init = coda_decoder_queue_init, - .reqbufs = coda_decoder_reqbufs, - .start_streaming = coda_start_decoding, - .prepare_run = coda_prepare_decode, - .finish_run = coda_finish_decode, - .run_timeout = coda_decode_timeout, - .seq_init_work = coda_dec_seq_init_work, - .seq_end_work = coda_seq_end_work, - .release = coda_bit_release, -}; - -irqreturn_t coda_irq_handler(int irq, void *data) -{ - struct coda_dev *dev = data; - struct coda_ctx *ctx; - - /* read status register to attend the IRQ */ - coda_read(dev, CODA_REG_BIT_INT_STATUS); - coda_write(dev, 0, CODA_REG_BIT_INT_REASON); - coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, - CODA_REG_BIT_INT_CLEAR); - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (ctx == NULL) { - v4l2_err(&dev->v4l2_dev, - "Instance released before the end of transaction\n"); - return IRQ_HANDLED; - } - - trace_coda_bit_done(ctx); - - if (ctx->aborting) { - coda_dbg(1, ctx, "task has been aborted\n"); - } - - if (coda_isbusy(ctx->dev)) { - coda_dbg(1, ctx, "coda is still busy!!!!\n"); - return IRQ_NONE; - } - - complete(&ctx->completion); - - return IRQ_HANDLED; -} diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda-common.c deleted file mode 100644 index cc4892129..000000000 --- a/drivers/media/platform/chips-media/coda-common.c +++ /dev/null @@ -1,3361 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "imx-vdoa.h" - -#define CODA_NAME "coda" - -#define CODADX6_MAX_INSTANCES 4 -#define CODA_MAX_FORMATS 5 - -#define CODA_ISRAM_SIZE (2048 * 2) - -#define MIN_W 48 -#define MIN_H 16 - -#define S_ALIGN 1 /* multiple of 2 */ -#define W_ALIGN 1 /* multiple of 2 */ -#define H_ALIGN 1 /* multiple of 2 */ - -#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) - -int coda_debug; -module_param(coda_debug, int, 0644); -MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); - -static int disable_tiling; -module_param(disable_tiling, int, 0644); -MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); - -static int disable_vdoa; -module_param(disable_vdoa, int, 0644); -MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion"); - -static int enable_bwb = 0; -module_param(enable_bwb, int, 0644); -MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain streams"); - -void coda_write(struct coda_dev *dev, u32 data, u32 reg) -{ - v4l2_dbg(3, coda_debug, &dev->v4l2_dev, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - writel(data, dev->regs_base + reg); -} - -unsigned int coda_read(struct coda_dev *dev, u32 reg) -{ - u32 data; - - data = readl(dev->regs_base + reg); - v4l2_dbg(3, coda_debug, &dev->v4l2_dev, - "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); - return data; -} - -void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_v4l2_buffer *buf, unsigned int reg_y) -{ - u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); - u32 base_cb, base_cr; - - switch (q_data->fourcc) { - case V4L2_PIX_FMT_YUYV: - /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */ - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - default: - base_cb = base_y + q_data->bytesperline * q_data->height; - base_cr = base_cb + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YVU420: - /* Switch Cb and Cr for YVU420 format */ - base_cr = base_y + q_data->bytesperline * q_data->height; - base_cb = base_cr + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YUV422P: - base_cb = base_y + q_data->bytesperline * q_data->height; - base_cr = base_cb + q_data->bytesperline * q_data->height / 2; - } - - coda_write(ctx->dev, base_y, reg_y); - coda_write(ctx->dev, base_cb, reg_y + 4); - coda_write(ctx->dev, base_cr, reg_y + 8); -} - -#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ - { mode, src_fourcc, dst_fourcc, max_w, max_h } - -/* - * Arrays of codecs supported by each given version of Coda: - * i.MX27 -> codadx6 - * i.MX51 -> codahx4 - * i.MX53 -> coda7 - * i.MX6 -> coda960 - * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants - */ -static const struct coda_codec codadx6_codecs[] = { - CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), - CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), -}; - -static const struct coda_codec codahx4_codecs[] = { - CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), - CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), -}; - -static const struct coda_codec coda7_codecs[] = { - CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), - CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), - CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), - CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), -}; - -static const struct coda_codec coda9_codecs[] = { - CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), - CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), - CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), - CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), - CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), -}; - -struct coda_video_device { - const char *name; - enum coda_inst_type type; - const struct coda_context_ops *ops; - bool direct; - u32 src_formats[CODA_MAX_FORMATS]; - u32 dst_formats[CODA_MAX_FORMATS]; -}; - -static const struct coda_video_device coda_bit_encoder = { - .name = "coda-video-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda_bit_encode_ops, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - }, - .dst_formats = { - V4L2_PIX_FMT_H264, - V4L2_PIX_FMT_MPEG4, - }, -}; - -static const struct coda_video_device coda_bit_jpeg_encoder = { - .name = "coda-jpeg-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda_bit_encode_ops, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, - .dst_formats = { - V4L2_PIX_FMT_JPEG, - }, -}; - -static const struct coda_video_device coda_bit_decoder = { - .name = "coda-video-decoder", - .type = CODA_INST_DECODER, - .ops = &coda_bit_decode_ops, - .src_formats = { - V4L2_PIX_FMT_H264, - V4L2_PIX_FMT_MPEG2, - V4L2_PIX_FMT_MPEG4, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - /* - * If V4L2_PIX_FMT_YUYV should be default, - * set_default_params() must be adjusted. - */ - V4L2_PIX_FMT_YUYV, - }, -}; - -static const struct coda_video_device coda_bit_jpeg_decoder = { - .name = "coda-jpeg-decoder", - .type = CODA_INST_DECODER, - .ops = &coda_bit_decode_ops, - .src_formats = { - V4L2_PIX_FMT_JPEG, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, -}; - -static const struct coda_video_device coda9_jpeg_encoder = { - .name = "coda-jpeg-encoder", - .type = CODA_INST_ENCODER, - .ops = &coda9_jpeg_encode_ops, - .direct = true, - .src_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - V4L2_PIX_FMT_GREY, - }, - .dst_formats = { - V4L2_PIX_FMT_JPEG, - }, -}; - -static const struct coda_video_device coda9_jpeg_decoder = { - .name = "coda-jpeg-decoder", - .type = CODA_INST_DECODER, - .ops = &coda9_jpeg_decode_ops, - .direct = true, - .src_formats = { - V4L2_PIX_FMT_JPEG, - }, - .dst_formats = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUV422P, - }, -}; - -static const struct coda_video_device *codadx6_video_devices[] = { - &coda_bit_encoder, -}; - -static const struct coda_video_device *codahx4_video_devices[] = { - &coda_bit_encoder, - &coda_bit_decoder, -}; - -static const struct coda_video_device *coda7_video_devices[] = { - &coda_bit_jpeg_encoder, - &coda_bit_jpeg_decoder, - &coda_bit_encoder, - &coda_bit_decoder, -}; - -static const struct coda_video_device *coda9_video_devices[] = { - &coda9_jpeg_encoder, - &coda9_jpeg_decoder, - &coda_bit_encoder, - &coda_bit_decoder, -}; - -/* - * Normalize all supported YUV 4:2:0 formats to the value used in the codec - * tables. - */ -static u32 coda_format_normalize_yuv(u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YUYV: - return V4L2_PIX_FMT_YUV420; - default: - return fourcc; - } -} - -static const struct coda_codec *coda_find_codec(struct coda_dev *dev, - int src_fourcc, int dst_fourcc) -{ - const struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - int k; - - src_fourcc = coda_format_normalize_yuv(src_fourcc); - dst_fourcc = coda_format_normalize_yuv(dst_fourcc); - if (src_fourcc == dst_fourcc) - return NULL; - - for (k = 0; k < num_codecs; k++) { - if (codecs[k].src_fourcc == src_fourcc && - codecs[k].dst_fourcc == dst_fourcc) - break; - } - - if (k == num_codecs) - return NULL; - - return &codecs[k]; -} - -static void coda_get_max_dimensions(struct coda_dev *dev, - const struct coda_codec *codec, - int *max_w, int *max_h) -{ - const struct coda_codec *codecs = dev->devtype->codecs; - int num_codecs = dev->devtype->num_codecs; - unsigned int w, h; - int k; - - if (codec) { - w = codec->max_w; - h = codec->max_h; - } else { - for (k = 0, w = 0, h = 0; k < num_codecs; k++) { - w = max(w, codecs[k].max_w); - h = max(h, codecs[k].max_h); - } - } - - if (max_w) - *max_w = w; - if (max_h) - *max_h = h; -} - -static const struct coda_video_device *to_coda_video_device(struct video_device - *vdev) -{ - struct coda_dev *dev = video_get_drvdata(vdev); - unsigned int i = vdev - dev->vfd; - - if (i >= dev->devtype->num_vdevs) - return NULL; - - return dev->devtype->vdevs[i]; -} - -const char *coda_product_name(int product) -{ - static char buf[9]; - - switch (product) { - case CODA_DX6: - return "CodaDx6"; - case CODA_HX4: - return "CodaHx4"; - case CODA_7541: - return "CODA7541"; - case CODA_960: - return "CODA960"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", product); - return buf; - } -} - -static struct vdoa_data *coda_get_vdoa_data(void) -{ - struct device_node *vdoa_node; - struct platform_device *vdoa_pdev; - struct vdoa_data *vdoa_data = NULL; - - vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa"); - if (!vdoa_node) - return NULL; - - vdoa_pdev = of_find_device_by_node(vdoa_node); - if (!vdoa_pdev) - goto out; - - vdoa_data = platform_get_drvdata(vdoa_pdev); - if (!vdoa_data) - vdoa_data = ERR_PTR(-EPROBE_DEFER); - - put_device(&vdoa_pdev->dev); -out: - of_node_put(vdoa_node); - - return vdoa_data; -} - -/* - * V4L2 ioctl() operations. - */ -static int coda_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - strscpy(cap->driver, CODA_NAME, sizeof(cap->driver)); - strscpy(cap->card, coda_product_name(ctx->dev->devtype->product), - sizeof(cap->card)); - strscpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); - return 0; -} - -static const u32 coda_formats_420[CODA_MAX_FORMATS] = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, -}; - -static int coda_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct video_device *vdev = video_devdata(file); - const struct coda_video_device *cvd = to_coda_video_device(vdev); - struct coda_ctx *ctx = fh_to_ctx(priv); - const u32 *formats; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - formats = cvd->src_formats; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - struct coda_q_data *q_data_src; - struct vb2_queue *src_vq; - - formats = cvd->dst_formats; - - /* - * If the source format is already fixed, only allow the same - * chroma subsampling. - */ - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - vb2_is_streaming(src_vq)) { - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420) { - formats = coda_formats_420; - } else if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_422) { - f->pixelformat = V4L2_PIX_FMT_YUV422P; - return f->index ? -EINVAL : 0; - } - } - } else { - return -EINVAL; - } - - if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) - return -EINVAL; - - /* Skip YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[f->index] == V4L2_PIX_FMT_YUYV) - return -EINVAL; - - f->pixelformat = formats[f->index]; - - return 0; -} - -static int coda_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_q_data *q_data; - struct coda_ctx *ctx = fh_to_ctx(priv); - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fourcc; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; - f->fmt.pix.bytesperline = q_data->bytesperline; - - f->fmt.pix.sizeimage = q_data->sizeimage; - f->fmt.pix.colorspace = ctx->colorspace; - f->fmt.pix.xfer_func = ctx->xfer_func; - f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; - f->fmt.pix.quantization = ctx->quantization; - - return 0; -} - -static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) -{ - struct coda_q_data *q_data; - const u32 *formats; - int i; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - formats = ctx->cvd->src_formats; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - formats = ctx->cvd->dst_formats; - else - return -EINVAL; - - for (i = 0; i < CODA_MAX_FORMATS; i++) { - /* Skip YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[i] == V4L2_PIX_FMT_YUYV) - continue; - - if (formats[i] == f->fmt.pix.pixelformat) { - f->fmt.pix.pixelformat = formats[i]; - return 0; - } - } - - /* Fall back to currently set pixelformat */ - q_data = get_q_data(ctx, f->type); - f->fmt.pix.pixelformat = q_data->fourcc; - - return 0; -} - -static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f, - bool *use_vdoa) -{ - int err; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!use_vdoa) - return -EINVAL; - - if (!ctx->vdoa) { - *use_vdoa = false; - return 0; - } - - err = vdoa_context_configure(NULL, round_up(f->fmt.pix.width, 16), - f->fmt.pix.height, f->fmt.pix.pixelformat); - if (err) { - *use_vdoa = false; - return 0; - } - - *use_vdoa = true; - return 0; -} - -static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, - u32 width, u32 height) -{ - /* - * This is a rough estimate for sensible compressed buffer - * sizes (between 1 and 16 bits per pixel). This could be - * improved by better format specific worst case estimates. - */ - return round_up(clamp(sizeimage, width * height / 8, - width * height * 2), PAGE_SIZE); -} - -static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, - struct v4l2_format *f) -{ - struct coda_dev *dev = ctx->dev; - unsigned int max_w, max_h; - enum v4l2_field field; - - field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_NONE; - else if (V4L2_FIELD_NONE != field) - return -EINVAL; - - /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported */ - f->fmt.pix.field = field; - - coda_get_max_dimensions(dev, codec, &max_w, &max_h); - v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, - &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, - S_ALIGN); - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - /* - * Frame stride must be at least multiple of 8, - * but multiple of 16 for h.264 or JPEG 4:2:x - */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - break; - case V4L2_PIX_FMT_YUYV: - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height; - break; - case V4L2_PIX_FMT_YUV422P: - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 2; - break; - case V4L2_PIX_FMT_GREY: - /* keep 16 pixel alignment of 8-bit pixel data */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_MPEG2: - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, - f->fmt.pix.sizeimage, - f->fmt.pix.width, - f->fmt.pix.height); - break; - default: - BUG(); - } - - return 0; -} - -static int coda_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - const struct coda_q_data *q_data_src; - const struct coda_codec *codec; - struct vb2_queue *src_vq; - int hscale = 0; - int vscale = 0; - int ret; - bool use_vdoa; - - ret = coda_try_pixelformat(ctx, f); - if (ret < 0) - return ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - /* - * If the source format is already fixed, only allow the same output - * resolution. When decoding JPEG images, we also have to make sure to - * use the same chroma subsampling. - */ - src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (vb2_is_streaming(src_vq)) { - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - ctx->dev->devtype->product == CODA_960) { - hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); - vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); - } - f->fmt.pix.width = q_data_src->width >> hscale; - f->fmt.pix.height = q_data_src->height >> vscale; - - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420 && - f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; - else if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_422) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; - } - } - - f->fmt.pix.colorspace = ctx->colorspace; - f->fmt.pix.xfer_func = ctx->xfer_func; - f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; - f->fmt.pix.quantization = ctx->quantization; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - f->fmt.pix.pixelformat); - if (!codec) - return -EINVAL; - - ret = coda_try_fmt(ctx, codec, f); - if (ret < 0) - return ret; - - /* The decoders always write complete macroblocks or MCUs */ - if (ctx->inst_type == CODA_INST_DECODER) { - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16 >> hscale); - f->fmt.pix.height = round_up(f->fmt.pix.height, 16 >> vscale); - if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && - f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 2; - } else { - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; - } - - ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); - if (ret < 0) - return ret; - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { - if (!use_vdoa) - return -EINVAL; - - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height; - } - } - - return 0; -} - -static void coda_set_default_colorspace(struct v4l2_pix_format *fmt) -{ - enum v4l2_colorspace colorspace; - - if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) - colorspace = V4L2_COLORSPACE_JPEG; - else if (fmt->width <= 720 && fmt->height <= 576) - colorspace = V4L2_COLORSPACE_SMPTE170M; - else - colorspace = V4L2_COLORSPACE_REC709; - - fmt->colorspace = colorspace; - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - fmt->quantization = V4L2_QUANTIZATION_DEFAULT; -} - -static int coda_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_dev *dev = ctx->dev; - const struct coda_q_data *q_data_dst; - const struct coda_codec *codec; - int ret; - - ret = coda_try_pixelformat(ctx, f); - if (ret < 0) - return ret; - - if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) - coda_set_default_colorspace(&f->fmt.pix); - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); - - return coda_try_fmt(ctx, codec, f); -} - -static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, - struct v4l2_rect *r) -{ - struct coda_q_data *q_data; - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - q_data = get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - if (vb2_is_busy(vq)) { - v4l2_err(&ctx->dev->v4l2_dev, "%s: %s queue busy: %d\n", - __func__, v4l2_type_names[f->type], vq->num_buffers); - return -EBUSY; - } - - q_data->fourcc = f->fmt.pix.pixelformat; - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->bytesperline = f->fmt.pix.bytesperline; - q_data->sizeimage = f->fmt.pix.sizeimage; - if (r) { - q_data->rect = *r; - } else { - q_data->rect.left = 0; - q_data->rect.top = 0; - q_data->rect.width = f->fmt.pix.width; - q_data->rect.height = f->fmt.pix.height; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: - ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; - break; - case V4L2_PIX_FMT_NV12: - if (!disable_tiling && ctx->use_bit && - ctx->dev->devtype->product == CODA_960) { - ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; - break; - } - fallthrough; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; - break; - default: - break; - } - - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP && - !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) && - ctx->use_vdoa) - vdoa_context_configure(ctx->vdoa, - round_up(f->fmt.pix.width, 16), - f->fmt.pix.height, - f->fmt.pix.pixelformat); - else - ctx->use_vdoa = false; - - coda_dbg(1, ctx, "Setting %s format, wxh: %dx%d, fmt: %4.4s %c\n", - v4l2_type_names[f->type], q_data->width, q_data->height, - (char *)&q_data->fourcc, - (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) ? 'L' : 'T'); - - return 0; -} - -static int coda_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_q_data *q_data_src; - const struct coda_codec *codec; - struct v4l2_rect r; - int hscale = 0; - int vscale = 0; - int ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - ctx->dev->devtype->product == CODA_960) { - hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); - vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); - } - - ret = coda_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - r.left = 0; - r.top = 0; - r.width = q_data_src->width >> hscale; - r.height = q_data_src->height >> vscale; - - ret = coda_s_fmt(ctx, f, &r); - if (ret) - return ret; - - if (ctx->inst_type != CODA_INST_ENCODER) - return 0; - - /* Setting the coded format determines the selected codec */ - codec = coda_find_codec(ctx->dev, q_data_src->fourcc, - f->fmt.pix.pixelformat); - if (!codec) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); - return -EINVAL; - } - ctx->codec = codec; - - ctx->colorspace = f->fmt.pix.colorspace; - ctx->xfer_func = f->fmt.pix.xfer_func; - ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; - ctx->quantization = f->fmt.pix.quantization; - - return 0; -} - -static int coda_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - const struct coda_codec *codec; - struct v4l2_format f_cap; - struct vb2_queue *dst_vq; - int ret; - - ret = coda_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - ret = coda_s_fmt(ctx, f, NULL); - if (ret) - return ret; - - ctx->colorspace = f->fmt.pix.colorspace; - ctx->xfer_func = f->fmt.pix.xfer_func; - ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; - ctx->quantization = f->fmt.pix.quantization; - - if (ctx->inst_type != CODA_INST_DECODER) - return 0; - - /* Setting the coded format determines the selected codec */ - codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, - V4L2_PIX_FMT_YUV420); - if (!codec) { - v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); - return -EINVAL; - } - ctx->codec = codec; - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (!dst_vq) - return -EINVAL; - - /* - * Setting the capture queue format is not possible while the capture - * queue is still busy. This is not an error, but the user will have to - * make sure themselves that the capture format is set correctly before - * starting the output queue again. - */ - if (vb2_is_busy(dst_vq)) - return 0; - - memset(&f_cap, 0, sizeof(f_cap)); - f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - coda_g_fmt(file, priv, &f_cap); - f_cap.fmt.pix.width = f->fmt.pix.width; - f_cap.fmt.pix.height = f->fmt.pix.height; - - return coda_s_fmt_vid_cap(file, priv, &f_cap); -} - -static int coda_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); - if (ret) - return ret; - - /* - * Allow to allocate instance specific per-context buffers, such as - * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. - */ - if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) - return ctx->ops->reqbufs(ctx, rb); - - return 0; -} - -static int coda_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - if (ctx->inst_type == CODA_INST_DECODER && - buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - buf->flags &= ~V4L2_BUF_FLAG_LAST; - - return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); -} - -static int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); - - if (ctx->inst_type == CODA_INST_DECODER && - buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - buf->flags &= ~V4L2_BUF_FLAG_LAST; - - return ret; -} - -void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) -{ - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - if (buf->flags & V4L2_BUF_FLAG_LAST) - v4l2_event_queue_fh(&ctx->fh, &eos_event); - - v4l2_m2m_buf_done(buf, state); -} - -static int coda_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - struct v4l2_rect r, *rsel; - - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - r.left = 0; - r.top = 0; - r.width = q_data->width; - r.height = q_data->height; - rsel = &q_data->rect; - - switch (s->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - rsel = &r; - fallthrough; - case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || - ctx->inst_type == CODA_INST_DECODER) - return -EINVAL; - break; - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: - rsel = &r; - fallthrough; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - ctx->inst_type == CODA_INST_ENCODER) - return -EINVAL; - break; - default: - return -EINVAL; - } - - s->r = *rsel; - - return 0; -} - -static int coda_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - - switch (s->target) { - case V4L2_SEL_TGT_CROP: - if (ctx->inst_type == CODA_INST_ENCODER && - s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - q_data = get_q_data(ctx, s->type); - if (!q_data) - return -EINVAL; - - s->r.left = 0; - s->r.top = 0; - s->r.width = clamp(s->r.width, 2U, q_data->width); - s->r.height = clamp(s->r.height, 2U, q_data->height); - - if (s->flags & V4L2_SEL_FLAG_LE) { - s->r.width = round_up(s->r.width, 2); - s->r.height = round_up(s->r.height, 2); - } else { - s->r.width = round_down(s->r.width, 2); - s->r.height = round_down(s->r.height, 2); - } - - q_data->rect = s->r; - - coda_dbg(1, ctx, "Setting crop rectangle: %dx%d\n", - s->r.width, s->r.height); - - return 0; - } - fallthrough; - case V4L2_SEL_TGT_NATIVE_SIZE: - case V4L2_SEL_TGT_COMPOSE: - return coda_g_selection(file, fh, s); - default: - /* v4l2-compliance expects this to fail for read-only targets */ - return -EINVAL; - } -} - -static void coda_wake_up_capture_queue(struct coda_ctx *ctx) -{ - struct vb2_queue *dst_vq; - - coda_dbg(1, ctx, "waking up capture queue\n"); - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_vq->last_buffer_dequeued = true; - wake_up(&dst_vq->done_wq); -} - -static int coda_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *ec) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct vb2_v4l2_buffer *buf; - int ret; - - ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); - if (ret < 0) - return ret; - - mutex_lock(&ctx->wakeup_mutex); - buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (buf) { - /* - * If the last output buffer is still on the queue, make sure - * that decoder finish_run will see the last flag and report it - * to userspace. - */ - buf->flags |= V4L2_BUF_FLAG_LAST; - } else { - /* Set the stream-end flag on this context */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - - /* - * If the last output buffer has already been taken from the - * queue, wake up the capture queue and signal end of stream - * via the -EPIPE mechanism. - */ - coda_wake_up_capture_queue(ctx); - } - mutex_unlock(&ctx->wakeup_mutex); - - return 0; -} - -static bool coda_mark_last_meta(struct coda_ctx *ctx) -{ - struct coda_buffer_meta *meta; - - coda_dbg(1, ctx, "marking last meta\n"); - - spin_lock(&ctx->buffer_meta_lock); - if (list_empty(&ctx->buffer_meta_list)) { - spin_unlock(&ctx->buffer_meta_lock); - return false; - } - - meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, - list); - meta->last = true; - - spin_unlock(&ctx->buffer_meta_lock); - return true; -} - -static bool coda_mark_last_dst_buf(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *buf; - struct vb2_buffer *dst_vb; - struct vb2_queue *dst_vq; - unsigned long flags; - - coda_dbg(1, ctx, "marking last capture buffer\n"); - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - spin_lock_irqsave(&dst_vq->done_lock, flags); - if (list_empty(&dst_vq->done_list)) { - spin_unlock_irqrestore(&dst_vq->done_lock, flags); - return false; - } - - dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, - done_entry); - buf = to_vb2_v4l2_buffer(dst_vb); - buf->flags |= V4L2_BUF_FLAG_LAST; - - spin_unlock_irqrestore(&dst_vq->done_lock, flags); - return true; -} - -static int coda_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *buf; - struct vb2_queue *dst_vq; - bool stream_end; - bool wakeup; - int ret; - - ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); - if (ret < 0) - return ret; - - switch (dc->cmd) { - case V4L2_DEC_CMD_START: - mutex_lock(&dev->coda_mutex); - mutex_lock(&ctx->bitstream_mutex); - coda_bitstream_flush(ctx); - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - vb2_clear_last_buffer_dequeued(dst_vq); - ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - mutex_unlock(&dev->coda_mutex); - break; - case V4L2_DEC_CMD_STOP: - stream_end = false; - wakeup = false; - - mutex_lock(&ctx->wakeup_mutex); - - buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (buf) { - coda_dbg(1, ctx, "marking last pending buffer\n"); - - /* Mark last buffer */ - buf->flags |= V4L2_BUF_FLAG_LAST; - - if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { - coda_dbg(1, ctx, "all remaining buffers queued\n"); - stream_end = true; - } - } else { - if (ctx->use_bit) - if (coda_mark_last_meta(ctx)) - stream_end = true; - else - wakeup = true; - else - if (!coda_mark_last_dst_buf(ctx)) - wakeup = true; - } - - if (stream_end) { - coda_dbg(1, ctx, "all remaining buffers queued\n"); - - /* Set the stream-end flag on this context */ - coda_bit_stream_end_flag(ctx); - ctx->hold = false; - v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); - } - - if (wakeup) { - /* If there is no buffer in flight, wake up */ - coda_wake_up_capture_queue(ctx); - } - - mutex_unlock(&ctx->wakeup_mutex); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int coda_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data_dst; - const struct coda_codec *codec; - - if (fsize->index) - return -EINVAL; - - if (coda_format_normalize_yuv(fsize->pixel_format) == - V4L2_PIX_FMT_YUV420) { - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - codec = coda_find_codec(ctx->dev, fsize->pixel_format, - q_data_dst->fourcc); - } else { - codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, - fsize->pixel_format); - } - if (!codec) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = MIN_W; - fsize->stepwise.max_width = codec->max_w; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = MIN_H; - fsize->stepwise.max_height = codec->max_h; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int coda_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *f) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct coda_q_data *q_data; - const struct coda_codec *codec; - - if (f->index) - return -EINVAL; - - /* Disallow YUYV if the vdoa is not available */ - if (!ctx->vdoa && f->pixel_format == V4L2_PIX_FMT_YUYV) - return -EINVAL; - - if (coda_format_normalize_yuv(f->pixel_format) == V4L2_PIX_FMT_YUV420) { - q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - codec = coda_find_codec(ctx->dev, f->pixel_format, - q_data->fourcc); - } else { - codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, - f->pixel_format); - } - if (!codec) - return -EINVAL; - - if (f->width < MIN_W || f->width > codec->max_w || - f->height < MIN_H || f->height > codec->max_h) - return -EINVAL; - - f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - f->stepwise.min.numerator = 1; - f->stepwise.min.denominator = 65535; - f->stepwise.max.numerator = 65536; - f->stepwise.max.denominator = 1; - f->stepwise.step.numerator = 1; - f->stepwise.step.denominator = 1; - - return 0; -} - -static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct v4l2_fract *tpf; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - tpf = &a->parm.output.timeperframe; - tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; - tpf->numerator = 1 + (ctx->params.framerate >> - CODA_FRATE_DIV_OFFSET); - - return 0; -} - -/* - * Approximate timeperframe v4l2_fract with values that can be written - * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. - */ -static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) -{ - struct v4l2_fract s = *timeperframe; - struct v4l2_fract f0; - struct v4l2_fract f1 = { 1, 0 }; - struct v4l2_fract f2 = { 0, 1 }; - unsigned int i, div, s_denominator; - - /* Lower bound is 1/65535 */ - if (s.numerator == 0 || s.denominator / s.numerator > 65535) { - timeperframe->numerator = 1; - timeperframe->denominator = 65535; - return; - } - - /* Upper bound is 65536/1 */ - if (s.denominator == 0 || s.numerator / s.denominator > 65536) { - timeperframe->numerator = 65536; - timeperframe->denominator = 1; - return; - } - - /* Reduce fraction to lowest terms */ - div = gcd(s.numerator, s.denominator); - if (div > 1) { - s.numerator /= div; - s.denominator /= div; - } - - if (s.numerator <= 65536 && s.denominator < 65536) { - *timeperframe = s; - return; - } - - /* Find successive convergents from continued fraction expansion */ - while (f2.numerator <= 65536 && f2.denominator < 65536) { - f0 = f1; - f1 = f2; - - /* Stop when f2 exactly equals timeperframe */ - if (s.numerator == 0) - break; - - i = s.denominator / s.numerator; - - f2.numerator = f0.numerator + i * f1.numerator; - f2.denominator = f0.denominator + i * f2.denominator; - - s_denominator = s.numerator; - s.numerator = s.denominator % s.numerator; - s.denominator = s_denominator; - } - - *timeperframe = f1; -} - -static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) -{ - return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | - timeperframe->denominator; -} - -static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - struct v4l2_fract *tpf; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - tpf = &a->parm.output.timeperframe; - coda_approximate_timeperframe(tpf); - ctx->params.framerate = coda_timeperframe_to_frate(tpf); - ctx->params.framerate_changed = true; - - return 0; -} - -static int coda_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - struct coda_ctx *ctx = fh_to_ctx(fh); - - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 0, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - if (ctx->inst_type == CODA_INST_DECODER) - return v4l2_event_subscribe(fh, sub, 0, NULL); - else - return -EINVAL; - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static const struct v4l2_ioctl_ops coda_ioctl_ops = { - .vidioc_querycap = coda_querycap, - - .vidioc_enum_fmt_vid_cap = coda_enum_fmt, - .vidioc_g_fmt_vid_cap = coda_g_fmt, - .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, - - .vidioc_enum_fmt_vid_out = coda_enum_fmt, - .vidioc_g_fmt_vid_out = coda_g_fmt, - .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, - - .vidioc_reqbufs = coda_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - - .vidioc_qbuf = coda_qbuf, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_dqbuf = coda_dqbuf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_g_selection = coda_g_selection, - .vidioc_s_selection = coda_s_selection, - - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = coda_encoder_cmd, - .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, - .vidioc_decoder_cmd = coda_decoder_cmd, - - .vidioc_g_parm = coda_g_parm, - .vidioc_s_parm = coda_s_parm, - - .vidioc_enum_framesizes = coda_enum_framesizes, - .vidioc_enum_frameintervals = coda_enum_frameintervals, - - .vidioc_subscribe_event = coda_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* - * Mem-to-mem operations. - */ - -static void coda_device_run(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *dev = ctx->dev; - - queue_work(dev->workqueue, &ctx->pic_run_work); -} - -static void coda_pic_run_work(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); - struct coda_dev *dev = ctx->dev; - int ret; - - mutex_lock(&ctx->buffer_mutex); - mutex_lock(&dev->coda_mutex); - - ret = ctx->ops->prepare_run(ctx); - if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) - goto out; - - if (!wait_for_completion_timeout(&ctx->completion, - msecs_to_jiffies(1000))) { - if (ctx->use_bit) { - dev_err(dev->dev, "CODA PIC_RUN timeout\n"); - - ctx->hold = true; - - coda_hw_reset(ctx); - } - - if (ctx->ops->run_timeout) - ctx->ops->run_timeout(ctx); - } else { - ctx->ops->finish_run(ctx); - } - - if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && - ctx->ops->seq_end_work) - queue_work(dev->workqueue, &ctx->seq_end_work); - -out: - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); -} - -static int coda_job_ready(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); - - /* - * For both 'P' and 'key' frame cases 1 picture - * and 1 frame are needed. In the decoder case, - * the compressed frame can be in the bitstream. - */ - if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { - coda_dbg(1, ctx, "not ready: not enough vid-out buffers.\n"); - return 0; - } - - if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { - coda_dbg(1, ctx, "not ready: not enough vid-cap buffers.\n"); - return 0; - } - - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - bool stream_end = ctx->bit_stream_param & - CODA_BIT_STREAM_END_FLAG; - int num_metas = ctx->num_metas; - struct coda_buffer_meta *meta; - unsigned int count; - - count = hweight32(ctx->frm_dis_flg); - if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) { - coda_dbg(1, ctx, - "not ready: all internal buffers in use: %d/%d (0x%x)", - count, ctx->num_internal_frames, - ctx->frm_dis_flg); - return 0; - } - - if (ctx->hold && !src_bufs) { - coda_dbg(1, ctx, - "not ready: on hold for more buffers.\n"); - return 0; - } - - if (!stream_end && (num_metas + src_bufs) < 2) { - coda_dbg(1, ctx, - "not ready: need 2 buffers available (queue:%d + bitstream:%d)\n", - num_metas, src_bufs); - return 0; - } - - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - if (!coda_bitstream_can_fetch_past(ctx, meta->end) && - !stream_end) { - coda_dbg(1, ctx, - "not ready: not enough bitstream data to read past %u (%u)\n", - meta->end, ctx->bitstream_fifo.kfifo.in); - return 0; - } - } - - if (ctx->aborting) { - coda_dbg(1, ctx, "not ready: aborting\n"); - return 0; - } - - coda_dbg(2, ctx, "job ready\n"); - - return 1; -} - -static void coda_job_abort(void *priv) -{ - struct coda_ctx *ctx = priv; - - ctx->aborting = 1; - - coda_dbg(1, ctx, "job abort\n"); -} - -static const struct v4l2_m2m_ops coda_m2m_ops = { - .device_run = coda_device_run, - .job_ready = coda_job_ready, - .job_abort = coda_job_abort, -}; - -static void set_default_params(struct coda_ctx *ctx) -{ - unsigned int max_w, max_h, usize, csize; - - ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], - ctx->cvd->dst_formats[0]); - max_w = min(ctx->codec->max_w, 1920U); - max_h = min(ctx->codec->max_h, 1088U); - usize = max_w * max_h * 3 / 2; - csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); - - ctx->params.codec_mode = ctx->codec->mode; - if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG || - ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) { - ctx->colorspace = V4L2_COLORSPACE_SRGB; - ctx->xfer_func = V4L2_XFER_FUNC_SRGB; - ctx->ycbcr_enc = V4L2_YCBCR_ENC_601; - ctx->quantization = V4L2_QUANTIZATION_FULL_RANGE; - } else { - ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; - ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - ctx->quantization = V4L2_QUANTIZATION_DEFAULT; - } - ctx->params.framerate = 30; - - /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; - ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; - ctx->q_data[V4L2_M2M_SRC].width = max_w; - ctx->q_data[V4L2_M2M_SRC].height = max_h; - ctx->q_data[V4L2_M2M_DST].width = max_w; - ctx->q_data[V4L2_M2M_DST].height = max_h; - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { - ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; - ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; - ctx->q_data[V4L2_M2M_DST].bytesperline = 0; - ctx->q_data[V4L2_M2M_DST].sizeimage = csize; - } else { - ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; - ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; - ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; - ctx->q_data[V4L2_M2M_DST].sizeimage = usize; - } - ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; - ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; - ctx->q_data[V4L2_M2M_DST].rect.width = max_w; - ctx->q_data[V4L2_M2M_DST].rect.height = max_h; - - /* - * Since the RBC2AXI logic only supports a single chroma plane, - * macroblock tiling only works for to NV12 pixel format. - */ - ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; -} - -/* - * Queue operations - */ -static int coda_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(vq); - struct coda_q_data *q_data; - unsigned int size; - - q_data = get_q_data(ctx, vq->type); - size = q_data->sizeimage; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - coda_dbg(1, ctx, "get %d buffer(s) of size %d each.\n", *nbuffers, - size); - - return 0; -} - -static int coda_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - v4l2_warn(&ctx->dev->v4l2_dev, - "%s field isn't supported\n", __func__); - return -EINVAL; - } - } - - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - v4l2_warn(&ctx->dev->v4l2_dev, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - - return 0; -} - -static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) -{ - if (!ctrl) - return; - - v4l2_ctrl_lock(ctrl); - - /* - * Extend the control range if the parsed stream contains a known but - * unsupported value or level. - */ - if (value > ctrl->maximum) { - __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, - ctrl->menu_skip_mask & ~(1 << value), - ctrl->default_value); - } else if (value < ctrl->minimum) { - __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, - ctrl->menu_skip_mask & ~(1 << value), - ctrl->default_value); - } - - __v4l2_ctrl_s_ctrl(ctrl, value); - - v4l2_ctrl_unlock(ctrl); -} - -void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, - u8 level_idc) -{ - const char * const *profile_names; - const char * const *level_names; - struct v4l2_ctrl *profile_ctrl; - struct v4l2_ctrl *level_ctrl; - const char *codec_name; - u32 profile_cid; - u32 level_cid; - int profile; - int level; - - switch (ctx->codec->src_fourcc) { - case V4L2_PIX_FMT_H264: - codec_name = "H264"; - profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - profile_ctrl = ctx->h264_profile_ctrl; - level_ctrl = ctx->h264_level_ctrl; - profile = coda_h264_profile(profile_idc); - level = coda_h264_level(level_idc); - break; - case V4L2_PIX_FMT_MPEG2: - codec_name = "MPEG-2"; - profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; - profile_ctrl = ctx->mpeg2_profile_ctrl; - level_ctrl = ctx->mpeg2_level_ctrl; - profile = coda_mpeg2_profile(profile_idc); - level = coda_mpeg2_level(level_idc); - break; - case V4L2_PIX_FMT_MPEG4: - codec_name = "MPEG-4"; - profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; - level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; - profile_ctrl = ctx->mpeg4_profile_ctrl; - level_ctrl = ctx->mpeg4_level_ctrl; - profile = coda_mpeg4_profile(profile_idc); - level = coda_mpeg4_level(level_idc); - break; - default: - return; - } - - profile_names = v4l2_ctrl_get_menu(profile_cid); - level_names = v4l2_ctrl_get_menu(level_cid); - - if (profile < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", - codec_name, profile_idc); - } else { - coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, - profile_names[profile]); - coda_update_menu_ctrl(profile_ctrl, profile); - } - - if (level < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", - codec_name, level_idc); - } else { - coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, - level_names[level]); - coda_update_menu_ctrl(level_ctrl, level); - } -} - -static void coda_queue_source_change_event(struct coda_ctx *ctx) -{ - static const struct v4l2_event source_change_event = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, - }; - - v4l2_event_queue_fh(&ctx->fh, &source_change_event); -} - -static void coda_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *vq = vb->vb2_queue; - struct coda_q_data *q_data; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - - /* - * In the decoder case, immediately try to copy the buffer into the - * bitstream ringbuffer and mark it as ready to be dequeued. - */ - if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* - * For backwards compatibility, queuing an empty buffer marks - * the stream end - */ - if (vb2_get_plane_payload(vb, 0) == 0) - coda_bit_stream_end_flag(ctx); - - if (q_data->fourcc == V4L2_PIX_FMT_H264) { - /* - * Unless already done, try to obtain profile_idc and - * level_idc from the SPS header. This allows to decide - * whether to enable reordering during sequence - * initialization. - */ - if (!ctx->params.h264_profile_idc) { - coda_sps_parse_profile(ctx, vb); - coda_update_profile_level_ctrls(ctx, - ctx->params.h264_profile_idc, - ctx->params.h264_level_idc); - } - } - - mutex_lock(&ctx->bitstream_mutex); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); - if (vb2_is_streaming(vb->vb2_queue)) - /* This set buf->sequence = ctx->qsequence++ */ - coda_fill_bitstream(ctx, NULL); - mutex_unlock(&ctx->bitstream_mutex); - - if (!ctx->initialized) { - /* - * Run sequence initialization in case the queued - * buffer contained headers. - */ - if (vb2_is_streaming(vb->vb2_queue) && - ctx->ops->seq_init_work) { - queue_work(ctx->dev->workqueue, - &ctx->seq_init_work); - flush_work(&ctx->seq_init_work); - } - - if (ctx->initialized) - coda_queue_source_change_event(ctx); - } - } else { - if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) && - vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - vbuf->sequence = ctx->qsequence++; - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); - } -} - -int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, - size_t size, const char *name, struct dentry *parent) -{ - buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, - GFP_KERNEL); - if (!buf->vaddr) { - v4l2_err(&dev->v4l2_dev, - "Failed to allocate %s buffer of size %zu\n", - name, size); - return -ENOMEM; - } - - buf->size = size; - - if (name && parent) { - buf->blob.data = buf->vaddr; - buf->blob.size = size; - buf->dentry = debugfs_create_blob(name, 0444, parent, - &buf->blob); - } - - return 0; -} - -void coda_free_aux_buf(struct coda_dev *dev, - struct coda_aux_buf *buf) -{ - if (buf->vaddr) { - dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); - buf->vaddr = NULL; - buf->size = 0; - debugfs_remove(buf->dentry); - buf->dentry = NULL; - } -} - -static int coda_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; - struct coda_q_data *q_data_src, *q_data_dst; - struct v4l2_m2m_buffer *m2m_buf, *tmp; - struct vb2_v4l2_buffer *buf; - struct list_head list; - int ret = 0; - - if (count < 1) - return -EINVAL; - - coda_dbg(1, ctx, "start streaming %s\n", v4l2_type_names[q->type]); - - INIT_LIST_HEAD(&list); - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - /* copy the buffers that were queued before streamon */ - mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx, &list); - mutex_unlock(&ctx->bitstream_mutex); - - if (ctx->dev->devtype->product != CODA_960 && - coda_get_bitstream_payload(ctx) < 512) { - v4l2_err(v4l2_dev, "start payload < 512\n"); - ret = -EINVAL; - goto err; - } - - if (!ctx->initialized) { - /* Run sequence initialization */ - if (ctx->ops->seq_init_work) { - queue_work(ctx->dev->workqueue, - &ctx->seq_init_work); - flush_work(&ctx->seq_init_work); - } - } - } - - /* - * Check the first input JPEG buffer to determine chroma - * subsampling. - */ - if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { - buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - coda_jpeg_decode_header(ctx, &buf->vb2_buf); - /* - * We have to start streaming even if the first buffer - * does not contain a valid JPEG image. The error will - * be caught during device run and will be signalled - * via the capture buffer error flag. - */ - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - q_data_dst->width = round_up(q_data_src->width, 16); - q_data_dst->height = round_up(q_data_src->height, 16); - q_data_dst->bytesperline = q_data_dst->width; - if (ctx->params.jpeg_chroma_subsampling == - V4L2_JPEG_CHROMA_SUBSAMPLING_420) { - q_data_dst->sizeimage = - q_data_dst->bytesperline * - q_data_dst->height * 3 / 2; - if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) - q_data_dst->fourcc = V4L2_PIX_FMT_NV12; - } else { - q_data_dst->sizeimage = - q_data_dst->bytesperline * - q_data_dst->height * 2; - q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; - } - q_data_dst->rect.left = 0; - q_data_dst->rect.top = 0; - q_data_dst->rect.width = q_data_src->width; - q_data_dst->rect.height = q_data_src->height; - } - ctx->streamon_out = 1; - } else { - ctx->streamon_cap = 1; - } - - /* Don't start the coda unless both queues are on */ - if (!(ctx->streamon_out && ctx->streamon_cap)) - goto out; - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if ((q_data_src->rect.width != q_data_dst->width && - round_up(q_data_src->rect.width, 16) != q_data_dst->width) || - (q_data_src->rect.height != q_data_dst->height && - round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { - v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", - q_data_src->rect.width, q_data_src->rect.height, - q_data_dst->width, q_data_dst->height); - ret = -EINVAL; - goto err; - } - - /* Allow BIT decoder device_run with no new buffers queued */ - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) - v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); - - ctx->gopcounter = ctx->params.gop_size - 1; - - if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) - ctx->params.gop_size = 1; - ctx->gopcounter = ctx->params.gop_size - 1; - /* Only decoders have this control */ - if (ctx->mb_err_cnt_ctrl) - v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); - - ret = ctx->ops->start_streaming(ctx); - if (ctx->inst_type == CODA_INST_DECODER) { - if (ret == -EAGAIN) - goto out; - } - if (ret < 0) - goto err; - -out: - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - list_for_each_entry_safe(m2m_buf, tmp, &list, list) { - list_del(&m2m_buf->list); - v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_DONE); - } - } - return 0; - -err: - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - list_for_each_entry_safe(m2m_buf, tmp, &list, list) { - list_del(&m2m_buf->list); - v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_QUEUED); - } - while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - } else { - while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - } - return ret; -} - -static void coda_stop_streaming(struct vb2_queue *q) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *buf; - bool stop; - - stop = ctx->streamon_out && ctx->streamon_cap; - - coda_dbg(1, ctx, "stop streaming %s\n", v4l2_type_names[q->type]); - - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - ctx->streamon_out = 0; - - coda_bit_stream_end_flag(ctx); - - ctx->qsequence = 0; - - while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - } else { - ctx->streamon_cap = 0; - - ctx->osequence = 0; - ctx->sequence_offset = 0; - - while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - } - - if (stop) { - struct coda_buffer_meta *meta; - - if (ctx->ops->seq_end_work) { - queue_work(dev->workqueue, &ctx->seq_end_work); - flush_work(&ctx->seq_end_work); - } - spin_lock(&ctx->buffer_meta_lock); - while (!list_empty(&ctx->buffer_meta_list)) { - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); - list_del(&meta->list); - kfree(meta); - } - ctx->num_metas = 0; - spin_unlock(&ctx->buffer_meta_lock); - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - ctx->runcounter = 0; - ctx->aborting = 0; - ctx->hold = false; - } - - if (!ctx->streamon_out && !ctx->streamon_cap) - ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; -} - -static const struct vb2_ops coda_qops = { - .queue_setup = coda_queue_setup, - .buf_prepare = coda_buf_prepare, - .buf_queue = coda_buf_queue, - .start_streaming = coda_start_streaming, - .stop_streaming = coda_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int coda_s_ctrl(struct v4l2_ctrl *ctrl) -{ - const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); - struct coda_ctx *ctx = - container_of(ctrl->handler, struct coda_ctx, ctrls); - - if (val_names) - coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", - ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); - else - coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", - ctrl->id, ctrl->name, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_HOR; - else - ctx->params.rot_mode &= ~CODA_MIR_HOR; - break; - case V4L2_CID_VFLIP: - if (ctrl->val) - ctx->params.rot_mode |= CODA_MIR_VER; - else - ctx->params.rot_mode &= ~CODA_MIR_VER; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctx->params.bitrate = ctrl->val / 1000; - ctx->params.bitrate_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctx->params.gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - ctx->params.h264_intra_qp = ctrl->val; - ctx->params.h264_intra_qp_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - ctx->params.h264_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: - ctx->params.h264_min_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - ctx->params.h264_max_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: - ctx->params.h264_slice_alpha_c0_offset_div2 = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: - ctx->params.h264_slice_beta_offset_div2 = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: - ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: - ctx->params.h264_constrained_intra_pred_flag = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - ctx->params.frame_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - ctx->params.mb_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: - ctx->params.h264_chroma_qp_index_offset = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - /* TODO: switch between baseline and constrained baseline */ - if (ctx->inst_type == CODA_INST_ENCODER) - ctx->params.h264_profile_idc = 66; - break; - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - /* nothing to do, this is set by the encoder */ - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: - ctx->params.mpeg4_intra_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: - ctx->params.mpeg4_inter_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: - case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - /* nothing to do, these are fixed */ - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: - ctx->params.slice_mode = ctrl->val; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: - ctx->params.slice_max_mb = ctrl->val; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: - ctx->params.slice_max_bits = ctrl->val * 8; - ctx->params.slice_mode_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - break; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: - ctx->params.intra_refresh = ctrl->val; - ctx->params.intra_refresh_changed = true; - break; - case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: - ctx->params.force_ipicture = true; - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - coda_set_jpeg_compression_quality(ctx, ctrl->val); - break; - case V4L2_CID_JPEG_RESTART_INTERVAL: - ctx->params.jpeg_restart_interval = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_VBV_DELAY: - ctx->params.vbv_delay = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_VBV_SIZE: - ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); - break; - default: - coda_dbg(1, ctx, "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops coda_ctrl_ops = { - .s_ctrl = coda_s_ctrl, -}; - -static void coda_encode_ctrls(struct coda_ctx *ctx) -{ - int max_gop_size = (ctx->dev->devtype->product == CODA_DX6) ? 60 : 99; - - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, max_gop_size, 1, 16); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); - if (ctx->dev->devtype->product != CODA_960) { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); - } - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, - V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, - 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, - 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, 0x0, - V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE); - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_3_1, - ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1)), - V4L2_MPEG_VIDEO_H264_LEVEL_3_1); - } - if (ctx->dev->devtype->product == CODA_960) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_4_2, - ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | - (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2)), - V4L2_MPEG_VIDEO_H264_LEVEL_4_0); - } - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541 || - ctx->dev->devtype->product == CODA_960) { - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, - ~(1 << V4L2_MPEG_VIDEO_MPEG4_LEVEL_5), - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); - } - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, - V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, - 500); - v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_HEADER_MODE, - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, - 1920 * 1088 / 256, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); - /* - * The maximum VBV size value is 0x7fffffff bits, - * one bit less than 262144 KiB - */ - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); -} - -static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) -{ - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); -} - -static void coda_decode_ctrls(struct coda_ctx *ctx) -{ - u8 max; - - ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), - V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); - if (ctx->h264_profile_ctrl) - ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - if (ctx->dev->devtype->product == CODA_HX4 || - ctx->dev->devtype->product == CODA_7541) - max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - else if (ctx->dev->devtype->product == CODA_960) - max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; - else - return; - ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); - if (ctx->h264_level_ctrl) - ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, - V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, - V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); - if (ctx->mpeg2_profile_ctrl) - ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, - V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, - V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); - if (ctx->mpeg2_level_ctrl) - ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, - V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); - if (ctx->mpeg4_profile_ctrl) - ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, - &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, - V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); - if (ctx->mpeg4_level_ctrl) - ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; -} - -static const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { - .id = V4L2_CID_CODA_MB_ERR_CNT, - .name = "Macroblocks Error Count", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 0x7fffffff, - .step = 1, -}; - -static int coda_ctrls_setup(struct coda_ctx *ctx) -{ - v4l2_ctrl_handler_init(&ctx->ctrls, 2); - - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - if (ctx->inst_type == CODA_INST_ENCODER) { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, - 1, 1, 1, 1); - if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) - coda_jpeg_encode_ctrls(ctx); - else - coda_encode_ctrls(ctx); - } else { - v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, - 1, 1, 1, 1); - if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) - coda_decode_ctrls(ctx); - - ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, - &coda_mb_err_cnt_ctrl_config, - NULL); - if (ctx->mb_err_cnt_ctrl) - ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - } - - if (ctx->ctrls.error) { - v4l2_err(&ctx->dev->v4l2_dev, - "control initialization error (%d)", - ctx->ctrls.error); - return -EINVAL; - } - - return v4l2_ctrl_handler_setup(&ctx->ctrls); -} - -static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) -{ - vq->drv_priv = ctx; - vq->ops = &coda_qops; - vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - vq->lock = &ctx->dev->dev_mutex; - /* One way to indicate end-of-stream for coda is to set the - * bytesused == 0. However by default videobuf2 handles bytesused - * equal to 0 as a special case and changes its value to the size - * of the buffer. Set the allow_zero_bytesused flag, so - * that videobuf2 will keep the value of bytesused intact. - */ - vq->allow_zero_bytesused = 1; - /* - * We might be fine with no buffers on some of the queues, but that - * would need to be reflected in job_ready(). Currently we expect all - * queues to have at least one buffer queued. - */ - vq->min_buffers_needed = 1; - vq->dev = ctx->dev->dev; - - return vb2_queue_init(vq); -} - -int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; - - ret = coda_queue_init(priv, src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->mem_ops = &vb2_dma_contig_memops; - - return coda_queue_init(priv, dst_vq); -} - -int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - src_vq->mem_ops = &vb2_vmalloc_memops; - - ret = coda_queue_init(priv, src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; - dst_vq->mem_ops = &vb2_dma_contig_memops; - - return coda_queue_init(priv, dst_vq); -} - -/* - * File operations - */ - -static int coda_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct coda_dev *dev = video_get_drvdata(vdev); - struct coda_ctx *ctx; - unsigned int max = ~0; - char *name; - int ret; - int idx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - if (dev->devtype->product == CODA_DX6) - max = CODADX6_MAX_INSTANCES - 1; - idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL); - if (idx < 0) { - ret = idx; - goto err_coda_max; - } - - name = kasprintf(GFP_KERNEL, "context%d", idx); - if (!name) { - ret = -ENOMEM; - goto err_coda_name_init; - } - - ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); - kfree(name); - - ctx->cvd = to_coda_video_device(vdev); - ctx->inst_type = ctx->cvd->type; - ctx->ops = ctx->cvd->ops; - ctx->use_bit = !ctx->cvd->direct; - init_completion(&ctx->completion); - INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); - if (ctx->ops->seq_init_work) - INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); - if (ctx->ops->seq_end_work) - INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - ctx->dev = dev; - ctx->idx = idx; - - coda_dbg(1, ctx, "open instance (%p)\n", ctx); - - switch (dev->devtype->product) { - case CODA_960: - /* - * Enabling the BWB when decoding can hang the firmware with - * certain streams. The issue was tracked as ENGR00293425 by - * Freescale. As a workaround, disable BWB for all decoders. - * The enable_bwb module parameter allows to override this. - */ - if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) - ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; - fallthrough; - case CODA_HX4: - case CODA_7541: - ctx->reg_idx = 0; - break; - default: - ctx->reg_idx = idx; - } - if (ctx->dev->vdoa && !disable_vdoa) { - ctx->vdoa = vdoa_context_create(dev->vdoa); - if (!ctx->vdoa) - v4l2_warn(&dev->v4l2_dev, - "Failed to create vdoa context: not using vdoa"); - } - ctx->use_vdoa = false; - - /* Power up and upload firmware if necessary */ - ret = pm_runtime_resume_and_get(dev->dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); - goto err_pm_get; - } - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_enable; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - set_default_params(ctx); - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, - ctx->ops->queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - - v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", - __func__, ret); - goto err_ctx_init; - } - - ret = coda_ctrls_setup(ctx); - if (ret) { - v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); - goto err_ctrls_setup; - } - - ctx->fh.ctrl_handler = &ctx->ctrls; - - mutex_init(&ctx->bitstream_mutex); - mutex_init(&ctx->buffer_mutex); - mutex_init(&ctx->wakeup_mutex); - INIT_LIST_HEAD(&ctx->buffer_meta_list); - spin_lock_init(&ctx->buffer_meta_lock); - - return 0; - -err_ctrls_setup: - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); -err_ctx_init: - clk_disable_unprepare(dev->clk_ahb); -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_enable: - pm_runtime_put_sync(dev->dev); -err_pm_get: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); -err_coda_name_init: - ida_free(&dev->ida, ctx->idx); -err_coda_max: - kfree(ctx); - return ret; -} - -static int coda_release(struct file *file) -{ - struct coda_dev *dev = video_drvdata(file); - struct coda_ctx *ctx = fh_to_ctx(file->private_data); - - coda_dbg(1, ctx, "release instance (%p)\n", ctx); - - if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) - coda_bit_stream_end_flag(ctx); - - /* If this instance is running, call .job_abort and wait for it to end */ - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - - if (ctx->vdoa) - vdoa_context_destroy(ctx->vdoa); - - /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->ops->seq_end_work) { - queue_work(dev->workqueue, &ctx->seq_end_work); - flush_work(&ctx->seq_end_work); - } - - if (ctx->dev->devtype->product == CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - - v4l2_ctrl_handler_free(&ctx->ctrls); - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - pm_runtime_put_sync(dev->dev); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - ida_free(&dev->ida, ctx->idx); - if (ctx->ops->release) - ctx->ops->release(ctx); - debugfs_remove_recursive(ctx->debugfs_entry); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations coda_fops = { - .owner = THIS_MODULE, - .open = coda_open, - .release = coda_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int coda_hw_init(struct coda_dev *dev) -{ - u32 data; - u16 *p; - int i, ret; - - ret = clk_prepare_enable(dev->clk_per); - if (ret) - goto err_clk_per; - - ret = clk_prepare_enable(dev->clk_ahb); - if (ret) - goto err_clk_ahb; - - reset_control_reset(dev->rstc); - - /* - * Copy the first CODA_ISRAM_SIZE in the internal SRAM. - * The 16-bit chars in the code buffer are in memory access - * order, re-sort them to CODA order for register download. - * Data in this SRAM survives a reboot. - */ - p = (u16 *)dev->codebuf.vaddr; - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[i ^ 1]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } else { - for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { - data = CODA_DOWN_ADDRESS_SET(i) | - CODA_DOWN_DATA_SET(p[round_down(i, 4) + - 3 - (i % 4)]); - coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); - } - } - - /* Clear registers */ - for (i = 0; i < 64; i++) - coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); - - /* Tell the BIT where to find everything it needs */ - if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541 || - dev->devtype->product == CODA_HX4) { - coda_write(dev, dev->tempbuf.paddr, - CODA_REG_BIT_TEMP_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); - } else { - coda_write(dev, dev->workbuf.paddr, - CODA_REG_BIT_WORK_BUF_ADDR); - } - coda_write(dev, dev->codebuf.paddr, - CODA_REG_BIT_CODE_BUF_ADDR); - coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); - - /* Set default values */ - switch (dev->devtype->product) { - case CODA_DX6: - coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, - CODA_REG_BIT_STREAM_CTRL); - break; - default: - coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, - CODA_REG_BIT_STREAM_CTRL); - } - if (dev->devtype->product == CODA_960) - coda_write(dev, CODA9_FRAME_ENABLE_BWB, - CODA_REG_BIT_FRAME_MEM_CTRL); - else - coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); - - if (dev->devtype->product != CODA_DX6) - coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); - - coda_write(dev, CODA_INT_INTERRUPT_ENABLE, - CODA_REG_BIT_INT_ENABLE); - - /* Reset VPU and start processor */ - data = coda_read(dev, CODA_REG_BIT_CODE_RESET); - data |= CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - udelay(10); - data &= ~CODA_REG_RESET_ENABLE; - coda_write(dev, data, CODA_REG_BIT_CODE_RESET); - coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - - clk_disable_unprepare(dev->clk_ahb); - clk_disable_unprepare(dev->clk_per); - - return 0; - -err_clk_ahb: - clk_disable_unprepare(dev->clk_per); -err_clk_per: - return ret; -} - -static int coda_register_device(struct coda_dev *dev, int i) -{ - struct video_device *vfd = &dev->vfd[i]; - const char *name; - int ret; - - if (i >= dev->devtype->num_vdevs) - return -EINVAL; - name = dev->devtype->vdevs[i]->name; - - strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); - vfd->fops = &coda_fops; - vfd->ioctl_ops = &coda_ioctl_ops; - vfd->release = video_device_release_empty; - vfd->lock = &dev->dev_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - video_set_drvdata(vfd, dev); - - /* Not applicable, use the selection API instead */ - v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); - v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); - v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - - if (dev->devtype->vdevs[i]->type == CODA_INST_ENCODER) { - v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); - v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); - if (dev->devtype->vdevs[i]->dst_formats[0] == V4L2_PIX_FMT_JPEG) { - v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); - v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); - v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); - } - } else { - v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); - v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); - v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMESIZES); - v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); - v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); - v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (!ret) - v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", - name, video_device_node_name(vfd)); - return ret; -} - -static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, - size_t size) -{ - u32 *src = (u32 *)buf; - - /* Check if the firmware has a 16-byte Freescale header, skip it */ - if (buf[0] == 'M' && buf[1] == 'X') - src += 4; - /* - * Check whether the firmware is in native order or pre-reordered for - * memory access. The first instruction opcode always is 0xe40e. - */ - if (__le16_to_cpup((__le16 *)src) == 0xe40e) { - u32 *dst = dev->codebuf.vaddr; - int i; - - /* Firmware in native order, reorder while copying */ - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < (size - 16) / 4; i++) - dst[i] = (src[i] << 16) | (src[i] >> 16); - } else { - for (i = 0; i < (size - 16) / 4; i += 2) { - dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); - dst[i + 1] = (src[i] << 16) | (src[i] >> 16); - } - } - } else { - /* Copy the already reordered firmware image */ - memcpy(dev->codebuf.vaddr, src, size); - } -} - -static void coda_fw_callback(const struct firmware *fw, void *context); - -static int coda_firmware_request(struct coda_dev *dev) -{ - char *fw; - - if (dev->firmware >= ARRAY_SIZE(dev->devtype->firmware)) - return -EINVAL; - - fw = dev->devtype->firmware[dev->firmware]; - - dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, - coda_product_name(dev->devtype->product)); - - return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, - GFP_KERNEL, dev, coda_fw_callback); -} - -static void coda_fw_callback(const struct firmware *fw, void *context) -{ - struct coda_dev *dev = context; - int i, ret; - - if (!fw) { - dev->firmware++; - ret = coda_firmware_request(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); - goto put_pm; - } - return; - } - if (dev->firmware > 0) { - /* - * Since we can't suppress warnings for failed asynchronous - * firmware requests, report that the fallback firmware was - * found. - */ - dev_info(dev->dev, "Using fallback firmware %s\n", - dev->devtype->firmware[dev->firmware]); - } - - /* allocate auxiliary per-device code buffer for the BIT processor */ - ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", - dev->debugfs_root); - if (ret < 0) - goto put_pm; - - coda_copy_firmware(dev, fw->data, fw->size); - release_firmware(fw); - - ret = coda_hw_init(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); - goto put_pm; - } - - ret = coda_check_firmware(dev); - if (ret < 0) - goto put_pm; - - dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); - goto put_pm; - } - - for (i = 0; i < dev->devtype->num_vdevs; i++) { - ret = coda_register_device(dev, i); - if (ret) { - v4l2_err(&dev->v4l2_dev, - "Failed to register %s video device: %d\n", - dev->devtype->vdevs[i]->name, ret); - goto rel_vfd; - } - } - - pm_runtime_put_sync(dev->dev); - return; - -rel_vfd: - while (--i >= 0) - video_unregister_device(&dev->vfd[i]); - v4l2_m2m_release(dev->m2m_dev); -put_pm: - pm_runtime_put_sync(dev->dev); -} - -enum coda_platform { - CODA_IMX27, - CODA_IMX51, - CODA_IMX53, - CODA_IMX6Q, - CODA_IMX6DL, -}; - -static const struct coda_devtype coda_devdata[] = { - [CODA_IMX27] = { - .firmware = { - "vpu_fw_imx27_TO2.bin", - "vpu/vpu_fw_imx27_TO2.bin", - "v4l-codadx6-imx27.bin" - }, - .product = CODA_DX6, - .codecs = codadx6_codecs, - .num_codecs = ARRAY_SIZE(codadx6_codecs), - .vdevs = codadx6_video_devices, - .num_vdevs = ARRAY_SIZE(codadx6_video_devices), - .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, - .iram_size = 0xb000, - }, - [CODA_IMX51] = { - .firmware = { - "vpu_fw_imx51.bin", - "vpu/vpu_fw_imx51.bin", - "v4l-codahx4-imx51.bin" - }, - .product = CODA_HX4, - .codecs = codahx4_codecs, - .num_codecs = ARRAY_SIZE(codahx4_codecs), - .vdevs = codahx4_video_devices, - .num_vdevs = ARRAY_SIZE(codahx4_video_devices), - .workbuf_size = 128 * 1024, - .tempbuf_size = 304 * 1024, - .iram_size = 0x14000, - }, - [CODA_IMX53] = { - .firmware = { - "vpu_fw_imx53.bin", - "vpu/vpu_fw_imx53.bin", - "v4l-coda7541-imx53.bin" - }, - .product = CODA_7541, - .codecs = coda7_codecs, - .num_codecs = ARRAY_SIZE(coda7_codecs), - .vdevs = coda7_video_devices, - .num_vdevs = ARRAY_SIZE(coda7_video_devices), - .workbuf_size = 128 * 1024, - .tempbuf_size = 304 * 1024, - .iram_size = 0x14000, - }, - [CODA_IMX6Q] = { - .firmware = { - "vpu_fw_imx6q.bin", - "vpu/vpu_fw_imx6q.bin", - "v4l-coda960-imx6q.bin" - }, - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .vdevs = coda9_video_devices, - .num_vdevs = ARRAY_SIZE(coda9_video_devices), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x21000, - }, - [CODA_IMX6DL] = { - .firmware = { - "vpu_fw_imx6d.bin", - "vpu/vpu_fw_imx6d.bin", - "v4l-coda960-imx6dl.bin" - }, - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), - .vdevs = coda9_video_devices, - .num_vdevs = ARRAY_SIZE(coda9_video_devices), - .workbuf_size = 80 * 1024, - .tempbuf_size = 204 * 1024, - .iram_size = 0x1f000, /* leave 4k for suspend code */ - }, -}; - -static const struct of_device_id coda_dt_ids[] = { - { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, - { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, - { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, - { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, - { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, coda_dt_ids); - -static int coda_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct gen_pool *pool; - struct coda_dev *dev; - int ret, irq; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->devtype = of_device_get_match_data(&pdev->dev); - - dev->dev = &pdev->dev; - dev->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(dev->clk_per)) { - dev_err(&pdev->dev, "Could not get per clock\n"); - return PTR_ERR(dev->clk_per); - } - - dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(dev->clk_ahb)) { - dev_err(&pdev->dev, "Could not get ahb clock\n"); - return PTR_ERR(dev->clk_ahb); - } - - /* Get memory for physical registers */ - dev->regs_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dev->regs_base)) - return PTR_ERR(dev->regs_base); - - /* IRQ */ - irq = platform_get_irq_byname(pdev, "bit"); - if (irq < 0) - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, - CODA_NAME "-video", dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - /* JPEG IRQ */ - if (dev->devtype->product == CODA_960) { - irq = platform_get_irq_byname(pdev, "jpeg"); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - coda9_jpeg_irq_handler, - IRQF_ONESHOT, CODA_NAME "-jpeg", - dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request jpeg irq\n"); - return ret; - } - } - - dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, - NULL); - if (IS_ERR(dev->rstc)) { - ret = PTR_ERR(dev->rstc); - dev_err(&pdev->dev, "failed get reset control: %d\n", ret); - return ret; - } - - /* Get IRAM pool from device tree */ - pool = of_gen_pool_get(np, "iram", 0); - if (!pool) { - dev_err(&pdev->dev, "iram pool not available\n"); - return -ENOMEM; - } - dev->iram_pool = pool; - - /* Get vdoa_data if supported by the platform */ - dev->vdoa = coda_get_vdoa_data(); - if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - ratelimit_default_init(&dev->mb_err_rs); - mutex_init(&dev->dev_mutex); - mutex_init(&dev->coda_mutex); - ida_init(&dev->ida); - - dev->debugfs_root = debugfs_create_dir("coda", NULL); - - /* allocate auxiliary per-device buffers for the BIT processor */ - if (dev->devtype->product == CODA_DX6) { - ret = coda_alloc_aux_buf(dev, &dev->workbuf, - dev->devtype->workbuf_size, "workbuf", - dev->debugfs_root); - if (ret < 0) - goto err_v4l2_register; - } - - if (dev->devtype->tempbuf_size) { - ret = coda_alloc_aux_buf(dev, &dev->tempbuf, - dev->devtype->tempbuf_size, "tempbuf", - dev->debugfs_root); - if (ret < 0) - goto err_v4l2_register; - } - - dev->iram.size = dev->devtype->iram_size; - dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, - &dev->iram.paddr); - if (!dev->iram.vaddr) { - dev_warn(&pdev->dev, "unable to alloc iram\n"); - } else { - memset(dev->iram.vaddr, 0, dev->iram.size); - dev->iram.blob.data = dev->iram.vaddr; - dev->iram.blob.size = dev->iram.size; - dev->iram.dentry = debugfs_create_blob("iram", 0444, - dev->debugfs_root, - &dev->iram.blob); - } - - dev->workqueue = alloc_ordered_workqueue("coda", WQ_MEM_RECLAIM); - if (!dev->workqueue) { - dev_err(&pdev->dev, "unable to alloc workqueue\n"); - ret = -ENOMEM; - goto err_v4l2_register; - } - - platform_set_drvdata(pdev, dev); - - /* - * Start activated so we can directly call coda_hw_init in - * coda_fw_callback regardless of whether CONFIG_PM is - * enabled or whether the device is associated with a PM domain. - */ - pm_runtime_get_noresume(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - ret = coda_firmware_request(dev); - if (ret) - goto err_alloc_workqueue; - return 0; - -err_alloc_workqueue: - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - destroy_workqueue(dev->workqueue); -err_v4l2_register: - v4l2_device_unregister(&dev->v4l2_dev); - return ret; -} - -static void coda_remove(struct platform_device *pdev) -{ - struct coda_dev *dev = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) { - if (video_get_drvdata(&dev->vfd[i])) - video_unregister_device(&dev->vfd[i]); - } - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - pm_runtime_disable(&pdev->dev); - v4l2_device_unregister(&dev->v4l2_dev); - destroy_workqueue(dev->workqueue); - if (dev->iram.vaddr) - gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, - dev->iram.size); - coda_free_aux_buf(dev, &dev->codebuf); - coda_free_aux_buf(dev, &dev->tempbuf); - coda_free_aux_buf(dev, &dev->workbuf); - debugfs_remove_recursive(dev->debugfs_root); - ida_destroy(&dev->ida); -} - -#ifdef CONFIG_PM -static int coda_runtime_resume(struct device *dev) -{ - struct coda_dev *cdev = dev_get_drvdata(dev); - int ret = 0; - - if (dev->pm_domain && cdev->codebuf.vaddr) { - ret = coda_hw_init(cdev); - if (ret) - v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); - } - - return ret; -} -#endif - -static const struct dev_pm_ops coda_pm_ops = { - SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) -}; - -static struct platform_driver coda_driver = { - .probe = coda_probe, - .remove_new = coda_remove, - .driver = { - .name = CODA_NAME, - .of_match_table = coda_dt_ids, - .pm = &coda_pm_ops, - }, -}; - -module_platform_driver(coda_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Javier Martin "); -MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/chips-media/coda-gdi.c b/drivers/media/platform/chips-media/coda-gdi.c deleted file mode 100644 index 59d65daca..000000000 --- a/drivers/media/platform/chips-media/coda-gdi.c +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include -#include "coda.h" - -#define XY2_INVERT BIT(7) -#define XY2_ZERO BIT(6) -#define XY2_TB_XOR BIT(5) -#define XY2_XYSEL BIT(4) -#define XY2_Y (1 << 4) -#define XY2_X (0 << 4) - -#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ - (((XY2_##luma_sel) | (luma_bit)) << 8 | \ - (XY2_##chroma_sel) | (chroma_bit)) - -static const u16 xy2ca_zero_map[16] = { - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), -}; - -static const u16 xy2ca_tiled_map[16] = { - XY2(Y, 0, Y, 0), - XY2(Y, 1, Y, 1), - XY2(Y, 2, Y, 2), - XY2(Y, 3, X, 3), - XY2(X, 3, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), - XY2(ZERO, 0, ZERO, 0), -}; - -/* - * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock - * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). - * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. - */ - -#define RBC_CA (0 << 4) -#define RBC_BA (1 << 4) -#define RBC_RA (2 << 4) -#define RBC_ZERO (3 << 4) - -#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ - (((RBC_##luma_sel) | (luma_bit)) << 6 | \ - (RBC_##chroma_sel) | (chroma_bit)) - -static const u16 rbc2axi_tiled_map[32] = { - RBC(ZERO, 0, ZERO, 0), - RBC(ZERO, 0, ZERO, 0), - RBC(ZERO, 0, ZERO, 0), - RBC(CA, 0, CA, 0), - RBC(CA, 1, CA, 1), - RBC(CA, 2, CA, 2), - RBC(CA, 3, CA, 3), - RBC(CA, 4, CA, 8), - RBC(CA, 8, CA, 9), - RBC(CA, 9, CA, 10), - RBC(CA, 10, CA, 11), - RBC(CA, 11, CA, 12), - RBC(CA, 12, CA, 13), - RBC(CA, 13, CA, 14), - RBC(CA, 14, CA, 15), - RBC(CA, 15, RA, 0), - RBC(RA, 0, RA, 1), - RBC(RA, 1, RA, 2), - RBC(RA, 2, RA, 3), - RBC(RA, 3, RA, 4), - RBC(RA, 4, RA, 5), - RBC(RA, 5, RA, 6), - RBC(RA, 6, RA, 7), - RBC(RA, 7, RA, 8), - RBC(RA, 8, RA, 9), - RBC(RA, 9, RA, 10), - RBC(RA, 10, RA, 11), - RBC(RA, 11, RA, 12), - RBC(RA, 12, RA, 13), - RBC(RA, 13, RA, 14), - RBC(RA, 14, RA, 15), - RBC(RA, 15, ZERO, 0), -}; - -void coda_set_gdi_regs(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - const u16 *xy2ca_map; - u32 xy2rbc_config; - int i; - - switch (ctx->tiled_map_type) { - case GDI_LINEAR_FRAME_MAP: - default: - xy2ca_map = xy2ca_zero_map; - xy2rbc_config = 0; - break; - case GDI_TILED_FRAME_MB_RASTER_MAP: - xy2ca_map = xy2ca_tiled_map; - xy2rbc_config = CODA9_XY2RBC_TILED_MAP | - CODA9_XY2RBC_CA_INC_HOR | - (16 - 1) << 12 | (8 - 1) << 4; - break; - } - - for (i = 0; i < 16; i++) - coda_write(dev, xy2ca_map[i], - CODA9_GDI_XY2_CAS_0 + 4 * i); - for (i = 0; i < 4; i++) - coda_write(dev, XY2(ZERO, 0, ZERO, 0), - CODA9_GDI_XY2_BA_0 + 4 * i); - for (i = 0; i < 16; i++) - coda_write(dev, XY2(ZERO, 0, ZERO, 0), - CODA9_GDI_XY2_RAS_0 + 4 * i); - coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); - if (xy2rbc_config) { - for (i = 0; i < 32; i++) - coda_write(dev, rbc2axi_tiled_map[i], - CODA9_GDI_RBC2_AXI_0 + 4 * i); - } -} diff --git a/drivers/media/platform/chips-media/coda-h264.c b/drivers/media/platform/chips-media/coda-h264.c deleted file mode 100644 index 8bd0aa8af..000000000 --- a/drivers/media/platform/chips-media/coda-h264.c +++ /dev/null @@ -1,429 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - H.264 helper functions - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - */ - -#include -#include -#include - -#include "coda.h" - -static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; - -static const u8 *coda_find_nal_header(const u8 *buf, const u8 *end) -{ - u32 val = 0xffffffff; - - do { - val = val << 8 | *buf++; - if (buf >= end) - return NULL; - } while (val != 0x00000001); - - return buf; -} - -int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - const u8 *buf = vb2_plane_vaddr(vb, 0); - const u8 *end = buf + vb2_get_plane_payload(vb, 0); - - /* Find SPS header */ - do { - buf = coda_find_nal_header(buf, end); - if (!buf) - return -EINVAL; - } while ((*buf++ & 0x1f) != 0x7); - - ctx->params.h264_profile_idc = buf[0]; - ctx->params.h264_level_idc = buf[2]; - - return 0; -} - -int coda_h264_filler_nal(int size, char *p) -{ - if (size < 6) - return -EINVAL; - - p[0] = 0x00; - p[1] = 0x00; - p[2] = 0x00; - p[3] = 0x01; - p[4] = 0x0c; - memset(p + 5, 0xff, size - 6); - /* Add rbsp stop bit and trailing at the end */ - p[size - 1] = 0x80; - - return 0; -} - -int coda_h264_padding(int size, char *p) -{ - int nal_size; - int diff; - - diff = size - (size & ~0x7); - if (diff == 0) - return 0; - - nal_size = coda_filler_size[diff]; - coda_h264_filler_nal(nal_size, p); - - return nal_size; -} - -int coda_h264_profile(int profile_idc) -{ - switch (profile_idc) { - case 66: return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - case 77: return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; - case 88: return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; - case 100: return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; - default: return -EINVAL; - } -} - -int coda_h264_level(int level_idc) -{ - switch (level_idc) { - case 10: return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; - case 9: return V4L2_MPEG_VIDEO_H264_LEVEL_1B; - case 11: return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; - case 12: return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; - case 13: return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; - case 20: return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; - case 21: return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; - case 22: return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; - case 30: return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; - case 31: return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; - case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; - case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; - case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; - case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; - case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; - default: return -EINVAL; - } -} - -struct rbsp { - char *buf; - int size; - int pos; -}; - -static inline int rbsp_read_bit(struct rbsp *rbsp) -{ - int shift = 7 - (rbsp->pos % 8); - int ofs = rbsp->pos++ / 8; - - if (ofs >= rbsp->size) - return -EINVAL; - - return (rbsp->buf[ofs] >> shift) & 1; -} - -static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) -{ - int shift = 7 - (rbsp->pos % 8); - int ofs = rbsp->pos++ / 8; - - if (ofs >= rbsp->size) - return -EINVAL; - - rbsp->buf[ofs] &= ~(1 << shift); - rbsp->buf[ofs] |= bit << shift; - - return 0; -} - -static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) -{ - int i, ret; - int tmp = 0; - - if (num > 32) - return -EINVAL; - - for (i = 0; i < num; i++) { - ret = rbsp_read_bit(rbsp); - if (ret < 0) - return ret; - tmp |= ret << (num - i - 1); - } - - if (val) - *val = tmp; - - return 0; -} - -static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) -{ - int ret; - - while (num--) { - ret = rbsp_write_bit(rbsp, (value >> num) & 1); - if (ret) - return ret; - } - - return 0; -} - -static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) -{ - int leading_zero_bits = 0; - unsigned int tmp = 0; - int ret; - - while ((ret = rbsp_read_bit(rbsp)) == 0) - leading_zero_bits++; - if (ret < 0) - return ret; - - if (leading_zero_bits > 0) { - ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); - if (ret) - return ret; - } - - if (val) - *val = (1 << leading_zero_bits) - 1 + tmp; - - return 0; -} - -static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) -{ - int i; - int ret; - int tmp = value + 1; - int leading_zero_bits = fls(tmp) - 1; - - for (i = 0; i < leading_zero_bits; i++) { - ret = rbsp_write_bit(rbsp, 0); - if (ret) - return ret; - } - - return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); -} - -static int rbsp_read_sev(struct rbsp *rbsp, int *val) -{ - unsigned int tmp; - int ret; - - ret = rbsp_read_uev(rbsp, &tmp); - if (ret) - return ret; - - if (val) { - if (tmp & 1) - *val = (tmp + 1) / 2; - else - *val = -(tmp / 2); - } - - return 0; -} - -/** - * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS - * @ctx: encoder context - * @width: visible width - * @height: visible height - * @buf: buffer containing h.264 SPS RBSP, starting with NAL header - * @size: modified RBSP size return value - * @max_size: available size in buf - * - * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the - * given visible width and height. - */ -int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, - int *size, int max_size) -{ - int profile_idc; - unsigned int pic_order_cnt_type; - int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; - int frame_mbs_only_flag, frame_cropping_flag; - int vui_parameters_present_flag; - unsigned int crop_right, crop_bottom; - struct rbsp sps; - int pos; - int ret; - - if (*size < 8 || *size >= max_size) - return -EINVAL; - - sps.buf = buf + 5; /* Skip NAL header */ - sps.size = *size - 5; - - profile_idc = sps.buf[0]; - /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ - /* Skip level_idc */ - sps.pos = 24; - - /* seq_parameter_set_id */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || - profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || - profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || - profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || - profile_idc == 135) { - dev_err(ctx->fh.vdev->dev_parent, - "%s: Handling profile_idc %d not implemented\n", - __func__, profile_idc); - return -EINVAL; - } - - /* log2_max_frame_num_minus4 */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - ret = rbsp_read_uev(&sps, &pic_order_cnt_type); - if (ret) - return ret; - - if (pic_order_cnt_type == 0) { - /* log2_max_pic_order_cnt_lsb_minus4 */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - } else if (pic_order_cnt_type == 1) { - unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; - - /* delta_pic_order_always_zero_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - /* offset_for_non_ref_pic */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - /* offset_for_top_to_bottom_field */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - - ret = rbsp_read_uev(&sps, - &num_ref_frames_in_pic_order_cnt_cycle); - if (ret) - return ret; - for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { - /* offset_for_ref_frame */ - ret = rbsp_read_sev(&sps, NULL); - if (ret) - return ret; - } - } - - /* max_num_ref_frames */ - ret = rbsp_read_uev(&sps, NULL); - if (ret) - return ret; - - /* gaps_in_frame_num_value_allowed_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); - if (ret) - return ret; - frame_mbs_only_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (!frame_mbs_only_flag) { - /* mb_adaptive_frame_field_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - } - /* direct_8x8_inference_flag */ - ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - - /* Mark position of the frame cropping flag */ - pos = sps.pos; - frame_cropping_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (frame_cropping_flag) { - unsigned int crop_left, crop_top; - - ret = rbsp_read_uev(&sps, &crop_left); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_right); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_top); - if (ret) - return ret; - ret = rbsp_read_uev(&sps, &crop_bottom); - if (ret) - return ret; - } - vui_parameters_present_flag = ret = rbsp_read_bit(&sps); - if (ret < 0) - return ret; - if (vui_parameters_present_flag) { - dev_err(ctx->fh.vdev->dev_parent, - "%s: Handling vui_parameters not implemented\n", - __func__); - return -EINVAL; - } - - crop_right = round_up(width, 16) - width; - crop_bottom = round_up(height, 16) - height; - crop_right /= 2; - if (frame_mbs_only_flag) - crop_bottom /= 2; - else - crop_bottom /= 4; - - - sps.size = max_size - 5; - sps.pos = pos; - frame_cropping_flag = 1; - ret = rbsp_write_bit(&sps, frame_cropping_flag); - if (ret) - return ret; - ret = rbsp_write_uev(&sps, 0); /* crop_left */ - if (ret) - return ret; - ret = rbsp_write_uev(&sps, crop_right); - if (ret) - return ret; - ret = rbsp_write_uev(&sps, 0); /* crop_top */ - if (ret) - return ret; - ret = rbsp_write_uev(&sps, crop_bottom); - if (ret) - return ret; - ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ - if (ret) - return ret; - ret = rbsp_write_bit(&sps, 1); - if (ret) - return ret; - - *size = 5 + DIV_ROUND_UP(sps.pos, 8); - - return 0; -} diff --git a/drivers/media/platform/chips-media/coda-jpeg.c b/drivers/media/platform/chips-media/coda-jpeg.c deleted file mode 100644 index ba8f41002..000000000 --- a/drivers/media/platform/chips-media/coda-jpeg.c +++ /dev/null @@ -1,1547 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - JPEG support functions - * - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "coda.h" -#include "trace.h" - -#define SOI_MARKER 0xffd8 -#define APP9_MARKER 0xffe9 -#define DRI_MARKER 0xffdd -#define DQT_MARKER 0xffdb -#define DHT_MARKER 0xffc4 -#define SOF_MARKER 0xffc0 -#define SOS_MARKER 0xffda -#define EOI_MARKER 0xffd9 - -enum { - CODA9_JPEG_FORMAT_420, - CODA9_JPEG_FORMAT_422, - CODA9_JPEG_FORMAT_224, - CODA9_JPEG_FORMAT_444, - CODA9_JPEG_FORMAT_400, -}; - -struct coda_huff_tab { - u8 luma_dc[16 + 12]; - u8 chroma_dc[16 + 12]; - u8 luma_ac[16 + 162]; - u8 chroma_ac[16 + 162]; - - /* DC Luma, DC Chroma, AC Luma, AC Chroma */ - s16 min[4 * 16]; - s16 max[4 * 16]; - s8 ptr[4 * 16]; -}; - -#define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) - -/* - * Typical Huffman tables for 8-bit precision luminance and - * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3 - */ - -static const unsigned char luma_dc[16 + 12] = { - /* bits */ - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc[16 + 12] = { - /* bits */ - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac[16 + 162 + 2] = { - /* bits */ - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - /* values */ - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, /* padded to 32-bit */ -}; - -static const unsigned char chroma_ac[16 + 162 + 2] = { - /* bits */ - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - /* values */ - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, /* padded to 32-bit */ -}; - -/* - * Quantization tables for luminance and chrominance components in - * zig-zag scan order from the Freescale i.MX VPU libraries - */ - -static unsigned char luma_q[64] = { - 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05, - 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b, - 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a, - 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -}; - -static unsigned char chroma_q[64] = { - 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10, - 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, - 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -}; - -static const unsigned char width_align[] = { - [CODA9_JPEG_FORMAT_420] = 16, - [CODA9_JPEG_FORMAT_422] = 16, - [CODA9_JPEG_FORMAT_224] = 8, - [CODA9_JPEG_FORMAT_444] = 8, - [CODA9_JPEG_FORMAT_400] = 8, -}; - -static const unsigned char height_align[] = { - [CODA9_JPEG_FORMAT_420] = 16, - [CODA9_JPEG_FORMAT_422] = 8, - [CODA9_JPEG_FORMAT_224] = 16, - [CODA9_JPEG_FORMAT_444] = 8, - [CODA9_JPEG_FORMAT_400] = 8, -}; - -static int coda9_jpeg_chroma_format(u32 pixfmt) -{ - switch (pixfmt) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_NV12: - return CODA9_JPEG_FORMAT_420; - case V4L2_PIX_FMT_YUV422P: - return CODA9_JPEG_FORMAT_422; - case V4L2_PIX_FMT_YUV444: - return CODA9_JPEG_FORMAT_444; - case V4L2_PIX_FMT_GREY: - return CODA9_JPEG_FORMAT_400; - } - return -EINVAL; -} - -struct coda_memcpy_desc { - int offset; - const void *src; - size_t len; -}; - -static void coda_memcpy_parabuf(void *parabuf, - const struct coda_memcpy_desc *desc) -{ - u32 *dst = parabuf + desc->offset; - const u32 *src = desc->src; - int len = desc->len / 4; - int i; - - for (i = 0; i < len; i += 2) { - dst[i + 1] = swab32(src[i]); - dst[i] = swab32(src[i + 1]); - } -} - -int coda_jpeg_write_tables(struct coda_ctx *ctx) -{ - int i; - static const struct coda_memcpy_desc huff[8] = { - { 0, luma_dc, sizeof(luma_dc) }, - { 32, luma_ac, sizeof(luma_ac) }, - { 216, chroma_dc, sizeof(chroma_dc) }, - { 248, chroma_ac, sizeof(chroma_ac) }, - }; - struct coda_memcpy_desc qmat[3] = { - { 512, ctx->params.jpeg_qmat_tab[0], 64 }, - { 576, ctx->params.jpeg_qmat_tab[1], 64 }, - { 640, ctx->params.jpeg_qmat_tab[1], 64 }, - }; - - /* Write huffman tables to parameter memory */ - for (i = 0; i < ARRAY_SIZE(huff); i++) - coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i); - - /* Write Q-matrix to parameter memory */ - for (i = 0; i < ARRAY_SIZE(qmat); i++) - coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i); - - return 0; -} - -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - void *vaddr = vb2_plane_vaddr(vb, 0); - u16 soi, eoi; - int len, i; - - soi = be16_to_cpup((__be16 *)vaddr); - if (soi != SOI_MARKER) - return false; - - len = vb2_get_plane_payload(vb, 0); - vaddr += len - 2; - for (i = 0; i < 32; i++) { - eoi = be16_to_cpup((__be16 *)(vaddr - i)); - if (eoi == EOI_MARKER) { - if (i > 0) - vb2_set_plane_payload(vb, 0, len - i); - return true; - } - } - - return false; -} - -static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); - -int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) -{ - struct coda_dev *dev = ctx->dev; - u8 *buf = vb2_plane_vaddr(vb, 0); - size_t len = vb2_get_plane_payload(vb, 0); - struct v4l2_jpeg_scan_header scan_header; - struct v4l2_jpeg_reference quantization_tables[4] = { }; - struct v4l2_jpeg_reference huffman_tables[4] = { }; - struct v4l2_jpeg_header header = { - .scan = &scan_header, - .quantization_tables = quantization_tables, - .huffman_tables = huffman_tables, - }; - struct coda_q_data *q_data_src; - struct coda_huff_tab *huff_tab; - int i, j, ret; - - ret = v4l2_jpeg_parse_header(buf, len, &header); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to parse JPEG header: %pe\n", - ERR_PTR(ret)); - return ret; - } - - ctx->params.jpeg_restart_interval = header.restart_interval; - - /* check frame header */ - if (header.frame.height > ctx->codec->max_h || - header.frame.width > ctx->codec->max_w) { - v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", - header.frame.width, header.frame.height); - return -EINVAL; - } - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (header.frame.height != q_data_src->height || - header.frame.width != q_data_src->width) { - v4l2_err(&dev->v4l2_dev, - "dimensions don't match format: %dx%d\n", - header.frame.width, header.frame.height); - return -EINVAL; - } - - if (header.frame.num_components != 3) { - v4l2_err(&dev->v4l2_dev, - "unsupported number of components: %d\n", - header.frame.num_components); - return -EINVAL; - } - - /* install quantization tables */ - if (quantization_tables[3].start) { - v4l2_err(&dev->v4l2_dev, - "only 3 quantization tables supported\n"); - return -EINVAL; - } - for (i = 0; i < 3; i++) { - if (!quantization_tables[i].start) - continue; - if (quantization_tables[i].length != 64) { - v4l2_err(&dev->v4l2_dev, - "only 8-bit quantization tables supported\n"); - continue; - } - if (!ctx->params.jpeg_qmat_tab[i]) { - ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[i]) - return -ENOMEM; - } - memcpy(ctx->params.jpeg_qmat_tab[i], - quantization_tables[i].start, 64); - } - - /* install Huffman tables */ - for (i = 0; i < 4; i++) { - if (!huffman_tables[i].start) { - v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); - return -EINVAL; - } - /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ - if (huffman_tables[i].length < 17 || - huffman_tables[i].length > 178 || - ((i & 2) == 0 && huffman_tables[i].length > 28)) { - v4l2_err(&dev->v4l2_dev, - "invalid Huffman table %d length: %zu\n", - i, huffman_tables[i].length); - return -EINVAL; - } - } - huff_tab = ctx->params.jpeg_huff_tab; - if (!huff_tab) { - huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); - if (!huff_tab) - return -ENOMEM; - ctx->params.jpeg_huff_tab = huff_tab; - } - - memset(huff_tab, 0, sizeof(*huff_tab)); - memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); - memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); - memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); - memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); - - /* check scan header */ - for (i = 0; i < scan_header.num_components; i++) { - struct v4l2_jpeg_scan_component_spec *scan_component; - - scan_component = &scan_header.component[i]; - for (j = 0; j < header.frame.num_components; j++) { - if (header.frame.component[j].component_identifier == - scan_component->component_selector) - break; - } - if (j == header.frame.num_components) - continue; - - ctx->params.jpeg_huff_dc_index[j] = - scan_component->dc_entropy_coding_table_selector; - ctx->params.jpeg_huff_ac_index[j] = - scan_component->ac_entropy_coding_table_selector; - } - - /* Generate Huffman table information */ - for (i = 0; i < 4; i++) - coda9_jpeg_gen_dec_huff_tab(ctx, i); - - /* start of entropy coded segment */ - ctx->jpeg_ecs_offset = header.ecs_offset; - - switch (header.frame.subsampling) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; - break; - default: - v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", - header.frame.subsampling); - return -EINVAL; - } - - return 0; -} - -static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, - int num_values) -{ - s8 *values = (s8 *)(bits + 16); - int huff_length, i; - - for (huff_length = 0, i = 0; i < 16; i++) - huff_length += bits[i]; - for (i = huff_length; i < num_values; i++) - values[i] = -1; - for (i = 0; i < num_values; i++) - coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); -} - -static void coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) -{ - struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; - struct coda_dev *dev = ctx->dev; - s16 *huff_min = huff_tab->min; - s16 *huff_max = huff_tab->max; - s8 *huff_ptr = huff_tab->ptr; - int i; - - /* MIN Tables */ - coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); - - /* MAX Tables */ - coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); - - /* PTR Tables */ - coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); - coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); - for (i = 0; i < 4 * 16; i++) - coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); - - /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ - coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); - coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); - coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); - coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); - coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); - coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); -} - -static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, - u8 *qmat, int index) -{ - int i; - - coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); - for (i = 0; i < 64; i++) - coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); - coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); -} - -static void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int *qmat_index = ctx->params.jpeg_qmat_index; - u8 **qmat_tab = ctx->params.jpeg_qmat_tab; - - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); - coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); -} - -static void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, - struct vb2_buffer *buf, u32 ecs_offset) -{ - struct coda_dev *dev = ctx->dev; - int page_ptr, word_ptr, bit_ptr; - u32 bbc_base_addr, end_addr; - int bbc_cur_pos; - int ret, val; - - bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); - end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); - - page_ptr = ecs_offset / 256; - word_ptr = (ecs_offset % 256) / 4; - if (page_ptr & 1) - word_ptr += 64; - bit_ptr = (ecs_offset % 4) * 8; - if (word_ptr & 1) - bit_ptr += 32; - word_ptr &= ~0x1; - - coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); - coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); - - /* Leave 3 256-byte page margin to avoid a BBC interrupt */ - coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); - val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; - coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); - - bbc_cur_pos = page_ptr; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), - CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); - do { - ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); - } while (ret == 1); - - bbc_cur_pos++; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), - CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); - do { - ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); - } while (ret == 1); - - bbc_cur_pos++; - coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); - coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); - - coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); - coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); - coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); - if (page_ptr & 1) { - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); - } else { - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); - } - coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); - coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); - coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); -} - -static const int bus_req_num[] = { - [CODA9_JPEG_FORMAT_420] = 2, - [CODA9_JPEG_FORMAT_422] = 3, - [CODA9_JPEG_FORMAT_224] = 3, - [CODA9_JPEG_FORMAT_444] = 4, - [CODA9_JPEG_FORMAT_400] = 4, -}; - -#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \ - (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \ - ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \ - ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \ - ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \ - ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET)) - -static const u32 mcu_info[] = { - [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5), - [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5), - [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5), - [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5), - [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0), -}; - -/* - * Convert Huffman table specifcations to tables of codes and code lengths. - * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] - * - * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf - */ -static int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num, - int *ehufsi, int *ehufco) -{ - int i, j, k, lastk, si, code, maxsymbol; - const u8 *bits, *huffval; - struct { - int size[256]; - int code[256]; - } *huff; - static const unsigned char *huff_tabs[4] = { - luma_dc, luma_ac, chroma_dc, chroma_ac, - }; - int ret = -EINVAL; - - huff = kzalloc(sizeof(*huff), GFP_KERNEL); - if (!huff) - return -ENOMEM; - - bits = huff_tabs[tab_num]; - huffval = huff_tabs[tab_num] + 16; - - maxsymbol = tab_num & 1 ? 256 : 16; - - /* Figure C.1 - Generation of table of Huffman code sizes */ - k = 0; - for (i = 1; i <= 16; i++) { - j = bits[i - 1]; - if (k + j > maxsymbol) - goto out; - while (j--) - huff->size[k++] = i; - } - lastk = k; - - /* Figure C.2 - Generation of table of Huffman codes */ - k = 0; - code = 0; - si = huff->size[0]; - while (k < lastk) { - while (huff->size[k] == si) { - huff->code[k++] = code; - code++; - } - if (code >= (1 << si)) - goto out; - code <<= 1; - si++; - } - - /* Figure C.3 - Ordering procedure for encoding procedure code tables */ - for (k = 0; k < lastk; k++) { - i = huffval[k]; - if (i >= maxsymbol || ehufsi[i]) - goto out; - ehufco[i] = huff->code[k]; - ehufsi[i] = huff->size[k]; - } - - ret = 0; -out: - kfree(huff); - return ret; -} - -#define DC_TABLE_INDEX0 0 -#define AC_TABLE_INDEX0 1 -#define DC_TABLE_INDEX1 2 -#define AC_TABLE_INDEX1 3 - -static u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) -{ - struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; - - if (!huff_tab) - return NULL; - - switch (tab_num) { - case DC_TABLE_INDEX0: return huff_tab->luma_dc; - case AC_TABLE_INDEX0: return huff_tab->luma_ac; - case DC_TABLE_INDEX1: return huff_tab->chroma_dc; - case AC_TABLE_INDEX1: return huff_tab->chroma_ac; - } - - return NULL; -} - -static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) -{ - int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; - u8 *huff_bits; - s16 *huff_max; - s16 *huff_min; - s8 *huff_ptr; - int ofs; - int i; - - huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); - if (!huff_bits) - return -EINVAL; - - /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ - ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); - ofs *= 16; - - huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; - huff_max = ctx->params.jpeg_huff_tab->max + ofs; - huff_min = ctx->params.jpeg_huff_tab->min + ofs; - - for (i = 0; i < 16; i++) { - if (huff_bits[i]) { - huff_ptr[i] = ptr_cnt; - ptr_cnt += huff_bits[i]; - huff_min[i] = huff_code; - huff_max[i] = huff_code + (huff_bits[i] - 1); - data_flag = 1; - zero_flag = 0; - } else { - huff_ptr[i] = -1; - huff_min[i] = -1; - huff_max[i] = -1; - zero_flag = 1; - } - - if (data_flag == 1) { - if (zero_flag == 1) - huff_code <<= 1; - else - huff_code = (huff_max[i] + 1) << 1; - } - } - - return 0; -} - -static int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) -{ - struct { - int size[4][256]; - int code[4][256]; - } *huff; - u32 *huff_data; - int i, j; - int ret; - - huff = kzalloc(sizeof(*huff), GFP_KERNEL); - if (!huff) - return -ENOMEM; - - /* Generate all four (luma/chroma DC/AC) code/size lookup tables */ - for (i = 0; i < 4; i++) { - ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i], - huff->code[i]); - if (ret) - goto out; - } - - if (!ctx->params.jpeg_huff_data) { - ctx->params.jpeg_huff_data = - kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE, - GFP_KERNEL); - if (!ctx->params.jpeg_huff_data) { - ret = -ENOMEM; - goto out; - } - } - huff_data = ctx->params.jpeg_huff_data; - - for (j = 0; j < 4; j++) { - /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */ - int t = (j == 0) ? AC_TABLE_INDEX0 : - (j == 1) ? AC_TABLE_INDEX1 : - (j == 2) ? DC_TABLE_INDEX0 : - DC_TABLE_INDEX1; - /* DC tables only have 16 entries */ - int len = (j < 2) ? 256 : 16; - - for (i = 0; i < len; i++) { - if (huff->size[t][i] == 0 && huff->code[t][i] == 0) - *(huff_data++) = 0; - else - *(huff_data++) = - ((huff->size[t][i] - 1) << 16) | - huff->code[t][i]; - } - } - - ret = 0; -out: - kfree(huff); - return ret; -} - -static void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 *huff_data = ctx->params.jpeg_huff_data; - int i; - - /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */ - coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL); - for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++) - coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA); - coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL); -} - -static inline void coda9_jpeg_write_qmat_quotients(struct coda_dev *dev, - u8 *qmat, int index) -{ - int i; - - coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); - for (i = 0; i < 64; i++) - coda_write(dev, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA); - coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL); -} - -static void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u8 *luma_tab; - u8 *chroma_tab; - - luma_tab = ctx->params.jpeg_qmat_tab[0]; - if (!luma_tab) - luma_tab = luma_q; - - chroma_tab = ctx->params.jpeg_qmat_tab[1]; - if (!chroma_tab) - chroma_tab = chroma_q; - - coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00); - coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40); - coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80); -} - -struct coda_jpeg_stream { - u8 *curr; - u8 *end; -}; - -static inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream) -{ - if (stream->curr >= stream->end) - return -EINVAL; - - *stream->curr++ = byte; - - return 0; -} - -static inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream) -{ - if (stream->curr + sizeof(__be16) > stream->end) - return -EINVAL; - - put_unaligned_be16(word, stream->curr); - stream->curr += sizeof(__be16); - - return 0; -} - -static int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table, - size_t len, struct coda_jpeg_stream *stream) -{ - int i, ret; - - ret = coda_jpeg_put_word(marker, stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(3 + len, stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(index, stream); - for (i = 0; i < len && ret == 0; i++) - ret = coda_jpeg_put_byte(table[i], stream); - - return ret; -} - -static int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index, - struct coda_jpeg_stream *stream) -{ - return coda_jpeg_put_table(DQT_MARKER, index, - ctx->params.jpeg_qmat_tab[index], 64, - stream); -} - -static int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len, - struct coda_jpeg_stream *stream) -{ - return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream); -} - -static int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf) -{ - struct coda_jpeg_stream stream = { buf, buf + len }; - struct coda_q_data *q_data_src; - int chroma_format, comp_num; - int i, ret, pad; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); - if (chroma_format < 0) - return 0; - - /* Start Of Image */ - ret = coda_jpeg_put_word(SOI_MARKER, &stream); - if (ret < 0) - return ret; - - /* Define Restart Interval */ - if (ctx->params.jpeg_restart_interval) { - ret = coda_jpeg_put_word(DRI_MARKER, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(4, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval, - &stream); - if (ret < 0) - return ret; - } - - /* Define Quantization Tables */ - ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream); - if (ret < 0) - return ret; - if (chroma_format != CODA9_JPEG_FORMAT_400) { - ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream); - if (ret < 0) - return ret; - } - - /* Define Huffman Tables */ - ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream); - if (ret < 0) - return ret; - if (chroma_format != CODA9_JPEG_FORMAT_400) { - ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12, - &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162, - &stream); - if (ret < 0) - return ret; - } - - /* Start Of Frame */ - ret = coda_jpeg_put_word(SOF_MARKER, &stream); - if (ret < 0) - return ret; - comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3; - ret = coda_jpeg_put_word(8 + comp_num * 3, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(0x08, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(q_data_src->height, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_word(q_data_src->width, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(comp_num, &stream); - if (ret < 0) - return ret; - for (i = 0; i < comp_num; i++) { - static unsigned char subsampling[5][3] = { - [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 }, - [CODA9_JPEG_FORMAT_400] = { 0x11 }, - }; - - /* Component identifier, matches SOS */ - ret = coda_jpeg_put_byte(i + 1, &stream); - if (ret < 0) - return ret; - ret = coda_jpeg_put_byte(subsampling[chroma_format][i], - &stream); - if (ret < 0) - return ret; - /* Chroma table index */ - ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream); - if (ret < 0) - return ret; - } - - /* Pad to multiple of 8 bytes */ - pad = (stream.curr - buf) % 8; - if (pad) { - pad = 8 - pad; - while (pad--) { - ret = coda_jpeg_put_byte(0x00, &stream); - if (ret < 0) - return ret; - } - } - - return stream.curr - buf; -} - -/* - * Scale quantization table using nonlinear scaling factor - * u8 qtab[64], scale [50,190] - */ -static void coda_scale_quant_table(u8 *q_tab, int scale) -{ - unsigned int temp; - int i; - - for (i = 0; i < 64; i++) { - temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - q_tab[i] = (unsigned char)temp; - } -} - -void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality) -{ - unsigned int scale; - - ctx->params.jpeg_quality = quality; - - /* Clip quality setting to [5,100] interval */ - if (quality > 100) - quality = 100; - if (quality < 5) - quality = 5; - - /* - * Non-linear scaling factor: - * [5,50] -> [1000..100], [51,100] -> [98..0] - */ - if (quality < 50) - scale = 5000 / quality; - else - scale = 200 - 2 * quality; - - if (ctx->params.jpeg_qmat_tab[0]) { - memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64); - coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale); - } - if (ctx->params.jpeg_qmat_tab[1]) { - memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64); - coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale); - } -} - -/* - * Encoder context operations - */ - -static int coda9_jpeg_start_encoding(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int ret; - - ret = coda9_jpeg_load_huff_tab(ctx); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); - return ret; - } - if (!ctx->params.jpeg_qmat_tab[0]) { - ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[0]) - return -ENOMEM; - } - if (!ctx->params.jpeg_qmat_tab[1]) { - ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[1]) - return -ENOMEM; - } - coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); - - return 0; -} - -static int coda9_jpeg_prepare_encode(struct coda_ctx *ctx) -{ - struct coda_q_data *q_data_src; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 start_addr, end_addr; - u16 aligned_width, aligned_height; - bool chroma_interleave; - int chroma_format; - int header_len; - int ret; - ktime_t timeout; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - - if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) - vb2_set_plane_payload(&src_buf->vb2_buf, 0, - vb2_plane_size(&src_buf->vb2_buf, 0)); - - src_buf->sequence = ctx->osequence; - dst_buf->sequence = ctx->osequence; - ctx->osequence++; - - src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; - - coda_set_gdi_regs(ctx); - - start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0); - - chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); - if (chroma_format < 0) - return chroma_format; - - /* Round image dimensions to multiple of MCU size */ - aligned_width = round_up(q_data_src->width, width_align[chroma_format]); - aligned_height = round_up(q_data_src->height, - height_align[chroma_format]); - if (aligned_width != q_data_src->bytesperline) { - v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n", - aligned_width, q_data_src->bytesperline); - } - - header_len = - coda9_jpeg_encode_header(ctx, - vb2_plane_size(&dst_buf->vb2_buf, 0), - vb2_plane_vaddr(&dst_buf->vb2_buf, 0)); - if (header_len < 0) - return header_len; - - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR); - coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR); - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR); - coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS); - /* 64 words per 256-byte page */ - coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); - coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR); - coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR); - - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); - coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256), - CODA9_REG_JPEG_BBC_STRM_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR); - coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); - coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); - - chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12); - coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION | - CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL); - coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); - coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA9_REG_JPEG_RST_INTVAL); - coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); - - coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); - - coda9_jpeg_write_huff_tab(ctx); - coda9_jpeg_load_qmat_tab(ctx); - - if (ctx->params.rot_mode & CODA_ROT_90) { - aligned_width = aligned_height; - aligned_height = q_data_src->bytesperline; - if (chroma_format == CODA9_JPEG_FORMAT_422) - chroma_format = CODA9_JPEG_FORMAT_224; - else if (chroma_format == CODA9_JPEG_FORMAT_224) - chroma_format = CODA9_JPEG_FORMAT_422; - } - /* These need to be multiples of MCU size */ - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_REG_JPEG_PIC_SIZE); - coda_write(dev, ctx->params.rot_mode ? - (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0, - CODA9_REG_JPEG_ROT_INFO); - - coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); - - coda_write(dev, 1, CODA9_GDI_CONTROL); - timeout = ktime_add_us(ktime_get(), 100000); - do { - ret = coda_read(dev, CODA9_GDI_STATUS); - if (ktime_compare(ktime_get(), timeout) > 0) { - v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n"); - return -ETIMEDOUT; - } - } while (!ret); - - coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) | - q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL); - /* The content of this register seems to be irrelevant: */ - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_GDI_INFO_PIC_SIZE); - - coda_write_base(ctx, q_data_src, src_buf, CODA9_GDI_INFO_BASE_Y); - - coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); - coda_write(dev, 0, CODA9_GDI_CONTROL); - coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); - - coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); - coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - - trace_coda_jpeg_run(ctx, src_buf); - - coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); - - return 0; -} - -static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = ctx->dev; - u32 wr_ptr, start_ptr; - u32 err_mb; - - if (ctx->aborting) { - coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - return; - } - - /* - * Lock to make sure that an encoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - - trace_coda_jpeg_done(ctx, dst_buf); - - /* - * Set plane payload to the number of bytes written out - * by the JPEG processing unit - */ - start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) - coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb); - - coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : - VB2_BUF_STATE_DONE); - mutex_unlock(&ctx->wakeup_mutex); - - coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", - dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); - - /* - * Reset JPEG processing unit after each encode run to work - * around hangups when switching context between encoder and - * decoder. - */ - coda_hw_reset(ctx); -} - -static void coda9_jpeg_encode_timeout(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - u32 end_addr, wr_ptr; - - /* Handle missing BBC overflow interrupt via timeout */ - end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR); - wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); - if (wr_ptr >= end_addr - 256) { - v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n"); - coda9_jpeg_finish_encode(ctx); - return; - } - - coda_hw_reset(ctx); -} - -static void coda9_jpeg_release(struct coda_ctx *ctx) -{ - int i; - - if (ctx->params.jpeg_qmat_tab[0] == luma_q) - ctx->params.jpeg_qmat_tab[0] = NULL; - if (ctx->params.jpeg_qmat_tab[1] == chroma_q) - ctx->params.jpeg_qmat_tab[1] = NULL; - for (i = 0; i < 3; i++) - kfree(ctx->params.jpeg_qmat_tab[i]); - kfree(ctx->params.jpeg_huff_data); - kfree(ctx->params.jpeg_huff_tab); -} - -const struct coda_context_ops coda9_jpeg_encode_ops = { - .queue_init = coda_encoder_queue_init, - .start_streaming = coda9_jpeg_start_encoding, - .prepare_run = coda9_jpeg_prepare_encode, - .finish_run = coda9_jpeg_finish_encode, - .run_timeout = coda9_jpeg_encode_timeout, - .release = coda9_jpeg_release, -}; - -/* - * Decoder context operations - */ - -static int coda9_jpeg_start_decoding(struct coda_ctx *ctx) -{ - ctx->params.jpeg_qmat_index[0] = 0; - ctx->params.jpeg_qmat_index[1] = 1; - ctx->params.jpeg_qmat_index[2] = 1; - ctx->params.jpeg_qmat_tab[0] = luma_q; - ctx->params.jpeg_qmat_tab[1] = chroma_q; - /* nothing more to do here */ - - /* TODO: we could already scan the first header to get the chroma - * format. - */ - - return 0; -} - -static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - int aligned_width, aligned_height; - int chroma_format; - int ret; - u32 val, dst_fourcc; - struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - int chroma_interleave; - int scl_hor_mode, scl_ver_mode; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fourcc; - - scl_hor_mode = coda_jpeg_scale(q_data_src->width, q_data_dst->width); - scl_ver_mode = coda_jpeg_scale(q_data_src->height, q_data_dst->height); - - if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) - vb2_set_plane_payload(&src_buf->vb2_buf, 0, - vb2_plane_size(&src_buf->vb2_buf, 0)); - - chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); - if (chroma_format < 0) - return chroma_format; - - ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); - if (ret < 0) { - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - - return ret; - } - - /* Round image dimensions to multiple of MCU size */ - aligned_width = round_up(q_data_src->width, width_align[chroma_format]); - aligned_height = round_up(q_data_src->height, height_align[chroma_format]); - if (aligned_width != q_data_dst->bytesperline) { - v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", - aligned_width, q_data_dst->bytesperline); - } - - coda_set_gdi_regs(ctx); - - val = ctx->params.jpeg_huff_ac_index[0] << 12 | - ctx->params.jpeg_huff_ac_index[1] << 11 | - ctx->params.jpeg_huff_ac_index[2] << 10 | - ctx->params.jpeg_huff_dc_index[0] << 9 | - ctx->params.jpeg_huff_dc_index[1] << 8 | - ctx->params.jpeg_huff_dc_index[2] << 7; - if (ctx->params.jpeg_huff_tab) - val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; - coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); - - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_REG_JPEG_PIC_SIZE); - - chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); - coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); - coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); - coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); - if (scl_hor_mode || scl_ver_mode) - val = CODA9_JPEG_SCL_ENABLE | (scl_hor_mode << 2) | scl_ver_mode; - else - val = 0; - coda_write(dev, val, CODA9_REG_JPEG_SCL_INFO); - coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); - coda_write(dev, ctx->params.jpeg_restart_interval, - CODA9_REG_JPEG_RST_INTVAL); - - if (ctx->params.jpeg_huff_tab) - coda9_jpeg_dec_huff_setup(ctx); - - coda9_jpeg_qmat_setup(ctx); - - coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, - ctx->jpeg_ecs_offset); - - coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); - coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); - - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); - coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); - - coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); - - coda_write(dev, 1, CODA9_GDI_CONTROL); - do { - ret = coda_read(dev, CODA9_GDI_STATUS); - } while (!ret); - - val = (chroma_format << 17) | (chroma_interleave << 16) | - q_data_dst->bytesperline; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - val |= 3 << 20; - coda_write(dev, val, CODA9_GDI_INFO_CONTROL); - - coda_write(dev, aligned_width << 16 | aligned_height, - CODA9_GDI_INFO_PIC_SIZE); - - coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); - - coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); - coda_write(dev, 0, CODA9_GDI_CONTROL); - coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); - - trace_coda_jpeg_run(ctx, src_buf); - - coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); - - return 0; -} - -static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) -{ - struct coda_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *dst_buf, *src_buf; - struct coda_q_data *q_data_dst; - u32 err_mb; - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) - v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); - - coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); - - /* - * Lock to make sure that a decoder stop command running in parallel - * will either already have marked src_buf as last, or it will wake up - * the capture queue after the buffers are returned. - */ - mutex_lock(&ctx->wakeup_mutex); - src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->sequence = ctx->osequence++; - - trace_coda_jpeg_done(ctx, dst_buf); - - dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); - - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : - VB2_BUF_STATE_DONE); - - mutex_unlock(&ctx->wakeup_mutex); - - coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", - dst_buf->sequence, - (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); - - /* - * Reset JPEG processing unit after each decode run to work - * around hangups when switching context between encoder and - * decoder. - */ - coda_hw_reset(ctx); -} - -const struct coda_context_ops coda9_jpeg_decode_ops = { - .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ - .start_streaming = coda9_jpeg_start_decoding, - .prepare_run = coda9_jpeg_prepare_decode, - .finish_run = coda9_jpeg_finish_decode, - .release = coda9_jpeg_release, -}; - -irqreturn_t coda9_jpeg_irq_handler(int irq, void *data) -{ - struct coda_dev *dev = data; - struct coda_ctx *ctx; - int status; - int err_mb; - - status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS); - if (status == 0) - return IRQ_HANDLED; - coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS); - - if (status & CODA9_JPEG_STATUS_OVERFLOW) - v4l2_err(&dev->v4l2_dev, "JPEG overflow\n"); - - if (status & CODA9_JPEG_STATUS_BBC_INT) - v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n"); - - if (status & CODA9_JPEG_STATUS_ERROR) { - v4l2_err(&dev->v4l2_dev, "JPEG error\n"); - - err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); - if (err_mb) { - v4l2_err(&dev->v4l2_dev, - "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n", - err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff, - err_mb & 0xfff); - } - } - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (!ctx) { - v4l2_err(&dev->v4l2_dev, - "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; - } - - complete(&ctx->completion); - - return IRQ_HANDLED; -} diff --git a/drivers/media/platform/chips-media/coda-mpeg2.c b/drivers/media/platform/chips-media/coda-mpeg2.c deleted file mode 100644 index 6f3f6721d..000000000 --- a/drivers/media/platform/chips-media/coda-mpeg2.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - MPEG-2 helper functions - * - * Copyright (C) 2019 Pengutronix, Philipp Zabel - */ - -#include -#include -#include "coda.h" - -int coda_mpeg2_profile(int profile_idc) -{ - switch (profile_idc) { - case 5: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; - case 4: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; - case 3: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; - case 2: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; - case 1: - return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; - default: - return -EINVAL; - } -} - -int coda_mpeg2_level(int level_idc) -{ - switch (level_idc) { - case 10: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; - case 8: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; - case 6: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; - case 4: - return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; - default: - return -EINVAL; - } -} - -/* - * Check if the buffer starts with the MPEG-2 sequence header (with or without - * quantization matrix) and extension header, for example: - * - * 00 00 01 b3 2d 01 e0 34 08 8b a3 81 - * 10 11 11 12 12 12 13 13 13 13 14 14 14 14 14 15 - * 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 - * 17 17 17 17 18 18 18 19 18 18 18 19 1a 1a 1a 1a - * 19 1b 1b 1b 1b 1b 1c 1c 1c 1c 1e 1e 1e 1f 1f 21 - * 00 00 01 b5 14 8a 00 01 00 00 - * - * or: - * - * 00 00 01 b3 08 00 40 15 ff ff e0 28 - * 00 00 01 b5 14 8a 00 01 00 00 - * - * Returns the detected header size in bytes or 0. - */ -u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) -{ - static const u8 sequence_header_start[4] = { 0x00, 0x00, 0x01, 0xb3 }; - static const union { - u8 extension_start[4]; - u8 start_code_prefix[3]; - } u = { { 0x00, 0x00, 0x01, 0xb5 } }; - - if (size < 22 || - memcmp(buf, sequence_header_start, 4) != 0) - return 0; - - if ((size == 22 || - (size >= 25 && memcmp(buf + 22, u.start_code_prefix, 3) == 0)) && - memcmp(buf + 12, u.extension_start, 4) == 0) - return 22; - - if ((size == 86 || - (size > 89 && memcmp(buf + 86, u.start_code_prefix, 3) == 0)) && - memcmp(buf + 76, u.extension_start, 4) == 0) - return 86; - - return 0; -} diff --git a/drivers/media/platform/chips-media/coda-mpeg4.c b/drivers/media/platform/chips-media/coda-mpeg4.c deleted file mode 100644 index 483a4fba1..000000000 --- a/drivers/media/platform/chips-media/coda-mpeg4.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Coda multi-standard codec IP - MPEG-4 helper functions - * - * Copyright (C) 2019 Pengutronix, Philipp Zabel - */ - -#include -#include - -#include "coda.h" - -int coda_mpeg4_profile(int profile_idc) -{ - switch (profile_idc) { - case 0: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; - case 15: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; - case 2: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; - case 1: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; - case 11: - return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; - default: - return -EINVAL; - } -} - -int coda_mpeg4_level(int level_idc) -{ - switch (level_idc) { - case 0: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; - case 1: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; - case 2: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; - case 3: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; - case 4: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; - case 5: - return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; - default: - return -EINVAL; - } -} - -/* - * Check if the buffer starts with the MPEG-4 visual object sequence and visual - * object headers, for example: - * - * 00 00 01 b0 f1 - * 00 00 01 b5 a9 13 00 00 01 00 00 00 01 20 08 - * d4 8d 88 00 f5 04 04 08 14 30 3f - * - * Returns the detected header size in bytes or 0. - */ -u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) -{ - static const u8 vos_start[4] = { 0x00, 0x00, 0x01, 0xb0 }; - static const union { - u8 vo_start[4]; - u8 start_code_prefix[3]; - } u = { { 0x00, 0x00, 0x01, 0xb5 } }; - - if (size < 30 || - memcmp(buf, vos_start, 4) != 0 || - memcmp(buf + 5, u.vo_start, 4) != 0) - return 0; - - if (size == 30 || - (size >= 33 && memcmp(buf + 30, u.start_code_prefix, 3) == 0)) - return 30; - - if (size == 31 || - (size >= 34 && memcmp(buf + 31, u.start_code_prefix, 3) == 0)) - return 31; - - if (size == 32 || - (size >= 35 && memcmp(buf + 32, u.start_code_prefix, 3) == 0)) - return 32; - - return 0; -} diff --git a/drivers/media/platform/chips-media/coda.h b/drivers/media/platform/chips-media/coda.h deleted file mode 100644 index ddfd0a32c..000000000 --- a/drivers/media/platform/chips-media/coda.h +++ /dev/null @@ -1,403 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Coda multi-standard codec IP - * - * Copyright (C) 2012 Vista Silicon S.L. - * Javier Martin, - * Xavier Duret - * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix - */ - -#ifndef __CODA_H__ -#define __CODA_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "coda_regs.h" - -#define CODA_MAX_FRAMEBUFFERS 19 -#define FMO_SLICE_SAVE_BUF_SIZE (32) - -/* - * This control allows applications to read the per-stream - * (i.e. per-context) Macroblocks Error Count. This value - * is CODA specific. - */ -#define V4L2_CID_CODA_MB_ERR_CNT (V4L2_CID_USER_CODA_BASE + 0) - -enum { - V4L2_M2M_SRC = 0, - V4L2_M2M_DST = 1, -}; - -enum coda_inst_type { - CODA_INST_ENCODER, - CODA_INST_DECODER, -}; - -enum coda_product { - CODA_DX6 = 0xf001, - CODA_HX4 = 0xf00a, - CODA_7541 = 0xf012, - CODA_960 = 0xf020, -}; - -struct coda_video_device; - -struct coda_devtype { - char *firmware[3]; - enum coda_product product; - const struct coda_codec *codecs; - unsigned int num_codecs; - const struct coda_video_device **vdevs; - unsigned int num_vdevs; - size_t workbuf_size; - size_t tempbuf_size; - size_t iram_size; -}; - -struct coda_aux_buf { - void *vaddr; - dma_addr_t paddr; - u32 size; - struct debugfs_blob_wrapper blob; - struct dentry *dentry; -}; - -struct coda_dev { - struct v4l2_device v4l2_dev; - struct video_device vfd[6]; - struct device *dev; - const struct coda_devtype *devtype; - int firmware; - struct vdoa_data *vdoa; - - void __iomem *regs_base; - struct clk *clk_per; - struct clk *clk_ahb; - struct reset_control *rstc; - - struct coda_aux_buf codebuf; - struct coda_aux_buf tempbuf; - struct coda_aux_buf workbuf; - struct gen_pool *iram_pool; - struct coda_aux_buf iram; - - struct mutex dev_mutex; - struct mutex coda_mutex; - struct workqueue_struct *workqueue; - struct v4l2_m2m_dev *m2m_dev; - struct ida ida; - struct dentry *debugfs_root; - struct ratelimit_state mb_err_rs; -}; - -struct coda_codec { - u32 mode; - u32 src_fourcc; - u32 dst_fourcc; - u32 max_w; - u32 max_h; -}; - -struct coda_huff_tab; - -struct coda_params { - u8 rot_mode; - u8 h264_intra_qp; - u8 h264_inter_qp; - u8 h264_min_qp; - u8 h264_max_qp; - u8 h264_disable_deblocking_filter_idc; - s8 h264_slice_alpha_c0_offset_div2; - s8 h264_slice_beta_offset_div2; - bool h264_constrained_intra_pred_flag; - s8 h264_chroma_qp_index_offset; - u8 h264_profile_idc; - u8 h264_level_idc; - u8 mpeg2_profile_idc; - u8 mpeg2_level_idc; - u8 mpeg4_intra_qp; - u8 mpeg4_inter_qp; - u8 gop_size; - int intra_refresh; - enum v4l2_jpeg_chroma_subsampling jpeg_chroma_subsampling; - u8 jpeg_quality; - u8 jpeg_restart_interval; - u8 *jpeg_qmat_tab[3]; - int jpeg_qmat_index[3]; - int jpeg_huff_dc_index[3]; - int jpeg_huff_ac_index[3]; - u32 *jpeg_huff_data; - struct coda_huff_tab *jpeg_huff_tab; - int codec_mode; - int codec_mode_aux; - enum v4l2_mpeg_video_multi_slice_mode slice_mode; - u32 framerate; - u16 bitrate; - u16 vbv_delay; - u32 vbv_size; - u32 slice_max_bits; - u32 slice_max_mb; - bool force_ipicture; - bool gop_size_changed; - bool bitrate_changed; - bool framerate_changed; - bool h264_intra_qp_changed; - bool intra_refresh_changed; - bool slice_mode_changed; - bool frame_rc_enable; - bool mb_rc_enable; -}; - -struct coda_buffer_meta { - struct list_head list; - u32 sequence; - struct v4l2_timecode timecode; - u64 timestamp; - unsigned int start; - unsigned int end; - bool last; -}; - -/* Per-queue, driver-specific private data */ -struct coda_q_data { - unsigned int width; - unsigned int height; - unsigned int bytesperline; - unsigned int sizeimage; - unsigned int fourcc; - struct v4l2_rect rect; -}; - -struct coda_iram_info { - u32 axi_sram_use; - phys_addr_t buf_bit_use; - phys_addr_t buf_ip_ac_dc_use; - phys_addr_t buf_dbk_y_use; - phys_addr_t buf_dbk_c_use; - phys_addr_t buf_ovl_use; - phys_addr_t buf_btp_use; - phys_addr_t search_ram_paddr; - int search_ram_size; - int remaining; - phys_addr_t next_paddr; -}; - -#define GDI_LINEAR_FRAME_MAP 0 -#define GDI_TILED_FRAME_MB_RASTER_MAP 1 - -struct coda_ctx; - -struct coda_context_ops { - int (*queue_init)(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb); - int (*start_streaming)(struct coda_ctx *ctx); - int (*prepare_run)(struct coda_ctx *ctx); - void (*finish_run)(struct coda_ctx *ctx); - void (*run_timeout)(struct coda_ctx *ctx); - void (*seq_init_work)(struct work_struct *work); - void (*seq_end_work)(struct work_struct *work); - void (*release)(struct coda_ctx *ctx); -}; - -struct coda_internal_frame { - struct coda_aux_buf buf; - struct coda_buffer_meta meta; - u32 type; - u32 error; -}; - -struct coda_ctx { - struct coda_dev *dev; - struct mutex buffer_mutex; - struct work_struct pic_run_work; - struct work_struct seq_init_work; - struct work_struct seq_end_work; - struct completion completion; - const struct coda_video_device *cvd; - const struct coda_context_ops *ops; - int aborting; - int initialized; - int streamon_out; - int streamon_cap; - u32 qsequence; - u32 osequence; - u32 sequence_offset; - struct coda_q_data q_data[2]; - enum coda_inst_type inst_type; - const struct coda_codec *codec; - enum v4l2_colorspace colorspace; - enum v4l2_xfer_func xfer_func; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - struct coda_params params; - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *h264_profile_ctrl; - struct v4l2_ctrl *h264_level_ctrl; - struct v4l2_ctrl *mpeg2_profile_ctrl; - struct v4l2_ctrl *mpeg2_level_ctrl; - struct v4l2_ctrl *mpeg4_profile_ctrl; - struct v4l2_ctrl *mpeg4_level_ctrl; - struct v4l2_ctrl *mb_err_cnt_ctrl; - struct v4l2_fh fh; - int gopcounter; - int runcounter; - int jpeg_ecs_offset; - char vpu_header[3][64]; - int vpu_header_size[3]; - struct kfifo bitstream_fifo; - struct mutex bitstream_mutex; - struct coda_aux_buf bitstream; - bool hold; - struct coda_aux_buf parabuf; - struct coda_aux_buf psbuf; - struct coda_aux_buf slicebuf; - struct coda_internal_frame internal_frames[CODA_MAX_FRAMEBUFFERS]; - struct list_head buffer_meta_list; - spinlock_t buffer_meta_lock; - int num_metas; - unsigned int first_frame_sequence; - struct coda_aux_buf workbuf; - int num_internal_frames; - int idx; - int reg_idx; - struct coda_iram_info iram_info; - int tiled_map_type; - u32 bit_stream_param; - u32 frm_dis_flg; - u32 frame_mem_ctrl; - u32 para_change; - int display_idx; - struct dentry *debugfs_entry; - bool use_bit; - bool use_vdoa; - struct vdoa_ctx *vdoa; - /* - * wakeup mutex used to serialize encoder stop command and finish_run, - * ensures that finish_run always either flags the last returned buffer - * or wakes up the capture queue to signal EOS afterwards. - */ - struct mutex wakeup_mutex; -}; - -extern int coda_debug; - -#define coda_dbg(level, ctx, fmt, arg...) \ - do { \ - if (coda_debug >= (level)) \ - v4l2_dbg((level), coda_debug, &(ctx)->dev->v4l2_dev, \ - "%u: " fmt, (ctx)->idx, ##arg); \ - } while (0) - -void coda_write(struct coda_dev *dev, u32 data, u32 reg); -unsigned int coda_read(struct coda_dev *dev, u32 reg); -void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_v4l2_buffer *buf, unsigned int reg_y); - -int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, - size_t size, const char *name, struct dentry *parent); -void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); - -int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); -int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - -int coda_hw_reset(struct coda_ctx *ctx); - -void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list); - -void coda_set_gdi_regs(struct coda_ctx *ctx); - -static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx, - enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return &(ctx->q_data[V4L2_M2M_SRC]); - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &(ctx->q_data[V4L2_M2M_DST]); - default: - return NULL; - } -} - -const char *coda_product_name(int product); - -int coda_check_firmware(struct coda_dev *dev); - -static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) -{ - return kfifo_len(&ctx->bitstream_fifo); -} - -/* - * The bitstream prefetcher needs to read at least 2 256 byte periods past - * the desired bitstream position for all data to reach the decoder. - */ -static inline bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, - unsigned int pos) -{ - return (int)(ctx->bitstream_fifo.kfifo.in - ALIGN(pos, 256)) > 512; -} - -bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, unsigned int pos); -int coda_bitstream_flush(struct coda_ctx *ctx); - -void coda_bit_stream_end_flag(struct coda_ctx *ctx); - -void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state); - -int coda_h264_filler_nal(int size, char *p); -int coda_h264_padding(int size, char *p); -int coda_h264_profile(int profile_idc); -int coda_h264_level(int level_idc); -int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, - int *size, int max_size); - -int coda_mpeg2_profile(int profile_idc); -int coda_mpeg2_level(int level_idc); -u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); -int coda_mpeg4_profile(int profile_idc); -int coda_mpeg4_level(int level_idc); -u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); - -void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, - u8 level_idc); - -static inline int coda_jpeg_scale(int src, int dst) -{ - return (dst <= src / 8) ? 3 : - (dst <= src / 4) ? 2 : - (dst <= src / 2) ? 1 : 0; -} - -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb); -int coda_jpeg_write_tables(struct coda_ctx *ctx); -void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); - -extern const struct coda_context_ops coda_bit_encode_ops; -extern const struct coda_context_ops coda_bit_decode_ops; -extern const struct coda_context_ops coda9_jpeg_encode_ops; -extern const struct coda_context_ops coda9_jpeg_decode_ops; - -irqreturn_t coda_irq_handler(int irq, void *data); -irqreturn_t coda9_jpeg_irq_handler(int irq, void *data); - -#endif /* __CODA_H__ */ diff --git a/drivers/media/platform/chips-media/coda/Kconfig b/drivers/media/platform/chips-media/coda/Kconfig new file mode 100644 index 000000000..cb7b66c71 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_CODA + tristate "Chips&Media Coda multi-standard codec IP" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST) + select SRAM + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_JPEG_HELPER + select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR + help + Coda is a range of video codec IPs that supports + H.264, MPEG-4, and other video formats. + +config VIDEO_IMX_VDOA + def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST diff --git a/drivers/media/platform/chips-media/coda/Makefile b/drivers/media/platform/chips-media/coda/Makefile new file mode 100644 index 000000000..bbb16425a --- /dev/null +++ b/drivers/media/platform/chips-media/coda/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o + +obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o +obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c new file mode 100644 index 000000000..ed47d5bd8 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-bit.c @@ -0,0 +1,2666 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - BIT processor functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "imx-vdoa.h" +#define CREATE_TRACE_POINTS +#include "trace.h" + +#define CODA_PARA_BUF_SIZE (10 * 1024) +#define CODA7_PS_BUF_SIZE 0x28000 +#define CODA9_PS_SAVE_SIZE (512 * 1024) + +#define CODA_DEFAULT_GAMMA 4096 +#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ + +static void coda_free_bitstream_buffer(struct coda_ctx *ctx); + +static inline int coda_is_initialized(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; +} + +static inline unsigned long coda_isbusy(struct coda_dev *dev) +{ + return coda_read(dev, CODA_REG_BIT_BUSY); +} + +static int coda_wait_timeout(struct coda_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while (coda_isbusy(dev)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static void coda_command_async(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_960) { + /* Restore context related registers to CODA */ + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + coda_write(dev, ctx->frame_mem_ctrl, + CODA_REG_BIT_FRAME_MEM_CTRL); + coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + } + + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + + coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); + + trace_coda_bit_run(ctx, cmd); + + coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); +} + +static int coda_command_sync(struct coda_ctx *ctx, int cmd) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + coda_command_async(ctx, cmd); + ret = coda_wait_timeout(dev); + trace_coda_bit_done(ctx); + + return ret; +} + +int coda_hw_reset(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + unsigned long timeout; + unsigned int idx; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + if (!dev->rstc) + return -ENOENT; + + idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); + + if (dev->devtype->product == CODA_960) { + timeout = jiffies + msecs_to_jiffies(100); + coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); + while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + } + + ret = reset_control_reset(dev->rstc); + if (ret < 0) + return ret; + + if (dev->devtype->product == CODA_960) + coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + ret = coda_wait_timeout(dev); + coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); + + return ret; +} + +static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr; + + rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + kfifo->out = (kfifo->in & ~kfifo->mask) | + (rd_ptr - ctx->bitstream.paddr); + if (kfifo->out > kfifo->in) + kfifo->out -= kfifo->mask + 1; +} + +static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr, wr_ptr; + + rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); + coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr; + + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static int coda_h264_bitstream_pad(struct coda_ctx *ctx, u32 size) +{ + unsigned char *buf; + u32 n; + + if (size < 6) + size = 6; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + coda_h264_filler_nal(size, buf); + n = kfifo_in(&ctx->bitstream_fifo, buf, size); + kfree(buf); + + return (n < size) ? -ENOSPC : 0; +} + +int coda_bitstream_flush(struct coda_ctx *ctx) +{ + int ret; + + if (ctx->inst_type != CODA_INST_DECODER || !ctx->use_bit) + return 0; + + ret = coda_command_sync(ctx, CODA_COMMAND_DEC_BUF_FLUSH); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to flush bitstream\n"); + return ret; + } + + kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, + ctx->bitstream.size); + coda_kfifo_sync_to_device_full(ctx); + + return 0; +} + +static int coda_bitstream_queue(struct coda_ctx *ctx, const u8 *buf, u32 size) +{ + u32 n = kfifo_in(&ctx->bitstream_fifo, buf, size); + + return (n < size) ? -ENOSPC : 0; +} + +static u32 coda_buffer_parse_headers(struct coda_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + u32 payload) +{ + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + u32 size = 0; + + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_MPEG2: + size = coda_mpeg2_parse_headers(ctx, vaddr, payload); + break; + case V4L2_PIX_FMT_MPEG4: + size = coda_mpeg4_parse_headers(ctx, vaddr, payload); + break; + default: + break; + } + + return size; +} + +static bool coda_bitstream_try_queue(struct coda_ctx *ctx, + struct vb2_v4l2_buffer *src_buf) +{ + unsigned long payload = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + int ret; + int i; + + if (coda_get_bitstream_payload(ctx) + payload + 512 >= + ctx->bitstream.size) + return false; + + if (!vaddr) { + v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); + return true; + } + + if (ctx->qsequence == 0 && payload < 512) { + /* + * Add padding after the first buffer, if it is too small to be + * fetched by the CODA, by repeating the headers. Without + * repeated headers, or the first frame already queued, decoder + * sequence initialization fails with error code 0x2000 on i.MX6 + * or error code 0x1 on i.MX51. + */ + u32 header_size = coda_buffer_parse_headers(ctx, src_buf, + payload); + + if (header_size) { + coda_dbg(1, ctx, "pad with %u-byte header\n", + header_size); + for (i = payload; i < 512; i += header_size) { + ret = coda_bitstream_queue(ctx, vaddr, + header_size); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, + "bitstream buffer overflow\n"); + return false; + } + if (ctx->dev->devtype->product == CODA_960) + break; + } + } else { + coda_dbg(1, ctx, + "could not parse header, sequence initialization might fail\n"); + } + + /* Add padding before the first buffer, if it is too small */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) + coda_h264_bitstream_pad(ctx, 512 - payload); + } + + ret = coda_bitstream_queue(ctx, vaddr, payload); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); + return false; + } + + src_buf->sequence = ctx->qsequence++; + + /* Sync read pointer to device */ + if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) + coda_kfifo_sync_to_device_write(ctx); + + /* Set the stream-end flag after the last buffer is queued */ + if (src_buf->flags & V4L2_BUF_FLAG_LAST) + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + + return true; +} + +void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) +{ + struct vb2_v4l2_buffer *src_buf; + struct coda_buffer_meta *meta; + u32 start; + + lockdep_assert_held(&ctx->bitstream_mutex); + + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) + return; + + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { + /* + * Only queue two JPEGs into the bitstream buffer to keep + * latency low. We need at least one complete buffer and the + * header of another buffer (for prescan) in the bitstream. + */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && + ctx->num_metas > 1) + break; + + if (ctx->num_internal_frames && + ctx->num_metas >= ctx->num_internal_frames) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + + /* + * If we managed to fill in at least a full reorder + * window of buffers (num_internal_frames is a + * conservative estimate for this) and the bitstream + * prefetcher has at least 2 256 bytes periods beyond + * the first buffer to fetch, we can safely stop queuing + * in order to limit the decoder drain latency. + */ + if (coda_bitstream_can_fetch_past(ctx, meta->end)) + break; + } + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + /* Drop frames that do not start/end with a SOI/EOI markers */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && + !coda_jpeg_check_buffer(ctx, &src_buf->vb2_buf)) { + v4l2_err(&ctx->dev->v4l2_dev, + "dropping invalid JPEG frame %d\n", + ctx->qsequence); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (buffer_list) { + struct v4l2_m2m_buffer *m2m_buf; + + m2m_buf = container_of(src_buf, + struct v4l2_m2m_buffer, + vb); + list_add_tail(&m2m_buf->list, buffer_list); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + } + continue; + } + + /* Dump empty buffers */ + if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) { + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + continue; + } + + /* Buffer start position */ + start = ctx->bitstream_fifo.kfifo.in; + + if (coda_bitstream_try_queue(ctx, src_buf)) { + /* + * Source buffer is queued in the bitstream ringbuffer; + * queue the timestamp and mark source buffer as done + */ + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + + meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (meta) { + meta->sequence = src_buf->sequence; + meta->timecode = src_buf->timecode; + meta->timestamp = src_buf->vb2_buf.timestamp; + meta->start = start; + meta->end = ctx->bitstream_fifo.kfifo.in; + meta->last = src_buf->flags & V4L2_BUF_FLAG_LAST; + if (meta->last) + coda_dbg(1, ctx, "marking last meta"); + spin_lock(&ctx->buffer_meta_lock); + list_add_tail(&meta->list, + &ctx->buffer_meta_list); + ctx->num_metas++; + spin_unlock(&ctx->buffer_meta_lock); + + trace_coda_bit_queue(ctx, src_buf, meta); + } + + if (buffer_list) { + struct v4l2_m2m_buffer *m2m_buf; + + m2m_buf = container_of(src_buf, + struct v4l2_m2m_buffer, + vb); + list_add_tail(&m2m_buf->list, buffer_list); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } + } else { + break; + } + } +} + +void coda_bit_stream_end_flag(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + /* If this context is currently running, update the hardware flag */ + if ((dev->devtype->product == CODA_960) && + coda_isbusy(dev) && + (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } +} + +static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) +{ + struct coda_dev *dev = ctx->dev; + u32 *p = ctx->parabuf.vaddr; + + if (dev->devtype->product == CODA_DX6) + p[index] = value; + else + p[index ^ 1] = value; +} + +static inline int coda_alloc_context_buf(struct coda_ctx *ctx, + struct coda_aux_buf *buf, size_t size, + const char *name) +{ + return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); +} + + +static void coda_free_framebuffers(struct coda_ctx *ctx) +{ + int i; + + for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) + coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i].buf); +} + +static int coda_alloc_framebuffers(struct coda_ctx *ctx, + struct coda_q_data *q_data, u32 fourcc) +{ + struct coda_dev *dev = ctx->dev; + unsigned int ysize, ycbcr_size; + int ret; + int i; + + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) + ysize = round_up(q_data->rect.width, 16) * + round_up(q_data->rect.height, 16); + else + ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; + + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ycbcr_size = round_up(ysize, 4096) + ysize / 2; + else + ycbcr_size = ysize + ysize / 2; + + /* Allocate frame buffers */ + for (i = 0; i < ctx->num_internal_frames; i++) { + size_t size = ycbcr_size; + char *name; + + /* Add space for mvcol buffers */ + if (dev->devtype->product != CODA_DX6 && + (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0))) + size += ysize / 4; + name = kasprintf(GFP_KERNEL, "fb%d", i); + if (!name) { + coda_free_framebuffers(ctx); + return -ENOMEM; + } + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i].buf, + size, name); + kfree(name); + if (ret < 0) { + coda_free_framebuffers(ctx); + return ret; + } + } + + /* Register frame buffers in the parameter buffer */ + for (i = 0; i < ctx->num_internal_frames; i++) { + u32 y, cb, cr, mvcol; + + /* Start addresses of Y, Cb, Cr planes */ + y = ctx->internal_frames[i].buf.paddr; + cb = y + ysize; + cr = y + ysize + ysize/4; + mvcol = y + ysize + ysize/4 + ysize/4; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { + cb = round_up(cb, 4096); + mvcol = cb + ysize/2; + cr = 0; + /* Packed 20-bit MSB of base addresses */ + /* YYYYYCCC, CCyyyyyc, cccc.... */ + y = (y & 0xfffff000) | cb >> 20; + cb = (cb & 0x000ff000) << 12; + } + coda_parabuf_write(ctx, i * 3 + 0, y); + coda_parabuf_write(ctx, i * 3 + 1, cb); + coda_parabuf_write(ctx, i * 3 + 2, cr); + + if (dev->devtype->product == CODA_DX6) + continue; + + /* mvcol buffer for h.264 and mpeg4 */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) + coda_parabuf_write(ctx, 96 + i, mvcol); + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 && i == 0) + coda_parabuf_write(ctx, 97, mvcol); + } + + return 0; +} + +static void coda_free_context_buffers(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + coda_free_aux_buf(dev, &ctx->slicebuf); + coda_free_aux_buf(dev, &ctx->psbuf); + if (dev->devtype->product != CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + coda_free_aux_buf(dev, &ctx->parabuf); +} + +static int coda_alloc_context_buffers(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + struct coda_dev *dev = ctx->dev; + size_t size; + int ret; + + if (!ctx->parabuf.vaddr) { + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, + CODA_PARA_BUF_SIZE, "parabuf"); + if (ret < 0) + return ret; + } + + if (dev->devtype->product == CODA_DX6) + return 0; + + if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { + /* worst case slice size */ + size = (DIV_ROUND_UP(q_data->rect.width, 16) * + DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; + ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, + "slicebuf"); + if (ret < 0) + goto err; + } + + if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541)) { + ret = coda_alloc_context_buf(ctx, &ctx->psbuf, + CODA7_PS_BUF_SIZE, "psbuf"); + if (ret < 0) + goto err; + } + + if (!ctx->workbuf.vaddr) { + size = dev->devtype->workbuf_size; + if (dev->devtype->product == CODA_960 && + q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, + "workbuf"); + if (ret < 0) + goto err; + } + + return 0; + +err: + coda_free_context_buffers(ctx); + return ret; +} + +static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + int header_code, u8 *header, int *size) +{ + struct vb2_buffer *vb = &buf->vb2_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct v4l2_rect *r; + size_t bufsize; + int ret; + int i; + + if (dev->devtype->product == CODA_960) + memset(vb2_plane_vaddr(vb, 0), 0, 64); + + coda_write(dev, vb2_dma_contig_plane_dma_addr(vb, 0), + CODA_CMD_ENC_HEADER_BB_START); + bufsize = vb2_plane_size(vb, 0); + if (dev->devtype->product == CODA_960) + bufsize /= 1024; + coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); + if (dev->devtype->product == CODA_960 && + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && + header_code == CODA_HEADER_H264_SPS) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + r = &q_data_src->rect; + + if (r->width % 16 || r->height % 16) { + u32 crop_right = round_up(r->width, 16) - r->width; + u32 crop_bottom = round_up(r->height, 16) - r->height; + + coda_write(dev, crop_right, + CODA9_CMD_ENC_HEADER_FRAME_CROP_H); + coda_write(dev, crop_bottom, + CODA9_CMD_ENC_HEADER_FRAME_CROP_V); + header_code |= CODA9_HEADER_FRAME_CROP; + } + } + coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); + ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); + return ret; + } + + if (dev->devtype->product == CODA_960) { + for (i = 63; i > 0; i--) + if (((char *)vb2_plane_vaddr(vb, 0))[i] != 0) + break; + *size = i + 1; + } else { + *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - + coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + } + memcpy(header, vb2_plane_vaddr(vb, 0), *size); + + return 0; +} + +static u32 coda_slice_mode(struct coda_ctx *ctx) +{ + int size, unit; + + switch (ctx->params.slice_mode) { + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + default: + return 0; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: + size = ctx->params.slice_max_mb; + unit = 1; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: + size = ctx->params.slice_max_bits; + unit = 0; + break; + } + + return ((size & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET) | + ((unit & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET) | + ((1 & CODA_SLICING_MODE_MASK) << CODA_SLICING_MODE_OFFSET); +} + +static int coda_enc_param_change(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 change_enable = 0; + u32 success; + int ret; + + if (ctx->params.gop_size_changed) { + change_enable |= CODA_PARAM_CHANGE_RC_GOP; + coda_write(dev, ctx->params.gop_size, + CODA_CMD_ENC_PARAM_RC_GOP); + ctx->gopcounter = ctx->params.gop_size - 1; + ctx->params.gop_size_changed = false; + } + if (ctx->params.h264_intra_qp_changed) { + coda_dbg(1, ctx, "parameter change: intra Qp %u\n", + ctx->params.h264_intra_qp); + + if (ctx->params.bitrate) { + change_enable |= CODA_PARAM_CHANGE_RC_INTRA_QP; + coda_write(dev, ctx->params.h264_intra_qp, + CODA_CMD_ENC_PARAM_RC_INTRA_QP); + } + ctx->params.h264_intra_qp_changed = false; + } + if (ctx->params.bitrate_changed) { + coda_dbg(1, ctx, "parameter change: bitrate %u kbit/s\n", + ctx->params.bitrate); + change_enable |= CODA_PARAM_CHANGE_RC_BITRATE; + coda_write(dev, ctx->params.bitrate, + CODA_CMD_ENC_PARAM_RC_BITRATE); + ctx->params.bitrate_changed = false; + } + if (ctx->params.framerate_changed) { + coda_dbg(1, ctx, "parameter change: frame rate %u/%u Hz\n", + ctx->params.framerate & 0xffff, + (ctx->params.framerate >> 16) + 1); + change_enable |= CODA_PARAM_CHANGE_RC_FRAME_RATE; + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_PARAM_RC_FRAME_RATE); + ctx->params.framerate_changed = false; + } + if (ctx->params.intra_refresh_changed) { + coda_dbg(1, ctx, "parameter change: intra refresh MBs %u\n", + ctx->params.intra_refresh); + change_enable |= CODA_PARAM_CHANGE_INTRA_MB_NUM; + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_PARAM_INTRA_MB_NUM); + ctx->params.intra_refresh_changed = false; + } + if (ctx->params.slice_mode_changed) { + change_enable |= CODA_PARAM_CHANGE_SLICE_MODE; + coda_write(dev, coda_slice_mode(ctx), + CODA_CMD_ENC_PARAM_SLICE_MODE); + ctx->params.slice_mode_changed = false; + } + + if (!change_enable) + return 0; + + coda_write(dev, change_enable, CODA_CMD_ENC_PARAM_CHANGE_ENABLE); + + ret = coda_command_sync(ctx, CODA_COMMAND_RC_CHANGE_PARAMETER); + if (ret < 0) + return ret; + + success = coda_read(dev, CODA_RET_ENC_PARAM_CHANGE_SUCCESS); + if (success != 1) + coda_dbg(1, ctx, "parameter change failed: %u\n", success); + + return 0; +} + +static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) +{ + phys_addr_t ret; + + size = round_up(size, 1024); + if (size > iram->remaining) + return 0; + iram->remaining -= size; + + ret = iram->next_paddr; + iram->next_paddr += size; + + return ret; +} + +static void coda_setup_iram(struct coda_ctx *ctx) +{ + struct coda_iram_info *iram_info = &ctx->iram_info; + struct coda_dev *dev = ctx->dev; + int w64, w128; + int mb_width; + int dbk_bits; + int bit_bits; + int ip_bits; + int me_bits; + + memset(iram_info, 0, sizeof(*iram_info)); + iram_info->next_paddr = dev->iram.paddr; + iram_info->remaining = dev->iram.size; + + if (!dev->iram.vaddr) + return; + + switch (dev->devtype->product) { + case CODA_HX4: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE; + break; + case CODA_7541: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE; + break; + case CODA_960: + dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; + bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = 0; + break; + default: /* CODA_DX6 */ + return; + } + + if (ctx->inst_type == CODA_INST_ENCODER) { + struct coda_q_data *q_data_src; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); + w128 = mb_width * 128; + w64 = mb_width * 64; + + /* Prioritize in case IRAM is too small for everything */ + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + iram_info->search_ram_size = round_up(mb_width * 16 * + 36 + 2048, 1024); + iram_info->search_ram_paddr = coda_iram_alloc(iram_info, + iram_info->search_ram_size); + if (!iram_info->search_ram_paddr) { + pr_err("IRAM is smaller than the search ram size\n"); + goto out; + } + iram_info->axi_sram_use |= me_bits; + } + + /* Only H.264BP and H.263P3 are considered */ + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64); + if (!iram_info->buf_dbk_y_use || !iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP disabled for encoder */ + } else if (ctx->inst_type == CODA_INST_DECODER) { + struct coda_q_data *q_data_dst; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + mb_width = DIV_ROUND_UP(q_data_dst->width, 16); + w128 = mb_width * 128; + + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_dbk_y_use || !iram_info->buf_dbk_c_use) + goto out; + iram_info->axi_sram_use |= dbk_bits; + + iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_bit_use) + goto out; + iram_info->axi_sram_use |= bit_bits; + + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; + + /* OVL and BTP unused as there is no VC1 support yet */ + } + +out: + if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) + coda_dbg(1, ctx, "IRAM smaller than needed\n"); + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + /* TODO - Enabling these causes picture errors on CODA7541 */ + if (ctx->inst_type == CODA_INST_DECODER) { + /* fw 1.4.50 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_IP_ENABLE); + } else { + /* fw 13.4.29 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_HOST_DBK_ENABLE | + CODA7_USE_IP_ENABLE | + CODA7_USE_DBK_ENABLE); + } + } +} + +static u32 coda_supported_firmwares[] = { + CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), + CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), + CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), +}; + +static bool coda_firmware_supported(u32 vernum) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) + if (vernum == coda_supported_firmwares[i]) + return true; + return false; +} + +int coda_check_firmware(struct coda_dev *dev) +{ + u16 product, major, minor, release; + u32 data; + int ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); + coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); + if (coda_wait_timeout(dev)) { + v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); + ret = -EIO; + goto err_run_cmd; + } + + if (dev->devtype->product == CODA_960) { + data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); + v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", + data); + } + + /* Check we are compatible with the loaded firmware */ + data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); + product = CODA_FIRMWARE_PRODUCT(data); + major = CODA_FIRMWARE_MAJOR(data); + minor = CODA_FIRMWARE_MINOR(data); + release = CODA_FIRMWARE_RELEASE(data); + + clk_disable_unprepare(dev->clk_per); + clk_disable_unprepare(dev->clk_ahb); + + if (product != dev->devtype->product) { + v4l2_err(&dev->v4l2_dev, + "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n", + coda_product_name(dev->devtype->product), + coda_product_name(product), major, minor, release); + return -EINVAL; + } + + v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", + coda_product_name(product)); + + if (coda_firmware_supported(data)) { + v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", + major, minor, release); + } else { + v4l2_warn(&dev->v4l2_dev, + "Unsupported firmware version: %u.%u.%u\n", + major, minor, release); + } + + return 0; + +err_run_cmd: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) +{ + u32 cache_size, cache_config; + + if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { + /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ + cache_size = 0x20262024; + cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + } else { + /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ + cache_size = 0x02440243; + cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; + } + coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); + if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_YUYV) { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } else { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } + coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); +} + +/* + * Encoder context operations + */ + +static int coda_encoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + } else { + coda_free_context_buffers(ctx); + } + + return 0; +} + +static int coda_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct vb2_v4l2_buffer *buf; + int gamma, ret, value; + u32 dst_fourcc; + int num_fb; + u32 stride; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + bitstream_buf = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); + bitstream_size = q_data_dst->sizeimage; + + if (!coda_is_initialized(dev)) { + v4l2_err(v4l2_dev, "coda is not initialized.\n"); + return -EFAULT; + } + + if (dst_fourcc == V4L2_PIX_FMT_JPEG) { + if (!ctx->params.jpeg_qmat_tab[0]) { + ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[0]) + return -ENOMEM; + } + if (!ctx->params.jpeg_qmat_tab[1]) { + ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[1]) + return -ENOMEM; + } + coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); + } + + mutex_lock(&dev->coda_mutex); + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | + CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + case CODA_960: + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + fallthrough; + case CODA_HX4: + case CODA_7541: + coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | + CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; + } + + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; + coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product == CODA_DX6) { + /* Configure the coda */ + coda_write(dev, dev->iram.paddr, + CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); + } + + /* Could set rotation here if needed */ + value = 0; + switch (dev->devtype->product) { + case CODA_DX6: + value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) + << CODADX6_PICWIDTH_OFFSET; + value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + break; + case CODA_HX4: + case CODA_7541: + if (dst_fourcc == V4L2_PIX_FMT_H264) { + value = (round_up(q_data_src->rect.width, 16) & + CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; + value |= (round_up(q_data_src->rect.height, 16) & + CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; + break; + } + fallthrough; + case CODA_960: + value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) + << CODA7_PICWIDTH_OFFSET; + value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) + << CODA_PICHEIGHT_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); + if (dst_fourcc == V4L2_PIX_FMT_JPEG) + ctx->params.framerate = 0; + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_SEQ_SRC_F_RATE); + + ctx->params.codec_mode = ctx->codec->mode; + switch (dst_fourcc) { + case V4L2_PIX_FMT_MPEG4: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_MPEG4, + CODA_CMD_ENC_SEQ_COD_STD); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); + break; + case V4L2_PIX_FMT_H264: + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_H264, + CODA_CMD_ENC_SEQ_COD_STD); + value = ((ctx->params.h264_disable_deblocking_filter_idc & + CODA_264PARAM_DISABLEDEBLK_MASK) << + CODA_264PARAM_DISABLEDEBLK_OFFSET) | + ((ctx->params.h264_slice_alpha_c0_offset_div2 & + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | + ((ctx->params.h264_slice_beta_offset_div2 & + CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | + (ctx->params.h264_constrained_intra_pred_flag << + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | + (ctx->params.h264_chroma_qp_index_offset & + CODA_264PARAM_CHROMAQPOFFSET_MASK); + coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); + break; + case V4L2_PIX_FMT_JPEG: + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_PARA); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_EN); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET); + + coda_jpeg_write_tables(ctx); + break; + default: + v4l2_err(v4l2_dev, + "dst format (0x%08x) invalid.\n", dst_fourcc); + ret = -EINVAL; + goto out; + } + + /* + * slice mode and GOP size registers are used for thumb size/offset + * in JPEG mode + */ + if (dst_fourcc != V4L2_PIX_FMT_JPEG) { + value = coda_slice_mode(ctx); + coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); + value = ctx->params.gop_size; + coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); + } + + if (ctx->params.bitrate && (ctx->params.frame_rc_enable || + ctx->params.mb_rc_enable)) { + ctx->params.bitrate_changed = false; + ctx->params.h264_intra_qp_changed = false; + + /* Rate control enabled */ + value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) + << CODA_RATECONTROL_BITRATE_OFFSET; + value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + value |= (ctx->params.vbv_delay & + CODA_RATECONTROL_INITIALDELAY_MASK) + << CODA_RATECONTROL_INITIALDELAY_OFFSET; + if (dev->devtype->product == CODA_960) + value |= BIT(31); /* disable autoskip */ + } else { + value = 0; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); + + coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_SEQ_INTRA_REFRESH); + + coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); + + + value = 0; + if (dev->devtype->product == CODA_960) + gamma = CODA9_DEFAULT_GAMMA; + else + gamma = CODA_DEFAULT_GAMMA; + if (gamma > 0) { + coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, + CODA_CMD_ENC_SEQ_RC_GAMMA); + } + + if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { + coda_write(dev, + ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | + ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, + CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); + } + if (dev->devtype->product == CODA_960) { + if (ctx->params.h264_max_qp) + value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; + if (CODA_DEFAULT_GAMMA > 0) + value |= 1 << CODA9_OPTION_GAMMA_OFFSET; + } else { + if (CODA_DEFAULT_GAMMA > 0) { + if (dev->devtype->product == CODA_DX6) + value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; + else + value |= 1 << CODA7_OPTION_GAMMA_OFFSET; + } + if (ctx->params.h264_min_qp) + value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; + if (ctx->params.h264_max_qp) + value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); + + if (ctx->params.frame_rc_enable && !ctx->params.mb_rc_enable) + value = 1; + else + value = 0; + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); + + coda_setup_iram(ctx); + + if (dst_fourcc == V4L2_PIX_FMT_H264) { + switch (dev->devtype->product) { + case CODA_DX6: + value = FMO_SLICE_SAVE_BUF_SIZE << 7; + coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); + break; + case CODA_HX4: + case CODA_7541: + coda_write(dev, ctx->iram_info.search_ram_paddr, + CODA7_CMD_ENC_SEQ_SEARCH_BASE); + coda_write(dev, ctx->iram_info.search_ram_size, + CODA7_CMD_ENC_SEQ_SEARCH_SIZE); + break; + case CODA_960: + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + goto out; + } + + if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); + ret = -EFAULT; + goto out; + } + ctx->initialized = 1; + + if (dst_fourcc != V4L2_PIX_FMT_JPEG) { + if (dev->devtype->product == CODA_960) + ctx->num_internal_frames = 4; + else + ctx->num_internal_frames = 2; + ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); + goto out; + } + num_fb = 2; + stride = q_data_src->bytesperline; + } else { + ctx->num_internal_frames = 0; + num_fb = 0; + stride = 0; + } + coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + coda_write(dev, q_data_src->bytesperline, + CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); + } + if (dev->devtype->product != CODA_DX6) { + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + coda9_set_frame_cache(ctx, q_data_src->fourcc); + + /* FIXME */ + coda_write(dev, ctx->internal_frames[2].buf.paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_A); + coda_write(dev, ctx->internal_frames[3].buf.paddr, + CODA9_CMD_SET_FRAME_SUBSAMP_B); + } + } + + ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); + if (ret < 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + goto out; + } + + coda_dbg(1, ctx, "start encoding %dx%d %4.4s->%4.4s @ %d/%d Hz\n", + q_data_src->rect.width, q_data_src->rect.height, + (char *)&ctx->codec->src_fourcc, (char *)&dst_fourcc, + ctx->params.framerate & 0xffff, + (ctx->params.framerate >> 16) + 1); + + /* Save stream headers */ + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + /* + * Get SPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + /* + * If visible width or height are not aligned to macroblock + * size, the crop_right and crop_bottom SPS fields must be set + * to the difference between visible and coded size. This is + * only supported by CODA960 firmware. All others do not allow + * writing frame cropping parameters, so we have to manually + * fix up the SPS RBSP (Sequence Parameter Set Raw Byte + * Sequence Payload) ourselves. + */ + if (ctx->dev->devtype->product != CODA_960 && + ((q_data_src->rect.width % 16) || + (q_data_src->rect.height % 16))) { + ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, + q_data_src->rect.height, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0], + sizeof(ctx->vpu_header[0])); + if (ret < 0) + goto out; + } + + /* + * Get PPS in the first frame and copy it to an + * intermediate buffer. + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); + break; + case V4L2_PIX_FMT_MPEG4: + /* + * Get VOS in the first frame and copy it to an + * intermediate buffer + */ + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, + &ctx->vpu_header[2][0], + &ctx->vpu_header_size[2]); + if (ret < 0) + goto out; + break; + default: + /* No more formats need to save headers at the moment */ + break; + } + +out: + mutex_unlock(&dev->coda_mutex); + return ret; +} + +static int coda_prepare_encode(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + int force_ipicture; + int quant_param = 0; + u32 pic_stream_buffer_addr, pic_stream_buffer_size; + u32 rot_mode = 0; + u32 dst_fourcc; + u32 reg; + int ret; + + ret = coda_enc_param_change(ctx); + if (ret < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "parameter change failed: %d\n", + ret); + } + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + src_buf->sequence = ctx->osequence; + dst_buf->sequence = ctx->osequence; + ctx->osequence++; + + force_ipicture = ctx->params.force_ipicture; + if (force_ipicture) + ctx->params.force_ipicture = false; + else if (ctx->params.gop_size != 0 && + (src_buf->sequence % ctx->params.gop_size) == 0) + force_ipicture = 1; + + /* + * Workaround coda firmware BUG that only marks the first + * frame as IDR. This is a problem for some decoders that can't + * recover when a frame is lost. + */ + if (!force_ipicture) { + src_buf->flags |= V4L2_BUF_FLAG_PFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } else { + src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + /* + * Copy headers in front of the first frame and forced I frames for + * H.264 only. In MPEG4 they are already copied by the CODA. + */ + if (src_buf->sequence == 0 || force_ipicture) { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]; + pic_stream_buffer_size = q_data_dst->sizeimage - + ctx->vpu_header_size[0] - + ctx->vpu_header_size[1] - + ctx->vpu_header_size[2]; + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0), + &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0], &ctx->vpu_header[1][0], + ctx->vpu_header_size[1]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0] + ctx->vpu_header_size[1], + &ctx->vpu_header[2][0], ctx->vpu_header_size[2]); + } else { + pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + pic_stream_buffer_size = q_data_dst->sizeimage; + } + + if (force_ipicture) { + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_intra_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_intra_qp; + break; + case V4L2_PIX_FMT_JPEG: + quant_param = 30; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set intra qp, fmt not supported\n"); + break; + } + } else { + switch (dst_fourcc) { + case V4L2_PIX_FMT_H264: + quant_param = ctx->params.h264_inter_qp; + break; + case V4L2_PIX_FMT_MPEG4: + quant_param = ctx->params.mpeg4_inter_qp; + break; + default: + v4l2_warn(&ctx->dev->v4l2_dev, + "cannot set inter qp, fmt not supported\n"); + break; + } + } + + /* submit */ + if (ctx->params.rot_mode) + rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; + coda_write(dev, rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); + coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); + + if (dev->devtype->product == CODA_960) { + coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); + coda_write(dev, q_data_src->bytesperline, + CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); + + reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; + } else { + reg = CODA_CMD_ENC_PIC_SRC_ADDR_Y; + } + coda_write_base(ctx, q_data_src, src_buf, reg); + + coda_write(dev, force_ipicture << 1 & 0x2, + CODA_CMD_ENC_PIC_OPTION); + + coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); + coda_write(dev, pic_stream_buffer_size / 1024, + CODA_CMD_ENC_PIC_BB_SIZE); + + if (!ctx->streamon_out) { + /* After streamoff on the output side, set stream end flag */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + coda_write(dev, ctx->bit_stream_param, + CODA_REG_BIT_BIT_STREAM_PARAM); + } + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + trace_coda_enc_pic_run(ctx, src_buf); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static char coda_frame_type_char(u32 flags) +{ + return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : + (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : + (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; +} + +static void coda_finish_encode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr, start_ptr; + + if (ctx->aborting) + return; + + /* + * Lock to make sure that an encoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + trace_coda_enc_pic_done(ctx, dst_buf); + + /* Get results from the coda */ + start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); + wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + + /* Calculate bytesused field */ + if (dst_buf->sequence == 0 || + src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]); + } else { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); + } + + coda_dbg(1, ctx, "frame size = %u\n", wr_ptr - start_ptr); + + coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); + coda_read(dev, CODA_RET_ENC_PIC_FLAG); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_LAST); + if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); + mutex_unlock(&ctx->wakeup_mutex); + + ctx->gopcounter--; + if (ctx->gopcounter < 0) + ctx->gopcounter = ctx->params.gop_size - 1; + + coda_dbg(1, ctx, "job finished: encoded %c frame (%d)%s\n", + coda_frame_type_char(dst_buf->flags), dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); +} + +static void coda_seq_end_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); + struct coda_dev *dev = ctx->dev; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + if (ctx->initialized == 0) + goto out; + + coda_dbg(1, ctx, "%s: sent command 'SEQ_END' to coda\n", __func__); + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_END failed\n"); + } + + /* + * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing + * from the output stream after the h.264 decoder has run. Resetting the + * hardware after the decoder has finished seems to help. + */ + if (dev->devtype->product == CODA_960) + coda_hw_reset(ctx); + + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + coda_free_framebuffers(ctx); + + ctx->initialized = 0; + +out: + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static void coda_bit_release(struct coda_ctx *ctx) +{ + mutex_lock(&ctx->buffer_mutex); + coda_free_framebuffers(ctx); + coda_free_context_buffers(ctx); + coda_free_bitstream_buffer(ctx); + mutex_unlock(&ctx->buffer_mutex); +} + +const struct coda_context_ops coda_bit_encode_ops = { + .queue_init = coda_encoder_queue_init, + .reqbufs = coda_encoder_reqbufs, + .start_streaming = coda_start_encoding, + .prepare_run = coda_prepare_encode, + .finish_run = coda_finish_encode, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +/* + * Decoder context operations + */ + +static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + if (ctx->bitstream.vaddr) + return 0; + + ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); + ctx->bitstream.vaddr = dma_alloc_wc(ctx->dev->dev, ctx->bitstream.size, + &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&ctx->dev->v4l2_dev, + "failed to allocate bitstream ringbuffer"); + return -ENOMEM; + } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + return 0; +} + +static void coda_free_bitstream_buffer(struct coda_ctx *ctx) +{ + if (ctx->bitstream.vaddr == NULL) + return; + + dma_free_wc(ctx->dev->dev, ctx->bitstream.size, ctx->bitstream.vaddr, + ctx->bitstream.paddr); + ctx->bitstream.vaddr = NULL; + kfifo_init(&ctx->bitstream_fifo, NULL, 0); +} + +static int coda_decoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + ret = coda_alloc_bitstream_buffer(ctx, q_data_src); + if (ret < 0) { + coda_free_context_buffers(ctx); + return ret; + } + } else { + coda_free_bitstream_buffer(ctx); + coda_free_context_buffers(ctx); + } + + return 0; +} + +static bool coda_reorder_enable(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int profile; + + if (dev->devtype->product != CODA_HX4 && + dev->devtype->product != CODA_7541 && + dev->devtype->product != CODA_960) + return false; + + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) + return false; + + if (ctx->codec->src_fourcc != V4L2_PIX_FMT_H264) + return true; + + profile = coda_h264_profile(ctx->params.h264_profile_idc); + if (profile < 0) + v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", + ctx->params.h264_profile_idc); + + /* Baseline profile does not support reordering */ + return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; +} + +static void coda_decoder_drop_used_metas(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta, *tmp; + + /* + * All metas that end at or before the RD pointer (fifo out), + * are now consumed by the VPU and should be released. + */ + spin_lock(&ctx->buffer_meta_lock); + list_for_each_entry_safe(meta, tmp, &ctx->buffer_meta_list, list) { + if (ctx->bitstream_fifo.kfifo.out >= meta->end) { + coda_dbg(2, ctx, "releasing meta: seq=%d start=%d end=%d\n", + meta->sequence, meta->start, meta->end); + + list_del(&meta->list); + ctx->num_metas--; + ctx->first_frame_sequence++; + kfree(meta); + } + } + spin_unlock(&ctx->buffer_meta_lock); +} + +static int __coda_decoder_seq_init(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct coda_dev *dev = ctx->dev; + int width, height; + u32 src_fourcc, dst_fourcc; + u32 val; + int ret; + + lockdep_assert_held(&dev->coda_mutex); + + coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", + ctx->use_vdoa ? "Enabled" : "Disabled"); + + /* Start decoding */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + bitstream_buf = ctx->bitstream.paddr; + bitstream_size = ctx->bitstream.size; + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + + /* Update coda bitstream read and write pointers from kfifo */ + coda_kfifo_sync_to_device_full(ctx); + + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | + ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); + coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); + + ctx->display_idx = -1; + ctx->frm_dis_flg = 0; + coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); + coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); + val = 0; + if (coda_reorder_enable(ctx)) + val |= CODA_REORDER_ENABLE; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) + val |= CODA_NO_INT_ENABLE; + coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); + + ctx->params.codec_mode = ctx->codec->mode; + if (dev->devtype->product == CODA_960 && + src_fourcc == V4L2_PIX_FMT_MPEG4) + ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; + else + ctx->params.codec_mode_aux = 0; + if (src_fourcc == V4L2_PIX_FMT_MPEG4) { + coda_write(dev, CODA_MP4_CLASS_MPEG4, + CODA_CMD_DEC_SEQ_MP4_ASP_CLASS); + } + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + coda_write(dev, ctx->psbuf.paddr, + CODA_CMD_DEC_SEQ_PS_BB_START); + coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), + CODA_CMD_DEC_SEQ_PS_BB_SIZE); + } + if (dev->devtype->product == CODA_960) { + coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); + coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); + } + } + if (src_fourcc == V4L2_PIX_FMT_JPEG) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); + if (dev->devtype->product != CODA_960) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); + + ctx->bit_stream_param = CODA_BIT_DEC_SEQ_INIT_ESCAPE; + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + ctx->bit_stream_param = 0; + if (ret) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + return ret; + } + ctx->sequence_offset = ~0U; + ctx->initialized = 1; + ctx->first_frame_sequence = 0; + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * After updating the read pointer, we need to check if + * any metas are consumed and should be released. + */ + coda_decoder_drop_used_metas(ctx); + + if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_INIT failed, error code = 0x%x\n", + coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); + return -EAGAIN; + } + + val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); + if (dev->devtype->product == CODA_DX6) { + width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; + height = val & CODADX6_PICHEIGHT_MASK; + } else { + width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; + height = val & CODA7_PICHEIGHT_MASK; + } + + if (width > q_data_dst->bytesperline || height > q_data_dst->height) { + v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", + width, height, q_data_dst->bytesperline, + q_data_dst->height); + return -EINVAL; + } + + width = round_up(width, 16); + height = round_up(height, 16); + + coda_dbg(1, ctx, "start decoding: %dx%d\n", width, height); + + ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); + /* + * If the VDOA is used, the decoder needs one additional frame, + * because the frames are freed when the next frame is decoded. + * Otherwise there are visible errors in the decoded frames (green + * regions in displayed frames) and a broken order of frames (earlier + * frames are sporadically displayed after later frames). + */ + if (ctx->use_vdoa) + ctx->num_internal_frames += 1; + if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { + v4l2_err(&dev->v4l2_dev, + "not enough framebuffers to decode (%d < %d)\n", + CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); + return -EINVAL; + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); + + q_data_dst->rect.left = (left_right >> 10) & 0x3ff; + q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; + q_data_dst->rect.width = width - q_data_dst->rect.left - + (left_right & 0x3ff); + q_data_dst->rect.height = height - q_data_dst->rect.top - + (top_bottom & 0x3ff); + } + + if (dev->devtype->product != CODA_DX6) { + u8 profile, level; + + val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); + profile = val & 0xff; + level = (val >> 8) & 0x7f; + + if (profile || level) + coda_update_profile_level_ctrls(ctx, profile, level); + } + + return 0; +} + +static void coda_dec_seq_init_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, + struct coda_ctx, seq_init_work); + struct coda_dev *dev = ctx->dev; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + if (!ctx->initialized) + __coda_decoder_seq_init(ctx); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static int __coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct coda_dev *dev = ctx->dev; + u32 src_fourcc, dst_fourcc; + int ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + + if (!ctx->initialized) { + ret = __coda_decoder_seq_init(ctx); + if (ret < 0) + return ret; + } else { + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | + ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); + } + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); + return ret; + } + + /* Tell the decoder how many frame buffers we allocated. */ + coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, round_up(q_data_dst->rect.width, 16), + CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product != CODA_DX6) { + /* Set secondary AXI IRAM */ + coda_setup_iram(ctx); + + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + coda9_set_frame_cache(ctx, dst_fourcc); + } + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + coda_write(dev, ctx->slicebuf.paddr, + CODA_CMD_SET_FRAME_SLICE_BB_START); + coda_write(dev, ctx->slicebuf.size / 1024, + CODA_CMD_SET_FRAME_SLICE_BB_SIZE); + } + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); + } else if (dev->devtype->product == CODA_960) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); + } + + if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { + v4l2_err(&ctx->dev->v4l2_dev, + "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&dev->coda_mutex); + ret = __coda_start_decoding(ctx); + mutex_unlock(&dev->coda_mutex); + + return ret; +} + +static int coda_prepare_decode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *dst_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_dst; + struct coda_buffer_meta *meta; + u32 rot_mode = 0; + u32 reg_addr, reg_stride; + + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* Try to copy source buffer contents into the bitstream ringbuffer */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + + if (coda_get_bitstream_payload(ctx) < 512 && + (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + coda_dbg(1, ctx, "bitstream payload: %d, skipping\n", + coda_get_bitstream_payload(ctx)); + return -EAGAIN; + } + + /* Run coda_start_decoding (again) if not yet initialized */ + if (!ctx->initialized) { + int ret = __coda_start_decoding(ctx); + + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); + return -EAGAIN; + } else { + ctx->initialized = 1; + } + } + + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + + if (ctx->use_vdoa && + ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + vdoa_device_run(ctx->vdoa, + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), + ctx->internal_frames[ctx->display_idx].buf.paddr); + } else { + if (dev->devtype->product == CODA_960) { + /* + * It was previously assumed that the CODA960 has an + * internal list of 64 buffer entries that contains + * both the registered internal frame buffers as well + * as the rotator buffer output, and that the ROT_INDEX + * register must be set to a value between the last + * internal frame buffers' index and 64. + * At least on firmware version 3.1.1 it turns out that + * setting ROT_INDEX to any value >= 32 causes CODA + * hangups that it can not recover from with the SRC VPU + * reset. + * It does appear to work however, to just set it to a + * fixed value in the [ctx->num_internal_frames, 31] + * range, for example CODA_MAX_FRAMEBUFFERS. + */ + coda_write(dev, CODA_MAX_FRAMEBUFFERS, + CODA9_CMD_DEC_PIC_ROT_INDEX); + + reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y; + reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE; + } else { + reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y; + reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE; + } + coda_write_base(ctx, q_data_dst, dst_buf, reg_addr); + coda_write(dev, q_data_dst->bytesperline, reg_stride); + + rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode; + } + + coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE); + + switch (dev->devtype->product) { + case CODA_DX6: + /* TBD */ + case CODA_HX4: + case CODA_7541: + coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); + break; + case CODA_960: + /* 'hardcode to use interrupt disable mode'? */ + coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); + break; + } + + coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); + + coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); + coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, ctx->iram_info.axi_sram_use, + CODA7_REG_BIT_AXI_SRAM_USE); + + spin_lock(&ctx->buffer_meta_lock); + meta = list_first_entry_or_null(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + + if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { + + /* If this is the last buffer in the bitstream, add padding */ + if (meta->end == ctx->bitstream_fifo.kfifo.in) { + static unsigned char buf[512]; + unsigned int pad; + + /* Pad to multiple of 256 and then add 256 more */ + pad = ((0 - meta->end) & 0xff) + 256; + + memset(buf, 0xff, sizeof(buf)); + + kfifo_in(&ctx->bitstream_fifo, buf, pad); + } + } + spin_unlock(&ctx->buffer_meta_lock); + + coda_kfifo_sync_to_device_full(ctx); + + /* Clear decode success flag */ + coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); + + /* Clear error return value */ + coda_write(dev, 0, CODA_RET_DEC_PIC_ERR_MB); + + trace_coda_dec_pic_run(ctx, meta); + + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + return 0; +} + +static void coda_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct coda_q_data *q_data_dst; + struct vb2_v4l2_buffer *dst_buf; + struct coda_buffer_meta *meta; + int width, height; + int decoded_idx; + int display_idx; + struct coda_internal_frame *decoded_frame = NULL; + u32 src_fourcc; + int success; + u32 err_mb; + int err_vdoa = 0; + u32 val; + + if (ctx->aborting) + return; + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * in stream-end mode, the read pointer can overshoot the write pointer + * by up to 512 bytes + */ + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { + if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512) + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_fourcc = q_data_src->fourcc; + + val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); + if (val != 1) + pr_err("DEC_PIC_SUCCESS = %d\n", val); + + success = val & 0x1; + if (!success) + v4l2_err(&dev->v4l2_dev, "decode failed\n"); + + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (val & (1 << 3)) + v4l2_err(&dev->v4l2_dev, + "insufficient PS buffer space (%d bytes)\n", + ctx->psbuf.size); + if (val & (1 << 2)) + v4l2_err(&dev->v4l2_dev, + "insufficient slice buffer space (%d bytes)\n", + ctx->slicebuf.size); + } + + val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); + width = (val >> 16) & 0xffff; + height = val & 0xffff; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* frame crop information */ + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); + + if (left_right == 0xffffffff && top_bottom == 0xffffffff) { + /* Keep current crop information */ + } else { + struct v4l2_rect *rect = &q_data_dst->rect; + + rect->left = left_right >> 16 & 0xffff; + rect->top = top_bottom >> 16 & 0xffff; + rect->width = width - rect->left - + (left_right & 0xffff); + rect->height = height - rect->top - + (top_bottom & 0xffff); + } + } else { + /* no cropping */ + } + + err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); + if (err_mb > 0) { + if (__ratelimit(&dev->mb_err_rs)) + coda_dbg(1, ctx, "errors in %d macroblocks\n", err_mb); + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, + v4l2_ctrl_g_ctrl(ctx->mb_err_cnt_ctrl) + err_mb); + } + + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { + val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); + if (val == 0) { + /* not enough bitstream data */ + coda_dbg(1, ctx, "prescan failed: %d\n", val); + ctx->hold = true; + return; + } + } + + /* Wait until the VDOA finished writing the previous display frame */ + if (ctx->use_vdoa && + ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + err_vdoa = vdoa_wait_for_completion(ctx->vdoa); + } + + ctx->frm_dis_flg = coda_read(dev, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + /* The previous display frame was copied out and can be overwritten */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + ctx->frm_dis_flg &= ~(1 << ctx->display_idx); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + } + + /* + * The index of the last decoded frame, not necessarily in + * display order, and the index of the next display frame. + * The latter could have been decoded in a previous run. + */ + decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); + display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); + + if (decoded_idx == -1) { + /* no frame was decoded, but we might have a display frame */ + if (display_idx >= 0 && display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + else if (ctx->display_idx < 0) + ctx->hold = true; + } else if (decoded_idx == -2) { + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + /* no frame was decoded, we still return remaining buffers */ + } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "decoded frame index out of range: %d\n", decoded_idx); + } else { + int sequence; + + decoded_frame = &ctx->internal_frames[decoded_idx]; + + val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); + if (ctx->sequence_offset == -1) + ctx->sequence_offset = val; + + sequence = val + ctx->first_frame_sequence + - ctx->sequence_offset; + spin_lock(&ctx->buffer_meta_lock); + if (!list_empty(&ctx->buffer_meta_list)) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + list_del(&meta->list); + ctx->num_metas--; + spin_unlock(&ctx->buffer_meta_lock); + /* + * Clamp counters to 16 bits for comparison, as the HW + * counter rolls over at this point for h.264. This + * may be different for other formats, but using 16 bits + * should be enough to detect most errors and saves us + * from doing different things based on the format. + */ + if ((sequence & 0xffff) != (meta->sequence & 0xffff)) { + v4l2_err(&dev->v4l2_dev, + "sequence number mismatch (%d(%d) != %d)\n", + sequence, ctx->sequence_offset, + meta->sequence); + } + decoded_frame->meta = *meta; + kfree(meta); + } else { + spin_unlock(&ctx->buffer_meta_lock); + v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); + memset(&decoded_frame->meta, 0, + sizeof(struct coda_buffer_meta)); + decoded_frame->meta.sequence = sequence; + decoded_frame->meta.last = false; + ctx->sequence_offset++; + } + + trace_coda_dec_pic_done(ctx, &decoded_frame->meta); + + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; + decoded_frame->type = (val == 0) ? V4L2_BUF_FLAG_KEYFRAME : + (val == 1) ? V4L2_BUF_FLAG_PFRAME : + V4L2_BUF_FLAG_BFRAME; + + decoded_frame->error = err_mb; + } + + if (display_idx == -1) { + /* + * no more frames to be decoded, but there could still + * be rotator output to dequeue + */ + ctx->hold = true; + } else if (display_idx == -3) { + /* possibly prescan failure */ + } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "presentation frame index out of range: %d\n", + display_idx); + } + + /* If a frame was copied out, return it */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + struct coda_internal_frame *ready_frame; + + ready_frame = &ctx->internal_frames[ctx->display_idx]; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->osequence++; + + dst_buf->field = V4L2_FIELD_NONE; + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + dst_buf->flags |= ready_frame->type; + meta = &ready_frame->meta; + if (meta->last && !coda_reorder_enable(ctx)) { + /* + * If this was the last decoded frame, and reordering + * is disabled, this will be the last display frame. + */ + coda_dbg(1, ctx, "last meta, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } else if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG && + display_idx == -1) { + /* + * If there is no designated presentation frame anymore, + * this frame has to be the last one. + */ + coda_dbg(1, ctx, + "no more frames to return, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } + dst_buf->timecode = meta->timecode; + dst_buf->vb2_buf.timestamp = meta->timestamp; + + trace_coda_dec_rot_done(ctx, dst_buf, meta); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + q_data_dst->sizeimage); + + if (ready_frame->error || err_vdoa) + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); + else + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); + + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", + decoded_idx, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } + } else { + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + ctx->display_idx); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", + decoded_idx, ctx->display_idx); + } + } + + /* The rotator will copy the current display frame next time */ + ctx->display_idx = display_idx; + + /* + * The current decode run might have brought the bitstream fill level + * below the size where we can start the next decode run. As userspace + * might have filled the output queue completely and might thus be + * blocked, we can't rely on the next qbuf to trigger the bitstream + * refill. Check if we have data to refill the bitstream now. + */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); +} + +static void coda_decode_timeout(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *dst_buf; + + /* + * For now this only handles the case where we would deadlock with + * userspace, i.e. userspace issued DEC_CMD_STOP and waits for EOS, + * but after a failed decode run we would hold the context and wait for + * userspace to queue more buffers. + */ + if (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG)) + return; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->qsequence - 1; + + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); +} + +const struct coda_context_ops coda_bit_decode_ops = { + .queue_init = coda_decoder_queue_init, + .reqbufs = coda_decoder_reqbufs, + .start_streaming = coda_start_decoding, + .prepare_run = coda_prepare_decode, + .finish_run = coda_finish_decode, + .run_timeout = coda_decode_timeout, + .seq_init_work = coda_dec_seq_init_work, + .seq_end_work = coda_seq_end_work, + .release = coda_bit_release, +}; + +irqreturn_t coda_irq_handler(int irq, void *data) +{ + struct coda_dev *dev = data; + struct coda_ctx *ctx; + + /* read status register to attend the IRQ */ + coda_read(dev, CODA_REG_BIT_INT_STATUS); + coda_write(dev, 0, CODA_REG_BIT_INT_REASON); + coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, + CODA_REG_BIT_INT_CLEAR); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (ctx == NULL) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + trace_coda_bit_done(ctx); + + if (ctx->aborting) { + coda_dbg(1, ctx, "task has been aborted\n"); + } + + if (coda_isbusy(ctx->dev)) { + coda_dbg(1, ctx, "coda is still busy!!!!\n"); + return IRQ_NONE; + } + + complete(&ctx->completion); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c new file mode 100644 index 000000000..7da0194ec --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-common.c @@ -0,0 +1,3361 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "imx-vdoa.h" + +#define CODA_NAME "coda" + +#define CODADX6_MAX_INSTANCES 4 +#define CODA_MAX_FORMATS 5 + +#define CODA_ISRAM_SIZE (2048 * 2) + +#define MIN_W 48 +#define MIN_H 16 + +#define S_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN 1 /* multiple of 2 */ +#define H_ALIGN 1 /* multiple of 2 */ + +#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) + +int coda_debug; +module_param(coda_debug, int, 0644); +MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); + +static int disable_tiling; +module_param(disable_tiling, int, 0644); +MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); + +static int disable_vdoa; +module_param(disable_vdoa, int, 0644); +MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion"); + +static int enable_bwb = 0; +module_param(enable_bwb, int, 0644); +MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain streams"); + +void coda_write(struct coda_dev *dev, u32 data, u32 reg) +{ + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + writel(data, dev->regs_base + reg); +} + +unsigned int coda_read(struct coda_dev *dev, u32 reg) +{ + u32 data; + + data = readl(dev->regs_base + reg); + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, + "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); + return data; +} + +void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, + struct vb2_v4l2_buffer *buf, unsigned int reg_y) +{ + u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); + u32 base_cb, base_cr; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUYV: + /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */ + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + default: + base_cb = base_y + q_data->bytesperline * q_data->height; + base_cr = base_cb + q_data->bytesperline * q_data->height / 4; + break; + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + base_cr = base_y + q_data->bytesperline * q_data->height; + base_cb = base_cr + q_data->bytesperline * q_data->height / 4; + break; + case V4L2_PIX_FMT_YUV422P: + base_cb = base_y + q_data->bytesperline * q_data->height; + base_cr = base_cb + q_data->bytesperline * q_data->height / 2; + } + + coda_write(ctx->dev, base_y, reg_y); + coda_write(ctx->dev, base_cb, reg_y + 4); + coda_write(ctx->dev, base_cr, reg_y + 8); +} + +#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ + { mode, src_fourcc, dst_fourcc, max_w, max_h } + +/* + * Arrays of codecs supported by each given version of Coda: + * i.MX27 -> codadx6 + * i.MX51 -> codahx4 + * i.MX53 -> coda7 + * i.MX6 -> coda960 + * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants + */ +static const struct coda_codec codadx6_codecs[] = { + CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), +}; + +static const struct coda_codec codahx4_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), +}; + +static const struct coda_codec coda7_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), +}; + +static const struct coda_codec coda9_codecs[] = { + CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), + CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), + CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), + CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), +}; + +struct coda_video_device { + const char *name; + enum coda_inst_type type; + const struct coda_context_ops *ops; + bool direct; + u32 src_formats[CODA_MAX_FORMATS]; + u32 dst_formats[CODA_MAX_FORMATS]; +}; + +static const struct coda_video_device coda_bit_encoder = { + .name = "coda-video-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda_bit_encode_ops, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + }, + .dst_formats = { + V4L2_PIX_FMT_H264, + V4L2_PIX_FMT_MPEG4, + }, +}; + +static const struct coda_video_device coda_bit_jpeg_encoder = { + .name = "coda-jpeg-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda_bit_encode_ops, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, + .dst_formats = { + V4L2_PIX_FMT_JPEG, + }, +}; + +static const struct coda_video_device coda_bit_decoder = { + .name = "coda-video-decoder", + .type = CODA_INST_DECODER, + .ops = &coda_bit_decode_ops, + .src_formats = { + V4L2_PIX_FMT_H264, + V4L2_PIX_FMT_MPEG2, + V4L2_PIX_FMT_MPEG4, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + /* + * If V4L2_PIX_FMT_YUYV should be default, + * set_default_params() must be adjusted. + */ + V4L2_PIX_FMT_YUYV, + }, +}; + +static const struct coda_video_device coda_bit_jpeg_decoder = { + .name = "coda-jpeg-decoder", + .type = CODA_INST_DECODER, + .ops = &coda_bit_decode_ops, + .src_formats = { + V4L2_PIX_FMT_JPEG, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, +}; + +static const struct coda_video_device coda9_jpeg_encoder = { + .name = "coda-jpeg-encoder", + .type = CODA_INST_ENCODER, + .ops = &coda9_jpeg_encode_ops, + .direct = true, + .src_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_GREY, + }, + .dst_formats = { + V4L2_PIX_FMT_JPEG, + }, +}; + +static const struct coda_video_device coda9_jpeg_decoder = { + .name = "coda-jpeg-decoder", + .type = CODA_INST_DECODER, + .ops = &coda9_jpeg_decode_ops, + .direct = true, + .src_formats = { + V4L2_PIX_FMT_JPEG, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, +}; + +static const struct coda_video_device *codadx6_video_devices[] = { + &coda_bit_encoder, +}; + +static const struct coda_video_device *codahx4_video_devices[] = { + &coda_bit_encoder, + &coda_bit_decoder, +}; + +static const struct coda_video_device *coda7_video_devices[] = { + &coda_bit_jpeg_encoder, + &coda_bit_jpeg_decoder, + &coda_bit_encoder, + &coda_bit_decoder, +}; + +static const struct coda_video_device *coda9_video_devices[] = { + &coda9_jpeg_encoder, + &coda9_jpeg_decoder, + &coda_bit_encoder, + &coda_bit_decoder, +}; + +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUYV: + return V4L2_PIX_FMT_YUV420; + default: + return fourcc; + } +} + +static const struct coda_codec *coda_find_codec(struct coda_dev *dev, + int src_fourcc, int dst_fourcc) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + int k; + + src_fourcc = coda_format_normalize_yuv(src_fourcc); + dst_fourcc = coda_format_normalize_yuv(dst_fourcc); + if (src_fourcc == dst_fourcc) + return NULL; + + for (k = 0; k < num_codecs; k++) { + if (codecs[k].src_fourcc == src_fourcc && + codecs[k].dst_fourcc == dst_fourcc) + break; + } + + if (k == num_codecs) + return NULL; + + return &codecs[k]; +} + +static void coda_get_max_dimensions(struct coda_dev *dev, + const struct coda_codec *codec, + int *max_w, int *max_h) +{ + const struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + unsigned int w, h; + int k; + + if (codec) { + w = codec->max_w; + h = codec->max_h; + } else { + for (k = 0, w = 0, h = 0; k < num_codecs; k++) { + w = max(w, codecs[k].max_w); + h = max(h, codecs[k].max_h); + } + } + + if (max_w) + *max_w = w; + if (max_h) + *max_h = h; +} + +static const struct coda_video_device *to_coda_video_device(struct video_device + *vdev) +{ + struct coda_dev *dev = video_get_drvdata(vdev); + unsigned int i = vdev - dev->vfd; + + if (i >= dev->devtype->num_vdevs) + return NULL; + + return dev->devtype->vdevs[i]; +} + +const char *coda_product_name(int product) +{ + static char buf[9]; + + switch (product) { + case CODA_DX6: + return "CodaDx6"; + case CODA_HX4: + return "CodaHx4"; + case CODA_7541: + return "CODA7541"; + case CODA_960: + return "CODA960"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", product); + return buf; + } +} + +static struct vdoa_data *coda_get_vdoa_data(void) +{ + struct device_node *vdoa_node; + struct platform_device *vdoa_pdev; + struct vdoa_data *vdoa_data = NULL; + + vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa"); + if (!vdoa_node) + return NULL; + + vdoa_pdev = of_find_device_by_node(vdoa_node); + if (!vdoa_pdev) + goto out; + + vdoa_data = platform_get_drvdata(vdoa_pdev); + if (!vdoa_data) + vdoa_data = ERR_PTR(-EPROBE_DEFER); + + put_device(&vdoa_pdev->dev); +out: + of_node_put(vdoa_node); + + return vdoa_data; +} + +/* + * V4L2 ioctl() operations. + */ +static int coda_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + strscpy(cap->driver, CODA_NAME, sizeof(cap->driver)); + strscpy(cap->card, coda_product_name(ctx->dev->devtype->product), + sizeof(cap->card)); + strscpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); + return 0; +} + +static const u32 coda_formats_420[CODA_MAX_FORMATS] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, +}; + +static int coda_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + const struct coda_video_device *cvd = to_coda_video_device(vdev); + struct coda_ctx *ctx = fh_to_ctx(priv); + const u32 *formats; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + formats = cvd->src_formats; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + struct coda_q_data *q_data_src; + struct vb2_queue *src_vq; + + formats = cvd->dst_formats; + + /* + * If the source format is already fixed, only allow the same + * chroma subsampling. + */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && + vb2_is_streaming(src_vq)) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + formats = coda_formats_420; + } else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + f->pixelformat = V4L2_PIX_FMT_YUV422P; + return f->index ? -EINVAL : 0; + } + } + } else { + return -EINVAL; + } + + if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) + return -EINVAL; + + /* Skip YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[f->index] == V4L2_PIX_FMT_YUYV) + return -EINVAL; + + f->pixelformat = formats[f->index]; + + return 0; +} + +static int coda_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_q_data *q_data; + struct coda_ctx *ctx = fh_to_ctx(priv); + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fourcc; + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.bytesperline = q_data->bytesperline; + + f->fmt.pix.sizeimage = q_data->sizeimage; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + + return 0; +} + +static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) +{ + struct coda_q_data *q_data; + const u32 *formats; + int i; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + formats = ctx->cvd->src_formats; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + formats = ctx->cvd->dst_formats; + else + return -EINVAL; + + for (i = 0; i < CODA_MAX_FORMATS; i++) { + /* Skip YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[i] == V4L2_PIX_FMT_YUYV) + continue; + + if (formats[i] == f->fmt.pix.pixelformat) { + f->fmt.pix.pixelformat = formats[i]; + return 0; + } + } + + /* Fall back to currently set pixelformat */ + q_data = get_q_data(ctx, f->type); + f->fmt.pix.pixelformat = q_data->fourcc; + + return 0; +} + +static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f, + bool *use_vdoa) +{ + int err; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!use_vdoa) + return -EINVAL; + + if (!ctx->vdoa) { + *use_vdoa = false; + return 0; + } + + err = vdoa_context_configure(NULL, round_up(f->fmt.pix.width, 16), + f->fmt.pix.height, f->fmt.pix.pixelformat); + if (err) { + *use_vdoa = false; + return 0; + } + + *use_vdoa = true; + return 0; +} + +static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, + u32 width, u32 height) +{ + /* + * This is a rough estimate for sensible compressed buffer + * sizes (between 1 and 16 bits per pixel). This could be + * improved by better format specific worst case estimates. + */ + return round_up(clamp(sizeimage, width * height / 8, + width * height * 2), PAGE_SIZE); +} + +static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, + struct v4l2_format *f) +{ + struct coda_dev *dev = ctx->dev; + unsigned int max_w, max_h; + enum v4l2_field field; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + coda_get_max_dimensions(dev, codec, &max_w, &max_h); + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, + &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, + S_ALIGN); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + /* + * Frame stride must be at least multiple of 8, + * but multiple of 16 for h.264 or JPEG 4:2:x + */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + break; + case V4L2_PIX_FMT_YUV422P: + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 2; + break; + case V4L2_PIX_FMT_GREY: + /* keep 16 pixel alignment of 8-bit pixel data */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_MPEG2: + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, + f->fmt.pix.sizeimage, + f->fmt.pix.width, + f->fmt.pix.height); + break; + default: + BUG(); + } + + return 0; +} + +static int coda_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_q_data *q_data_src; + const struct coda_codec *codec; + struct vb2_queue *src_vq; + int hscale = 0; + int vscale = 0; + int ret; + bool use_vdoa; + + ret = coda_try_pixelformat(ctx, f); + if (ret < 0) + return ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + /* + * If the source format is already fixed, only allow the same output + * resolution. When decoding JPEG images, we also have to make sure to + * use the same chroma subsampling. + */ + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (vb2_is_streaming(src_vq)) { + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && + ctx->dev->devtype->product == CODA_960) { + hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); + vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); + } + f->fmt.pix.width = q_data_src->width >> hscale; + f->fmt.pix.height = q_data_src->height >> vscale; + + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420 && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + } + } + + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) + return -EINVAL; + + ret = coda_try_fmt(ctx, codec, f); + if (ret < 0) + return ret; + + /* The decoders always write complete macroblocks or MCUs */ + if (ctx->inst_type == CODA_INST_DECODER) { + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16 >> hscale); + f->fmt.pix.height = round_up(f->fmt.pix.height, 16 >> vscale); + if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 2; + } else { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + } + + ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); + if (ret < 0) + return ret; + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + if (!use_vdoa) + return -EINVAL; + + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + } + } + + return 0; +} + +static void coda_set_default_colorspace(struct v4l2_pix_format *fmt) +{ + enum v4l2_colorspace colorspace; + + if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) + colorspace = V4L2_COLORSPACE_JPEG; + else if (fmt->width <= 720 && fmt->height <= 576) + colorspace = V4L2_COLORSPACE_SMPTE170M; + else + colorspace = V4L2_COLORSPACE_REC709; + + fmt->colorspace = colorspace; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; +} + +static int coda_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + struct coda_dev *dev = ctx->dev; + const struct coda_q_data *q_data_dst; + const struct coda_codec *codec; + int ret; + + ret = coda_try_pixelformat(ctx, f); + if (ret < 0) + return ret; + + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) + coda_set_default_colorspace(&f->fmt.pix); + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); + + return coda_try_fmt(ctx, codec, f); +} + +static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, + struct v4l2_rect *r) +{ + struct coda_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s: %s queue busy: %d\n", + __func__, v4l2_type_names[f->type], vb2_get_num_buffers(vq)); + return -EBUSY; + } + + q_data->fourcc = f->fmt.pix.pixelformat; + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->bytesperline = f->fmt.pix.bytesperline; + q_data->sizeimage = f->fmt.pix.sizeimage; + if (r) { + q_data->rect = *r; + } else { + q_data->rect.left = 0; + q_data->rect.top = 0; + q_data->rect.width = f->fmt.pix.width; + q_data->rect.height = f->fmt.pix.height; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + break; + case V4L2_PIX_FMT_NV12: + if (!disable_tiling && ctx->use_bit && + ctx->dev->devtype->product == CODA_960) { + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + break; + } + fallthrough; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV422P: + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; + break; + default: + break; + } + + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP && + !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) && + ctx->use_vdoa) + vdoa_context_configure(ctx->vdoa, + round_up(f->fmt.pix.width, 16), + f->fmt.pix.height, + f->fmt.pix.pixelformat); + else + ctx->use_vdoa = false; + + coda_dbg(1, ctx, "Setting %s format, wxh: %dx%d, fmt: %4.4s %c\n", + v4l2_type_names[f->type], q_data->width, q_data->height, + (char *)&q_data->fourcc, + (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) ? 'L' : 'T'); + + return 0; +} + +static int coda_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + struct coda_q_data *q_data_src; + const struct coda_codec *codec; + struct v4l2_rect r; + int hscale = 0; + int vscale = 0; + int ret; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && + ctx->dev->devtype->product == CODA_960) { + hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); + vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); + } + + ret = coda_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + r.left = 0; + r.top = 0; + r.width = q_data_src->width >> hscale; + r.height = q_data_src->height >> vscale; + + ret = coda_s_fmt(ctx, f, &r); + if (ret) + return ret; + + if (ctx->inst_type != CODA_INST_ENCODER) + return 0; + + /* Setting the coded format determines the selected codec */ + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); + return -EINVAL; + } + ctx->codec = codec; + + ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; + + return 0; +} + +static int coda_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + const struct coda_codec *codec; + struct v4l2_format f_cap; + struct vb2_queue *dst_vq; + int ret; + + ret = coda_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = coda_s_fmt(ctx, f, NULL); + if (ret) + return ret; + + ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; + + if (ctx->inst_type != CODA_INST_DECODER) + return 0; + + /* Setting the coded format determines the selected codec */ + codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, + V4L2_PIX_FMT_YUV420); + if (!codec) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); + return -EINVAL; + } + ctx->codec = codec; + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (!dst_vq) + return -EINVAL; + + /* + * Setting the capture queue format is not possible while the capture + * queue is still busy. This is not an error, but the user will have to + * make sure themselves that the capture format is set correctly before + * starting the output queue again. + */ + if (vb2_is_busy(dst_vq)) + return 0; + + memset(&f_cap, 0, sizeof(f_cap)); + f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + coda_g_fmt(file, priv, &f_cap); + f_cap.fmt.pix.width = f->fmt.pix.width; + f_cap.fmt.pix.height = f->fmt.pix.height; + + return coda_s_fmt_vid_cap(file, priv, &f_cap); +} + +static int coda_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); + if (ret) + return ret; + + /* + * Allow to allocate instance specific per-context buffers, such as + * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. + */ + if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) + return ctx->ops->reqbufs(ctx, rb); + + return 0; +} + +static int coda_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); +} + +static int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); + + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + + return ret; +} + +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + if (buf->flags & V4L2_BUF_FLAG_LAST) + v4l2_event_queue_fh(&ctx->fh, &eos_event); + + v4l2_m2m_buf_done(buf, state); +} + +static int coda_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + struct v4l2_rect r, *rsel; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + r.left = 0; + r.top = 0; + r.width = q_data->width; + r.height = q_data->height; + rsel = &q_data->rect; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rsel = &r; + fallthrough; + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + ctx->inst_type == CODA_INST_DECODER) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + rsel = &r; + fallthrough; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + ctx->inst_type == CODA_INST_ENCODER) + return -EINVAL; + break; + default: + return -EINVAL; + } + + s->r = *rsel; + + return 0; +} + +static int coda_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (ctx->inst_type == CODA_INST_ENCODER && + s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + s->r.width = clamp(s->r.width, 2U, q_data->width); + s->r.height = clamp(s->r.height, 2U, q_data->height); + + if (s->flags & V4L2_SEL_FLAG_LE) { + s->r.width = round_up(s->r.width, 2); + s->r.height = round_up(s->r.height, 2); + } else { + s->r.width = round_down(s->r.width, 2); + s->r.height = round_down(s->r.height, 2); + } + + q_data->rect = s->r; + + coda_dbg(1, ctx, "Setting crop rectangle: %dx%d\n", + s->r.width, s->r.height); + + return 0; + } + fallthrough; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_COMPOSE: + return coda_g_selection(file, fh, s); + default: + /* v4l2-compliance expects this to fail for read-only targets */ + return -EINVAL; + } +} + +static void coda_wake_up_capture_queue(struct coda_ctx *ctx) +{ + struct vb2_queue *dst_vq; + + coda_dbg(1, ctx, "waking up capture queue\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_vq->last_buffer_dequeued = true; + wake_up(&dst_vq->done_wq); +} + +static int coda_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct vb2_v4l2_buffer *buf; + int ret; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + mutex_lock(&ctx->wakeup_mutex); + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + /* + * If the last output buffer is still on the queue, make sure + * that decoder finish_run will see the last flag and report it + * to userspace. + */ + buf->flags |= V4L2_BUF_FLAG_LAST; + } else { + /* Set the stream-end flag on this context */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + /* + * If the last output buffer has already been taken from the + * queue, wake up the capture queue and signal end of stream + * via the -EPIPE mechanism. + */ + coda_wake_up_capture_queue(ctx); + } + mutex_unlock(&ctx->wakeup_mutex); + + return 0; +} + +static bool coda_mark_last_meta(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta; + + coda_dbg(1, ctx, "marking last meta\n"); + + spin_lock(&ctx->buffer_meta_lock); + if (list_empty(&ctx->buffer_meta_list)) { + spin_unlock(&ctx->buffer_meta_lock); + return false; + } + + meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, + list); + meta->last = true; + + spin_unlock(&ctx->buffer_meta_lock); + return true; +} + +static bool coda_mark_last_dst_buf(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *buf; + struct vb2_buffer *dst_vb; + struct vb2_queue *dst_vq; + unsigned long flags; + + coda_dbg(1, ctx, "marking last capture buffer\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + spin_lock_irqsave(&dst_vq->done_lock, flags); + if (list_empty(&dst_vq->done_list)) { + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return false; + } + + dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, + done_entry); + buf = to_vb2_v4l2_buffer(dst_vb); + buf->flags |= V4L2_BUF_FLAG_LAST; + + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return true; +} + +static int coda_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *buf; + struct vb2_queue *dst_vq; + bool stream_end; + bool wakeup; + int ret; + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + switch (dc->cmd) { + case V4L2_DEC_CMD_START: + mutex_lock(&dev->coda_mutex); + mutex_lock(&ctx->bitstream_mutex); + coda_bitstream_flush(ctx); + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_clear_last_buffer_dequeued(dst_vq); + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + mutex_unlock(&dev->coda_mutex); + break; + case V4L2_DEC_CMD_STOP: + stream_end = false; + wakeup = false; + + mutex_lock(&ctx->wakeup_mutex); + + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + coda_dbg(1, ctx, "marking last pending buffer\n"); + + /* Mark last buffer */ + buf->flags |= V4L2_BUF_FLAG_LAST; + + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + stream_end = true; + } + } else { + if (ctx->use_bit) + if (coda_mark_last_meta(ctx)) + stream_end = true; + else + wakeup = true; + else + if (!coda_mark_last_dst_buf(ctx)) + wakeup = true; + } + + if (stream_end) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + + /* Set the stream-end flag on this context */ + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); + } + + if (wakeup) { + /* If there is no buffer in flight, wake up */ + coda_wake_up_capture_queue(ctx); + } + + mutex_unlock(&ctx->wakeup_mutex); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int coda_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data_dst; + const struct coda_codec *codec; + + if (fsize->index) + return -EINVAL; + + if (coda_format_normalize_yuv(fsize->pixel_format) == + V4L2_PIX_FMT_YUV420) { + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + codec = coda_find_codec(ctx->dev, fsize->pixel_format, + q_data_dst->fourcc); + } else { + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + fsize->pixel_format); + } + if (!codec) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = MIN_W; + fsize->stepwise.max_width = codec->max_w; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = MIN_H; + fsize->stepwise.max_height = codec->max_h; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int coda_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *f) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + const struct coda_codec *codec; + + if (f->index) + return -EINVAL; + + /* Disallow YUYV if the vdoa is not available */ + if (!ctx->vdoa && f->pixel_format == V4L2_PIX_FMT_YUYV) + return -EINVAL; + + if (coda_format_normalize_yuv(f->pixel_format) == V4L2_PIX_FMT_YUV420) { + q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + codec = coda_find_codec(ctx->dev, f->pixel_format, + q_data->fourcc); + } else { + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + f->pixel_format); + } + if (!codec) + return -EINVAL; + + if (f->width < MIN_W || f->width > codec->max_w || + f->height < MIN_H || f->height > codec->max_h) + return -EINVAL; + + f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + f->stepwise.min.numerator = 1; + f->stepwise.min.denominator = 65535; + f->stepwise.max.numerator = 65536; + f->stepwise.max.denominator = 1; + f->stepwise.step.numerator = 1; + f->stepwise.step.denominator = 1; + + return 0; +} + +static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; + tpf->numerator = 1 + (ctx->params.framerate >> + CODA_FRATE_DIV_OFFSET); + + return 0; +} + +/* + * Approximate timeperframe v4l2_fract with values that can be written + * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. + */ +static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) +{ + struct v4l2_fract s = *timeperframe; + struct v4l2_fract f0; + struct v4l2_fract f1 = { 1, 0 }; + struct v4l2_fract f2 = { 0, 1 }; + unsigned int i, div, s_denominator; + + /* Lower bound is 1/65535 */ + if (s.numerator == 0 || s.denominator / s.numerator > 65535) { + timeperframe->numerator = 1; + timeperframe->denominator = 65535; + return; + } + + /* Upper bound is 65536/1 */ + if (s.denominator == 0 || s.numerator / s.denominator > 65536) { + timeperframe->numerator = 65536; + timeperframe->denominator = 1; + return; + } + + /* Reduce fraction to lowest terms */ + div = gcd(s.numerator, s.denominator); + if (div > 1) { + s.numerator /= div; + s.denominator /= div; + } + + if (s.numerator <= 65536 && s.denominator < 65536) { + *timeperframe = s; + return; + } + + /* Find successive convergents from continued fraction expansion */ + while (f2.numerator <= 65536 && f2.denominator < 65536) { + f0 = f1; + f1 = f2; + + /* Stop when f2 exactly equals timeperframe */ + if (s.numerator == 0) + break; + + i = s.denominator / s.numerator; + + f2.numerator = f0.numerator + i * f1.numerator; + f2.denominator = f0.denominator + i * f2.denominator; + + s_denominator = s.numerator; + s.numerator = s.denominator % s.numerator; + s.denominator = s_denominator; + } + + *timeperframe = f1; +} + +static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) +{ + return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | + timeperframe->denominator; +} + +static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + coda_approximate_timeperframe(tpf); + ctx->params.framerate = coda_timeperframe_to_frate(tpf); + ctx->params.framerate_changed = true; + + return 0; +} + +static int coda_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (ctx->inst_type == CODA_INST_DECODER) + return v4l2_event_subscribe(fh, sub, 0, NULL); + else + return -EINVAL; + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops coda_ioctl_ops = { + .vidioc_querycap = coda_querycap, + + .vidioc_enum_fmt_vid_cap = coda_enum_fmt, + .vidioc_g_fmt_vid_cap = coda_g_fmt, + .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = coda_enum_fmt, + .vidioc_g_fmt_vid_out = coda_g_fmt, + .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, + + .vidioc_reqbufs = coda_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + + .vidioc_qbuf = coda_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = coda_dqbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = coda_g_selection, + .vidioc_s_selection = coda_s_selection, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = coda_encoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = coda_decoder_cmd, + + .vidioc_g_parm = coda_g_parm, + .vidioc_s_parm = coda_s_parm, + + .vidioc_enum_framesizes = coda_enum_framesizes, + .vidioc_enum_frameintervals = coda_enum_frameintervals, + + .vidioc_subscribe_event = coda_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Mem-to-mem operations. + */ + +static void coda_device_run(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *dev = ctx->dev; + + queue_work(dev->workqueue, &ctx->pic_run_work); +} + +static void coda_pic_run_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + ret = ctx->ops->prepare_run(ctx); + if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) + goto out; + + if (!wait_for_completion_timeout(&ctx->completion, + msecs_to_jiffies(1000))) { + if (ctx->use_bit) { + dev_err(dev->dev, "CODA PIC_RUN timeout\n"); + + ctx->hold = true; + + coda_hw_reset(ctx); + } + + if (ctx->ops->run_timeout) + ctx->ops->run_timeout(ctx); + } else { + ctx->ops->finish_run(ctx); + } + + if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && + ctx->ops->seq_end_work) + queue_work(dev->workqueue, &ctx->seq_end_work); + +out: + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static int coda_job_ready(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); + + /* + * For both 'P' and 'key' frame cases 1 picture + * and 1 frame are needed. In the decoder case, + * the compressed frame can be in the bitstream. + */ + if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { + coda_dbg(1, ctx, "not ready: not enough vid-out buffers.\n"); + return 0; + } + + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + coda_dbg(1, ctx, "not ready: not enough vid-cap buffers.\n"); + return 0; + } + + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { + bool stream_end = ctx->bit_stream_param & + CODA_BIT_STREAM_END_FLAG; + int num_metas = ctx->num_metas; + struct coda_buffer_meta *meta; + unsigned int count; + + count = hweight32(ctx->frm_dis_flg); + if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) { + coda_dbg(1, ctx, + "not ready: all internal buffers in use: %d/%d (0x%x)", + count, ctx->num_internal_frames, + ctx->frm_dis_flg); + return 0; + } + + if (ctx->hold && !src_bufs) { + coda_dbg(1, ctx, + "not ready: on hold for more buffers.\n"); + return 0; + } + + if (!stream_end && (num_metas + src_bufs) < 2) { + coda_dbg(1, ctx, + "not ready: need 2 buffers available (queue:%d + bitstream:%d)\n", + num_metas, src_bufs); + return 0; + } + + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + if (!coda_bitstream_can_fetch_past(ctx, meta->end) && + !stream_end) { + coda_dbg(1, ctx, + "not ready: not enough bitstream data to read past %u (%u)\n", + meta->end, ctx->bitstream_fifo.kfifo.in); + return 0; + } + } + + if (ctx->aborting) { + coda_dbg(1, ctx, "not ready: aborting\n"); + return 0; + } + + coda_dbg(2, ctx, "job ready\n"); + + return 1; +} + +static void coda_job_abort(void *priv) +{ + struct coda_ctx *ctx = priv; + + ctx->aborting = 1; + + coda_dbg(1, ctx, "job abort\n"); +} + +static const struct v4l2_m2m_ops coda_m2m_ops = { + .device_run = coda_device_run, + .job_ready = coda_job_ready, + .job_abort = coda_job_abort, +}; + +static void set_default_params(struct coda_ctx *ctx) +{ + unsigned int max_w, max_h, usize, csize; + + ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], + ctx->cvd->dst_formats[0]); + max_w = min(ctx->codec->max_w, 1920U); + max_h = min(ctx->codec->max_h, 1088U); + usize = max_w * max_h * 3 / 2; + csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); + + ctx->params.codec_mode = ctx->codec->mode; + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG || + ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) { + ctx->colorspace = V4L2_COLORSPACE_SRGB; + ctx->xfer_func = V4L2_XFER_FUNC_SRGB; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_601; + ctx->quantization = V4L2_QUANTIZATION_FULL_RANGE; + } else { + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + } + ctx->params.framerate = 30; + + /* Default formats for output and input queues */ + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; + ctx->q_data[V4L2_M2M_SRC].width = max_w; + ctx->q_data[V4L2_M2M_SRC].height = max_h; + ctx->q_data[V4L2_M2M_DST].width = max_w; + ctx->q_data[V4L2_M2M_DST].height = max_h; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { + ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; + ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; + ctx->q_data[V4L2_M2M_DST].bytesperline = 0; + ctx->q_data[V4L2_M2M_DST].sizeimage = csize; + } else { + ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; + ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; + ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; + ctx->q_data[V4L2_M2M_DST].sizeimage = usize; + } + ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; + ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; + ctx->q_data[V4L2_M2M_DST].rect.width = max_w; + ctx->q_data[V4L2_M2M_DST].rect.height = max_h; + + /* + * Since the RBC2AXI logic only supports a single chroma plane, + * macroblock tiling only works for to NV12 pixel format. + */ + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; +} + +/* + * Queue operations + */ +static int coda_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(vq); + struct coda_q_data *q_data; + unsigned int size; + + q_data = get_q_data(ctx, vq->type); + size = q_data->sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + coda_dbg(1, ctx, "get %d buffer(s) of size %d each.\n", *nbuffers, + size); + + return 0; +} + +static int coda_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + v4l2_warn(&ctx->dev->v4l2_dev, + "%s field isn't supported\n", __func__); + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + v4l2_warn(&ctx->dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + return 0; +} + +static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) +{ + if (!ctrl) + return; + + v4l2_ctrl_lock(ctrl); + + /* + * Extend the control range if the parsed stream contains a known but + * unsupported value or level. + */ + if (value > ctrl->maximum) { + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } else if (value < ctrl->minimum) { + __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } + + __v4l2_ctrl_s_ctrl(ctrl, value); + + v4l2_ctrl_unlock(ctrl); +} + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc) +{ + const char * const *profile_names; + const char * const *level_names; + struct v4l2_ctrl *profile_ctrl; + struct v4l2_ctrl *level_ctrl; + const char *codec_name; + u32 profile_cid; + u32 level_cid; + int profile; + int level; + + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_H264: + codec_name = "H264"; + profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + profile_ctrl = ctx->h264_profile_ctrl; + level_ctrl = ctx->h264_level_ctrl; + profile = coda_h264_profile(profile_idc); + level = coda_h264_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG2: + codec_name = "MPEG-2"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; + profile_ctrl = ctx->mpeg2_profile_ctrl; + level_ctrl = ctx->mpeg2_level_ctrl; + profile = coda_mpeg2_profile(profile_idc); + level = coda_mpeg2_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG4: + codec_name = "MPEG-4"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; + profile_ctrl = ctx->mpeg4_profile_ctrl; + level_ctrl = ctx->mpeg4_level_ctrl; + profile = coda_mpeg4_profile(profile_idc); + level = coda_mpeg4_level(level_idc); + break; + default: + return; + } + + profile_names = v4l2_ctrl_get_menu(profile_cid); + level_names = v4l2_ctrl_get_menu(level_cid); + + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", + codec_name, profile_idc); + } else { + coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, + profile_names[profile]); + coda_update_menu_ctrl(profile_ctrl, profile); + } + + if (level < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", + codec_name, level_idc); + } else { + coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, + level_names[level]); + coda_update_menu_ctrl(level_ctrl, level); + } +} + +static void coda_queue_source_change_event(struct coda_ctx *ctx) +{ + static const struct v4l2_event source_change_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + v4l2_event_queue_fh(&ctx->fh, &source_change_event); +} + +static void coda_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *vq = vb->vb2_queue; + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + /* + * In the decoder case, immediately try to copy the buffer into the + * bitstream ringbuffer and mark it as ready to be dequeued. + */ + if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* + * For backwards compatibility, queuing an empty buffer marks + * the stream end + */ + if (vb2_get_plane_payload(vb, 0) == 0) + coda_bit_stream_end_flag(ctx); + + if (q_data->fourcc == V4L2_PIX_FMT_H264) { + /* + * Unless already done, try to obtain profile_idc and + * level_idc from the SPS header. This allows to decide + * whether to enable reordering during sequence + * initialization. + */ + if (!ctx->params.h264_profile_idc) { + coda_sps_parse_profile(ctx, vb); + coda_update_profile_level_ctrls(ctx, + ctx->params.h264_profile_idc, + ctx->params.h264_level_idc); + } + } + + mutex_lock(&ctx->bitstream_mutex); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + if (vb2_is_streaming(vb->vb2_queue)) + /* This set buf->sequence = ctx->qsequence++ */ + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + + if (!ctx->initialized) { + /* + * Run sequence initialization in case the queued + * buffer contained headers. + */ + if (vb2_is_streaming(vb->vb2_queue) && + ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + + if (ctx->initialized) + coda_queue_source_change_event(ctx); + } + } else { + if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) && + vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + vbuf->sequence = ctx->qsequence++; + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + } +} + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent) +{ + buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, + GFP_KERNEL); + if (!buf->vaddr) { + v4l2_err(&dev->v4l2_dev, + "Failed to allocate %s buffer of size %zu\n", + name, size); + return -ENOMEM; + } + + buf->size = size; + + if (name && parent) { + buf->blob.data = buf->vaddr; + buf->blob.size = size; + buf->dentry = debugfs_create_blob(name, 0444, parent, + &buf->blob); + } + + return 0; +} + +void coda_free_aux_buf(struct coda_dev *dev, + struct coda_aux_buf *buf) +{ + if (buf->vaddr) { + dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); + buf->vaddr = NULL; + buf->size = 0; + debugfs_remove(buf->dentry); + buf->dentry = NULL; + } +} + +static int coda_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + struct v4l2_m2m_buffer *m2m_buf, *tmp; + struct vb2_v4l2_buffer *buf; + struct list_head list; + int ret = 0; + + if (count < 1) + return -EINVAL; + + coda_dbg(1, ctx, "start streaming %s\n", v4l2_type_names[q->type]); + + INIT_LIST_HEAD(&list); + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { + /* copy the buffers that were queued before streamon */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx, &list); + mutex_unlock(&ctx->bitstream_mutex); + + if (ctx->dev->devtype->product != CODA_960 && + coda_get_bitstream_payload(ctx) < 512) { + v4l2_err(v4l2_dev, "start payload < 512\n"); + ret = -EINVAL; + goto err; + } + + if (!ctx->initialized) { + /* Run sequence initialization */ + if (ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + } + } + + /* + * Check the first input JPEG buffer to determine chroma + * subsampling. + */ + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + coda_jpeg_decode_header(ctx, &buf->vb2_buf); + /* + * We have to start streaming even if the first buffer + * does not contain a valid JPEG image. The error will + * be caught during device run and will be signalled + * via the capture buffer error flag. + */ + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + q_data_dst->width = round_up(q_data_src->width, 16); + q_data_dst->height = round_up(q_data_src->height, 16); + q_data_dst->bytesperline = q_data_dst->width; + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 3 / 2; + if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) + q_data_dst->fourcc = V4L2_PIX_FMT_NV12; + } else { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 2; + q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; + } + q_data_dst->rect.left = 0; + q_data_dst->rect.top = 0; + q_data_dst->rect.width = q_data_src->width; + q_data_dst->rect.height = q_data_src->height; + } + ctx->streamon_out = 1; + } else { + ctx->streamon_cap = 1; + } + + /* Don't start the coda unless both queues are on */ + if (!(ctx->streamon_out && ctx->streamon_cap)) + goto out; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if ((q_data_src->rect.width != q_data_dst->width && + round_up(q_data_src->rect.width, 16) != q_data_dst->width) || + (q_data_src->rect.height != q_data_dst->height && + round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { + v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", + q_data_src->rect.width, q_data_src->rect.height, + q_data_dst->width, q_data_dst->height); + ret = -EINVAL; + goto err; + } + + /* Allow BIT decoder device_run with no new buffers queued */ + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) + v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); + + ctx->gopcounter = ctx->params.gop_size - 1; + + if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) + ctx->params.gop_size = 1; + ctx->gopcounter = ctx->params.gop_size - 1; + /* Only decoders have this control */ + if (ctx->mb_err_cnt_ctrl) + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); + + ret = ctx->ops->start_streaming(ctx); + if (ctx->inst_type == CODA_INST_DECODER) { + if (ret == -EAGAIN) + goto out; + } + if (ret < 0) + goto err; + +out: + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + list_for_each_entry_safe(m2m_buf, tmp, &list, list) { + list_del(&m2m_buf->list); + v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_DONE); + } + } + return 0; + +err: + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + list_for_each_entry_safe(m2m_buf, tmp, &list, list) { + list_del(&m2m_buf->list); + v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_QUEUED); + } + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } + return ret; +} + +static void coda_stop_streaming(struct vb2_queue *q) +{ + struct coda_ctx *ctx = vb2_get_drv_priv(q); + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *buf; + bool stop; + + stop = ctx->streamon_out && ctx->streamon_cap; + + coda_dbg(1, ctx, "stop streaming %s\n", v4l2_type_names[q->type]); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + ctx->streamon_out = 0; + + coda_bit_stream_end_flag(ctx); + + ctx->qsequence = 0; + + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } else { + ctx->streamon_cap = 0; + + ctx->osequence = 0; + ctx->sequence_offset = 0; + + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + if (stop) { + struct coda_buffer_meta *meta; + + if (ctx->ops->seq_end_work) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } + spin_lock(&ctx->buffer_meta_lock); + while (!list_empty(&ctx->buffer_meta_list)) { + meta = list_first_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + list_del(&meta->list); + kfree(meta); + } + ctx->num_metas = 0; + spin_unlock(&ctx->buffer_meta_lock); + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + ctx->runcounter = 0; + ctx->aborting = 0; + ctx->hold = false; + } + + if (!ctx->streamon_out && !ctx->streamon_cap) + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; +} + +static const struct vb2_ops coda_qops = { + .queue_setup = coda_queue_setup, + .buf_prepare = coda_buf_prepare, + .buf_queue = coda_buf_queue, + .start_streaming = coda_start_streaming, + .stop_streaming = coda_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int coda_s_ctrl(struct v4l2_ctrl *ctrl) +{ + const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); + struct coda_ctx *ctx = + container_of(ctrl->handler, struct coda_ctx, ctrls); + + if (val_names) + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", + ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); + else + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", + ctrl->id, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_HOR; + else + ctx->params.rot_mode &= ~CODA_MIR_HOR; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + ctx->params.rot_mode |= CODA_MIR_VER; + else + ctx->params.rot_mode &= ~CODA_MIR_VER; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctx->params.bitrate = ctrl->val / 1000; + ctx->params.bitrate_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctx->params.gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + ctx->params.h264_intra_qp = ctrl->val; + ctx->params.h264_intra_qp_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + ctx->params.h264_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->params.h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->params.h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctx->params.h264_slice_alpha_c0_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctx->params.h264_slice_beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + ctx->params.h264_constrained_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + ctx->params.frame_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + ctx->params.mb_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + ctx->params.h264_chroma_qp_index_offset = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + /* TODO: switch between baseline and constrained baseline */ + if (ctx->inst_type == CODA_INST_ENCODER) + ctx->params.h264_profile_idc = 66; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + /* nothing to do, this is set by the encoder */ + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: + ctx->params.mpeg4_intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: + ctx->params.mpeg4_inter_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + /* nothing to do, these are fixed */ + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + ctx->params.slice_mode = ctrl->val; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + ctx->params.slice_max_mb = ctrl->val; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + ctx->params.slice_max_bits = ctrl->val * 8; + ctx->params.slice_mode_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + ctx->params.intra_refresh = ctrl->val; + ctx->params.intra_refresh_changed = true; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + ctx->params.force_ipicture = true; + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + coda_set_jpeg_compression_quality(ctx, ctrl->val); + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->params.jpeg_restart_interval = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_DELAY: + ctx->params.vbv_delay = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); + break; + default: + coda_dbg(1, ctx, "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops coda_ctrl_ops = { + .s_ctrl = coda_s_ctrl, +}; + +static void coda_encode_ctrls(struct coda_ctx *ctx) +{ + int max_gop_size = (ctx->dev->devtype->product == CODA_DX6) ? 60 : 99; + + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, max_gop_size, 1, 16); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); + if (ctx->dev->devtype->product != CODA_960) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, + 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, + 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, 0x0, + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE); + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_3_1, + ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1)), + V4L2_MPEG_VIDEO_H264_LEVEL_3_1); + } + if (ctx->dev->devtype->product == CODA_960) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2)), + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541 || + ctx->dev->devtype->product == CODA_960) { + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + ~(1 << V4L2_MPEG_VIDEO_MPEG4_LEVEL_5), + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + } + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, + 500); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, + 1920 * 1088 / 256, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); + /* + * The maximum VBV size value is 0x7fffffff bits, + * one bit less than 262144 KiB + */ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); +} + +static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); +} + +static void coda_decode_ctrls(struct coda_ctx *ctx) +{ + u8 max; + + ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + if (ctx->h264_profile_ctrl) + ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else if (ctx->dev->devtype->product == CODA_960) + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + else + return; + ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); + if (ctx->h264_level_ctrl) + ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); + if (ctx->mpeg2_profile_ctrl) + ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); + if (ctx->mpeg2_level_ctrl) + ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); + if (ctx->mpeg4_profile_ctrl) + ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + if (ctx->mpeg4_level_ctrl) + ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; +} + +static const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { + .id = V4L2_CID_CODA_MB_ERR_CNT, + .name = "Macroblocks Error Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x7fffffff, + .step = 1, +}; + +static int coda_ctrls_setup(struct coda_ctx *ctx) +{ + v4l2_ctrl_handler_init(&ctx->ctrls, 2); + + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctx->inst_type == CODA_INST_ENCODER) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 1, 1, 1); + if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) + coda_jpeg_encode_ctrls(ctx); + else + coda_encode_ctrls(ctx); + } else { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + 1, 1, 1, 1); + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) + coda_decode_ctrls(ctx); + + ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, + &coda_mb_err_cnt_ctrl_config, + NULL); + if (ctx->mb_err_cnt_ctrl) + ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + } + + if (ctx->ctrls.error) { + v4l2_err(&ctx->dev->v4l2_dev, + "control initialization error (%d)", + ctx->ctrls.error); + return -EINVAL; + } + + return v4l2_ctrl_handler_setup(&ctx->ctrls); +} + +static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) +{ + vq->drv_priv = ctx; + vq->ops = &coda_qops; + vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + vq->lock = &ctx->dev->dev_mutex; + /* One way to indicate end-of-stream for coda is to set the + * bytesused == 0. However by default videobuf2 handles bytesused + * equal to 0 as a special case and changes its value to the size + * of the buffer. Set the allow_zero_bytesused flag, so + * that videobuf2 will keep the value of bytesused intact. + */ + vq->allow_zero_bytesused = 1; + /* + * We might be fine with no buffers on some of the queues, but that + * would need to be reflected in job_ready(). Currently we expect all + * queues to have at least one buffer queued. + */ + vq->min_queued_buffers = 1; + vq->dev = ctx->dev->dev; + + return vb2_queue_init(vq); +} + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->mem_ops = &vb2_vmalloc_memops; + + ret = coda_queue_init(priv, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return coda_queue_init(priv, dst_vq); +} + +/* + * File operations + */ + +static int coda_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct coda_dev *dev = video_get_drvdata(vdev); + struct coda_ctx *ctx; + unsigned int max = ~0; + char *name; + int ret; + int idx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (dev->devtype->product == CODA_DX6) + max = CODADX6_MAX_INSTANCES - 1; + idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL); + if (idx < 0) { + ret = idx; + goto err_coda_max; + } + + name = kasprintf(GFP_KERNEL, "context%d", idx); + if (!name) { + ret = -ENOMEM; + goto err_coda_name_init; + } + + ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); + kfree(name); + + ctx->cvd = to_coda_video_device(vdev); + ctx->inst_type = ctx->cvd->type; + ctx->ops = ctx->cvd->ops; + ctx->use_bit = !ctx->cvd->direct; + init_completion(&ctx->completion); + INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); + if (ctx->ops->seq_init_work) + INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); + if (ctx->ops->seq_end_work) + INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->dev = dev; + ctx->idx = idx; + + coda_dbg(1, ctx, "open instance (%p)\n", ctx); + + switch (dev->devtype->product) { + case CODA_960: + /* + * Enabling the BWB when decoding can hang the firmware with + * certain streams. The issue was tracked as ENGR00293425 by + * Freescale. As a workaround, disable BWB for all decoders. + * The enable_bwb module parameter allows to override this. + */ + if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) + ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; + fallthrough; + case CODA_HX4: + case CODA_7541: + ctx->reg_idx = 0; + break; + default: + ctx->reg_idx = idx; + } + if (ctx->dev->vdoa && !disable_vdoa) { + ctx->vdoa = vdoa_context_create(dev->vdoa); + if (!ctx->vdoa) + v4l2_warn(&dev->v4l2_dev, + "Failed to create vdoa context: not using vdoa"); + } + ctx->use_vdoa = false; + + /* Power up and upload firmware if necessary */ + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); + goto err_pm_get; + } + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_enable; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + set_default_params(ctx); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + ctx->ops->queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", + __func__, ret); + goto err_ctx_init; + } + + ret = coda_ctrls_setup(ctx); + if (ret) { + v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); + goto err_ctrls_setup; + } + + ctx->fh.ctrl_handler = &ctx->ctrls; + + mutex_init(&ctx->bitstream_mutex); + mutex_init(&ctx->buffer_mutex); + mutex_init(&ctx->wakeup_mutex); + INIT_LIST_HEAD(&ctx->buffer_meta_list); + spin_lock_init(&ctx->buffer_meta_lock); + + return 0; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +err_ctx_init: + clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_enable: + pm_runtime_put_sync(dev->dev); +err_pm_get: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); +err_coda_name_init: + ida_free(&dev->ida, ctx->idx); +err_coda_max: + kfree(ctx); + return ret; +} + +static int coda_release(struct file *file) +{ + struct coda_dev *dev = video_drvdata(file); + struct coda_ctx *ctx = fh_to_ctx(file->private_data); + + coda_dbg(1, ctx, "release instance (%p)\n", ctx); + + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) + coda_bit_stream_end_flag(ctx); + + /* If this instance is running, call .job_abort and wait for it to end */ + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + if (ctx->vdoa) + vdoa_context_destroy(ctx->vdoa); + + /* In case the instance was not running, we still need to call SEQ_END */ + if (ctx->ops->seq_end_work) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } + + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + + v4l2_ctrl_handler_free(&ctx->ctrls); + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + pm_runtime_put_sync(dev->dev); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + ida_free(&dev->ida, ctx->idx); + if (ctx->ops->release) + ctx->ops->release(ctx); + debugfs_remove_recursive(ctx->debugfs_entry); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations coda_fops = { + .owner = THIS_MODULE, + .open = coda_open, + .release = coda_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int coda_hw_init(struct coda_dev *dev) +{ + u32 data; + u16 *p; + int i, ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + + reset_control_reset(dev->rstc); + + /* + * Copy the first CODA_ISRAM_SIZE in the internal SRAM. + * The 16-bit chars in the code buffer are in memory access + * order, re-sort them to CODA order for register download. + * Data in this SRAM survives a reboot. + */ + p = (u16 *)dev->codebuf.vaddr; + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[i ^ 1]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } else { + for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { + data = CODA_DOWN_ADDRESS_SET(i) | + CODA_DOWN_DATA_SET(p[round_down(i, 4) + + 3 - (i % 4)]); + coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); + } + } + + /* Clear registers */ + for (i = 0; i < 64; i++) + coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); + + /* Tell the BIT where to find everything it needs */ + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_HX4) { + coda_write(dev, dev->tempbuf.paddr, + CODA_REG_BIT_TEMP_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + } else { + coda_write(dev, dev->workbuf.paddr, + CODA_REG_BIT_WORK_BUF_ADDR); + } + coda_write(dev, dev->codebuf.paddr, + CODA_REG_BIT_CODE_BUF_ADDR); + coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); + + /* Set default values */ + switch (dev->devtype->product) { + case CODA_DX6: + coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + break; + default: + coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, + CODA_REG_BIT_STREAM_CTRL); + } + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_FRAME_ENABLE_BWB, + CODA_REG_BIT_FRAME_MEM_CTRL); + else + coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); + + if (dev->devtype->product != CODA_DX6) + coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); + + coda_write(dev, CODA_INT_INTERRUPT_ENABLE, + CODA_REG_BIT_INT_ENABLE); + + /* Reset VPU and start processor */ + data = coda_read(dev, CODA_REG_BIT_CODE_RESET); + data |= CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + udelay(10); + data &= ~CODA_REG_RESET_ENABLE; + coda_write(dev, data, CODA_REG_BIT_CODE_RESET); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + + return 0; + +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static int coda_register_device(struct coda_dev *dev, int i) +{ + struct video_device *vfd = &dev->vfd[i]; + const char *name; + int ret; + + if (i >= dev->devtype->num_vdevs) + return -EINVAL; + name = dev->devtype->vdevs[i]->name; + + strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); + vfd->fops = &coda_fops; + vfd->ioctl_ops = &coda_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(vfd, dev); + + /* Not applicable, use the selection API instead */ + v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); + v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); + v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); + + if (dev->devtype->vdevs[i]->type == CODA_INST_ENCODER) { + v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); + if (dev->devtype->vdevs[i]->dst_formats[0] == V4L2_PIX_FMT_JPEG) { + v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); + v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); + } + } else { + v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMESIZES); + v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); + v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); + v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (!ret) + v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", + name, video_device_node_name(vfd)); + return ret; +} + +static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, + size_t size) +{ + u32 *src = (u32 *)buf; + + /* Check if the firmware has a 16-byte Freescale header, skip it */ + if (buf[0] == 'M' && buf[1] == 'X') + src += 4; + /* + * Check whether the firmware is in native order or pre-reordered for + * memory access. The first instruction opcode always is 0xe40e. + */ + if (__le16_to_cpup((__le16 *)src) == 0xe40e) { + u32 *dst = dev->codebuf.vaddr; + int i; + + /* Firmware in native order, reorder while copying */ + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (size - 16) / 4; i++) + dst[i] = (src[i] << 16) | (src[i] >> 16); + } else { + for (i = 0; i < (size - 16) / 4; i += 2) { + dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); + dst[i + 1] = (src[i] << 16) | (src[i] >> 16); + } + } + } else { + /* Copy the already reordered firmware image */ + memcpy(dev->codebuf.vaddr, src, size); + } +} + +static void coda_fw_callback(const struct firmware *fw, void *context); + +static int coda_firmware_request(struct coda_dev *dev) +{ + char *fw; + + if (dev->firmware >= ARRAY_SIZE(dev->devtype->firmware)) + return -EINVAL; + + fw = dev->devtype->firmware[dev->firmware]; + + dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, + coda_product_name(dev->devtype->product)); + + return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, + GFP_KERNEL, dev, coda_fw_callback); +} + +static void coda_fw_callback(const struct firmware *fw, void *context) +{ + struct coda_dev *dev = context; + int i, ret; + + if (!fw) { + dev->firmware++; + ret = coda_firmware_request(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); + goto put_pm; + } + return; + } + if (dev->firmware > 0) { + /* + * Since we can't suppress warnings for failed asynchronous + * firmware requests, report that the fallback firmware was + * found. + */ + dev_info(dev->dev, "Using fallback firmware %s\n", + dev->devtype->firmware[dev->firmware]); + } + + /* allocate auxiliary per-device code buffer for the BIT processor */ + ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", + dev->debugfs_root); + if (ret < 0) + goto put_pm; + + coda_copy_firmware(dev, fw->data, fw->size); + release_firmware(fw); + + ret = coda_hw_init(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); + goto put_pm; + } + + ret = coda_check_firmware(dev); + if (ret < 0) + goto put_pm; + + dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + goto put_pm; + } + + for (i = 0; i < dev->devtype->num_vdevs; i++) { + ret = coda_register_device(dev, i); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register %s video device: %d\n", + dev->devtype->vdevs[i]->name, ret); + goto rel_vfd; + } + } + + pm_runtime_put_sync(dev->dev); + return; + +rel_vfd: + while (--i >= 0) + video_unregister_device(&dev->vfd[i]); + v4l2_m2m_release(dev->m2m_dev); +put_pm: + pm_runtime_put_sync(dev->dev); +} + +enum coda_platform { + CODA_IMX27, + CODA_IMX51, + CODA_IMX53, + CODA_IMX6Q, + CODA_IMX6DL, +}; + +static const struct coda_devtype coda_devdata[] = { + [CODA_IMX27] = { + .firmware = { + "vpu_fw_imx27_TO2.bin", + "vpu/vpu_fw_imx27_TO2.bin", + "v4l-codadx6-imx27.bin" + }, + .product = CODA_DX6, + .codecs = codadx6_codecs, + .num_codecs = ARRAY_SIZE(codadx6_codecs), + .vdevs = codadx6_video_devices, + .num_vdevs = ARRAY_SIZE(codadx6_video_devices), + .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, + .iram_size = 0xb000, + }, + [CODA_IMX51] = { + .firmware = { + "vpu_fw_imx51.bin", + "vpu/vpu_fw_imx51.bin", + "v4l-codahx4-imx51.bin" + }, + .product = CODA_HX4, + .codecs = codahx4_codecs, + .num_codecs = ARRAY_SIZE(codahx4_codecs), + .vdevs = codahx4_video_devices, + .num_vdevs = ARRAY_SIZE(codahx4_video_devices), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, + [CODA_IMX53] = { + .firmware = { + "vpu_fw_imx53.bin", + "vpu/vpu_fw_imx53.bin", + "v4l-coda7541-imx53.bin" + }, + .product = CODA_7541, + .codecs = coda7_codecs, + .num_codecs = ARRAY_SIZE(coda7_codecs), + .vdevs = coda7_video_devices, + .num_vdevs = ARRAY_SIZE(coda7_video_devices), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, + [CODA_IMX6Q] = { + .firmware = { + "vpu_fw_imx6q.bin", + "vpu/vpu_fw_imx6q.bin", + "v4l-coda960-imx6q.bin" + }, + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .vdevs = coda9_video_devices, + .num_vdevs = ARRAY_SIZE(coda9_video_devices), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x21000, + }, + [CODA_IMX6DL] = { + .firmware = { + "vpu_fw_imx6d.bin", + "vpu/vpu_fw_imx6d.bin", + "v4l-coda960-imx6dl.bin" + }, + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .vdevs = coda9_video_devices, + .num_vdevs = ARRAY_SIZE(coda9_video_devices), + .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, + .iram_size = 0x1f000, /* leave 4k for suspend code */ + }, +}; + +static const struct of_device_id coda_dt_ids[] = { + { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, + { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, + { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, + { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, + { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, coda_dt_ids); + +static int coda_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct gen_pool *pool; + struct coda_dev *dev; + int ret, irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->devtype = of_device_get_match_data(&pdev->dev); + + dev->dev = &pdev->dev; + dev->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(dev->clk_per)) { + dev_err(&pdev->dev, "Could not get per clock\n"); + return PTR_ERR(dev->clk_per); + } + + dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(dev->clk_ahb)) { + dev_err(&pdev->dev, "Could not get ahb clock\n"); + return PTR_ERR(dev->clk_ahb); + } + + /* Get memory for physical registers */ + dev->regs_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); + + /* IRQ */ + irq = platform_get_irq_byname(pdev, "bit"); + if (irq < 0) + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, + CODA_NAME "-video", dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + /* JPEG IRQ */ + if (dev->devtype->product == CODA_960) { + irq = platform_get_irq_byname(pdev, "jpeg"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + coda9_jpeg_irq_handler, + IRQF_ONESHOT, CODA_NAME "-jpeg", + dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request jpeg irq\n"); + return ret; + } + } + + dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, + NULL); + if (IS_ERR(dev->rstc)) { + ret = PTR_ERR(dev->rstc); + dev_err(&pdev->dev, "failed get reset control: %d\n", ret); + return ret; + } + + /* Get IRAM pool from device tree */ + pool = of_gen_pool_get(np, "iram", 0); + if (!pool) { + dev_err(&pdev->dev, "iram pool not available\n"); + return -ENOMEM; + } + dev->iram_pool = pool; + + /* Get vdoa_data if supported by the platform */ + dev->vdoa = coda_get_vdoa_data(); + if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + ratelimit_default_init(&dev->mb_err_rs); + mutex_init(&dev->dev_mutex); + mutex_init(&dev->coda_mutex); + ida_init(&dev->ida); + + dev->debugfs_root = debugfs_create_dir("coda", NULL); + + /* allocate auxiliary per-device buffers for the BIT processor */ + if (dev->devtype->product == CODA_DX6) { + ret = coda_alloc_aux_buf(dev, &dev->workbuf, + dev->devtype->workbuf_size, "workbuf", + dev->debugfs_root); + if (ret < 0) + goto err_v4l2_register; + } + + if (dev->devtype->tempbuf_size) { + ret = coda_alloc_aux_buf(dev, &dev->tempbuf, + dev->devtype->tempbuf_size, "tempbuf", + dev->debugfs_root); + if (ret < 0) + goto err_v4l2_register; + } + + dev->iram.size = dev->devtype->iram_size; + dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, + &dev->iram.paddr); + if (!dev->iram.vaddr) { + dev_warn(&pdev->dev, "unable to alloc iram\n"); + } else { + memset(dev->iram.vaddr, 0, dev->iram.size); + dev->iram.blob.data = dev->iram.vaddr; + dev->iram.blob.size = dev->iram.size; + dev->iram.dentry = debugfs_create_blob("iram", 0444, + dev->debugfs_root, + &dev->iram.blob); + } + + dev->workqueue = alloc_ordered_workqueue("coda", WQ_MEM_RECLAIM); + if (!dev->workqueue) { + dev_err(&pdev->dev, "unable to alloc workqueue\n"); + ret = -ENOMEM; + goto err_v4l2_register; + } + + platform_set_drvdata(pdev, dev); + + /* + * Start activated so we can directly call coda_hw_init in + * coda_fw_callback regardless of whether CONFIG_PM is + * enabled or whether the device is associated with a PM domain. + */ + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = coda_firmware_request(dev); + if (ret) + goto err_alloc_workqueue; + return 0; + +err_alloc_workqueue: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + destroy_workqueue(dev->workqueue); +err_v4l2_register: + v4l2_device_unregister(&dev->v4l2_dev); + return ret; +} + +static void coda_remove(struct platform_device *pdev) +{ + struct coda_dev *dev = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) { + if (video_get_drvdata(&dev->vfd[i])) + video_unregister_device(&dev->vfd[i]); + } + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + pm_runtime_disable(&pdev->dev); + v4l2_device_unregister(&dev->v4l2_dev); + destroy_workqueue(dev->workqueue); + if (dev->iram.vaddr) + gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, + dev->iram.size); + coda_free_aux_buf(dev, &dev->codebuf); + coda_free_aux_buf(dev, &dev->tempbuf); + coda_free_aux_buf(dev, &dev->workbuf); + debugfs_remove_recursive(dev->debugfs_root); + ida_destroy(&dev->ida); +} + +#ifdef CONFIG_PM +static int coda_runtime_resume(struct device *dev) +{ + struct coda_dev *cdev = dev_get_drvdata(dev); + int ret = 0; + + if (dev->pm_domain && cdev->codebuf.vaddr) { + ret = coda_hw_init(cdev); + if (ret) + v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); + } + + return ret; +} +#endif + +static const struct dev_pm_ops coda_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) +}; + +static struct platform_driver coda_driver = { + .probe = coda_probe, + .remove_new = coda_remove, + .driver = { + .name = CODA_NAME, + .of_match_table = coda_dt_ids, + .pm = &coda_pm_ops, + }, +}; + +module_platform_driver(coda_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Martin "); +MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); diff --git a/drivers/media/platform/chips-media/coda/coda-gdi.c b/drivers/media/platform/chips-media/coda/coda-gdi.c new file mode 100644 index 000000000..59d65daca --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-gdi.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + */ + +#include +#include "coda.h" + +#define XY2_INVERT BIT(7) +#define XY2_ZERO BIT(6) +#define XY2_TB_XOR BIT(5) +#define XY2_XYSEL BIT(4) +#define XY2_Y (1 << 4) +#define XY2_X (0 << 4) + +#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((XY2_##luma_sel) | (luma_bit)) << 8 | \ + (XY2_##chroma_sel) | (chroma_bit)) + +static const u16 xy2ca_zero_map[16] = { + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +static const u16 xy2ca_tiled_map[16] = { + XY2(Y, 0, Y, 0), + XY2(Y, 1, Y, 1), + XY2(Y, 2, Y, 2), + XY2(Y, 3, X, 3), + XY2(X, 3, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +/* + * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock + * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). + * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. + */ + +#define RBC_CA (0 << 4) +#define RBC_BA (1 << 4) +#define RBC_RA (2 << 4) +#define RBC_ZERO (3 << 4) + +#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((RBC_##luma_sel) | (luma_bit)) << 6 | \ + (RBC_##chroma_sel) | (chroma_bit)) + +static const u16 rbc2axi_tiled_map[32] = { + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(CA, 0, CA, 0), + RBC(CA, 1, CA, 1), + RBC(CA, 2, CA, 2), + RBC(CA, 3, CA, 3), + RBC(CA, 4, CA, 8), + RBC(CA, 8, CA, 9), + RBC(CA, 9, CA, 10), + RBC(CA, 10, CA, 11), + RBC(CA, 11, CA, 12), + RBC(CA, 12, CA, 13), + RBC(CA, 13, CA, 14), + RBC(CA, 14, CA, 15), + RBC(CA, 15, RA, 0), + RBC(RA, 0, RA, 1), + RBC(RA, 1, RA, 2), + RBC(RA, 2, RA, 3), + RBC(RA, 3, RA, 4), + RBC(RA, 4, RA, 5), + RBC(RA, 5, RA, 6), + RBC(RA, 6, RA, 7), + RBC(RA, 7, RA, 8), + RBC(RA, 8, RA, 9), + RBC(RA, 9, RA, 10), + RBC(RA, 10, RA, 11), + RBC(RA, 11, RA, 12), + RBC(RA, 12, RA, 13), + RBC(RA, 13, RA, 14), + RBC(RA, 14, RA, 15), + RBC(RA, 15, ZERO, 0), +}; + +void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + const u16 *xy2ca_map; + u32 xy2rbc_config; + int i; + + switch (ctx->tiled_map_type) { + case GDI_LINEAR_FRAME_MAP: + default: + xy2ca_map = xy2ca_zero_map; + xy2rbc_config = 0; + break; + case GDI_TILED_FRAME_MB_RASTER_MAP: + xy2ca_map = xy2ca_tiled_map; + xy2rbc_config = CODA9_XY2RBC_TILED_MAP | + CODA9_XY2RBC_CA_INC_HOR | + (16 - 1) << 12 | (8 - 1) << 4; + break; + } + + for (i = 0; i < 16; i++) + coda_write(dev, xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + if (xy2rbc_config) { + for (i = 0; i < 32; i++) + coda_write(dev, rbc2axi_tiled_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); + } +} diff --git a/drivers/media/platform/chips-media/coda/coda-h264.c b/drivers/media/platform/chips-media/coda/coda-h264.c new file mode 100644 index 000000000..8bd0aa8af --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-h264.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - H.264 helper functions + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + */ + +#include +#include +#include + +#include "coda.h" + +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; + +static const u8 *coda_find_nal_header(const u8 *buf, const u8 *end) +{ + u32 val = 0xffffffff; + + do { + val = val << 8 | *buf++; + if (buf >= end) + return NULL; + } while (val != 0x00000001); + + return buf; +} + +int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + const u8 *buf = vb2_plane_vaddr(vb, 0); + const u8 *end = buf + vb2_get_plane_payload(vb, 0); + + /* Find SPS header */ + do { + buf = coda_find_nal_header(buf, end); + if (!buf) + return -EINVAL; + } while ((*buf++ & 0x1f) != 0x7); + + ctx->params.h264_profile_idc = buf[0]; + ctx->params.h264_level_idc = buf[2]; + + return 0; +} + +int coda_h264_filler_nal(int size, char *p) +{ + if (size < 6) + return -EINVAL; + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + p[4] = 0x0c; + memset(p + 5, 0xff, size - 6); + /* Add rbsp stop bit and trailing at the end */ + p[size - 1] = 0x80; + + return 0; +} + +int coda_h264_padding(int size, char *p) +{ + int nal_size; + int diff; + + diff = size - (size & ~0x7); + if (diff == 0) + return 0; + + nal_size = coda_filler_size[diff]; + coda_h264_filler_nal(nal_size, p); + + return nal_size; +} + +int coda_h264_profile(int profile_idc) +{ + switch (profile_idc) { + case 66: return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case 77: return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case 88: return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case 100: return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + default: return -EINVAL; + } +} + +int coda_h264_level(int level_idc) +{ + switch (level_idc) { + case 10: return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + case 9: return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + case 11: return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + case 12: return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + case 13: return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + case 20: return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + case 21: return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + case 22: return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + case 30: return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + case 31: return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + default: return -EINVAL; + } +} + +struct rbsp { + char *buf; + int size; + int pos; +}; + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + return (rbsp->buf[ofs] >> shift) & 1; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->buf[ofs] &= ~(1 << shift); + rbsp->buf[ofs] |= bit << shift; + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) +{ + int i, ret; + int tmp = 0; + + if (num > 32) + return -EINVAL; + + for (i = 0; i < num; i++) { + ret = rbsp_read_bit(rbsp); + if (ret < 0) + return ret; + tmp |= ret << (num - i - 1); + } + + if (val) + *val = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) +{ + int ret; + + while (num--) { + ret = rbsp_write_bit(rbsp, (value >> num) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (val) + *val = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) +{ + int i; + int ret; + int tmp = value + 1; + int leading_zero_bits = fls(tmp) - 1; + + for (i = 0; i < leading_zero_bits; i++) { + ret = rbsp_write_bit(rbsp, 0); + if (ret) + return ret; + } + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *val) +{ + unsigned int tmp; + int ret; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (val) { + if (tmp & 1) + *val = (tmp + 1) / 2; + else + *val = -(tmp / 2); + } + + return 0; +} + +/** + * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS + * @ctx: encoder context + * @width: visible width + * @height: visible height + * @buf: buffer containing h.264 SPS RBSP, starting with NAL header + * @size: modified RBSP size return value + * @max_size: available size in buf + * + * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the + * given visible width and height. + */ +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size) +{ + int profile_idc; + unsigned int pic_order_cnt_type; + int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; + int frame_mbs_only_flag, frame_cropping_flag; + int vui_parameters_present_flag; + unsigned int crop_right, crop_bottom; + struct rbsp sps; + int pos; + int ret; + + if (*size < 8 || *size >= max_size) + return -EINVAL; + + sps.buf = buf + 5; /* Skip NAL header */ + sps.size = *size - 5; + + profile_idc = sps.buf[0]; + /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ + /* Skip level_idc */ + sps.pos = 24; + + /* seq_parameter_set_id */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || + profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || + profile_idc == 135) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling profile_idc %d not implemented\n", + __func__, profile_idc); + return -EINVAL; + } + + /* log2_max_frame_num_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, &pic_order_cnt_type); + if (ret) + return ret; + + if (pic_order_cnt_type == 0) { + /* log2_max_pic_order_cnt_lsb_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + } else if (pic_order_cnt_type == 1) { + unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; + + /* delta_pic_order_always_zero_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + /* offset_for_non_ref_pic */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + /* offset_for_top_to_bottom_field */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, + &num_ref_frames_in_pic_order_cnt_cycle); + if (ret) + return ret; + for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* offset_for_ref_frame */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + } + } + + /* max_num_ref_frames */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + /* gaps_in_frame_num_value_allowed_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); + if (ret) + return ret; + frame_mbs_only_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (!frame_mbs_only_flag) { + /* mb_adaptive_frame_field_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + } + /* direct_8x8_inference_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + + /* Mark position of the frame cropping flag */ + pos = sps.pos; + frame_cropping_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (frame_cropping_flag) { + unsigned int crop_left, crop_top; + + ret = rbsp_read_uev(&sps, &crop_left); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_right); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_top); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_bottom); + if (ret) + return ret; + } + vui_parameters_present_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (vui_parameters_present_flag) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling vui_parameters not implemented\n", + __func__); + return -EINVAL; + } + + crop_right = round_up(width, 16) - width; + crop_bottom = round_up(height, 16) - height; + crop_right /= 2; + if (frame_mbs_only_flag) + crop_bottom /= 2; + else + crop_bottom /= 4; + + + sps.size = max_size - 5; + sps.pos = pos; + frame_cropping_flag = 1; + ret = rbsp_write_bit(&sps, frame_cropping_flag); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_left */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_right); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_top */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_bottom); + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 1); + if (ret) + return ret; + + *size = 5 + DIV_ROUND_UP(sps.pos, 8); + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c new file mode 100644 index 000000000..ba8f41002 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c @@ -0,0 +1,1547 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - JPEG support functions + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "coda.h" +#include "trace.h" + +#define SOI_MARKER 0xffd8 +#define APP9_MARKER 0xffe9 +#define DRI_MARKER 0xffdd +#define DQT_MARKER 0xffdb +#define DHT_MARKER 0xffc4 +#define SOF_MARKER 0xffc0 +#define SOS_MARKER 0xffda +#define EOI_MARKER 0xffd9 + +enum { + CODA9_JPEG_FORMAT_420, + CODA9_JPEG_FORMAT_422, + CODA9_JPEG_FORMAT_224, + CODA9_JPEG_FORMAT_444, + CODA9_JPEG_FORMAT_400, +}; + +struct coda_huff_tab { + u8 luma_dc[16 + 12]; + u8 chroma_dc[16 + 12]; + u8 luma_ac[16 + 162]; + u8 chroma_ac[16 + 162]; + + /* DC Luma, DC Chroma, AC Luma, AC Chroma */ + s16 min[4 * 16]; + s16 max[4 * 16]; + s8 ptr[4 * 16]; +}; + +#define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) + +/* + * Typical Huffman tables for 8-bit precision luminance and + * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3 + */ + +static const unsigned char luma_dc[16 + 12] = { + /* bits */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char chroma_dc[16 + 12] = { + /* bits */ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char luma_ac[16 + 162 + 2] = { + /* bits */ + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + /* values */ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, /* padded to 32-bit */ +}; + +static const unsigned char chroma_ac[16 + 162 + 2] = { + /* bits */ + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + /* values */ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, /* padded to 32-bit */ +}; + +/* + * Quantization tables for luminance and chrominance components in + * zig-zag scan order from the Freescale i.MX VPU libraries + */ + +static unsigned char luma_q[64] = { + 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05, + 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b, + 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a, + 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, +}; + +static unsigned char chroma_q[64] = { + 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10, + 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, + 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, +}; + +static const unsigned char width_align[] = { + [CODA9_JPEG_FORMAT_420] = 16, + [CODA9_JPEG_FORMAT_422] = 16, + [CODA9_JPEG_FORMAT_224] = 8, + [CODA9_JPEG_FORMAT_444] = 8, + [CODA9_JPEG_FORMAT_400] = 8, +}; + +static const unsigned char height_align[] = { + [CODA9_JPEG_FORMAT_420] = 16, + [CODA9_JPEG_FORMAT_422] = 8, + [CODA9_JPEG_FORMAT_224] = 16, + [CODA9_JPEG_FORMAT_444] = 8, + [CODA9_JPEG_FORMAT_400] = 8, +}; + +static int coda9_jpeg_chroma_format(u32 pixfmt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + return CODA9_JPEG_FORMAT_420; + case V4L2_PIX_FMT_YUV422P: + return CODA9_JPEG_FORMAT_422; + case V4L2_PIX_FMT_YUV444: + return CODA9_JPEG_FORMAT_444; + case V4L2_PIX_FMT_GREY: + return CODA9_JPEG_FORMAT_400; + } + return -EINVAL; +} + +struct coda_memcpy_desc { + int offset; + const void *src; + size_t len; +}; + +static void coda_memcpy_parabuf(void *parabuf, + const struct coda_memcpy_desc *desc) +{ + u32 *dst = parabuf + desc->offset; + const u32 *src = desc->src; + int len = desc->len / 4; + int i; + + for (i = 0; i < len; i += 2) { + dst[i + 1] = swab32(src[i]); + dst[i] = swab32(src[i + 1]); + } +} + +int coda_jpeg_write_tables(struct coda_ctx *ctx) +{ + int i; + static const struct coda_memcpy_desc huff[8] = { + { 0, luma_dc, sizeof(luma_dc) }, + { 32, luma_ac, sizeof(luma_ac) }, + { 216, chroma_dc, sizeof(chroma_dc) }, + { 248, chroma_ac, sizeof(chroma_ac) }, + }; + struct coda_memcpy_desc qmat[3] = { + { 512, ctx->params.jpeg_qmat_tab[0], 64 }, + { 576, ctx->params.jpeg_qmat_tab[1], 64 }, + { 640, ctx->params.jpeg_qmat_tab[1], 64 }, + }; + + /* Write huffman tables to parameter memory */ + for (i = 0; i < ARRAY_SIZE(huff); i++) + coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i); + + /* Write Q-matrix to parameter memory */ + for (i = 0; i < ARRAY_SIZE(qmat); i++) + coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i); + + return 0; +} + +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + void *vaddr = vb2_plane_vaddr(vb, 0); + u16 soi, eoi; + int len, i; + + soi = be16_to_cpup((__be16 *)vaddr); + if (soi != SOI_MARKER) + return false; + + len = vb2_get_plane_payload(vb, 0); + vaddr += len - 2; + for (i = 0; i < 32; i++) { + eoi = be16_to_cpup((__be16 *)(vaddr - i)); + if (eoi == EOI_MARKER) { + if (i > 0) + vb2_set_plane_payload(vb, 0, len - i); + return true; + } + } + + return false; +} + +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); + +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + struct coda_dev *dev = ctx->dev; + u8 *buf = vb2_plane_vaddr(vb, 0); + size_t len = vb2_get_plane_payload(vb, 0); + struct v4l2_jpeg_scan_header scan_header; + struct v4l2_jpeg_reference quantization_tables[4] = { }; + struct v4l2_jpeg_reference huffman_tables[4] = { }; + struct v4l2_jpeg_header header = { + .scan = &scan_header, + .quantization_tables = quantization_tables, + .huffman_tables = huffman_tables, + }; + struct coda_q_data *q_data_src; + struct coda_huff_tab *huff_tab; + int i, j, ret; + + ret = v4l2_jpeg_parse_header(buf, len, &header); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to parse JPEG header: %pe\n", + ERR_PTR(ret)); + return ret; + } + + ctx->params.jpeg_restart_interval = header.restart_interval; + + /* check frame header */ + if (header.frame.height > ctx->codec->max_h || + header.frame.width > ctx->codec->max_w) { + v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (header.frame.height != q_data_src->height || + header.frame.width != q_data_src->width) { + v4l2_err(&dev->v4l2_dev, + "dimensions don't match format: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + if (header.frame.num_components != 3) { + v4l2_err(&dev->v4l2_dev, + "unsupported number of components: %d\n", + header.frame.num_components); + return -EINVAL; + } + + /* install quantization tables */ + if (quantization_tables[3].start) { + v4l2_err(&dev->v4l2_dev, + "only 3 quantization tables supported\n"); + return -EINVAL; + } + for (i = 0; i < 3; i++) { + if (!quantization_tables[i].start) + continue; + if (quantization_tables[i].length != 64) { + v4l2_err(&dev->v4l2_dev, + "only 8-bit quantization tables supported\n"); + continue; + } + if (!ctx->params.jpeg_qmat_tab[i]) { + ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[i]) + return -ENOMEM; + } + memcpy(ctx->params.jpeg_qmat_tab[i], + quantization_tables[i].start, 64); + } + + /* install Huffman tables */ + for (i = 0; i < 4; i++) { + if (!huffman_tables[i].start) { + v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); + return -EINVAL; + } + /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ + if (huffman_tables[i].length < 17 || + huffman_tables[i].length > 178 || + ((i & 2) == 0 && huffman_tables[i].length > 28)) { + v4l2_err(&dev->v4l2_dev, + "invalid Huffman table %d length: %zu\n", + i, huffman_tables[i].length); + return -EINVAL; + } + } + huff_tab = ctx->params.jpeg_huff_tab; + if (!huff_tab) { + huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); + if (!huff_tab) + return -ENOMEM; + ctx->params.jpeg_huff_tab = huff_tab; + } + + memset(huff_tab, 0, sizeof(*huff_tab)); + memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); + memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); + memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); + memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); + + /* check scan header */ + for (i = 0; i < scan_header.num_components; i++) { + struct v4l2_jpeg_scan_component_spec *scan_component; + + scan_component = &scan_header.component[i]; + for (j = 0; j < header.frame.num_components; j++) { + if (header.frame.component[j].component_identifier == + scan_component->component_selector) + break; + } + if (j == header.frame.num_components) + continue; + + ctx->params.jpeg_huff_dc_index[j] = + scan_component->dc_entropy_coding_table_selector; + ctx->params.jpeg_huff_ac_index[j] = + scan_component->ac_entropy_coding_table_selector; + } + + /* Generate Huffman table information */ + for (i = 0; i < 4; i++) + coda9_jpeg_gen_dec_huff_tab(ctx, i); + + /* start of entropy coded segment */ + ctx->jpeg_ecs_offset = header.ecs_offset; + + switch (header.frame.subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; + break; + default: + v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", + header.frame.subsampling); + return -EINVAL; + } + + return 0; +} + +static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, + int num_values) +{ + s8 *values = (s8 *)(bits + 16); + int huff_length, i; + + for (huff_length = 0, i = 0; i < 16; i++) + huff_length += bits[i]; + for (i = huff_length; i < num_values; i++) + values[i] = -1; + for (i = 0; i < num_values; i++) + coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); +} + +static void coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + struct coda_dev *dev = ctx->dev; + s16 *huff_min = huff_tab->min; + s16 *huff_max = huff_tab->max; + s8 *huff_ptr = huff_tab->ptr; + int i; + + /* MIN Tables */ + coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); + + /* MAX Tables */ + coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); + + /* PTR Tables */ + coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); + + /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ + coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); +} + +static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, + u8 *qmat, int index) +{ + int i; + + coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); + for (i = 0; i < 64; i++) + coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); + coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); +} + +static void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int *qmat_index = ctx->params.jpeg_qmat_index; + u8 **qmat_tab = ctx->params.jpeg_qmat_tab; + + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); +} + +static void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, + struct vb2_buffer *buf, u32 ecs_offset) +{ + struct coda_dev *dev = ctx->dev; + int page_ptr, word_ptr, bit_ptr; + u32 bbc_base_addr, end_addr; + int bbc_cur_pos; + int ret, val; + + bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); + end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); + + page_ptr = ecs_offset / 256; + word_ptr = (ecs_offset % 256) / 4; + if (page_ptr & 1) + word_ptr += 64; + bit_ptr = (ecs_offset % 4) * 8; + if (word_ptr & 1) + bit_ptr += 32; + word_ptr &= ~0x1; + + coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); + coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); + + /* Leave 3 256-byte page margin to avoid a BBC interrupt */ + coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); + val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; + coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); + + bbc_cur_pos = page_ptr; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); + + coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); + coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); + coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); + if (page_ptr & 1) { + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); + } else { + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); + } + coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); + coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); + coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); +} + +static const int bus_req_num[] = { + [CODA9_JPEG_FORMAT_420] = 2, + [CODA9_JPEG_FORMAT_422] = 3, + [CODA9_JPEG_FORMAT_224] = 3, + [CODA9_JPEG_FORMAT_444] = 4, + [CODA9_JPEG_FORMAT_400] = 4, +}; + +#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \ + (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \ + ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \ + ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \ + ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \ + ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET)) + +static const u32 mcu_info[] = { + [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5), + [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5), + [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5), + [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5), + [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0), +}; + +/* + * Convert Huffman table specifcations to tables of codes and code lengths. + * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] + * + * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ +static int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num, + int *ehufsi, int *ehufco) +{ + int i, j, k, lastk, si, code, maxsymbol; + const u8 *bits, *huffval; + struct { + int size[256]; + int code[256]; + } *huff; + static const unsigned char *huff_tabs[4] = { + luma_dc, luma_ac, chroma_dc, chroma_ac, + }; + int ret = -EINVAL; + + huff = kzalloc(sizeof(*huff), GFP_KERNEL); + if (!huff) + return -ENOMEM; + + bits = huff_tabs[tab_num]; + huffval = huff_tabs[tab_num] + 16; + + maxsymbol = tab_num & 1 ? 256 : 16; + + /* Figure C.1 - Generation of table of Huffman code sizes */ + k = 0; + for (i = 1; i <= 16; i++) { + j = bits[i - 1]; + if (k + j > maxsymbol) + goto out; + while (j--) + huff->size[k++] = i; + } + lastk = k; + + /* Figure C.2 - Generation of table of Huffman codes */ + k = 0; + code = 0; + si = huff->size[0]; + while (k < lastk) { + while (huff->size[k] == si) { + huff->code[k++] = code; + code++; + } + if (code >= (1 << si)) + goto out; + code <<= 1; + si++; + } + + /* Figure C.3 - Ordering procedure for encoding procedure code tables */ + for (k = 0; k < lastk; k++) { + i = huffval[k]; + if (i >= maxsymbol || ehufsi[i]) + goto out; + ehufco[i] = huff->code[k]; + ehufsi[i] = huff->size[k]; + } + + ret = 0; +out: + kfree(huff); + return ret; +} + +#define DC_TABLE_INDEX0 0 +#define AC_TABLE_INDEX0 1 +#define DC_TABLE_INDEX1 2 +#define AC_TABLE_INDEX1 3 + +static u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + + if (!huff_tab) + return NULL; + + switch (tab_num) { + case DC_TABLE_INDEX0: return huff_tab->luma_dc; + case AC_TABLE_INDEX0: return huff_tab->luma_ac; + case DC_TABLE_INDEX1: return huff_tab->chroma_dc; + case AC_TABLE_INDEX1: return huff_tab->chroma_ac; + } + + return NULL; +} + +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) +{ + int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; + u8 *huff_bits; + s16 *huff_max; + s16 *huff_min; + s8 *huff_ptr; + int ofs; + int i; + + huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); + if (!huff_bits) + return -EINVAL; + + /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ + ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); + ofs *= 16; + + huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; + huff_max = ctx->params.jpeg_huff_tab->max + ofs; + huff_min = ctx->params.jpeg_huff_tab->min + ofs; + + for (i = 0; i < 16; i++) { + if (huff_bits[i]) { + huff_ptr[i] = ptr_cnt; + ptr_cnt += huff_bits[i]; + huff_min[i] = huff_code; + huff_max[i] = huff_code + (huff_bits[i] - 1); + data_flag = 1; + zero_flag = 0; + } else { + huff_ptr[i] = -1; + huff_min[i] = -1; + huff_max[i] = -1; + zero_flag = 1; + } + + if (data_flag == 1) { + if (zero_flag == 1) + huff_code <<= 1; + else + huff_code = (huff_max[i] + 1) << 1; + } + } + + return 0; +} + +static int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) +{ + struct { + int size[4][256]; + int code[4][256]; + } *huff; + u32 *huff_data; + int i, j; + int ret; + + huff = kzalloc(sizeof(*huff), GFP_KERNEL); + if (!huff) + return -ENOMEM; + + /* Generate all four (luma/chroma DC/AC) code/size lookup tables */ + for (i = 0; i < 4; i++) { + ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i], + huff->code[i]); + if (ret) + goto out; + } + + if (!ctx->params.jpeg_huff_data) { + ctx->params.jpeg_huff_data = + kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE, + GFP_KERNEL); + if (!ctx->params.jpeg_huff_data) { + ret = -ENOMEM; + goto out; + } + } + huff_data = ctx->params.jpeg_huff_data; + + for (j = 0; j < 4; j++) { + /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */ + int t = (j == 0) ? AC_TABLE_INDEX0 : + (j == 1) ? AC_TABLE_INDEX1 : + (j == 2) ? DC_TABLE_INDEX0 : + DC_TABLE_INDEX1; + /* DC tables only have 16 entries */ + int len = (j < 2) ? 256 : 16; + + for (i = 0; i < len; i++) { + if (huff->size[t][i] == 0 && huff->code[t][i] == 0) + *(huff_data++) = 0; + else + *(huff_data++) = + ((huff->size[t][i] - 1) << 16) | + huff->code[t][i]; + } + } + + ret = 0; +out: + kfree(huff); + return ret; +} + +static void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 *huff_data = ctx->params.jpeg_huff_data; + int i; + + /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */ + coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL); + for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++) + coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA); + coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL); +} + +static inline void coda9_jpeg_write_qmat_quotients(struct coda_dev *dev, + u8 *qmat, int index) +{ + int i; + + coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); + for (i = 0; i < 64; i++) + coda_write(dev, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA); + coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL); +} + +static void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u8 *luma_tab; + u8 *chroma_tab; + + luma_tab = ctx->params.jpeg_qmat_tab[0]; + if (!luma_tab) + luma_tab = luma_q; + + chroma_tab = ctx->params.jpeg_qmat_tab[1]; + if (!chroma_tab) + chroma_tab = chroma_q; + + coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00); + coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40); + coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80); +} + +struct coda_jpeg_stream { + u8 *curr; + u8 *end; +}; + +static inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream) +{ + if (stream->curr >= stream->end) + return -EINVAL; + + *stream->curr++ = byte; + + return 0; +} + +static inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream) +{ + if (stream->curr + sizeof(__be16) > stream->end) + return -EINVAL; + + put_unaligned_be16(word, stream->curr); + stream->curr += sizeof(__be16); + + return 0; +} + +static int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table, + size_t len, struct coda_jpeg_stream *stream) +{ + int i, ret; + + ret = coda_jpeg_put_word(marker, stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(3 + len, stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(index, stream); + for (i = 0; i < len && ret == 0; i++) + ret = coda_jpeg_put_byte(table[i], stream); + + return ret; +} + +static int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index, + struct coda_jpeg_stream *stream) +{ + return coda_jpeg_put_table(DQT_MARKER, index, + ctx->params.jpeg_qmat_tab[index], 64, + stream); +} + +static int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len, + struct coda_jpeg_stream *stream) +{ + return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream); +} + +static int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf) +{ + struct coda_jpeg_stream stream = { buf, buf + len }; + struct coda_q_data *q_data_src; + int chroma_format, comp_num; + int i, ret, pad; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); + if (chroma_format < 0) + return 0; + + /* Start Of Image */ + ret = coda_jpeg_put_word(SOI_MARKER, &stream); + if (ret < 0) + return ret; + + /* Define Restart Interval */ + if (ctx->params.jpeg_restart_interval) { + ret = coda_jpeg_put_word(DRI_MARKER, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(4, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval, + &stream); + if (ret < 0) + return ret; + } + + /* Define Quantization Tables */ + ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream); + if (ret < 0) + return ret; + if (chroma_format != CODA9_JPEG_FORMAT_400) { + ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream); + if (ret < 0) + return ret; + } + + /* Define Huffman Tables */ + ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream); + if (ret < 0) + return ret; + if (chroma_format != CODA9_JPEG_FORMAT_400) { + ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12, + &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162, + &stream); + if (ret < 0) + return ret; + } + + /* Start Of Frame */ + ret = coda_jpeg_put_word(SOF_MARKER, &stream); + if (ret < 0) + return ret; + comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3; + ret = coda_jpeg_put_word(8 + comp_num * 3, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(0x08, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(q_data_src->height, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_word(q_data_src->width, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(comp_num, &stream); + if (ret < 0) + return ret; + for (i = 0; i < comp_num; i++) { + static unsigned char subsampling[5][3] = { + [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 }, + [CODA9_JPEG_FORMAT_400] = { 0x11 }, + }; + + /* Component identifier, matches SOS */ + ret = coda_jpeg_put_byte(i + 1, &stream); + if (ret < 0) + return ret; + ret = coda_jpeg_put_byte(subsampling[chroma_format][i], + &stream); + if (ret < 0) + return ret; + /* Chroma table index */ + ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream); + if (ret < 0) + return ret; + } + + /* Pad to multiple of 8 bytes */ + pad = (stream.curr - buf) % 8; + if (pad) { + pad = 8 - pad; + while (pad--) { + ret = coda_jpeg_put_byte(0x00, &stream); + if (ret < 0) + return ret; + } + } + + return stream.curr - buf; +} + +/* + * Scale quantization table using nonlinear scaling factor + * u8 qtab[64], scale [50,190] + */ +static void coda_scale_quant_table(u8 *q_tab, int scale) +{ + unsigned int temp; + int i; + + for (i = 0; i < 64; i++) { + temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + q_tab[i] = (unsigned char)temp; + } +} + +void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality) +{ + unsigned int scale; + + ctx->params.jpeg_quality = quality; + + /* Clip quality setting to [5,100] interval */ + if (quality > 100) + quality = 100; + if (quality < 5) + quality = 5; + + /* + * Non-linear scaling factor: + * [5,50] -> [1000..100], [51,100] -> [98..0] + */ + if (quality < 50) + scale = 5000 / quality; + else + scale = 200 - 2 * quality; + + if (ctx->params.jpeg_qmat_tab[0]) { + memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64); + coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale); + } + if (ctx->params.jpeg_qmat_tab[1]) { + memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64); + coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale); + } +} + +/* + * Encoder context operations + */ + +static int coda9_jpeg_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + ret = coda9_jpeg_load_huff_tab(ctx); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); + return ret; + } + if (!ctx->params.jpeg_qmat_tab[0]) { + ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[0]) + return -ENOMEM; + } + if (!ctx->params.jpeg_qmat_tab[1]) { + ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[1]) + return -ENOMEM; + } + coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); + + return 0; +} + +static int coda9_jpeg_prepare_encode(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 start_addr, end_addr; + u16 aligned_width, aligned_height; + bool chroma_interleave; + int chroma_format; + int header_len; + int ret; + ktime_t timeout; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) + vb2_set_plane_payload(&src_buf->vb2_buf, 0, + vb2_plane_size(&src_buf->vb2_buf, 0)); + + src_buf->sequence = ctx->osequence; + dst_buf->sequence = ctx->osequence; + ctx->osequence++; + + src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; + + coda_set_gdi_regs(ctx); + + start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0); + + chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); + if (chroma_format < 0) + return chroma_format; + + /* Round image dimensions to multiple of MCU size */ + aligned_width = round_up(q_data_src->width, width_align[chroma_format]); + aligned_height = round_up(q_data_src->height, + height_align[chroma_format]); + if (aligned_width != q_data_src->bytesperline) { + v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n", + aligned_width, q_data_src->bytesperline); + } + + header_len = + coda9_jpeg_encode_header(ctx, + vb2_plane_size(&dst_buf->vb2_buf, 0), + vb2_plane_vaddr(&dst_buf->vb2_buf, 0)); + if (header_len < 0) + return header_len; + + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR); + coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR); + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR); + coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS); + /* 64 words per 256-byte page */ + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR); + + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); + coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256), + CODA9_REG_JPEG_BBC_STRM_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR); + coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); + + chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12); + coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION | + CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL); + coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); + coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA9_REG_JPEG_RST_INTVAL); + coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); + + coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); + + coda9_jpeg_write_huff_tab(ctx); + coda9_jpeg_load_qmat_tab(ctx); + + if (ctx->params.rot_mode & CODA_ROT_90) { + aligned_width = aligned_height; + aligned_height = q_data_src->bytesperline; + if (chroma_format == CODA9_JPEG_FORMAT_422) + chroma_format = CODA9_JPEG_FORMAT_224; + else if (chroma_format == CODA9_JPEG_FORMAT_224) + chroma_format = CODA9_JPEG_FORMAT_422; + } + /* These need to be multiples of MCU size */ + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_REG_JPEG_PIC_SIZE); + coda_write(dev, ctx->params.rot_mode ? + (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0, + CODA9_REG_JPEG_ROT_INFO); + + coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); + + coda_write(dev, 1, CODA9_GDI_CONTROL); + timeout = ktime_add_us(ktime_get(), 100000); + do { + ret = coda_read(dev, CODA9_GDI_STATUS); + if (ktime_compare(ktime_get(), timeout) > 0) { + v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n"); + return -ETIMEDOUT; + } + } while (!ret); + + coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) | + q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL); + /* The content of this register seems to be irrelevant: */ + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_GDI_INFO_PIC_SIZE); + + coda_write_base(ctx, q_data_src, src_buf, CODA9_GDI_INFO_BASE_Y); + + coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); + coda_write(dev, 0, CODA9_GDI_CONTROL); + coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); + + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + + trace_coda_jpeg_run(ctx, src_buf); + + coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); + + return 0; +} + +static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr, start_ptr; + u32 err_mb; + + if (ctx->aborting) { + coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + return; + } + + /* + * Lock to make sure that an encoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + trace_coda_jpeg_done(ctx, dst_buf); + + /* + * Set plane payload to the number of bytes written out + * by the JPEG processing unit + */ + start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) + coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb); + + coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); + mutex_unlock(&ctx->wakeup_mutex); + + coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each encode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); +} + +static void coda9_jpeg_encode_timeout(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 end_addr, wr_ptr; + + /* Handle missing BBC overflow interrupt via timeout */ + end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR); + wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); + if (wr_ptr >= end_addr - 256) { + v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n"); + coda9_jpeg_finish_encode(ctx); + return; + } + + coda_hw_reset(ctx); +} + +static void coda9_jpeg_release(struct coda_ctx *ctx) +{ + int i; + + if (ctx->params.jpeg_qmat_tab[0] == luma_q) + ctx->params.jpeg_qmat_tab[0] = NULL; + if (ctx->params.jpeg_qmat_tab[1] == chroma_q) + ctx->params.jpeg_qmat_tab[1] = NULL; + for (i = 0; i < 3; i++) + kfree(ctx->params.jpeg_qmat_tab[i]); + kfree(ctx->params.jpeg_huff_data); + kfree(ctx->params.jpeg_huff_tab); +} + +const struct coda_context_ops coda9_jpeg_encode_ops = { + .queue_init = coda_encoder_queue_init, + .start_streaming = coda9_jpeg_start_encoding, + .prepare_run = coda9_jpeg_prepare_encode, + .finish_run = coda9_jpeg_finish_encode, + .run_timeout = coda9_jpeg_encode_timeout, + .release = coda9_jpeg_release, +}; + +/* + * Decoder context operations + */ + +static int coda9_jpeg_start_decoding(struct coda_ctx *ctx) +{ + ctx->params.jpeg_qmat_index[0] = 0; + ctx->params.jpeg_qmat_index[1] = 1; + ctx->params.jpeg_qmat_index[2] = 1; + ctx->params.jpeg_qmat_tab[0] = luma_q; + ctx->params.jpeg_qmat_tab[1] = chroma_q; + /* nothing more to do here */ + + /* TODO: we could already scan the first header to get the chroma + * format. + */ + + return 0; +} + +static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int aligned_width, aligned_height; + int chroma_format; + int ret; + u32 val, dst_fourcc; + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + int chroma_interleave; + int scl_hor_mode, scl_ver_mode; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + scl_hor_mode = coda_jpeg_scale(q_data_src->width, q_data_dst->width); + scl_ver_mode = coda_jpeg_scale(q_data_src->height, q_data_dst->height); + + if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) + vb2_set_plane_payload(&src_buf->vb2_buf, 0, + vb2_plane_size(&src_buf->vb2_buf, 0)); + + chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); + if (chroma_format < 0) + return chroma_format; + + ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); + if (ret < 0) { + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + + return ret; + } + + /* Round image dimensions to multiple of MCU size */ + aligned_width = round_up(q_data_src->width, width_align[chroma_format]); + aligned_height = round_up(q_data_src->height, height_align[chroma_format]); + if (aligned_width != q_data_dst->bytesperline) { + v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", + aligned_width, q_data_dst->bytesperline); + } + + coda_set_gdi_regs(ctx); + + val = ctx->params.jpeg_huff_ac_index[0] << 12 | + ctx->params.jpeg_huff_ac_index[1] << 11 | + ctx->params.jpeg_huff_ac_index[2] << 10 | + ctx->params.jpeg_huff_dc_index[0] << 9 | + ctx->params.jpeg_huff_dc_index[1] << 8 | + ctx->params.jpeg_huff_dc_index[2] << 7; + if (ctx->params.jpeg_huff_tab) + val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; + coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_REG_JPEG_PIC_SIZE); + + chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); + coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); + if (scl_hor_mode || scl_ver_mode) + val = CODA9_JPEG_SCL_ENABLE | (scl_hor_mode << 2) | scl_ver_mode; + else + val = 0; + coda_write(dev, val, CODA9_REG_JPEG_SCL_INFO); + coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA9_REG_JPEG_RST_INTVAL); + + if (ctx->params.jpeg_huff_tab) + coda9_jpeg_dec_huff_setup(ctx); + + coda9_jpeg_qmat_setup(ctx); + + coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, + ctx->jpeg_ecs_offset); + + coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); + coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); + + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); + + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + + coda_write(dev, 1, CODA9_GDI_CONTROL); + do { + ret = coda_read(dev, CODA9_GDI_STATUS); + } while (!ret); + + val = (chroma_format << 17) | (chroma_interleave << 16) | + q_data_dst->bytesperline; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + val |= 3 << 20; + coda_write(dev, val, CODA9_GDI_INFO_CONTROL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_GDI_INFO_PIC_SIZE); + + coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); + + coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); + coda_write(dev, 0, CODA9_GDI_CONTROL); + coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); + + trace_coda_jpeg_run(ctx, src_buf); + + coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); + + return 0; +} + +static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *dst_buf, *src_buf; + struct coda_q_data *q_data_dst; + u32 err_mb; + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) + v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); + + coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + + /* + * Lock to make sure that a decoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->osequence++; + + trace_coda_jpeg_done(ctx, dst_buf); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); + + mutex_unlock(&ctx->wakeup_mutex); + + coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each decode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); +} + +const struct coda_context_ops coda9_jpeg_decode_ops = { + .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ + .start_streaming = coda9_jpeg_start_decoding, + .prepare_run = coda9_jpeg_prepare_decode, + .finish_run = coda9_jpeg_finish_decode, + .release = coda9_jpeg_release, +}; + +irqreturn_t coda9_jpeg_irq_handler(int irq, void *data) +{ + struct coda_dev *dev = data; + struct coda_ctx *ctx; + int status; + int err_mb; + + status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS); + if (status == 0) + return IRQ_HANDLED; + coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS); + + if (status & CODA9_JPEG_STATUS_OVERFLOW) + v4l2_err(&dev->v4l2_dev, "JPEG overflow\n"); + + if (status & CODA9_JPEG_STATUS_BBC_INT) + v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n"); + + if (status & CODA9_JPEG_STATUS_ERROR) { + v4l2_err(&dev->v4l2_dev, "JPEG error\n"); + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) { + v4l2_err(&dev->v4l2_dev, + "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n", + err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff, + err_mb & 0xfff); + } + } + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + mutex_unlock(&dev->coda_mutex); + return IRQ_HANDLED; + } + + complete(&ctx->completion); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/chips-media/coda/coda-mpeg2.c b/drivers/media/platform/chips-media/coda/coda-mpeg2.c new file mode 100644 index 000000000..6f3f6721d --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-mpeg2.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-2 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include +#include +#include "coda.h" + +int coda_mpeg2_profile(int profile_idc) +{ + switch (profile_idc) { + case 5: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; + case 3: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; + case 1: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; + default: + return -EINVAL; + } +} + +int coda_mpeg2_level(int level_idc) +{ + switch (level_idc) { + case 10: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; + case 8: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; + case 6: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-2 sequence header (with or without + * quantization matrix) and extension header, for example: + * + * 00 00 01 b3 2d 01 e0 34 08 8b a3 81 + * 10 11 11 12 12 12 13 13 13 13 14 14 14 14 14 15 + * 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 + * 17 17 17 17 18 18 18 19 18 18 18 19 1a 1a 1a 1a + * 19 1b 1b 1b 1b 1b 1c 1c 1c 1c 1e 1e 1e 1f 1f 21 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * or: + * + * 00 00 01 b3 08 00 40 15 ff ff e0 28 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 sequence_header_start[4] = { 0x00, 0x00, 0x01, 0xb3 }; + static const union { + u8 extension_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 22 || + memcmp(buf, sequence_header_start, 4) != 0) + return 0; + + if ((size == 22 || + (size >= 25 && memcmp(buf + 22, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 12, u.extension_start, 4) == 0) + return 22; + + if ((size == 86 || + (size > 89 && memcmp(buf + 86, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 76, u.extension_start, 4) == 0) + return 86; + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda/coda-mpeg4.c b/drivers/media/platform/chips-media/coda/coda-mpeg4.c new file mode 100644 index 000000000..483a4fba1 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda-mpeg4.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-4 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include +#include + +#include "coda.h" + +int coda_mpeg4_profile(int profile_idc) +{ + switch (profile_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; + case 15: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; + case 11: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; + default: + return -EINVAL; + } +} + +int coda_mpeg4_level(int level_idc) +{ + switch (level_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; + case 3: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; + case 4: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; + case 5: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-4 visual object sequence and visual + * object headers, for example: + * + * 00 00 01 b0 f1 + * 00 00 01 b5 a9 13 00 00 01 00 00 00 01 20 08 + * d4 8d 88 00 f5 04 04 08 14 30 3f + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 vos_start[4] = { 0x00, 0x00, 0x01, 0xb0 }; + static const union { + u8 vo_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 30 || + memcmp(buf, vos_start, 4) != 0 || + memcmp(buf + 5, u.vo_start, 4) != 0) + return 0; + + if (size == 30 || + (size >= 33 && memcmp(buf + 30, u.start_code_prefix, 3) == 0)) + return 30; + + if (size == 31 || + (size >= 34 && memcmp(buf + 31, u.start_code_prefix, 3) == 0)) + return 31; + + if (size == 32 || + (size >= 35 && memcmp(buf + 32, u.start_code_prefix, 3) == 0)) + return 32; + + return 0; +} diff --git a/drivers/media/platform/chips-media/coda/coda.h b/drivers/media/platform/chips-media/coda/coda.h new file mode 100644 index 000000000..ddfd0a32c --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda.h @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2012 Vista Silicon S.L. + * Javier Martin, + * Xavier Duret + * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix + */ + +#ifndef __CODA_H__ +#define __CODA_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "coda_regs.h" + +#define CODA_MAX_FRAMEBUFFERS 19 +#define FMO_SLICE_SAVE_BUF_SIZE (32) + +/* + * This control allows applications to read the per-stream + * (i.e. per-context) Macroblocks Error Count. This value + * is CODA specific. + */ +#define V4L2_CID_CODA_MB_ERR_CNT (V4L2_CID_USER_CODA_BASE + 0) + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +enum coda_inst_type { + CODA_INST_ENCODER, + CODA_INST_DECODER, +}; + +enum coda_product { + CODA_DX6 = 0xf001, + CODA_HX4 = 0xf00a, + CODA_7541 = 0xf012, + CODA_960 = 0xf020, +}; + +struct coda_video_device; + +struct coda_devtype { + char *firmware[3]; + enum coda_product product; + const struct coda_codec *codecs; + unsigned int num_codecs; + const struct coda_video_device **vdevs; + unsigned int num_vdevs; + size_t workbuf_size; + size_t tempbuf_size; + size_t iram_size; +}; + +struct coda_aux_buf { + void *vaddr; + dma_addr_t paddr; + u32 size; + struct debugfs_blob_wrapper blob; + struct dentry *dentry; +}; + +struct coda_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd[6]; + struct device *dev; + const struct coda_devtype *devtype; + int firmware; + struct vdoa_data *vdoa; + + void __iomem *regs_base; + struct clk *clk_per; + struct clk *clk_ahb; + struct reset_control *rstc; + + struct coda_aux_buf codebuf; + struct coda_aux_buf tempbuf; + struct coda_aux_buf workbuf; + struct gen_pool *iram_pool; + struct coda_aux_buf iram; + + struct mutex dev_mutex; + struct mutex coda_mutex; + struct workqueue_struct *workqueue; + struct v4l2_m2m_dev *m2m_dev; + struct ida ida; + struct dentry *debugfs_root; + struct ratelimit_state mb_err_rs; +}; + +struct coda_codec { + u32 mode; + u32 src_fourcc; + u32 dst_fourcc; + u32 max_w; + u32 max_h; +}; + +struct coda_huff_tab; + +struct coda_params { + u8 rot_mode; + u8 h264_intra_qp; + u8 h264_inter_qp; + u8 h264_min_qp; + u8 h264_max_qp; + u8 h264_disable_deblocking_filter_idc; + s8 h264_slice_alpha_c0_offset_div2; + s8 h264_slice_beta_offset_div2; + bool h264_constrained_intra_pred_flag; + s8 h264_chroma_qp_index_offset; + u8 h264_profile_idc; + u8 h264_level_idc; + u8 mpeg2_profile_idc; + u8 mpeg2_level_idc; + u8 mpeg4_intra_qp; + u8 mpeg4_inter_qp; + u8 gop_size; + int intra_refresh; + enum v4l2_jpeg_chroma_subsampling jpeg_chroma_subsampling; + u8 jpeg_quality; + u8 jpeg_restart_interval; + u8 *jpeg_qmat_tab[3]; + int jpeg_qmat_index[3]; + int jpeg_huff_dc_index[3]; + int jpeg_huff_ac_index[3]; + u32 *jpeg_huff_data; + struct coda_huff_tab *jpeg_huff_tab; + int codec_mode; + int codec_mode_aux; + enum v4l2_mpeg_video_multi_slice_mode slice_mode; + u32 framerate; + u16 bitrate; + u16 vbv_delay; + u32 vbv_size; + u32 slice_max_bits; + u32 slice_max_mb; + bool force_ipicture; + bool gop_size_changed; + bool bitrate_changed; + bool framerate_changed; + bool h264_intra_qp_changed; + bool intra_refresh_changed; + bool slice_mode_changed; + bool frame_rc_enable; + bool mb_rc_enable; +}; + +struct coda_buffer_meta { + struct list_head list; + u32 sequence; + struct v4l2_timecode timecode; + u64 timestamp; + unsigned int start; + unsigned int end; + bool last; +}; + +/* Per-queue, driver-specific private data */ +struct coda_q_data { + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int sizeimage; + unsigned int fourcc; + struct v4l2_rect rect; +}; + +struct coda_iram_info { + u32 axi_sram_use; + phys_addr_t buf_bit_use; + phys_addr_t buf_ip_ac_dc_use; + phys_addr_t buf_dbk_y_use; + phys_addr_t buf_dbk_c_use; + phys_addr_t buf_ovl_use; + phys_addr_t buf_btp_use; + phys_addr_t search_ram_paddr; + int search_ram_size; + int remaining; + phys_addr_t next_paddr; +}; + +#define GDI_LINEAR_FRAME_MAP 0 +#define GDI_TILED_FRAME_MB_RASTER_MAP 1 + +struct coda_ctx; + +struct coda_context_ops { + int (*queue_init)(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb); + int (*start_streaming)(struct coda_ctx *ctx); + int (*prepare_run)(struct coda_ctx *ctx); + void (*finish_run)(struct coda_ctx *ctx); + void (*run_timeout)(struct coda_ctx *ctx); + void (*seq_init_work)(struct work_struct *work); + void (*seq_end_work)(struct work_struct *work); + void (*release)(struct coda_ctx *ctx); +}; + +struct coda_internal_frame { + struct coda_aux_buf buf; + struct coda_buffer_meta meta; + u32 type; + u32 error; +}; + +struct coda_ctx { + struct coda_dev *dev; + struct mutex buffer_mutex; + struct work_struct pic_run_work; + struct work_struct seq_init_work; + struct work_struct seq_end_work; + struct completion completion; + const struct coda_video_device *cvd; + const struct coda_context_ops *ops; + int aborting; + int initialized; + int streamon_out; + int streamon_cap; + u32 qsequence; + u32 osequence; + u32 sequence_offset; + struct coda_q_data q_data[2]; + enum coda_inst_type inst_type; + const struct coda_codec *codec; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + struct coda_params params; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *h264_profile_ctrl; + struct v4l2_ctrl *h264_level_ctrl; + struct v4l2_ctrl *mpeg2_profile_ctrl; + struct v4l2_ctrl *mpeg2_level_ctrl; + struct v4l2_ctrl *mpeg4_profile_ctrl; + struct v4l2_ctrl *mpeg4_level_ctrl; + struct v4l2_ctrl *mb_err_cnt_ctrl; + struct v4l2_fh fh; + int gopcounter; + int runcounter; + int jpeg_ecs_offset; + char vpu_header[3][64]; + int vpu_header_size[3]; + struct kfifo bitstream_fifo; + struct mutex bitstream_mutex; + struct coda_aux_buf bitstream; + bool hold; + struct coda_aux_buf parabuf; + struct coda_aux_buf psbuf; + struct coda_aux_buf slicebuf; + struct coda_internal_frame internal_frames[CODA_MAX_FRAMEBUFFERS]; + struct list_head buffer_meta_list; + spinlock_t buffer_meta_lock; + int num_metas; + unsigned int first_frame_sequence; + struct coda_aux_buf workbuf; + int num_internal_frames; + int idx; + int reg_idx; + struct coda_iram_info iram_info; + int tiled_map_type; + u32 bit_stream_param; + u32 frm_dis_flg; + u32 frame_mem_ctrl; + u32 para_change; + int display_idx; + struct dentry *debugfs_entry; + bool use_bit; + bool use_vdoa; + struct vdoa_ctx *vdoa; + /* + * wakeup mutex used to serialize encoder stop command and finish_run, + * ensures that finish_run always either flags the last returned buffer + * or wakes up the capture queue to signal EOS afterwards. + */ + struct mutex wakeup_mutex; +}; + +extern int coda_debug; + +#define coda_dbg(level, ctx, fmt, arg...) \ + do { \ + if (coda_debug >= (level)) \ + v4l2_dbg((level), coda_debug, &(ctx)->dev->v4l2_dev, \ + "%u: " fmt, (ctx)->idx, ##arg); \ + } while (0) + +void coda_write(struct coda_dev *dev, u32 data, u32 reg); +unsigned int coda_read(struct coda_dev *dev, u32 reg); +void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, + struct vb2_v4l2_buffer *buf, unsigned int reg_y); + +int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, + size_t size, const char *name, struct dentry *parent); +void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); + +int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +int coda_hw_reset(struct coda_ctx *ctx); + +void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list); + +void coda_set_gdi_regs(struct coda_ctx *ctx); + +static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &(ctx->q_data[V4L2_M2M_SRC]); + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &(ctx->q_data[V4L2_M2M_DST]); + default: + return NULL; + } +} + +const char *coda_product_name(int product); + +int coda_check_firmware(struct coda_dev *dev); + +static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) +{ + return kfifo_len(&ctx->bitstream_fifo); +} + +/* + * The bitstream prefetcher needs to read at least 2 256 byte periods past + * the desired bitstream position for all data to reach the decoder. + */ +static inline bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, + unsigned int pos) +{ + return (int)(ctx->bitstream_fifo.kfifo.in - ALIGN(pos, 256)) > 512; +} + +bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, unsigned int pos); +int coda_bitstream_flush(struct coda_ctx *ctx); + +void coda_bit_stream_end_flag(struct coda_ctx *ctx); + +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state); + +int coda_h264_filler_nal(int size, char *p); +int coda_h264_padding(int size, char *p); +int coda_h264_profile(int profile_idc); +int coda_h264_level(int level_idc); +int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size); + +int coda_mpeg2_profile(int profile_idc); +int coda_mpeg2_level(int level_idc); +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); +int coda_mpeg4_profile(int profile_idc); +int coda_mpeg4_level(int level_idc); +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc); + +static inline int coda_jpeg_scale(int src, int dst) +{ + return (dst <= src / 8) ? 3 : + (dst <= src / 4) ? 2 : + (dst <= src / 2) ? 1 : 0; +} + +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_jpeg_write_tables(struct coda_ctx *ctx); +void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); + +extern const struct coda_context_ops coda_bit_encode_ops; +extern const struct coda_context_ops coda_bit_decode_ops; +extern const struct coda_context_ops coda9_jpeg_encode_ops; +extern const struct coda_context_ops coda9_jpeg_decode_ops; + +irqreturn_t coda_irq_handler(int irq, void *data); +irqreturn_t coda9_jpeg_irq_handler(int irq, void *data); + +#endif /* __CODA_H__ */ diff --git a/drivers/media/platform/chips-media/coda/coda_regs.h b/drivers/media/platform/chips-media/coda/coda_regs.h new file mode 100644 index 000000000..db81a904c --- /dev/null +++ b/drivers/media/platform/chips-media/coda/coda_regs.h @@ -0,0 +1,563 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/drivers/media/platform/chips-media/coda_regs.h + * + * Copyright (C) 2012 Vista Silicon SL + * Javier Martin + * Xavier Duret + */ + +#ifndef _REGS_CODA_H_ +#define _REGS_CODA_H_ + +/* HW registers */ +#define CODA_REG_BIT_CODE_RUN 0x000 +#define CODA_REG_RUN_ENABLE (1 << 0) +#define CODA_REG_BIT_CODE_DOWN 0x004 +#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16) +#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff) +#define CODA_REG_BIT_HOST_IN_REQ 0x008 +#define CODA_REG_BIT_INT_CLEAR 0x00c +#define CODA_REG_BIT_INT_CLEAR_SET 0x1 +#define CODA_REG_BIT_INT_STATUS 0x010 +#define CODA_REG_BIT_CODE_RESET 0x014 +#define CODA_REG_RESET_ENABLE (1 << 0) +#define CODA_REG_BIT_CUR_PC 0x018 +#define CODA9_REG_BIT_SW_RESET 0x024 +#define CODA9_SW_RESET_BPU_CORE 0x008 +#define CODA9_SW_RESET_BPU_BUS 0x010 +#define CODA9_SW_RESET_VCE_CORE 0x020 +#define CODA9_SW_RESET_VCE_BUS 0x040 +#define CODA9_SW_RESET_GDI_CORE 0x080 +#define CODA9_SW_RESET_GDI_BUS 0x100 +#define CODA9_REG_BIT_SW_RESET_STATUS 0x034 + +/* Static SW registers */ +#define CODA_REG_BIT_CODE_BUF_ADDR 0x100 +#define CODA_REG_BIT_WORK_BUF_ADDR 0x104 +#define CODA_REG_BIT_PARA_BUF_ADDR 0x108 +#define CODA_REG_BIT_STREAM_CTRL 0x10c +#define CODA7_STREAM_BUF_PIC_RESET (1 << 4) +#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3) +#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3) +#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) +#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) +#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) +#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1) +#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) +#define CODA_STREAM_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 +#define CODA9_FRAME_ENABLE_BWB (1 << 12) +#define CODA9_FRAME_TILED2LINEAR (1 << 11) +#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) +#define CODA_IMAGE_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 +#define CODA_BIT_STREAM_END_FLAG (1 << 2) +#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0) +#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 +#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) +#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) +#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x)) +#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 +#define CODA7_REG_BIT_AXI_SRAM_USE 0x140 +#define CODA9_USE_HOST_BTP_ENABLE (1 << 13) +#define CODA9_USE_HOST_OVL_ENABLE (1 << 12) +#define CODA7_USE_HOST_ME_ENABLE (1 << 11) +#define CODA9_USE_HOST_DBK_ENABLE (3 << 10) +#define CODA7_USE_HOST_OVL_ENABLE (1 << 10) +#define CODA7_USE_HOST_DBK_ENABLE (1 << 9) +#define CODA9_USE_HOST_IP_ENABLE (1 << 9) +#define CODA7_USE_HOST_IP_ENABLE (1 << 8) +#define CODA9_USE_HOST_BIT_ENABLE (1 << 8) +#define CODA7_USE_HOST_BIT_ENABLE (1 << 7) +#define CODA9_USE_BTP_ENABLE (1 << 5) +#define CODA7_USE_ME_ENABLE (1 << 4) +#define CODA9_USE_OVL_ENABLE (1 << 4) +#define CODA7_USE_OVL_ENABLE (1 << 3) +#define CODA9_USE_DBK_ENABLE (3 << 2) +#define CODA7_USE_DBK_ENABLE (1 << 2) +#define CODA7_USE_IP_ENABLE (1 << 1) +#define CODA7_USE_BIT_ENABLE (1 << 0) + +#define CODA_REG_BIT_BUSY 0x160 +#define CODA_REG_BIT_BUSY_FLAG 1 +#define CODA_REG_BIT_RUN_COMMAND 0x164 +#define CODA_COMMAND_SEQ_INIT 1 +#define CODA_COMMAND_SEQ_END 2 +#define CODA_COMMAND_PIC_RUN 3 +#define CODA_COMMAND_SET_FRAME_BUF 4 +#define CODA_COMMAND_ENCODE_HEADER 5 +#define CODA_COMMAND_ENC_PARA_SET 6 +#define CODA_COMMAND_DEC_PARA_SET 7 +#define CODA_COMMAND_DEC_BUF_FLUSH 8 +#define CODA_COMMAND_RC_CHANGE_PARAMETER 9 +#define CODA_COMMAND_FIRMWARE_GET 0xf +#define CODA_REG_BIT_RUN_INDEX 0x168 +#define CODA_INDEX_SET(x) ((x) & 0x3) +#define CODA_REG_BIT_RUN_COD_STD 0x16c +#define CODADX6_MODE_DECODE_MP4 0 +#define CODADX6_MODE_ENCODE_MP4 1 +#define CODADX6_MODE_DECODE_H264 2 +#define CODADX6_MODE_ENCODE_H264 3 +#define CODA7_MODE_DECODE_H264 0 +#define CODA7_MODE_DECODE_VC1 1 +#define CODA7_MODE_DECODE_MP2 2 +#define CODA7_MODE_DECODE_MP4 3 +#define CODA7_MODE_DECODE_DV3 3 +#define CODA7_MODE_DECODE_RV 4 +#define CODA7_MODE_DECODE_MJPG 5 +#define CODA7_MODE_ENCODE_H264 8 +#define CODA7_MODE_ENCODE_MP4 11 +#define CODA7_MODE_ENCODE_MJPG 13 +#define CODA9_MODE_DECODE_H264 0 +#define CODA9_MODE_DECODE_VC1 1 +#define CODA9_MODE_DECODE_MP2 2 +#define CODA9_MODE_DECODE_MP4 3 +#define CODA9_MODE_DECODE_DV3 3 +#define CODA9_MODE_DECODE_RV 4 +#define CODA9_MODE_DECODE_AVS 5 +#define CODA9_MODE_DECODE_MJPG 6 +#define CODA9_MODE_DECODE_VPX 7 +#define CODA9_MODE_ENCODE_H264 8 +#define CODA9_MODE_ENCODE_MP4 11 +#define CODA9_MODE_ENCODE_MJPG 13 +#define CODA_MODE_INVALID 0xffff +#define CODA_REG_BIT_INT_ENABLE 0x170 +#define CODA_INT_INTERRUPT_ENABLE (1 << 3) +#define CODA_REG_BIT_INT_REASON 0x174 +#define CODA7_REG_BIT_RUN_AUX_STD 0x178 +#define CODA_MP4_AUX_MPEG4 0 +#define CODA_MP4_AUX_DIVX3 1 +#define CODA_VPX_AUX_THO 0 +#define CODA_VPX_AUX_VP6 1 +#define CODA_VPX_AUX_VP8 2 +#define CODA_H264_AUX_AVC 0 +#define CODA_H264_AUX_MVC 1 + +/* + * Commands' mailbox: + * registers with offsets in the range 0x180-0x1d0 + * have different meaning depending on the command being + * issued. + */ + +/* Decoder Sequence Initialization */ +#define CODA_CMD_DEC_SEQ_BB_START 0x180 +#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184 +#define CODA_CMD_DEC_SEQ_OPTION 0x188 +#define CODA_NO_INT_ENABLE (1 << 10) +#define CODA_REORDER_ENABLE (1 << 1) +#define CODADX6_QP_REPORT (1 << 0) +#define CODA7_MP4_DEBLK_ENABLE (1 << 0) +#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c +#define CODA_CMD_DEC_SEQ_START_BYTE 0x190 +#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 +#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 +#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c +#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c +#define CODA_MP4_CLASS_MPEG4 0 +#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c +#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 + +#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 +#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4 +#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0 +#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */ +#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4 +#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8 +#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8 +#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc +#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0 +#define CODA_RET_DEC_SEQ_INFO 0x1d4 +#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8 +#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc +#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0 +#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0 +#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4 +#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 +#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 +#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 +#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec + +/* Decoder Picture Run */ +#define CODA_CMD_DEC_PIC_ROT_MODE 0x180 +#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 +#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c +#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190 +#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8 + +#define CODA_CMD_DEC_PIC_OPTION 0x194 +#define CODA_PRE_SCAN_EN (1 << 0) +#define CODA_PRE_SCAN_MODE_DECODE (0 << 1) +#define CODA_PRE_SCAN_MODE_RETURN (1 << 1) +#define CODA_IFRAME_SEARCH_EN (1 << 2) +#define CODA_SKIP_FRAME_MODE (0x3 << 3) +#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198 +#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c +#define CODA_CMD_DEC_PIC_BB_START 0x1a0 +#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4 +#define CODA_RET_DEC_PIC_SIZE 0x1bc +#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0 +#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4 +#define CODA_RET_DEC_PIC_ERR_MB 0x1c8 +#define CODA_RET_DEC_PIC_TYPE 0x1cc +#define CODA_PIC_TYPE_MASK 0x7 +#define CODA_PIC_TYPE_MASK_VC1 0x3f +#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3) +#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6) +#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16) +#define CODA7_PIC_TYPE_INTERLACED (1 << 18) +#define CODA_RET_DEC_PIC_POST 0x1d0 +#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0 +#define CODA_RET_DEC_PIC_OPTION 0x1d4 +#define CODA_RET_DEC_PIC_SUCCESS 0x1d8 +#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc +#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0 +#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 +#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec + +#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8 +#define CODA9_RET_DEC_PIC_ASPECT 0x1f0 +#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0 +#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4 +#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8 + +/* Encoder Sequence Initialization */ +#define CODA_CMD_ENC_SEQ_BB_START 0x180 +#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 +#define CODA_CMD_ENC_SEQ_OPTION 0x188 +#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9 +#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9 +#define CODA7_OPTION_GAMMA_OFFSET 8 +#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8 +#define CODA7_OPTION_RCQPMAX_OFFSET 7 +#define CODA9_OPTION_GAMMA_OFFSET 7 +#define CODADX6_OPTION_GAMMA_OFFSET 7 +#define CODA7_OPTION_RCQPMIN_OFFSET 6 +#define CODA9_OPTION_RCQPMAX_OFFSET 6 +#define CODA_OPTION_LIMITQP_OFFSET 6 +#define CODA_OPTION_RCINTRAQP_OFFSET 5 +#define CODA_OPTION_FMO_OFFSET 4 +#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4 +#define CODA_OPTION_AVC_AUD_OFFSET 2 +#define CODA_OPTION_SLICEREPORT_OFFSET 1 +#define CODA_CMD_ENC_SEQ_COD_STD 0x18c +#define CODA_STD_MPEG4 0 +#define CODA9_STD_H264 0 +#define CODA_STD_H263 1 +#define CODA_STD_H264 2 +#define CODA9_STD_MPEG4 3 + +#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 +#define CODA7_PICWIDTH_OFFSET 16 +#define CODA7_PICWIDTH_MASK 0xffff +#define CODADX6_PICWIDTH_OFFSET 10 +#define CODADX6_PICWIDTH_MASK 0x3ff +#define CODA_PICHEIGHT_OFFSET 0 +#define CODADX6_PICHEIGHT_MASK 0x3ff +#define CODA7_PICHEIGHT_MASK 0xffff +#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 +#define CODA_FRATE_RES_OFFSET 0 +#define CODA_FRATE_RES_MASK 0xffff +#define CODA_FRATE_DIV_OFFSET 16 +#define CODA_FRATE_DIV_MASK 0xffff +#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 +#define CODA_MP4PARAM_VERID_OFFSET 6 +#define CODA_MP4PARAM_VERID_MASK 0x01 +#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2 +#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07 +#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1 +#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01 +#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0 +#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_263_PARA 0x19c +#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2 +#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01 +#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1 +#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01 +#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0 +#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0 +#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12 +#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f +#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8 +#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f +#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6 +#define CODA_264PARAM_DISABLEDEBLK_MASK 0x03 +#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5 +#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01 +#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0 +#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f +#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4 +#define CODA_SLICING_SIZE_OFFSET 2 +#define CODA_SLICING_SIZE_MASK 0x3fffffff +#define CODA_SLICING_UNIT_OFFSET 1 +#define CODA_SLICING_UNIT_MASK 0x01 +#define CODA_SLICING_MODE_OFFSET 0 +#define CODA_SLICING_MODE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8 +#define CODA_GOP_SIZE_OFFSET 0 +#define CODA_GOP_SIZE_MASK 0x3f +#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac +#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 +#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 +#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 +#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7fff +#define CODA_RATECONTROL_BITRATE_OFFSET 1 +#define CODA_RATECONTROL_BITRATE_MASK 0x7fff +#define CODA_RATECONTROL_ENABLE_OFFSET 0 +#define CODA_RATECONTROL_ENABLE_MASK 0x01 +#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 +#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4 +#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8 +#define CODA_FMOPARAM_TYPE_OFFSET 4 +#define CODA_FMOPARAM_TYPE_MASK 1 +#define CODA_FMOPARAM_SLICENUM_OFFSET 0 +#define CODA_FMOPARAM_SLICENUM_MASK 0x0f +#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc +#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 +#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc +#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4 +#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8 +#define CODA_QPMIN_OFFSET 8 +#define CODA_QPMIN_MASK 0x3f +#define CODA_QPMAX_OFFSET 0 +#define CODA_QPMAX_MASK 0x3f +#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc +#define CODA_GAMMA_OFFSET 0 +#define CODA_GAMMA_MASK 0xffff +#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0 +#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4 +#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8 +#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 + +#define CODA_CMD_ENC_SEQ_JPG_PARA 0x198 +#define CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL 0x19C +#define CODA_CMD_ENC_SEQ_JPG_THUMB_EN 0x1a0 +#define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4 +#define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8 + +/* Encoder Parameter Change */ +#define CODA_CMD_ENC_PARAM_CHANGE_ENABLE 0x180 +#define CODA_PARAM_CHANGE_RC_GOP BIT(0) +#define CODA_PARAM_CHANGE_RC_INTRA_QP BIT(1) +#define CODA_PARAM_CHANGE_RC_BITRATE BIT(2) +#define CODA_PARAM_CHANGE_RC_FRAME_RATE BIT(3) +#define CODA_PARAM_CHANGE_INTRA_MB_NUM BIT(4) +#define CODA_PARAM_CHANGE_SLICE_MODE BIT(5) +#define CODA_PARAM_CHANGE_HEC_MODE BIT(6) +#define CODA_CMD_ENC_PARAM_RC_GOP 0x184 +#define CODA_CMD_ENC_PARAM_RC_INTRA_QP 0x188 +#define CODA_CMD_ENC_PARAM_RC_BITRATE 0x18c +#define CODA_CMD_ENC_PARAM_RC_FRAME_RATE 0x190 +#define CODA_CMD_ENC_PARAM_INTRA_MB_NUM 0x194 +#define CODA_CMD_ENC_PARAM_SLICE_MODE 0x198 +#define CODA_CMD_ENC_PARAM_HEC_MODE 0x19c +#define CODA_RET_ENC_PARAM_CHANGE_SUCCESS 0x1c0 + +/* Encoder Picture Run */ +#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 +#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 +#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0 +#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 +#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 +#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 +#define CODA_CMD_ENC_PIC_QS 0x18c +#define CODA_CMD_ENC_PIC_ROT_MODE 0x190 +#define CODA_ROT_MIR_ENABLE (1 << 4) +#define CODA_ROT_0 (0x0 << 0) +#define CODA_ROT_90 (0x1 << 0) +#define CODA_ROT_180 (0x2 << 0) +#define CODA_ROT_270 (0x3 << 0) +#define CODA_MIR_NONE (0x0 << 2) +#define CODA_MIR_VER (0x1 << 2) +#define CODA_MIR_HOR (0x2 << 2) +#define CODA_MIR_VER_HOR (0x3 << 2) +#define CODA_CMD_ENC_PIC_OPTION 0x194 +#define CODA_FORCE_IPICTURE BIT(1) +#define CODA_REPORT_MB_INFO BIT(3) +#define CODA_REPORT_MV_INFO BIT(4) +#define CODA_REPORT_SLICE_INFO BIT(5) +#define CODA_CMD_ENC_PIC_BB_START 0x198 +#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c +#define CODA_RET_ENC_FRAME_NUM 0x1c0 +#define CODA_RET_ENC_PIC_TYPE 0x1c4 +#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8 +#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc +#define CODA_RET_ENC_PIC_FLAG 0x1d0 +#define CODA_RET_ENC_PIC_SUCCESS 0x1d8 + +/* Set Frame Buffer */ +#define CODA_CMD_SET_FRAME_BUF_NUM 0x180 +#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 +#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188 +#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c +#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c +#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 +#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 +#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 +#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c +#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 +#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4 +#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4 +#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac +#define CODA9_CACHE_BYPASS_OFFSET 28 +#define CODA9_CACHE_DUALCONF_OFFSET 26 +#define CODA9_CACHE_PAGEMERGE_OFFSET 24 +#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16 +#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8 +#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4 +#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0 +#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4 +#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8 +#define CODA9_CMD_SET_FRAME_DELAY 0x1bc + +/* Encoder Header */ +#define CODA_CMD_ENC_HEADER_CODE 0x180 +#define CODA_GAMMA_OFFSET 0 +#define CODA_HEADER_H264_SPS 0 +#define CODA_HEADER_H264_PPS 1 +#define CODA_HEADER_MP4V_VOL 0 +#define CODA_HEADER_MP4V_VOS 1 +#define CODA_HEADER_MP4V_VIS 2 +#define CODA9_HEADER_FRAME_CROP (1 << 3) +#define CODA_CMD_ENC_HEADER_BB_START 0x184 +#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190 + +/* Get Version */ +#define CODA_CMD_FIRMWARE_VERNUM 0x1c0 +#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff) +#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f) +#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f) +#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff) +#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ + ((product) << 16 | ((major) << 12) | \ + ((minor) << 8) | (release)) +#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4 + +#define CODA9_GDMA_BASE 0x1000 +#define CODA9_GDI_CONTROL (CODA9_GDMA_BASE + 0x034) +#define CODA9_GDI_PIC_INIT_HOST (CODA9_GDMA_BASE + 0x038) +#define CODA9_GDI_STATUS (CODA9_GDMA_BASE + 0x080) +#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0) +#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac) + +#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0) +#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4) + +#define CODA9_GDI_INFO_CONTROL (CODA9_GDMA_BASE + 0x400) +#define CODA9_GDI_INFO_PIC_SIZE (CODA9_GDMA_BASE + 0x404) +#define CODA9_GDI_INFO_BASE_Y (CODA9_GDMA_BASE + 0x408) +#define CODA9_GDI_INFO_BASE_CB (CODA9_GDMA_BASE + 0x40c) +#define CODA9_GDI_INFO_BASE_CR (CODA9_GDMA_BASE + 0x410) + +#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800) +#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c) + +#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840) +#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844) +#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848) +#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c) + +#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850) +#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) + +#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) +#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) +#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) +#define CODA9_XY2RBC_TILED_MAP BIT(17) +#define CODA9_XY2RBC_CA_INC_HOR BIT(16) +#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) +#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) +#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) + +#define CODA9_JPEG_BASE 0x3000 +#define CODA9_REG_JPEG_PIC_START (CODA9_JPEG_BASE + 0x000) +#define CODA9_REG_JPEG_PIC_STATUS (CODA9_JPEG_BASE + 0x004) +#define CODA9_JPEG_STATUS_OVERFLOW BIT(3) +#define CODA9_JPEG_STATUS_BBC_INT BIT(2) +#define CODA9_JPEG_STATUS_ERROR BIT(1) +#define CODA9_JPEG_STATUS_DONE BIT(0) +#define CODA9_REG_JPEG_PIC_ERRMB (CODA9_JPEG_BASE + 0x008) +#define CODA9_JPEG_ERRMB_RESTART_IDX_MASK (0xf << 24) +#define CODA9_JPEG_ERRMB_MCU_POS_X_MASK (0xfff << 12) +#define CODA9_JPEG_ERRMB_MCU_POS_Y_MASK 0xfff +#define CODA9_REG_JPEG_PIC_CTRL (CODA9_JPEG_BASE + 0x010) +#define CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN BIT(6) +#define CODA9_JPEG_PIC_CTRL_TC_DIRECTION BIT(4) +#define CODA9_JPEG_PIC_CTRL_ENCODER_EN BIT(3) +#define CODA9_REG_JPEG_PIC_SIZE (CODA9_JPEG_BASE + 0x014) +#define CODA9_REG_JPEG_MCU_INFO (CODA9_JPEG_BASE + 0x018) +#define CODA9_JPEG_MCU_BLOCK_NUM_OFFSET 16 +#define CODA9_JPEG_COMP_NUM_OFFSET 12 +#define CODA9_JPEG_COMP0_INFO_OFFSET 8 +#define CODA9_JPEG_COMP1_INFO_OFFSET 4 +#define CODA9_JPEG_COMP2_INFO_OFFSET 0 +#define CODA9_REG_JPEG_ROT_INFO (CODA9_JPEG_BASE + 0x01c) +#define CODA9_JPEG_ROT_MIR_ENABLE BIT(4) +#define CODA9_JPEG_ROT_MIR_MODE_MASK 0xf +#define CODA9_REG_JPEG_SCL_INFO (CODA9_JPEG_BASE + 0x020) +#define CODA9_JPEG_SCL_ENABLE BIT(4) +#define CODA9_JPEG_SCL_HOR_MODE_MASK (0x3 << 2) +#define CODA9_JPEG_SCL_VER_MODE_MASK (0x3 << 0) +#define CODA9_REG_JPEG_IF_INFO (CODA9_JPEG_BASE + 0x024) +#define CODA9_JPEG_SENS_IF_CLR BIT(1) +#define CODA9_JPEG_DISP_IF_CLR BIT(0) +#define CODA9_REG_JPEG_OP_INFO (CODA9_JPEG_BASE + 0x02c) +#define CODA9_JPEG_BUS_REQ_NUM_OFFSET 0 +#define CODA9_JPEG_BUS_REQ_NUM_MASK 0x7 +#define CODA9_REG_JPEG_DPB_CONFIG (CODA9_JPEG_BASE + 0x030) +#define CODA9_REG_JPEG_DPB_BASE00 (CODA9_JPEG_BASE + 0x040) +#define CODA9_REG_JPEG_HUFF_CTRL (CODA9_JPEG_BASE + 0x080) +#define CODA9_REG_JPEG_HUFF_ADDR (CODA9_JPEG_BASE + 0x084) +#define CODA9_REG_JPEG_HUFF_DATA (CODA9_JPEG_BASE + 0x088) +#define CODA9_REG_JPEG_QMAT_CTRL (CODA9_JPEG_BASE + 0x090) +#define CODA9_REG_JPEG_QMAT_ADDR (CODA9_JPEG_BASE + 0x094) +#define CODA9_REG_JPEG_QMAT_DATA (CODA9_JPEG_BASE + 0x098) +#define CODA9_REG_JPEG_RST_INTVAL (CODA9_JPEG_BASE + 0x0b0) +#define CODA9_REG_JPEG_RST_INDEX (CODA9_JPEG_BASE + 0x0b4) +#define CODA9_REG_JPEG_RST_COUNT (CODA9_JPEG_BASE + 0x0b8) +#define CODA9_REG_JPEG_DPCM_DIFF_Y (CODA9_JPEG_BASE + 0x0f0) +#define CODA9_REG_JPEG_DPCM_DIFF_CB (CODA9_JPEG_BASE + 0x0f4) +#define CODA9_REG_JPEG_DPCM_DIFF_CR (CODA9_JPEG_BASE + 0x0f8) +#define CODA9_REG_JPEG_GBU_CTRL (CODA9_JPEG_BASE + 0x100) +#define CODA9_REG_JPEG_GBU_BT_PTR (CODA9_JPEG_BASE + 0x110) +#define CODA9_REG_JPEG_GBU_WD_PTR (CODA9_JPEG_BASE + 0x114) +#define CODA9_REG_JPEG_GBU_TT_CNT (CODA9_JPEG_BASE + 0x118) +#define CODA9_REG_JPEG_GBU_BBSR (CODA9_JPEG_BASE + 0x140) +#define CODA9_REG_JPEG_GBU_BBER (CODA9_JPEG_BASE + 0x144) +#define CODA9_REG_JPEG_GBU_BBIR (CODA9_JPEG_BASE + 0x148) +#define CODA9_REG_JPEG_GBU_BBHR (CODA9_JPEG_BASE + 0x14c) +#define CODA9_REG_JPEG_GBU_BCNT (CODA9_JPEG_BASE + 0x158) +#define CODA9_REG_JPEG_GBU_FF_RPTR (CODA9_JPEG_BASE + 0x160) +#define CODA9_REG_JPEG_GBU_FF_WPTR (CODA9_JPEG_BASE + 0x164) +#define CODA9_REG_JPEG_BBC_END_ADDR (CODA9_JPEG_BASE + 0x208) +#define CODA9_REG_JPEG_BBC_WR_PTR (CODA9_JPEG_BASE + 0x20c) +#define CODA9_REG_JPEG_BBC_RD_PTR (CODA9_JPEG_BASE + 0x210) +#define CODA9_REG_JPEG_BBC_EXT_ADDR (CODA9_JPEG_BASE + 0x214) +#define CODA9_REG_JPEG_BBC_INT_ADDR (CODA9_JPEG_BASE + 0x218) +#define CODA9_REG_JPEG_BBC_DATA_CNT (CODA9_JPEG_BASE + 0x21c) +#define CODA9_REG_JPEG_BBC_COMMAND (CODA9_JPEG_BASE + 0x220) +#define CODA9_REG_JPEG_BBC_BUSY (CODA9_JPEG_BASE + 0x224) +#define CODA9_REG_JPEG_BBC_CTRL (CODA9_JPEG_BASE + 0x228) +#define CODA9_REG_JPEG_BBC_CUR_POS (CODA9_JPEG_BASE + 0x22c) +#define CODA9_REG_JPEG_BBC_BAS_ADDR (CODA9_JPEG_BASE + 0x230) +#define CODA9_REG_JPEG_BBC_STRM_CTRL (CODA9_JPEG_BASE + 0x234) +#define CODA9_REG_JPEG_BBC_FLUSH_CMD (CODA9_JPEG_BASE + 0x238) + +#endif diff --git a/drivers/media/platform/chips-media/coda/imx-vdoa.c b/drivers/media/platform/chips-media/coda/imx-vdoa.c new file mode 100644 index 000000000..c3561fcec --- /dev/null +++ b/drivers/media/platform/chips-media/coda/imx-vdoa.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * i.MX6 Video Data Order Adapter (VDOA) + * + * Copyright (C) 2014 Philipp Zabel + * Copyright (C) 2016 Pengutronix, Michael Tretter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-vdoa.h" + +#define VDOA_NAME "imx-vdoa" + +#define VDOAC 0x00 +#define VDOASRR 0x04 +#define VDOAIE 0x08 +#define VDOAIST 0x0c +#define VDOAFP 0x10 +#define VDOAIEBA00 0x14 +#define VDOAIEBA01 0x18 +#define VDOAIEBA02 0x1c +#define VDOAIEBA10 0x20 +#define VDOAIEBA11 0x24 +#define VDOAIEBA12 0x28 +#define VDOASL 0x2c +#define VDOAIUBO 0x30 +#define VDOAVEBA0 0x34 +#define VDOAVEBA1 0x38 +#define VDOAVEBA2 0x3c +#define VDOAVUBO 0x40 +#define VDOASR 0x44 + +#define VDOAC_ISEL BIT(6) +#define VDOAC_PFS BIT(5) +#define VDOAC_SO BIT(4) +#define VDOAC_SYNC BIT(3) +#define VDOAC_NF BIT(2) +#define VDOAC_BNDM_MASK 0x3 +#define VDOAC_BAND_HEIGHT_8 0x0 +#define VDOAC_BAND_HEIGHT_16 0x1 +#define VDOAC_BAND_HEIGHT_32 0x2 + +#define VDOASRR_START BIT(1) +#define VDOASRR_SWRST BIT(0) + +#define VDOAIE_EITERR BIT(1) +#define VDOAIE_EIEOT BIT(0) + +#define VDOAIST_TERR BIT(1) +#define VDOAIST_EOT BIT(0) + +#define VDOAFP_FH_MASK (0x1fff << 16) +#define VDOAFP_FW_MASK (0x3fff) + +#define VDOASL_VSLY_MASK (0x3fff << 16) +#define VDOASL_ISLY_MASK (0x7fff) + +#define VDOASR_ERRW BIT(4) +#define VDOASR_EOB BIT(3) +#define VDOASR_CURRENT_FRAME (0x3 << 1) +#define VDOASR_CURRENT_BUFFER BIT(1) + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +struct vdoa_data { + struct vdoa_ctx *curr_ctx; + struct device *dev; + struct clk *vdoa_clk; + void __iomem *regs; +}; + +struct vdoa_q_data { + unsigned int width; + unsigned int height; + unsigned int bytesperline; + unsigned int sizeimage; + u32 pixelformat; +}; + +struct vdoa_ctx { + struct vdoa_data *vdoa; + struct completion completion; + struct vdoa_q_data q_data[2]; + unsigned int submitted_job; + unsigned int completed_job; +}; + +static irqreturn_t vdoa_irq_handler(int irq, void *data) +{ + struct vdoa_data *vdoa = data; + struct vdoa_ctx *curr_ctx; + u32 val; + + /* Disable interrupts */ + writel(0, vdoa->regs + VDOAIE); + + curr_ctx = vdoa->curr_ctx; + if (!curr_ctx) { + dev_warn(vdoa->dev, + "Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + val = readl(vdoa->regs + VDOAIST); + writel(val, vdoa->regs + VDOAIST); + if (val & VDOAIST_TERR) { + val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW; + dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read"); + } else if (!(val & VDOAIST_EOT)) { + dev_warn(vdoa->dev, "Spurious interrupt\n"); + } + curr_ctx->completed_job++; + complete(&curr_ctx->completion); + + return IRQ_HANDLED; +} + +int vdoa_wait_for_completion(struct vdoa_ctx *ctx) +{ + struct vdoa_data *vdoa = ctx->vdoa; + + if (ctx->submitted_job == ctx->completed_job) + return 0; + + if (!wait_for_completion_timeout(&ctx->completion, + msecs_to_jiffies(300))) { + dev_err(vdoa->dev, + "Timeout waiting for transfer result\n"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(vdoa_wait_for_completion); + +void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src) +{ + struct vdoa_q_data *src_q_data, *dst_q_data; + struct vdoa_data *vdoa = ctx->vdoa; + u32 val; + + if (vdoa->curr_ctx) + vdoa_wait_for_completion(vdoa->curr_ctx); + + vdoa->curr_ctx = ctx; + + reinit_completion(&ctx->completion); + ctx->submitted_job++; + + src_q_data = &ctx->q_data[V4L2_M2M_SRC]; + dst_q_data = &ctx->q_data[V4L2_M2M_DST]; + + /* Progressive, no sync, 1 frame per run */ + if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV) + val = VDOAC_PFS; + else + val = 0; + writel(val, vdoa->regs + VDOAC); + + writel(dst_q_data->height << 16 | dst_q_data->width, + vdoa->regs + VDOAFP); + + val = dst; + writel(val, vdoa->regs + VDOAIEBA00); + + writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline, + vdoa->regs + VDOASL); + + if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 || + dst_q_data->pixelformat == V4L2_PIX_FMT_NV21) + val = dst_q_data->bytesperline * dst_q_data->height; + else + val = 0; + writel(val, vdoa->regs + VDOAIUBO); + + val = src; + writel(val, vdoa->regs + VDOAVEBA0); + val = round_up(src_q_data->bytesperline * src_q_data->height, 4096); + writel(val, vdoa->regs + VDOAVUBO); + + /* Enable interrupts and start transfer */ + writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE); + writel(VDOASRR_START, vdoa->regs + VDOASRR); +} +EXPORT_SYMBOL(vdoa_device_run); + +struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) +{ + struct vdoa_ctx *ctx; + int err; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + err = clk_prepare_enable(vdoa->vdoa_clk); + if (err) { + kfree(ctx); + return NULL; + } + + init_completion(&ctx->completion); + ctx->vdoa = vdoa; + + return ctx; +} +EXPORT_SYMBOL(vdoa_context_create); + +void vdoa_context_destroy(struct vdoa_ctx *ctx) +{ + struct vdoa_data *vdoa = ctx->vdoa; + + if (vdoa->curr_ctx == ctx) { + vdoa_wait_for_completion(vdoa->curr_ctx); + vdoa->curr_ctx = NULL; + } + + clk_disable_unprepare(vdoa->vdoa_clk); + kfree(ctx); +} +EXPORT_SYMBOL(vdoa_context_destroy); + +int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, unsigned int height, + u32 pixelformat) +{ + struct vdoa_q_data *src_q_data; + struct vdoa_q_data *dst_q_data; + + if (width < 16 || width > 8192 || width % 16 != 0 || + height < 16 || height > 4096 || height % 16 != 0) + return -EINVAL; + + if (pixelformat != V4L2_PIX_FMT_YUYV && + pixelformat != V4L2_PIX_FMT_NV12) + return -EINVAL; + + /* If no context is passed, only check if the format is valid */ + if (!ctx) + return 0; + + src_q_data = &ctx->q_data[V4L2_M2M_SRC]; + dst_q_data = &ctx->q_data[V4L2_M2M_DST]; + + src_q_data->width = width; + src_q_data->height = height; + src_q_data->bytesperline = width; + src_q_data->sizeimage = + round_up(src_q_data->bytesperline * height, 4096) + + src_q_data->bytesperline * height / 2; + + dst_q_data->width = width; + dst_q_data->height = height; + dst_q_data->pixelformat = pixelformat; + switch (pixelformat) { + case V4L2_PIX_FMT_YUYV: + dst_q_data->bytesperline = width * 2; + dst_q_data->sizeimage = dst_q_data->bytesperline * height; + break; + case V4L2_PIX_FMT_NV12: + default: + dst_q_data->bytesperline = width; + dst_q_data->sizeimage = + dst_q_data->bytesperline * height * 3 / 2; + break; + } + + return 0; +} +EXPORT_SYMBOL(vdoa_context_configure); + +static int vdoa_probe(struct platform_device *pdev) +{ + struct vdoa_data *vdoa; + int ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "DMA enable failed\n"); + return ret; + } + + vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL); + if (!vdoa) + return -ENOMEM; + + vdoa->dev = &pdev->dev; + + vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL); + if (IS_ERR(vdoa->vdoa_clk)) { + dev_err(vdoa->dev, "Failed to get clock\n"); + return PTR_ERR(vdoa->vdoa_clk); + } + + vdoa->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vdoa->regs)) + return PTR_ERR(vdoa->regs); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, + vdoa_irq_handler, IRQF_ONESHOT, + "vdoa", vdoa); + if (ret < 0) { + dev_err(vdoa->dev, "Failed to get irq\n"); + return ret; + } + + platform_set_drvdata(pdev, vdoa); + + return 0; +} + +static const struct of_device_id vdoa_dt_ids[] = { + { .compatible = "fsl,imx6q-vdoa" }, + {} +}; +MODULE_DEVICE_TABLE(of, vdoa_dt_ids); + +static struct platform_driver vdoa_driver = { + .probe = vdoa_probe, + .driver = { + .name = VDOA_NAME, + .of_match_table = vdoa_dt_ids, + }, +}; + +module_platform_driver(vdoa_driver); + +MODULE_DESCRIPTION("Video Data Order Adapter"); +MODULE_AUTHOR("Philipp Zabel "); +MODULE_ALIAS("platform:imx-vdoa"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/chips-media/coda/imx-vdoa.h b/drivers/media/platform/chips-media/coda/imx-vdoa.h new file mode 100644 index 000000000..a62eab476 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/imx-vdoa.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Pengutronix + */ + +#ifndef IMX_VDOA_H +#define IMX_VDOA_H + +struct vdoa_data; +struct vdoa_ctx; + +#if (defined CONFIG_VIDEO_IMX_VDOA || defined CONFIG_VIDEO_IMX_VDOA_MODULE) + +struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa); +int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, unsigned int height, + u32 pixelformat); +void vdoa_context_destroy(struct vdoa_ctx *ctx); + +void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src); +int vdoa_wait_for_completion(struct vdoa_ctx *ctx); + +#else + +static inline struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) +{ + return NULL; +} + +static inline int vdoa_context_configure(struct vdoa_ctx *ctx, + unsigned int width, + unsigned int height, + u32 pixelformat) +{ + return 0; +} + +static inline void vdoa_context_destroy(struct vdoa_ctx *ctx) { }; + +static inline void vdoa_device_run(struct vdoa_ctx *ctx, + dma_addr_t dst, dma_addr_t src) { }; + +static inline int vdoa_wait_for_completion(struct vdoa_ctx *ctx) +{ + return 0; +}; + +#endif + +#endif /* IMX_VDOA_H */ diff --git a/drivers/media/platform/chips-media/coda/trace.h b/drivers/media/platform/chips-media/coda/trace.h new file mode 100644 index 000000000..abc6a01a7 --- /dev/null +++ b/drivers/media/platform/chips-media/coda/trace.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM coda + +#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __CODA_TRACE_H__ + +#include +#include + +#include "coda.h" + +TRACE_EVENT(coda_bit_run, + TP_PROTO(struct coda_ctx *ctx, int cmd), + + TP_ARGS(ctx, cmd), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + __field(int, cmd) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + __entry->cmd = cmd; + ), + + TP_printk("minor = %d, ctx = %d, cmd = %d", + __entry->minor, __entry->ctx, __entry->cmd) +); + +TRACE_EVENT(coda_bit_done, + TP_PROTO(struct coda_ctx *ctx), + + TP_ARGS(ctx), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) +); + +DECLARE_EVENT_CLASS(coda_buf_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + + TP_ARGS(ctx, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->vb2_buf.index; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, ctx = %d", + __entry->minor, __entry->index, __entry->ctx) +); + +DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DECLARE_EVENT_CLASS(coda_buf_meta_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + + TP_ARGS(ctx, buf, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->vb2_buf.index; + __entry->start = meta->start & ctx->bitstream_fifo.kfifo.mask; + __entry->end = meta->end & ctx->bitstream_fifo.kfifo.mask; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->index, __entry->start, __entry->end, + __entry->ctx) +); + +DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DECLARE_EVENT_CLASS(coda_meta_class, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + + TP_ARGS(ctx, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->start = meta ? (meta->start & + ctx->bitstream_fifo.kfifo.mask) : 0; + __entry->end = meta ? (meta->end & + ctx->bitstream_fifo.kfifo.mask) : 0; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->start, __entry->end, __entry->ctx) +); + +DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); + +DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); + +DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DEFINE_EVENT(coda_buf_class, coda_jpeg_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +DEFINE_EVENT(coda_buf_class, coda_jpeg_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); + +#endif /* __CODA_TRACE_H__ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media/coda +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include diff --git a/drivers/media/platform/chips-media/coda_regs.h b/drivers/media/platform/chips-media/coda_regs.h deleted file mode 100644 index db81a904c..000000000 --- a/drivers/media/platform/chips-media/coda_regs.h +++ /dev/null @@ -1,563 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * linux/drivers/media/platform/chips-media/coda_regs.h - * - * Copyright (C) 2012 Vista Silicon SL - * Javier Martin - * Xavier Duret - */ - -#ifndef _REGS_CODA_H_ -#define _REGS_CODA_H_ - -/* HW registers */ -#define CODA_REG_BIT_CODE_RUN 0x000 -#define CODA_REG_RUN_ENABLE (1 << 0) -#define CODA_REG_BIT_CODE_DOWN 0x004 -#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16) -#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff) -#define CODA_REG_BIT_HOST_IN_REQ 0x008 -#define CODA_REG_BIT_INT_CLEAR 0x00c -#define CODA_REG_BIT_INT_CLEAR_SET 0x1 -#define CODA_REG_BIT_INT_STATUS 0x010 -#define CODA_REG_BIT_CODE_RESET 0x014 -#define CODA_REG_RESET_ENABLE (1 << 0) -#define CODA_REG_BIT_CUR_PC 0x018 -#define CODA9_REG_BIT_SW_RESET 0x024 -#define CODA9_SW_RESET_BPU_CORE 0x008 -#define CODA9_SW_RESET_BPU_BUS 0x010 -#define CODA9_SW_RESET_VCE_CORE 0x020 -#define CODA9_SW_RESET_VCE_BUS 0x040 -#define CODA9_SW_RESET_GDI_CORE 0x080 -#define CODA9_SW_RESET_GDI_BUS 0x100 -#define CODA9_REG_BIT_SW_RESET_STATUS 0x034 - -/* Static SW registers */ -#define CODA_REG_BIT_CODE_BUF_ADDR 0x100 -#define CODA_REG_BIT_WORK_BUF_ADDR 0x104 -#define CODA_REG_BIT_PARA_BUF_ADDR 0x108 -#define CODA_REG_BIT_STREAM_CTRL 0x10c -#define CODA7_STREAM_BUF_PIC_RESET (1 << 4) -#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3) -#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3) -#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) -#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) -#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) -#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1) -#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) -#define CODA_STREAM_ENDIAN_SELECT (1 << 0) -#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 -#define CODA9_FRAME_ENABLE_BWB (1 << 12) -#define CODA9_FRAME_TILED2LINEAR (1 << 11) -#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) -#define CODA_IMAGE_ENDIAN_SELECT (1 << 0) -#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 -#define CODA_BIT_STREAM_END_FLAG (1 << 2) -#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0) -#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 -#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) -#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) -#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x)) -#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 -#define CODA7_REG_BIT_AXI_SRAM_USE 0x140 -#define CODA9_USE_HOST_BTP_ENABLE (1 << 13) -#define CODA9_USE_HOST_OVL_ENABLE (1 << 12) -#define CODA7_USE_HOST_ME_ENABLE (1 << 11) -#define CODA9_USE_HOST_DBK_ENABLE (3 << 10) -#define CODA7_USE_HOST_OVL_ENABLE (1 << 10) -#define CODA7_USE_HOST_DBK_ENABLE (1 << 9) -#define CODA9_USE_HOST_IP_ENABLE (1 << 9) -#define CODA7_USE_HOST_IP_ENABLE (1 << 8) -#define CODA9_USE_HOST_BIT_ENABLE (1 << 8) -#define CODA7_USE_HOST_BIT_ENABLE (1 << 7) -#define CODA9_USE_BTP_ENABLE (1 << 5) -#define CODA7_USE_ME_ENABLE (1 << 4) -#define CODA9_USE_OVL_ENABLE (1 << 4) -#define CODA7_USE_OVL_ENABLE (1 << 3) -#define CODA9_USE_DBK_ENABLE (3 << 2) -#define CODA7_USE_DBK_ENABLE (1 << 2) -#define CODA7_USE_IP_ENABLE (1 << 1) -#define CODA7_USE_BIT_ENABLE (1 << 0) - -#define CODA_REG_BIT_BUSY 0x160 -#define CODA_REG_BIT_BUSY_FLAG 1 -#define CODA_REG_BIT_RUN_COMMAND 0x164 -#define CODA_COMMAND_SEQ_INIT 1 -#define CODA_COMMAND_SEQ_END 2 -#define CODA_COMMAND_PIC_RUN 3 -#define CODA_COMMAND_SET_FRAME_BUF 4 -#define CODA_COMMAND_ENCODE_HEADER 5 -#define CODA_COMMAND_ENC_PARA_SET 6 -#define CODA_COMMAND_DEC_PARA_SET 7 -#define CODA_COMMAND_DEC_BUF_FLUSH 8 -#define CODA_COMMAND_RC_CHANGE_PARAMETER 9 -#define CODA_COMMAND_FIRMWARE_GET 0xf -#define CODA_REG_BIT_RUN_INDEX 0x168 -#define CODA_INDEX_SET(x) ((x) & 0x3) -#define CODA_REG_BIT_RUN_COD_STD 0x16c -#define CODADX6_MODE_DECODE_MP4 0 -#define CODADX6_MODE_ENCODE_MP4 1 -#define CODADX6_MODE_DECODE_H264 2 -#define CODADX6_MODE_ENCODE_H264 3 -#define CODA7_MODE_DECODE_H264 0 -#define CODA7_MODE_DECODE_VC1 1 -#define CODA7_MODE_DECODE_MP2 2 -#define CODA7_MODE_DECODE_MP4 3 -#define CODA7_MODE_DECODE_DV3 3 -#define CODA7_MODE_DECODE_RV 4 -#define CODA7_MODE_DECODE_MJPG 5 -#define CODA7_MODE_ENCODE_H264 8 -#define CODA7_MODE_ENCODE_MP4 11 -#define CODA7_MODE_ENCODE_MJPG 13 -#define CODA9_MODE_DECODE_H264 0 -#define CODA9_MODE_DECODE_VC1 1 -#define CODA9_MODE_DECODE_MP2 2 -#define CODA9_MODE_DECODE_MP4 3 -#define CODA9_MODE_DECODE_DV3 3 -#define CODA9_MODE_DECODE_RV 4 -#define CODA9_MODE_DECODE_AVS 5 -#define CODA9_MODE_DECODE_MJPG 6 -#define CODA9_MODE_DECODE_VPX 7 -#define CODA9_MODE_ENCODE_H264 8 -#define CODA9_MODE_ENCODE_MP4 11 -#define CODA9_MODE_ENCODE_MJPG 13 -#define CODA_MODE_INVALID 0xffff -#define CODA_REG_BIT_INT_ENABLE 0x170 -#define CODA_INT_INTERRUPT_ENABLE (1 << 3) -#define CODA_REG_BIT_INT_REASON 0x174 -#define CODA7_REG_BIT_RUN_AUX_STD 0x178 -#define CODA_MP4_AUX_MPEG4 0 -#define CODA_MP4_AUX_DIVX3 1 -#define CODA_VPX_AUX_THO 0 -#define CODA_VPX_AUX_VP6 1 -#define CODA_VPX_AUX_VP8 2 -#define CODA_H264_AUX_AVC 0 -#define CODA_H264_AUX_MVC 1 - -/* - * Commands' mailbox: - * registers with offsets in the range 0x180-0x1d0 - * have different meaning depending on the command being - * issued. - */ - -/* Decoder Sequence Initialization */ -#define CODA_CMD_DEC_SEQ_BB_START 0x180 -#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184 -#define CODA_CMD_DEC_SEQ_OPTION 0x188 -#define CODA_NO_INT_ENABLE (1 << 10) -#define CODA_REORDER_ENABLE (1 << 1) -#define CODADX6_QP_REPORT (1 << 0) -#define CODA7_MP4_DEBLK_ENABLE (1 << 0) -#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c -#define CODA_CMD_DEC_SEQ_START_BYTE 0x190 -#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 -#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 -#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c -#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c -#define CODA_MP4_CLASS_MPEG4 0 -#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c -#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 - -#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 -#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4 -#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0 -#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */ -#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4 -#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8 -#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8 -#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc -#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0 -#define CODA_RET_DEC_SEQ_INFO 0x1d4 -#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8 -#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc -#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0 -#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0 -#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4 -#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 -#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 -#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 -#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec - -/* Decoder Picture Run */ -#define CODA_CMD_DEC_PIC_ROT_MODE 0x180 -#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 -#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184 -#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 -#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188 -#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c -#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c -#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 -#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190 -#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8 - -#define CODA_CMD_DEC_PIC_OPTION 0x194 -#define CODA_PRE_SCAN_EN (1 << 0) -#define CODA_PRE_SCAN_MODE_DECODE (0 << 1) -#define CODA_PRE_SCAN_MODE_RETURN (1 << 1) -#define CODA_IFRAME_SEARCH_EN (1 << 2) -#define CODA_SKIP_FRAME_MODE (0x3 << 3) -#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198 -#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c -#define CODA_CMD_DEC_PIC_BB_START 0x1a0 -#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4 -#define CODA_RET_DEC_PIC_SIZE 0x1bc -#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0 -#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4 -#define CODA_RET_DEC_PIC_ERR_MB 0x1c8 -#define CODA_RET_DEC_PIC_TYPE 0x1cc -#define CODA_PIC_TYPE_MASK 0x7 -#define CODA_PIC_TYPE_MASK_VC1 0x3f -#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3) -#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6) -#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16) -#define CODA7_PIC_TYPE_INTERLACED (1 << 18) -#define CODA_RET_DEC_PIC_POST 0x1d0 -#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0 -#define CODA_RET_DEC_PIC_OPTION 0x1d4 -#define CODA_RET_DEC_PIC_SUCCESS 0x1d8 -#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc -#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0 -#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 -#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec - -#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8 -#define CODA9_RET_DEC_PIC_ASPECT 0x1f0 -#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0 -#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4 -#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8 - -/* Encoder Sequence Initialization */ -#define CODA_CMD_ENC_SEQ_BB_START 0x180 -#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 -#define CODA_CMD_ENC_SEQ_OPTION 0x188 -#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9 -#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9 -#define CODA7_OPTION_GAMMA_OFFSET 8 -#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8 -#define CODA7_OPTION_RCQPMAX_OFFSET 7 -#define CODA9_OPTION_GAMMA_OFFSET 7 -#define CODADX6_OPTION_GAMMA_OFFSET 7 -#define CODA7_OPTION_RCQPMIN_OFFSET 6 -#define CODA9_OPTION_RCQPMAX_OFFSET 6 -#define CODA_OPTION_LIMITQP_OFFSET 6 -#define CODA_OPTION_RCINTRAQP_OFFSET 5 -#define CODA_OPTION_FMO_OFFSET 4 -#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4 -#define CODA_OPTION_AVC_AUD_OFFSET 2 -#define CODA_OPTION_SLICEREPORT_OFFSET 1 -#define CODA_CMD_ENC_SEQ_COD_STD 0x18c -#define CODA_STD_MPEG4 0 -#define CODA9_STD_H264 0 -#define CODA_STD_H263 1 -#define CODA_STD_H264 2 -#define CODA9_STD_MPEG4 3 - -#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 -#define CODA7_PICWIDTH_OFFSET 16 -#define CODA7_PICWIDTH_MASK 0xffff -#define CODADX6_PICWIDTH_OFFSET 10 -#define CODADX6_PICWIDTH_MASK 0x3ff -#define CODA_PICHEIGHT_OFFSET 0 -#define CODADX6_PICHEIGHT_MASK 0x3ff -#define CODA7_PICHEIGHT_MASK 0xffff -#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 -#define CODA_FRATE_RES_OFFSET 0 -#define CODA_FRATE_RES_MASK 0xffff -#define CODA_FRATE_DIV_OFFSET 16 -#define CODA_FRATE_DIV_MASK 0xffff -#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 -#define CODA_MP4PARAM_VERID_OFFSET 6 -#define CODA_MP4PARAM_VERID_MASK 0x01 -#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2 -#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07 -#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1 -#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01 -#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0 -#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_263_PARA 0x19c -#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2 -#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01 -#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1 -#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01 -#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0 -#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0 -#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12 -#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f -#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8 -#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f -#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6 -#define CODA_264PARAM_DISABLEDEBLK_MASK 0x03 -#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5 -#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01 -#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0 -#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f -#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4 -#define CODA_SLICING_SIZE_OFFSET 2 -#define CODA_SLICING_SIZE_MASK 0x3fffffff -#define CODA_SLICING_UNIT_OFFSET 1 -#define CODA_SLICING_UNIT_MASK 0x01 -#define CODA_SLICING_MODE_OFFSET 0 -#define CODA_SLICING_MODE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8 -#define CODA_GOP_SIZE_OFFSET 0 -#define CODA_GOP_SIZE_MASK 0x3f -#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac -#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 -#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 -#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 -#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7fff -#define CODA_RATECONTROL_BITRATE_OFFSET 1 -#define CODA_RATECONTROL_BITRATE_MASK 0x7fff -#define CODA_RATECONTROL_ENABLE_OFFSET 0 -#define CODA_RATECONTROL_ENABLE_MASK 0x01 -#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 -#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4 -#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8 -#define CODA_FMOPARAM_TYPE_OFFSET 4 -#define CODA_FMOPARAM_TYPE_MASK 1 -#define CODA_FMOPARAM_SLICENUM_OFFSET 0 -#define CODA_FMOPARAM_SLICENUM_MASK 0x0f -#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc -#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 -#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc -#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4 -#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8 -#define CODA_QPMIN_OFFSET 8 -#define CODA_QPMIN_MASK 0x3f -#define CODA_QPMAX_OFFSET 0 -#define CODA_QPMAX_MASK 0x3f -#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc -#define CODA_GAMMA_OFFSET 0 -#define CODA_GAMMA_MASK 0xffff -#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0 -#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4 -#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8 -#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 - -#define CODA_CMD_ENC_SEQ_JPG_PARA 0x198 -#define CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL 0x19C -#define CODA_CMD_ENC_SEQ_JPG_THUMB_EN 0x1a0 -#define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4 -#define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8 - -/* Encoder Parameter Change */ -#define CODA_CMD_ENC_PARAM_CHANGE_ENABLE 0x180 -#define CODA_PARAM_CHANGE_RC_GOP BIT(0) -#define CODA_PARAM_CHANGE_RC_INTRA_QP BIT(1) -#define CODA_PARAM_CHANGE_RC_BITRATE BIT(2) -#define CODA_PARAM_CHANGE_RC_FRAME_RATE BIT(3) -#define CODA_PARAM_CHANGE_INTRA_MB_NUM BIT(4) -#define CODA_PARAM_CHANGE_SLICE_MODE BIT(5) -#define CODA_PARAM_CHANGE_HEC_MODE BIT(6) -#define CODA_CMD_ENC_PARAM_RC_GOP 0x184 -#define CODA_CMD_ENC_PARAM_RC_INTRA_QP 0x188 -#define CODA_CMD_ENC_PARAM_RC_BITRATE 0x18c -#define CODA_CMD_ENC_PARAM_RC_FRAME_RATE 0x190 -#define CODA_CMD_ENC_PARAM_INTRA_MB_NUM 0x194 -#define CODA_CMD_ENC_PARAM_SLICE_MODE 0x198 -#define CODA_CMD_ENC_PARAM_HEC_MODE 0x19c -#define CODA_RET_ENC_PARAM_CHANGE_SUCCESS 0x1c0 - -/* Encoder Picture Run */ -#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 -#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 -#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4 -#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8 -#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac -#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0 -#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 -#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 -#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 -#define CODA_CMD_ENC_PIC_QS 0x18c -#define CODA_CMD_ENC_PIC_ROT_MODE 0x190 -#define CODA_ROT_MIR_ENABLE (1 << 4) -#define CODA_ROT_0 (0x0 << 0) -#define CODA_ROT_90 (0x1 << 0) -#define CODA_ROT_180 (0x2 << 0) -#define CODA_ROT_270 (0x3 << 0) -#define CODA_MIR_NONE (0x0 << 2) -#define CODA_MIR_VER (0x1 << 2) -#define CODA_MIR_HOR (0x2 << 2) -#define CODA_MIR_VER_HOR (0x3 << 2) -#define CODA_CMD_ENC_PIC_OPTION 0x194 -#define CODA_FORCE_IPICTURE BIT(1) -#define CODA_REPORT_MB_INFO BIT(3) -#define CODA_REPORT_MV_INFO BIT(4) -#define CODA_REPORT_SLICE_INFO BIT(5) -#define CODA_CMD_ENC_PIC_BB_START 0x198 -#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c -#define CODA_RET_ENC_FRAME_NUM 0x1c0 -#define CODA_RET_ENC_PIC_TYPE 0x1c4 -#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8 -#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc -#define CODA_RET_ENC_PIC_FLAG 0x1d0 -#define CODA_RET_ENC_PIC_SUCCESS 0x1d8 - -/* Set Frame Buffer */ -#define CODA_CMD_SET_FRAME_BUF_NUM 0x180 -#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 -#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188 -#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c -#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188 -#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c -#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 -#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 -#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 -#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c -#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 -#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4 -#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4 -#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 -#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8 -#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac -#define CODA9_CACHE_BYPASS_OFFSET 28 -#define CODA9_CACHE_DUALCONF_OFFSET 26 -#define CODA9_CACHE_PAGEMERGE_OFFSET 24 -#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16 -#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8 -#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0 -#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0 -#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4 -#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0 -#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4 -#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8 -#define CODA9_CMD_SET_FRAME_DELAY 0x1bc - -/* Encoder Header */ -#define CODA_CMD_ENC_HEADER_CODE 0x180 -#define CODA_GAMMA_OFFSET 0 -#define CODA_HEADER_H264_SPS 0 -#define CODA_HEADER_H264_PPS 1 -#define CODA_HEADER_MP4V_VOL 0 -#define CODA_HEADER_MP4V_VOS 1 -#define CODA_HEADER_MP4V_VIS 2 -#define CODA9_HEADER_FRAME_CROP (1 << 3) -#define CODA_CMD_ENC_HEADER_BB_START 0x184 -#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 -#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c -#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190 - -/* Get Version */ -#define CODA_CMD_FIRMWARE_VERNUM 0x1c0 -#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff) -#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f) -#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f) -#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff) -#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ - ((product) << 16 | ((major) << 12) | \ - ((minor) << 8) | (release)) -#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4 - -#define CODA9_GDMA_BASE 0x1000 -#define CODA9_GDI_CONTROL (CODA9_GDMA_BASE + 0x034) -#define CODA9_GDI_PIC_INIT_HOST (CODA9_GDMA_BASE + 0x038) -#define CODA9_GDI_STATUS (CODA9_GDMA_BASE + 0x080) -#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0) -#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac) - -#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0) -#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4) - -#define CODA9_GDI_INFO_CONTROL (CODA9_GDMA_BASE + 0x400) -#define CODA9_GDI_INFO_PIC_SIZE (CODA9_GDMA_BASE + 0x404) -#define CODA9_GDI_INFO_BASE_Y (CODA9_GDMA_BASE + 0x408) -#define CODA9_GDI_INFO_BASE_CB (CODA9_GDMA_BASE + 0x40c) -#define CODA9_GDI_INFO_BASE_CR (CODA9_GDMA_BASE + 0x410) - -#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800) -#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c) - -#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840) -#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844) -#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848) -#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c) - -#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850) -#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) - -#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) -#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) -#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) -#define CODA9_XY2RBC_TILED_MAP BIT(17) -#define CODA9_XY2RBC_CA_INC_HOR BIT(16) -#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) -#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) -#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) - -#define CODA9_JPEG_BASE 0x3000 -#define CODA9_REG_JPEG_PIC_START (CODA9_JPEG_BASE + 0x000) -#define CODA9_REG_JPEG_PIC_STATUS (CODA9_JPEG_BASE + 0x004) -#define CODA9_JPEG_STATUS_OVERFLOW BIT(3) -#define CODA9_JPEG_STATUS_BBC_INT BIT(2) -#define CODA9_JPEG_STATUS_ERROR BIT(1) -#define CODA9_JPEG_STATUS_DONE BIT(0) -#define CODA9_REG_JPEG_PIC_ERRMB (CODA9_JPEG_BASE + 0x008) -#define CODA9_JPEG_ERRMB_RESTART_IDX_MASK (0xf << 24) -#define CODA9_JPEG_ERRMB_MCU_POS_X_MASK (0xfff << 12) -#define CODA9_JPEG_ERRMB_MCU_POS_Y_MASK 0xfff -#define CODA9_REG_JPEG_PIC_CTRL (CODA9_JPEG_BASE + 0x010) -#define CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN BIT(6) -#define CODA9_JPEG_PIC_CTRL_TC_DIRECTION BIT(4) -#define CODA9_JPEG_PIC_CTRL_ENCODER_EN BIT(3) -#define CODA9_REG_JPEG_PIC_SIZE (CODA9_JPEG_BASE + 0x014) -#define CODA9_REG_JPEG_MCU_INFO (CODA9_JPEG_BASE + 0x018) -#define CODA9_JPEG_MCU_BLOCK_NUM_OFFSET 16 -#define CODA9_JPEG_COMP_NUM_OFFSET 12 -#define CODA9_JPEG_COMP0_INFO_OFFSET 8 -#define CODA9_JPEG_COMP1_INFO_OFFSET 4 -#define CODA9_JPEG_COMP2_INFO_OFFSET 0 -#define CODA9_REG_JPEG_ROT_INFO (CODA9_JPEG_BASE + 0x01c) -#define CODA9_JPEG_ROT_MIR_ENABLE BIT(4) -#define CODA9_JPEG_ROT_MIR_MODE_MASK 0xf -#define CODA9_REG_JPEG_SCL_INFO (CODA9_JPEG_BASE + 0x020) -#define CODA9_JPEG_SCL_ENABLE BIT(4) -#define CODA9_JPEG_SCL_HOR_MODE_MASK (0x3 << 2) -#define CODA9_JPEG_SCL_VER_MODE_MASK (0x3 << 0) -#define CODA9_REG_JPEG_IF_INFO (CODA9_JPEG_BASE + 0x024) -#define CODA9_JPEG_SENS_IF_CLR BIT(1) -#define CODA9_JPEG_DISP_IF_CLR BIT(0) -#define CODA9_REG_JPEG_OP_INFO (CODA9_JPEG_BASE + 0x02c) -#define CODA9_JPEG_BUS_REQ_NUM_OFFSET 0 -#define CODA9_JPEG_BUS_REQ_NUM_MASK 0x7 -#define CODA9_REG_JPEG_DPB_CONFIG (CODA9_JPEG_BASE + 0x030) -#define CODA9_REG_JPEG_DPB_BASE00 (CODA9_JPEG_BASE + 0x040) -#define CODA9_REG_JPEG_HUFF_CTRL (CODA9_JPEG_BASE + 0x080) -#define CODA9_REG_JPEG_HUFF_ADDR (CODA9_JPEG_BASE + 0x084) -#define CODA9_REG_JPEG_HUFF_DATA (CODA9_JPEG_BASE + 0x088) -#define CODA9_REG_JPEG_QMAT_CTRL (CODA9_JPEG_BASE + 0x090) -#define CODA9_REG_JPEG_QMAT_ADDR (CODA9_JPEG_BASE + 0x094) -#define CODA9_REG_JPEG_QMAT_DATA (CODA9_JPEG_BASE + 0x098) -#define CODA9_REG_JPEG_RST_INTVAL (CODA9_JPEG_BASE + 0x0b0) -#define CODA9_REG_JPEG_RST_INDEX (CODA9_JPEG_BASE + 0x0b4) -#define CODA9_REG_JPEG_RST_COUNT (CODA9_JPEG_BASE + 0x0b8) -#define CODA9_REG_JPEG_DPCM_DIFF_Y (CODA9_JPEG_BASE + 0x0f0) -#define CODA9_REG_JPEG_DPCM_DIFF_CB (CODA9_JPEG_BASE + 0x0f4) -#define CODA9_REG_JPEG_DPCM_DIFF_CR (CODA9_JPEG_BASE + 0x0f8) -#define CODA9_REG_JPEG_GBU_CTRL (CODA9_JPEG_BASE + 0x100) -#define CODA9_REG_JPEG_GBU_BT_PTR (CODA9_JPEG_BASE + 0x110) -#define CODA9_REG_JPEG_GBU_WD_PTR (CODA9_JPEG_BASE + 0x114) -#define CODA9_REG_JPEG_GBU_TT_CNT (CODA9_JPEG_BASE + 0x118) -#define CODA9_REG_JPEG_GBU_BBSR (CODA9_JPEG_BASE + 0x140) -#define CODA9_REG_JPEG_GBU_BBER (CODA9_JPEG_BASE + 0x144) -#define CODA9_REG_JPEG_GBU_BBIR (CODA9_JPEG_BASE + 0x148) -#define CODA9_REG_JPEG_GBU_BBHR (CODA9_JPEG_BASE + 0x14c) -#define CODA9_REG_JPEG_GBU_BCNT (CODA9_JPEG_BASE + 0x158) -#define CODA9_REG_JPEG_GBU_FF_RPTR (CODA9_JPEG_BASE + 0x160) -#define CODA9_REG_JPEG_GBU_FF_WPTR (CODA9_JPEG_BASE + 0x164) -#define CODA9_REG_JPEG_BBC_END_ADDR (CODA9_JPEG_BASE + 0x208) -#define CODA9_REG_JPEG_BBC_WR_PTR (CODA9_JPEG_BASE + 0x20c) -#define CODA9_REG_JPEG_BBC_RD_PTR (CODA9_JPEG_BASE + 0x210) -#define CODA9_REG_JPEG_BBC_EXT_ADDR (CODA9_JPEG_BASE + 0x214) -#define CODA9_REG_JPEG_BBC_INT_ADDR (CODA9_JPEG_BASE + 0x218) -#define CODA9_REG_JPEG_BBC_DATA_CNT (CODA9_JPEG_BASE + 0x21c) -#define CODA9_REG_JPEG_BBC_COMMAND (CODA9_JPEG_BASE + 0x220) -#define CODA9_REG_JPEG_BBC_BUSY (CODA9_JPEG_BASE + 0x224) -#define CODA9_REG_JPEG_BBC_CTRL (CODA9_JPEG_BASE + 0x228) -#define CODA9_REG_JPEG_BBC_CUR_POS (CODA9_JPEG_BASE + 0x22c) -#define CODA9_REG_JPEG_BBC_BAS_ADDR (CODA9_JPEG_BASE + 0x230) -#define CODA9_REG_JPEG_BBC_STRM_CTRL (CODA9_JPEG_BASE + 0x234) -#define CODA9_REG_JPEG_BBC_FLUSH_CMD (CODA9_JPEG_BASE + 0x238) - -#endif diff --git a/drivers/media/platform/chips-media/imx-vdoa.c b/drivers/media/platform/chips-media/imx-vdoa.c deleted file mode 100644 index c3561fcec..000000000 --- a/drivers/media/platform/chips-media/imx-vdoa.c +++ /dev/null @@ -1,346 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * i.MX6 Video Data Order Adapter (VDOA) - * - * Copyright (C) 2014 Philipp Zabel - * Copyright (C) 2016 Pengutronix, Michael Tretter - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-vdoa.h" - -#define VDOA_NAME "imx-vdoa" - -#define VDOAC 0x00 -#define VDOASRR 0x04 -#define VDOAIE 0x08 -#define VDOAIST 0x0c -#define VDOAFP 0x10 -#define VDOAIEBA00 0x14 -#define VDOAIEBA01 0x18 -#define VDOAIEBA02 0x1c -#define VDOAIEBA10 0x20 -#define VDOAIEBA11 0x24 -#define VDOAIEBA12 0x28 -#define VDOASL 0x2c -#define VDOAIUBO 0x30 -#define VDOAVEBA0 0x34 -#define VDOAVEBA1 0x38 -#define VDOAVEBA2 0x3c -#define VDOAVUBO 0x40 -#define VDOASR 0x44 - -#define VDOAC_ISEL BIT(6) -#define VDOAC_PFS BIT(5) -#define VDOAC_SO BIT(4) -#define VDOAC_SYNC BIT(3) -#define VDOAC_NF BIT(2) -#define VDOAC_BNDM_MASK 0x3 -#define VDOAC_BAND_HEIGHT_8 0x0 -#define VDOAC_BAND_HEIGHT_16 0x1 -#define VDOAC_BAND_HEIGHT_32 0x2 - -#define VDOASRR_START BIT(1) -#define VDOASRR_SWRST BIT(0) - -#define VDOAIE_EITERR BIT(1) -#define VDOAIE_EIEOT BIT(0) - -#define VDOAIST_TERR BIT(1) -#define VDOAIST_EOT BIT(0) - -#define VDOAFP_FH_MASK (0x1fff << 16) -#define VDOAFP_FW_MASK (0x3fff) - -#define VDOASL_VSLY_MASK (0x3fff << 16) -#define VDOASL_ISLY_MASK (0x7fff) - -#define VDOASR_ERRW BIT(4) -#define VDOASR_EOB BIT(3) -#define VDOASR_CURRENT_FRAME (0x3 << 1) -#define VDOASR_CURRENT_BUFFER BIT(1) - -enum { - V4L2_M2M_SRC = 0, - V4L2_M2M_DST = 1, -}; - -struct vdoa_data { - struct vdoa_ctx *curr_ctx; - struct device *dev; - struct clk *vdoa_clk; - void __iomem *regs; -}; - -struct vdoa_q_data { - unsigned int width; - unsigned int height; - unsigned int bytesperline; - unsigned int sizeimage; - u32 pixelformat; -}; - -struct vdoa_ctx { - struct vdoa_data *vdoa; - struct completion completion; - struct vdoa_q_data q_data[2]; - unsigned int submitted_job; - unsigned int completed_job; -}; - -static irqreturn_t vdoa_irq_handler(int irq, void *data) -{ - struct vdoa_data *vdoa = data; - struct vdoa_ctx *curr_ctx; - u32 val; - - /* Disable interrupts */ - writel(0, vdoa->regs + VDOAIE); - - curr_ctx = vdoa->curr_ctx; - if (!curr_ctx) { - dev_warn(vdoa->dev, - "Instance released before the end of transaction\n"); - return IRQ_HANDLED; - } - - val = readl(vdoa->regs + VDOAIST); - writel(val, vdoa->regs + VDOAIST); - if (val & VDOAIST_TERR) { - val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW; - dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read"); - } else if (!(val & VDOAIST_EOT)) { - dev_warn(vdoa->dev, "Spurious interrupt\n"); - } - curr_ctx->completed_job++; - complete(&curr_ctx->completion); - - return IRQ_HANDLED; -} - -int vdoa_wait_for_completion(struct vdoa_ctx *ctx) -{ - struct vdoa_data *vdoa = ctx->vdoa; - - if (ctx->submitted_job == ctx->completed_job) - return 0; - - if (!wait_for_completion_timeout(&ctx->completion, - msecs_to_jiffies(300))) { - dev_err(vdoa->dev, - "Timeout waiting for transfer result\n"); - return -ETIMEDOUT; - } - - return 0; -} -EXPORT_SYMBOL(vdoa_wait_for_completion); - -void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src) -{ - struct vdoa_q_data *src_q_data, *dst_q_data; - struct vdoa_data *vdoa = ctx->vdoa; - u32 val; - - if (vdoa->curr_ctx) - vdoa_wait_for_completion(vdoa->curr_ctx); - - vdoa->curr_ctx = ctx; - - reinit_completion(&ctx->completion); - ctx->submitted_job++; - - src_q_data = &ctx->q_data[V4L2_M2M_SRC]; - dst_q_data = &ctx->q_data[V4L2_M2M_DST]; - - /* Progressive, no sync, 1 frame per run */ - if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV) - val = VDOAC_PFS; - else - val = 0; - writel(val, vdoa->regs + VDOAC); - - writel(dst_q_data->height << 16 | dst_q_data->width, - vdoa->regs + VDOAFP); - - val = dst; - writel(val, vdoa->regs + VDOAIEBA00); - - writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline, - vdoa->regs + VDOASL); - - if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 || - dst_q_data->pixelformat == V4L2_PIX_FMT_NV21) - val = dst_q_data->bytesperline * dst_q_data->height; - else - val = 0; - writel(val, vdoa->regs + VDOAIUBO); - - val = src; - writel(val, vdoa->regs + VDOAVEBA0); - val = round_up(src_q_data->bytesperline * src_q_data->height, 4096); - writel(val, vdoa->regs + VDOAVUBO); - - /* Enable interrupts and start transfer */ - writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE); - writel(VDOASRR_START, vdoa->regs + VDOASRR); -} -EXPORT_SYMBOL(vdoa_device_run); - -struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) -{ - struct vdoa_ctx *ctx; - int err; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - err = clk_prepare_enable(vdoa->vdoa_clk); - if (err) { - kfree(ctx); - return NULL; - } - - init_completion(&ctx->completion); - ctx->vdoa = vdoa; - - return ctx; -} -EXPORT_SYMBOL(vdoa_context_create); - -void vdoa_context_destroy(struct vdoa_ctx *ctx) -{ - struct vdoa_data *vdoa = ctx->vdoa; - - if (vdoa->curr_ctx == ctx) { - vdoa_wait_for_completion(vdoa->curr_ctx); - vdoa->curr_ctx = NULL; - } - - clk_disable_unprepare(vdoa->vdoa_clk); - kfree(ctx); -} -EXPORT_SYMBOL(vdoa_context_destroy); - -int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, unsigned int height, - u32 pixelformat) -{ - struct vdoa_q_data *src_q_data; - struct vdoa_q_data *dst_q_data; - - if (width < 16 || width > 8192 || width % 16 != 0 || - height < 16 || height > 4096 || height % 16 != 0) - return -EINVAL; - - if (pixelformat != V4L2_PIX_FMT_YUYV && - pixelformat != V4L2_PIX_FMT_NV12) - return -EINVAL; - - /* If no context is passed, only check if the format is valid */ - if (!ctx) - return 0; - - src_q_data = &ctx->q_data[V4L2_M2M_SRC]; - dst_q_data = &ctx->q_data[V4L2_M2M_DST]; - - src_q_data->width = width; - src_q_data->height = height; - src_q_data->bytesperline = width; - src_q_data->sizeimage = - round_up(src_q_data->bytesperline * height, 4096) + - src_q_data->bytesperline * height / 2; - - dst_q_data->width = width; - dst_q_data->height = height; - dst_q_data->pixelformat = pixelformat; - switch (pixelformat) { - case V4L2_PIX_FMT_YUYV: - dst_q_data->bytesperline = width * 2; - dst_q_data->sizeimage = dst_q_data->bytesperline * height; - break; - case V4L2_PIX_FMT_NV12: - default: - dst_q_data->bytesperline = width; - dst_q_data->sizeimage = - dst_q_data->bytesperline * height * 3 / 2; - break; - } - - return 0; -} -EXPORT_SYMBOL(vdoa_context_configure); - -static int vdoa_probe(struct platform_device *pdev) -{ - struct vdoa_data *vdoa; - int ret; - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(&pdev->dev, "DMA enable failed\n"); - return ret; - } - - vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL); - if (!vdoa) - return -ENOMEM; - - vdoa->dev = &pdev->dev; - - vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL); - if (IS_ERR(vdoa->vdoa_clk)) { - dev_err(vdoa->dev, "Failed to get clock\n"); - return PTR_ERR(vdoa->vdoa_clk); - } - - vdoa->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(vdoa->regs)) - return PTR_ERR(vdoa->regs); - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, - vdoa_irq_handler, IRQF_ONESHOT, - "vdoa", vdoa); - if (ret < 0) { - dev_err(vdoa->dev, "Failed to get irq\n"); - return ret; - } - - platform_set_drvdata(pdev, vdoa); - - return 0; -} - -static const struct of_device_id vdoa_dt_ids[] = { - { .compatible = "fsl,imx6q-vdoa" }, - {} -}; -MODULE_DEVICE_TABLE(of, vdoa_dt_ids); - -static struct platform_driver vdoa_driver = { - .probe = vdoa_probe, - .driver = { - .name = VDOA_NAME, - .of_match_table = vdoa_dt_ids, - }, -}; - -module_platform_driver(vdoa_driver); - -MODULE_DESCRIPTION("Video Data Order Adapter"); -MODULE_AUTHOR("Philipp Zabel "); -MODULE_ALIAS("platform:imx-vdoa"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/chips-media/imx-vdoa.h b/drivers/media/platform/chips-media/imx-vdoa.h deleted file mode 100644 index a62eab476..000000000 --- a/drivers/media/platform/chips-media/imx-vdoa.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Pengutronix - */ - -#ifndef IMX_VDOA_H -#define IMX_VDOA_H - -struct vdoa_data; -struct vdoa_ctx; - -#if (defined CONFIG_VIDEO_IMX_VDOA || defined CONFIG_VIDEO_IMX_VDOA_MODULE) - -struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa); -int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, unsigned int height, - u32 pixelformat); -void vdoa_context_destroy(struct vdoa_ctx *ctx); - -void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src); -int vdoa_wait_for_completion(struct vdoa_ctx *ctx); - -#else - -static inline struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa) -{ - return NULL; -} - -static inline int vdoa_context_configure(struct vdoa_ctx *ctx, - unsigned int width, - unsigned int height, - u32 pixelformat) -{ - return 0; -} - -static inline void vdoa_context_destroy(struct vdoa_ctx *ctx) { }; - -static inline void vdoa_device_run(struct vdoa_ctx *ctx, - dma_addr_t dst, dma_addr_t src) { }; - -static inline int vdoa_wait_for_completion(struct vdoa_ctx *ctx) -{ - return 0; -}; - -#endif - -#endif /* IMX_VDOA_H */ diff --git a/drivers/media/platform/chips-media/trace.h b/drivers/media/platform/chips-media/trace.h deleted file mode 100644 index 19f98e6da..000000000 --- a/drivers/media/platform/chips-media/trace.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM coda - -#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) -#define __CODA_TRACE_H__ - -#include -#include - -#include "coda.h" - -TRACE_EVENT(coda_bit_run, - TP_PROTO(struct coda_ctx *ctx, int cmd), - - TP_ARGS(ctx, cmd), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, ctx) - __field(int, cmd) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->ctx = ctx->idx; - __entry->cmd = cmd; - ), - - TP_printk("minor = %d, ctx = %d, cmd = %d", - __entry->minor, __entry->ctx, __entry->cmd) -); - -TRACE_EVENT(coda_bit_done, - TP_PROTO(struct coda_ctx *ctx), - - TP_ARGS(ctx), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) -); - -DECLARE_EVENT_CLASS(coda_buf_class, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - - TP_ARGS(ctx, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->vb2_buf.index; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, index = %d, ctx = %d", - __entry->minor, __entry->index, __entry->ctx) -); - -DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DECLARE_EVENT_CLASS(coda_buf_meta_class, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - - TP_ARGS(ctx, buf, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->vb2_buf.index; - __entry->start = meta->start & ctx->bitstream_fifo.kfifo.mask; - __entry->end = meta->end & ctx->bitstream_fifo.kfifo.mask; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->index, __entry->start, __entry->end, - __entry->ctx) -); - -DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - TP_ARGS(ctx, buf, meta) -); - -DECLARE_EVENT_CLASS(coda_meta_class, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - - TP_ARGS(ctx, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta ? (meta->start & - ctx->bitstream_fifo.kfifo.mask) : 0; - __entry->end = meta ? (meta->end & - ctx->bitstream_fifo.kfifo.mask) : 0; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->ctx) -); - -DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - TP_ARGS(ctx, meta) -); - -DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - TP_ARGS(ctx, meta) -); - -DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, - struct coda_buffer_meta *meta), - TP_ARGS(ctx, buf, meta) -); - -DEFINE_EVENT(coda_buf_class, coda_jpeg_run, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -DEFINE_EVENT(coda_buf_class, coda_jpeg_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), - TP_ARGS(ctx, buf) -); - -#endif /* __CODA_TRACE_H__ */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE trace - -/* This part must be outside protection */ -#include diff --git a/drivers/media/platform/chips-media/wave5/Kconfig b/drivers/media/platform/chips-media/wave5/Kconfig new file mode 100644 index 000000000..f1bcef517 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_WAVE_VPU + tristate "Chips&Media Wave Codec Driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_K3 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR + help + Chips&Media stateful encoder and decoder driver. + The driver supports HEVC and H264 formats. + To compile this driver as modules, choose M here: the + modules will be called wave5. diff --git a/drivers/media/platform/chips-media/wave5/Makefile b/drivers/media/platform/chips-media/wave5/Makefile new file mode 100644 index 000000000..3d738a03b --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o +wave5-objs += wave5-hw.o \ + wave5-vpuapi.o \ + wave5-vdi.o \ + wave5-vpu-dec.o \ + wave5-vpu.o \ + wave5-vpu-enc.o \ + wave5-helper.o diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c new file mode 100644 index 000000000..8433ecab2 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - decoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +const char *state_to_str(enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + return "NONE"; + case VPU_INST_STATE_OPEN: + return "OPEN"; + case VPU_INST_STATE_INIT_SEQ: + return "INIT_SEQ"; + case VPU_INST_STATE_PIC_RUN: + return "PIC_RUN"; + case VPU_INST_STATE_STOP: + return "STOP"; + default: + return "UNKNOWN"; + } +} + +void wave5_cleanup_instance(struct vpu_instance *inst) +{ + int i; + + if (list_is_singular(&inst->list)) + wave5_vdi_free_sram(inst->dev); + + for (i = 0; i < inst->fbc_buf_count; i++) + wave5_vpu_dec_reset_framebuffer(inst, i); + + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf); + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); + if (inst->v4l2_fh.vdev) { + v4l2_fh_del(&inst->v4l2_fh); + v4l2_fh_exit(&inst->v4l2_fh); + } + list_del_init(&inst->list); + ida_free(&inst->dev->inst_ida, inst->id); + kfree(inst->codec_info); + kfree(inst); +} + +int wave5_vpu_release_device(struct file *filp, + int (*close_func)(struct vpu_instance *inst, u32 *fail_res), + char *name) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + if (inst->state != VPU_INST_STATE_NONE) { + u32 fail_res; + int ret; + + ret = close_func(inst, &fail_res); + if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_err(inst->dev->dev, "%s close failed, device is still running\n", + name); + return -EBUSY; + } + if (ret && ret != -EIO) { + dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret); + return ret; + } + } + + wave5_cleanup_instance(inst); + + return 0; +} + +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq, + const struct vb2_ops *ops) +{ + struct vpu_instance *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->ops = ops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->buf_struct_size = sizeof(struct vpu_src_buffer); + src_vq->drv_priv = inst; + src_vq->lock = &inst->dev->dev_lock; + src_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->ops = ops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer); + dst_vq->drv_priv = inst; + dst_vq->lock = &inst->dev->dev_lock; + dst_vq->dev = inst->dev->v4l2_dev.dev; + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + bool is_decoder = inst->type == VPU_INST_TYPE_DEC; + + dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__, + is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags); + + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (is_decoder) + return v4l2_src_change_event_subscribe(fh, sub); + return -EINVAL; + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->src_fmt.width; + f->fmt.pix_mp.height = inst->src_fmt.height; + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.field = inst->src_fmt.field; + f->fmt.pix_mp.flags = inst->src_fmt.flags; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt, + const struct vpu_format fmt_list[MAX_FMTS]) +{ + unsigned int index; + + for (index = 0; index < MAX_FMTS; index++) { + if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt) + return &fmt_list[index]; + } + + return NULL; +} + +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx, + const struct vpu_format fmt_list[MAX_FMTS]) +{ + if (idx >= MAX_FMTS) + return NULL; + + if (!fmt_list[idx].v4l2_pix_fmt) + return NULL; + + return &fmt_list[idx]; +} + +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type) +{ + switch (v4l2_pix_fmt) { + case V4L2_PIX_FMT_H264: + return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC; + case V4L2_PIX_FMT_HEVC: + return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC; + default: + return STD_UNKNOWN; + } +} + +void wave5_return_bufs(struct vb2_queue *q, u32 state) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_ctrl_handler v4l2_ctrl_hdl = inst->v4l2_ctrl_hdl; + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf = v4l2_m2m_src_buf_remove(m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!vbuf) + return; + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &v4l2_ctrl_hdl); + v4l2_m2m_buf_done(vbuf, state); + } +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h new file mode 100644 index 000000000..6cee1c14d --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - basic types + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE_HELPER_H__ +#define __WAVE_HELPER_H__ + +#include "wave5-vpu.h" + +#define FMT_TYPES 2 +#define MAX_FMTS 12 + +const char *state_to_str(enum vpu_instance_state state); +void wave5_cleanup_instance(struct vpu_instance *inst); +int wave5_vpu_release_device(struct file *filp, + int (*close_func)(struct vpu_instance *inst, u32 *fail_res), + char *name); +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq, + const struct vb2_ops *ops); +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub); +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f); +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt, + const struct vpu_format fmt_list[MAX_FMTS]); +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx, + const struct vpu_format fmt_list[MAX_FMTS]); +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type); +void wave5_return_bufs(struct vb2_queue *q, u32 state); +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c new file mode 100644 index 000000000..f1e022fb1 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -0,0 +1,2551 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - wave5 backend logic + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include +#include +#include "wave5-vpu.h" +#include "wave5.h" +#include "wave5-regdefine.h" + +#define FIO_TIMEOUT 10000000 +#define FIO_CTRL_READY BIT(31) +#define FIO_CTRL_WRITE BIT(16) +#define VPU_BUSY_CHECK_TIMEOUT 10000000 +#define QUEUE_REPORT_MASK 0xffff + +/* Encoder support fields */ +#define FEATURE_HEVC10BIT_ENC BIT(3) +#define FEATURE_AVC10BIT_ENC BIT(11) +#define FEATURE_AVC_ENCODER BIT(1) +#define FEATURE_HEVC_ENCODER BIT(0) + +/* Decoder support fields */ +#define FEATURE_AVC_DECODER BIT(3) +#define FEATURE_HEVC_DECODER BIT(2) + +#define FEATURE_BACKBONE BIT(16) +#define FEATURE_VCORE_BACKBONE BIT(22) +#define FEATURE_VCPU_BACKBONE BIT(28) + +#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff) +#define REMAP_CTRL_REGISTER_VALUE(index) ( \ + (BIT(31) | (index << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS) \ +) + +#define FASTIO_ADDRESS_MASK GENMASK(15, 0) +#define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24) + +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, + const char *func); +#define PRINT_REG_ERR(dev, reason) _wave5_print_reg_err((dev), (reason), __func__) + +static inline const char *cmd_to_str(int cmd, bool is_dec) +{ + switch (cmd) { + case W5_INIT_VPU: + return "W5_INIT_VPU"; + case W5_WAKEUP_VPU: + return "W5_WAKEUP_VPU"; + case W5_SLEEP_VPU: + return "W5_SLEEP_VPU"; + case W5_CREATE_INSTANCE: + return "W5_CREATE_INSTANCE"; + case W5_FLUSH_INSTANCE: + return "W5_FLUSH_INSTANCE"; + case W5_DESTROY_INSTANCE: + return "W5_DESTROY_INSTANCE"; + case W5_INIT_SEQ: + return "W5_INIT_SEQ"; + case W5_SET_FB: + return "W5_SET_FB"; + case W5_DEC_ENC_PIC: + if (is_dec) + return "W5_DEC_PIC"; + return "W5_ENC_PIC"; + case W5_ENC_SET_PARAM: + return "W5_ENC_SET_PARAM"; + case W5_QUERY: + return "W5_QUERY"; + case W5_UPDATE_BS: + return "W5_UPDATE_BS"; + case W5_MAX_VPU_COMD: + return "W5_MAX_VPU_COMD"; + default: + return "UNKNOWN"; + } +} + +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, + const char *func) +{ + struct device *dev = vpu_dev->dev; + u32 reg_val; + + switch (reg_fail_reason) { + case WAVE5_SYSERR_QUEUEING_FAIL: + reg_val = vpu_read_reg(vpu_dev, W5_RET_QUEUE_FAIL_REASON); + dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val); + break; + case WAVE5_SYSERR_RESULT_NOT_READY: + dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_ACCESS_VIOLATION_HW: + dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_WATCHDOG_TIMEOUT: + dev_err(dev, "%s: watchdog timeout: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_BUS_ERROR: + dev_err(dev, "%s: bus error: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_DOUBLE_FAULT: + dev_err(dev, "%s: double fault: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_VPU_STILL_RUNNING: + dev_err(dev, "%s: still running: 0x%x\n", func, reg_fail_reason); + break; + case WAVE5_SYSERR_VLC_BUF_FULL: + dev_err(dev, "%s: vlc buf full: 0x%x\n", func, reg_fail_reason); + break; + default: + dev_err(dev, "%s: failure:: 0x%x\n", func, reg_fail_reason); + break; + } +} + +static int wave5_wait_fio_readl(struct vpu_device *vpu_dev, u32 addr, u32 val) +{ + u32 ctrl; + int ret; + + ctrl = addr & 0xffff; + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl); + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, + 0, FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR); + if (ret) + return ret; + + if (wave5_vdi_read_register(vpu_dev, W5_VPU_FIO_DATA) != val) + return -ETIMEDOUT; + + return 0; +} + +static void wave5_fio_writel(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data) +{ + int ret; + unsigned int ctrl; + + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_DATA, data); + ctrl = FIELD_GET(FASTIO_ADDRESS_MASK, addr); + ctrl |= FIO_CTRL_WRITE; + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl); + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, 0, + FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR); + if (ret) + dev_dbg_ratelimited(vpu_dev->dev, "FIO write timeout: addr=0x%x data=%x\n", + ctrl, data); +} + +static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + u32 gdi_status_check_value = 0x3f; + + if (vpu_dev->product_code == WAVE521C_CODE || + vpu_dev->product_code == WAVE521_CODE || + vpu_dev->product_code == WAVE521E1_CODE) + gdi_status_check_value = 0x00ff1f3f; + + return wave5_wait_fio_readl(vpu_dev, addr, gdi_status_check_value); +} + +static int wave5_wait_vpu_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + u32 data; + + return read_poll_timeout(wave5_vdi_read_register, data, data == 0, + 0, VPU_BUSY_CHECK_TIMEOUT, false, vpu_dev, addr); +} + +static int wave5_wait_vcpu_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) +{ + return wave5_wait_fio_readl(vpu_dev, addr, 0); +} + +bool wave5_vpu_is_init(struct vpu_device *vpu_dev) +{ + return vpu_read_reg(vpu_dev, W5_VCPU_CUR_PC) != 0; +} + +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev) +{ + u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER); + + switch (val) { + case WAVE521C_CODE: + return PRODUCT_ID_521; + case WAVE521_CODE: + case WAVE521C_DUAL_CODE: + case WAVE521E1_CODE: + case WAVE511_CODE: + case WAVE517_CODE: + case WAVE537_CODE: + dev_err(vpu_dev->dev, "Unsupported product id (%x)\n", val); + break; + default: + dev_err(vpu_dev->dev, "Invalid product id (%x)\n", val); + break; + } + + return PRODUCT_ID_NONE; +} + +static void wave5_bit_issue_command(struct vpu_device *vpu_dev, struct vpu_instance *inst, u32 cmd) +{ + u32 instance_index; + u32 codec_mode; + + if (inst) { + instance_index = inst->id; + codec_mode = inst->std; + + vpu_write_reg(vpu_dev, W5_CMD_INSTANCE_INFO, (codec_mode << 16) | + (instance_index & 0xffff)); + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + } + + vpu_write_reg(vpu_dev, W5_COMMAND, cmd); + + if (inst) { + dev_dbg(vpu_dev->dev, "%s: cmd=0x%x (%s)\n", __func__, cmd, + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC)); + } else { + dev_dbg(vpu_dev->dev, "%s: cmd=0x%x\n", __func__, cmd); + } + + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1); +} + +static int wave5_vpu_firmware_command_queue_error_check(struct vpu_device *dev, u32 *fail_res) +{ + u32 reason = 0; + + /* Check if we were able to add a command into the VCPU QUEUE */ + if (!vpu_read_reg(dev, W5_RET_SUCCESS)) { + reason = vpu_read_reg(dev, W5_RET_FAIL_REASON); + PRINT_REG_ERR(dev, reason); + + /* + * The fail_res argument will be either NULL or 0. + * If the fail_res argument is NULL, then just return -EIO. + * Otherwise, assign the reason to fail_res, so that the + * calling function can use it. + */ + if (fail_res) + *fail_res = reason; + else + return -EIO; + + if (reason == WAVE5_SYSERR_VPU_STILL_RUNNING) + return -EBUSY; + } + return 0; +} + +static int send_firmware_command(struct vpu_instance *inst, u32 cmd, bool check_success, + u32 *queue_status, u32 *fail_result) +{ + int ret; + + wave5_bit_issue_command(inst->dev, inst, cmd); + ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_warn(inst->dev->dev, "%s: command: '%s', timed out\n", __func__, + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC)); + return -ETIMEDOUT; + } + + if (queue_status) + *queue_status = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + /* In some cases we want to send multiple commands before checking + * whether they are queued properly + */ + if (!check_success) + return 0; + + return wave5_vpu_firmware_command_queue_error_check(inst->dev, fail_result); +} + +static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *inst, + enum query_opt query_opt) +{ + int ret; + + vpu_write_reg(vpu_dev, W5_QUERY_OPTION, query_opt); + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + wave5_bit_issue_command(vpu_dev, inst, W5_QUERY); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_warn(vpu_dev->dev, "command: 'W5_QUERY', timed out opt=0x%x\n", query_opt); + return ret; + } + + return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); +} + +static int setup_wave5_properties(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + struct vpu_attr *p_attr = &vpu_dev->attr; + u32 reg_val; + u8 *str; + int ret; + u32 hw_config_def0, hw_config_def1, hw_config_feature; + + ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO); + if (ret) + return ret; + + reg_val = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_NAME); + str = (u8 *)®_val; + p_attr->product_name[0] = str[3]; + p_attr->product_name[1] = str[2]; + p_attr->product_name[2] = str[1]; + p_attr->product_name[3] = str[0]; + p_attr->product_name[4] = 0; + + p_attr->product_id = wave5_vpu_get_product_id(vpu_dev); + p_attr->product_version = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_VERSION); + p_attr->fw_version = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION); + p_attr->customer_id = vpu_read_reg(vpu_dev, W5_RET_CUSTOMER_ID); + hw_config_def0 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF0); + hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1); + hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE); + + p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature); + p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature); + + p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC; + p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC; + p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC; + p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC; + + p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0); + p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0); + p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0); + + return 0; +} + +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision) +{ + u32 reg_val; + int ret; + + ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO); + if (ret) + return ret; + + reg_val = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION); + if (revision) { + *revision = reg_val; + return 0; + } + + return -EINVAL; +} + +static void remap_page(struct vpu_device *vpu_dev, dma_addr_t code_base, u32 index) +{ + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CTRL, REMAP_CTRL_REGISTER_VALUE(index)); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_VADDR, index * W5_REMAP_MAX_SIZE); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_PADDR, code_base + index * W5_REMAP_MAX_SIZE); +} + +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) +{ + struct vpu_buf *common_vb; + dma_addr_t code_base, temp_base; + u32 code_size, temp_size; + u32 i, reg_val, reason_code; + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) + return -EINVAL; + + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_size = WAVE5_TEMPBUF_SIZE; + + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); + if (ret < 0) { + dev_err(vpu_dev->dev, "VPU init, Writing firmware to common buffer, fail: %d\n", + ret); + return ret; + } + + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + /* clear registers */ + + for (i = W5_CMD_REG_BASE; i < W5_CMD_REG_END; i += 4) + vpu_write_reg(vpu_dev, i, 0x00); + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base); + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU init(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + + return setup_wave5_properties(dev); +} + +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, + struct dec_open_param *param) +{ + int ret; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct vpu_device *vpu_dev = inst->dev; + + p_dec_info->cycle_per_tick = 256; + if (vpu_dev->sram_buf.size) { + p_dec_info->sec_axi_info.use_bit_enable = 1; + p_dec_info->sec_axi_info.use_ip_enable = 1; + p_dec_info->sec_axi_info.use_lf_row_enable = 1; + } + switch (inst->std) { + case W_HEVC_DEC: + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_HEVC; + break; + case W_AVC_DEC: + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_AVC; + break; + default: + return -EINVAL; + } + + p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work); + if (ret) + return ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); + + wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work); + + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr); + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size); + + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size); + + /* NOTE: SDMA reads MSB first */ + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN); + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); + if (ret) { + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work); + return ret; + } + + p_dec_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER); + + return 0; +} + +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 instance_queue_count, report_queue_count; + u32 reg_val = 0; + u32 fail_res = 0; + int ret; + + ret = send_firmware_command(inst, W5_FLUSH_INSTANCE, true, ®_val, &fail_res); + if (ret) + return ret; + + instance_queue_count = (reg_val >> 16) & 0xff; + report_queue_count = (reg_val & QUEUE_REPORT_MASK); + if (instance_queue_count != 0 || report_queue_count != 0) { + dev_warn(inst->dev->dev, + "FLUSH_INSTANCE cmd didn't reset the amount of queued commands & reports"); + } + + /* reset our local copy of the counts */ + p_dec_info->instance_queue_count = 0; + p_dec_info->report_queue_count = 0; + + return 0; +} + +static u32 get_bitstream_options(struct dec_info *info) +{ + u32 bs_option = BSOPTION_ENABLE_EXPLICIT_END; + + if (info->stream_endflag) + bs_option |= BSOPTION_HIGHLIGHT_STREAM_END; + return bs_option; +} + +int wave5_vpu_dec_init_seq(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 cmd_option = INIT_SEQ_NORMAL; + u32 reg_val, fail_res; + int ret; + + if (!inst->codec_info) + return -EINVAL; + + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option); + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); + + ret = send_firmware_command(inst, W5_INIT_SEQ, true, ®_val, &fail_res); + if (ret) + return ret; + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: init seq sent (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + return 0; +} + +static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initial_info *info) +{ + u32 reg_val; + u32 profile_compatibility_flag; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst); + info->rd_ptr = p_dec_info->stream_rd_ptr; + + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE); + info->pic_width = ((reg_val >> 16) & 0xffff); + info->pic_height = (reg_val & 0xffff); + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_DEC_NUM_REQUIRED_FB); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_LEFT_RIGHT); + info->pic_crop_rect.left = (reg_val >> 16) & 0xffff; + info->pic_crop_rect.right = reg_val & 0xffff; + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_TOP_BOTTOM); + info->pic_crop_rect.top = (reg_val >> 16) & 0xffff; + info->pic_crop_rect.bottom = reg_val & 0xffff; + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_COLOR_SAMPLE_INFO); + info->luma_bitdepth = reg_val & 0xf; + info->chroma_bitdepth = (reg_val >> 4) & 0xf; + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_SEQ_PARAM); + profile_compatibility_flag = (reg_val >> 12) & 0xff; + info->profile = (reg_val >> 24) & 0x1f; + + if (inst->std == W_HEVC_DEC) { + /* guessing profile */ + if (!info->profile) { + if ((profile_compatibility_flag & 0x06) == 0x06) + info->profile = HEVC_PROFILE_MAIN; /* main profile */ + else if (profile_compatibility_flag & 0x04) + info->profile = HEVC_PROFILE_MAIN10; /* main10 profile */ + else if (profile_compatibility_flag & 0x08) + /* main still picture profile */ + info->profile = HEVC_PROFILE_STILLPICTURE; + else + info->profile = HEVC_PROFILE_MAIN; /* for old version HM */ + } + } else if (inst->std == W_AVC_DEC) { + info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val); + } + + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_dec_info->vlc_buf_size = info->vlc_buf_size; + p_dec_info->param_buf_size = info->param_buf_size; +} + +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info) +{ + int ret; + u32 reg_val; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN); + + /* send QUERY cmd */ + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: init seq complete (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + /* this is not a fatal error, set ret to -EIO but don't return immediately */ + if (vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_SUCCESS) != 1) { + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_DEC_ERR_INFO); + ret = -EIO; + } + + wave5_get_dec_seq_result(inst, info); + + return ret; +} + +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_buffer *fb_arr, + enum tiled_map_type map_type, unsigned int count) +{ + int ret; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct dec_initial_info *init_info = &p_dec_info->initial_info; + size_t remain, idx, j, i, cnt_8_chunk, size; + u32 start_no, end_no; + u32 reg_val, cbcr_interleave, nv21, pic_size; + u32 addr_y, addr_cb, addr_cr; + u32 mv_col_size, frame_width, frame_height, fbc_y_tbl_size, fbc_c_tbl_size; + struct vpu_buf vb_buf; + bool justified = WTL_RIGHT_JUSTIFIED; + u32 format_no = WTL_PIXEL_8BIT; + u32 color_format = 0; + u32 pixel_order = 1; + u32 bwb_flag = (map_type == LINEAR_FRAME_MAP) ? 1 : 0; + + cbcr_interleave = inst->cbcr_interleave; + nv21 = inst->nv21; + mv_col_size = 0; + fbc_y_tbl_size = 0; + fbc_c_tbl_size = 0; + + if (map_type >= COMPRESSED_FRAME_MAP) { + cbcr_interleave = 0; + nv21 = 0; + + switch (inst->std) { + case W_HEVC_DEC: + mv_col_size = WAVE5_DEC_HEVC_BUF_SIZE(init_info->pic_width, + init_info->pic_height); + break; + case W_AVC_DEC: + mv_col_size = WAVE5_DEC_AVC_BUF_SIZE(init_info->pic_width, + init_info->pic_height); + break; + default: + return -EINVAL; + } + + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) { + size = ALIGN(ALIGN(mv_col_size, 16), BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_mv, count, size); + if (ret) + goto free_mv_buffers; + } + + frame_width = init_info->pic_width; + frame_height = init_info->pic_height; + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(frame_width, frame_height), 16); + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(frame_width, frame_height), 16); + + size = ALIGN(fbc_y_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_y_tbl, count, size); + if (ret) + goto free_fbc_y_tbl_buffers; + + size = ALIGN(fbc_c_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_c_tbl, count, size); + if (ret) + goto free_fbc_c_tbl_buffers; + + pic_size = (init_info->pic_width << 16) | (init_info->pic_height); + + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + + (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH); + vb_buf.daddr = 0; + + if (vb_buf.size != p_dec_info->vb_task.size) { + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); + ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf); + if (ret) + goto free_fbc_c_tbl_buffers; + + p_dec_info->vb_task = vb_buf; + } + + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_dec_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size); + } else { + pic_size = (init_info->pic_width << 16) | (init_info->pic_height); + + if (inst->output_format == FORMAT_422) + color_format = 1; + } + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size); + + reg_val = (bwb_flag << 28) | + (pixel_order << 23) | + (justified << 22) | + (format_no << 20) | + (color_format << 19) | + (nv21 << 17) | + (cbcr_interleave << 16) | + (fb_arr[0].stride); + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, reg_val); + + remain = count; + cnt_8_chunk = DIV_ROUND_UP(count, 8); + idx = 0; + for (j = 0; j < cnt_8_chunk; j++) { + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3); + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val); + start_no = j * 8; + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1; + + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no); + + for (i = 0; i < 8 && i < remain; i++) { + addr_y = fb_arr[i + start_no].buf_y; + addr_cb = fb_arr[i + start_no].buf_cb; + addr_cr = fb_arr[i + start_no].buf_cr; + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), addr_y); + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), addr_cb); + if (map_type >= COMPRESSED_FRAME_MAP) { + /* luma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4), + p_dec_info->vb_fbc_y_tbl[idx].daddr); + /* chroma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), + p_dec_info->vb_fbc_c_tbl[idx].daddr); + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), + p_dec_info->vb_mv[idx].daddr); + } else { + vpu_write_reg(inst->dev, W5_ADDR_CR_BASE0 + (i << 4), addr_cr); + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), 0); + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), 0); + } + idx++; + } + remain -= i; + + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL); + if (ret) + goto free_buffers; + } + + reg_val = vpu_read_reg(inst->dev, W5_RET_SUCCESS); + if (!reg_val) { + ret = -EIO; + goto free_buffers; + } + + return 0; + +free_buffers: + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); +free_fbc_c_tbl_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[i]); +free_fbc_y_tbl_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[i]); +free_mv_buffers: + for (i = 0; i < count; i++) + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[i]); + return ret; +} + +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) +{ + u32 reg_val; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + + /* secondary AXI */ + reg_val = p_dec_info->sec_axi_info.use_bit_enable | + (p_dec_info->sec_axi_info.use_ip_enable << 9) | + (p_dec_info->sec_axi_info.use_lf_row_enable << 15); + vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val); + + /* set attributes of user buffer */ + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); + + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, DEC_PIC_NORMAL); + vpu_write_reg(inst->dev, W5_CMD_DEC_TEMPORAL_ID_PLUS1, + (p_dec_info->target_spatial_id << 9) | + (p_dec_info->temp_id_select_mode << 8) | p_dec_info->target_temp_id); + vpu_write_reg(inst->dev, W5_CMD_SEQ_CHANGE_ENABLE_FLAG, p_dec_info->seq_change_mask); + /* When reordering is disabled we force the latency of the framebuffers */ + vpu_write_reg(inst->dev, W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1, !p_dec_info->reorder_enable); + + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, ®_val, fail_res); + if (ret == -ETIMEDOUT) + return ret; + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: dec pic sent (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result) +{ + int ret; + u32 index, nal_unit_type, reg_val, sub_layer_info; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct vpu_device *vpu_dev = inst->dev; + + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size); + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN); + + /* send QUERY cmd */ + ret = wave5_send_query(vpu_dev, inst, GET_RESULT); + if (ret) + return ret; + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + dev_dbg(inst->dev->dev, "%s: dec pic complete (queue %u : %u)\n", __func__, + p_dec_info->instance_queue_count, p_dec_info->report_queue_count); + + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_TYPE); + + nal_unit_type = (reg_val >> 4) & 0x3f; + + if (inst->std == W_HEVC_DEC) { + if (reg_val & 0x04) + result->pic_type = PIC_TYPE_B; + else if (reg_val & 0x02) + result->pic_type = PIC_TYPE_P; + else if (reg_val & 0x01) + result->pic_type = PIC_TYPE_I; + else + result->pic_type = PIC_TYPE_MAX; + if ((nal_unit_type == 19 || nal_unit_type == 20) && result->pic_type == PIC_TYPE_I) + /* IDR_W_RADL, IDR_N_LP */ + result->pic_type = PIC_TYPE_IDR; + } else if (inst->std == W_AVC_DEC) { + if (reg_val & 0x04) + result->pic_type = PIC_TYPE_B; + else if (reg_val & 0x02) + result->pic_type = PIC_TYPE_P; + else if (reg_val & 0x01) + result->pic_type = PIC_TYPE_I; + else + result->pic_type = PIC_TYPE_MAX; + if (nal_unit_type == 5 && result->pic_type == PIC_TYPE_I) + result->pic_type = PIC_TYPE_IDR; + } + index = vpu_read_reg(inst->dev, W5_RET_DEC_DISPLAY_INDEX); + result->index_frame_display = index; + index = vpu_read_reg(inst->dev, W5_RET_DEC_DECODED_INDEX); + result->index_frame_decoded = index; + result->index_frame_decoded_for_tiled = index; + + sub_layer_info = vpu_read_reg(inst->dev, W5_RET_DEC_SUB_LAYER_INFO); + result->temporal_id = sub_layer_info & 0x7; + + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) { + result->decoded_poc = -1; + if (result->index_frame_decoded >= 0 || + result->index_frame_decoded == DECODED_IDX_FLAG_SKIP) + result->decoded_poc = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_POC); + } + + result->sequence_changed = vpu_read_reg(inst->dev, W5_RET_DEC_NOTIFICATION); + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE); + result->dec_pic_width = reg_val >> 16; + result->dec_pic_height = reg_val & 0xffff; + + if (result->sequence_changed) { + memcpy((void *)&p_dec_info->new_seq_info, (void *)&p_dec_info->initial_info, + sizeof(struct dec_initial_info)); + wave5_get_dec_seq_result(inst, &p_dec_info->new_seq_info); + } + + result->dec_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_DEC_HOST_CMD_TICK); + result->dec_decode_end_tick = vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_ENC_TICK); + + if (!p_dec_info->first_cycle_check) { + result->frame_cycle = + (result->dec_decode_end_tick - result->dec_host_cmd_tick) * + p_dec_info->cycle_per_tick; + vpu_dev->last_performance_cycles = result->dec_decode_end_tick; + p_dec_info->first_cycle_check = true; + } else if (result->index_frame_decoded_for_tiled != -1) { + result->frame_cycle = + (result->dec_decode_end_tick - vpu_dev->last_performance_cycles) * + p_dec_info->cycle_per_tick; + vpu_dev->last_performance_cycles = result->dec_decode_end_tick; + if (vpu_dev->last_performance_cycles < result->dec_host_cmd_tick) + result->frame_cycle = + (result->dec_decode_end_tick - result->dec_host_cmd_tick) * + p_dec_info->cycle_per_tick; + } + + /* no remaining command. reset frame cycle. */ + if (p_dec_info->instance_queue_count == 0 && p_dec_info->report_queue_count == 0) + p_dec_info->first_cycle_check = false; + + return 0; +} + +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) +{ + struct vpu_buf *common_vb; + dma_addr_t code_base, temp_base; + dma_addr_t old_code_base, temp_size; + u32 code_size, reason_code; + u32 reg_val; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) + return -EINVAL; + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_size = WAVE5_TEMPBUF_SIZE; + + old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR); + + if (old_code_base != code_base + W5_REMAP_INDEX1 * W5_REMAP_MAX_SIZE) { + int ret; + + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); + if (ret < 0) { + dev_err(vpu_dev->dev, + "VPU init, Writing firmware to common buffer, fail: %d\n", ret); + return ret; + } + + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT); + if (ret < 0) { + dev_err(vpu_dev->dev, "VPU init, Resetting the VPU, fail: %d\n", ret); + return ret; + } + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base); + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU reinit(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + } + + return setup_wave5_properties(dev); +} + +static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code, + size_t size) +{ + u32 reg_val; + struct vpu_buf *common_vb; + dma_addr_t code_base; + u32 code_size, reason_code; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret; + + if (i_sleep_wake) { + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) + return ret; + + /* + * Declare who has ownership for the host interface access + * 1 = VPU + * 0 = Host processor + */ + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_SLEEP_VPU); + /* Send an interrupt named HOST to the VPU */ + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) + return ret; + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + if (ret) + return ret; + } else { /* restore */ + common_vb = &vpu_dev->common_mem; + + code_base = common_vb->daddr; + /* ALIGN TO 4KB */ + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + if (code_size < size * 2) { + dev_err(dev, "size too small\n"); + return -EINVAL; + } + + /* Power on without DEBUG mode */ + vpu_write_reg(vpu_dev, W5_PO_CONF, 0); + + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0); + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1); + + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base); + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size); + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0); + + /* These register must be reset explicitly */ + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + /* Encoder interrupt */ + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + reg_val = ((WAVE5_PROC_AXI_ID << 28) | + (WAVE5_PRP_AXI_ID << 24) | + (WAVE5_FBD_Y_AXI_ID << 20) | + (WAVE5_FBC_Y_AXI_ID << 16) | + (WAVE5_FBD_C_AXI_ID << 12) | + (WAVE5_FBC_C_AXI_ID << 8) | + (WAVE5_PRI_AXI_ID << 4) | + WAVE5_SEC_AXI_ID); + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); + } + + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); + vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU); + /* Start VPU after settings */ + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS); + if (ret) { + dev_err(vpu_dev->dev, "VPU wakeup(W5_VPU_REMAP_CORE_START) timeout\n"); + return ret; + } + + return wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code); + } + + return 0; +} + +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode) +{ + u32 val = 0; + int ret = 0; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + struct vpu_attr *p_attr = &vpu_dev->attr; + /* VPU doesn't send response. force to set BUSY flag to 0. */ + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 0); + + if (reset_mode == SW_RESET_SAFETY) { + ret = wave5_vpu_sleep_wake(dev, true, NULL, 0); + if (ret) + return ret; + } + + val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); + if ((val >> 16) & 0x1) + p_attr->support_backbone = true; + if ((val >> 22) & 0x1) + p_attr->support_vcore_backbone = true; + if ((val >> 28) & 0x1) + p_attr->support_vcpu_backbone = true; + + /* waiting for completion of bus transaction */ + if (p_attr->support_backbone) { + dev_dbg(dev, "%s: backbone supported\n", __func__); + + if (p_attr->support_vcore_backbone) { + if (p_attr->support_vcpu_backbone) { + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0xFF); + + /* step2 : waiting for completion of bus transaction */ + ret = wave5_wait_vcpu_bus_busy(vpu_dev, + W5_BACKBONE_BUS_STATUS_VCPU); + if (ret) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00); + return ret; + } + } + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x7); + + /* step2 : waiting for completion of bus transaction */ + if (wave5_wait_bus_busy(vpu_dev, W5_BACKBONE_BUS_STATUS_VCORE0)) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00); + return -EBUSY; + } + } else { + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x7); + + /* step2 : waiting for completion of bus transaction */ + if (wave5_wait_bus_busy(vpu_dev, W5_COMBINED_BACKBONE_BUS_STATUS)) { + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00); + return -EBUSY; + } + } + } else { + dev_dbg(dev, "%s: backbone NOT supported\n", __func__); + /* step1 : disable request */ + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x100); + + /* step2 : waiting for completion of bus transaction */ + ret = wave5_wait_bus_busy(vpu_dev, W5_GDI_BUS_STATUS); + if (ret) { + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00); + return ret; + } + } + + switch (reset_mode) { + case SW_RESET_ON_BOOT: + case SW_RESET_FORCE: + case SW_RESET_SAFETY: + val = W5_RST_BLOCK_ALL; + break; + default: + return -EINVAL; + } + + if (val) { + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, val); + + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_RESET_STATUS); + if (ret) { + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0); + return ret; + } + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0); + } + /* step3 : must clear GDI_BUS_CTRL after done SW_RESET */ + if (p_attr->support_backbone) { + if (p_attr->support_vcore_backbone) { + if (p_attr->support_vcpu_backbone) + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00); + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00); + } else { + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00); + } + } else { + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00); + } + if (reset_mode == SW_RESET_SAFETY || reset_mode == SW_RESET_FORCE) + ret = wave5_vpu_sleep_wake(dev, false, NULL, 0); + + return ret; +} + +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res) +{ + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res); +} + +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + p_dec_info->stream_endflag = eos ? 1 : 0; + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); + + return send_firmware_command(inst, W5_UPDATE_BS, true, NULL, NULL); +} + +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, BIT(index)); + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, 0); + + ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG); + if (ret) + return ret; + + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC); + + return 0; +} + +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index) +{ + int ret; + + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, 0); + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, BIT(index)); + + ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG); + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags) +{ + u32 interrupt_reason; + + interrupt_reason = vpu_read_reg(inst->dev, W5_VPU_VINT_REASON_USR); + interrupt_reason &= ~flags; + vpu_write_reg(inst->dev, W5_VPU_VINT_REASON_USR, interrupt_reason); + + return 0; +} + +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst) +{ + int ret; + + ret = wave5_send_query(inst->dev, inst, GET_BS_RD_PTR); + if (ret) + return inst->codec_info->dec_info.stream_rd_ptr; + + return vpu_read_reg(inst->dev, W5_RET_QUERY_DEC_BS_RD_PTR); +} + +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr) +{ + int ret; + + vpu_write_reg(inst->dev, W5_RET_QUERY_DEC_SET_BS_RD_PTR, addr); + + ret = wave5_send_query(inst->dev, inst, SET_BS_RD_PTR); + + return ret; +} + +/************************************************************************/ +/* ENCODER functions */ +/************************************************************************/ + +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, + struct enc_open_param *open_param) +{ + int ret; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + u32 reg_val; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + dma_addr_t buffer_addr; + size_t buffer_size; + + p_enc_info->cycle_per_tick = 256; + if (vpu_dev->sram_buf.size) { + p_enc_info->sec_axi_info.use_enc_rdo_enable = 1; + p_enc_info->sec_axi_info.use_enc_lf_enable = 1; + } + + p_enc_info->vb_work.size = WAVE521ENC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &p_enc_info->vb_work); + if (ret) { + memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work)); + return ret; + } + + wave5_vdi_clear_memory(vpu_dev, &p_enc_info->vb_work); + + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_enc_info->vb_work.daddr); + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_enc_info->vb_work.size); + + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + + reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN; + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val); + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_VCORE_INFO, 1); + + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); + if (ret) + goto free_vb_work; + + buffer_addr = open_param->bitstream_buffer; + buffer_size = open_param->bitstream_buffer_size; + p_enc_info->stream_rd_ptr = buffer_addr; + p_enc_info->stream_wr_ptr = buffer_addr; + p_enc_info->line_buf_int_en = open_param->line_buf_int_en; + p_enc_info->stream_buf_start_addr = buffer_addr; + p_enc_info->stream_buf_size = buffer_size; + p_enc_info->stream_buf_end_addr = buffer_addr + buffer_size; + p_enc_info->stride = 0; + p_enc_info->initial_info_obtained = false; + p_enc_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER); + + return 0; +free_vb_work: + if (wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work)) + memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work)); + return ret; +} + +static void wave5_set_enc_crop_info(u32 codec, struct enc_wave_param *param, int rot_mode, + int src_width, int src_height) +{ + int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(src_width, 32) : ALIGN(src_width, 16); + int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(src_height, 32) : ALIGN(src_height, 16); + int pad_right, pad_bot; + int crop_right, crop_left, crop_top, crop_bot; + int prp_mode = rot_mode >> 1; /* remove prp_enable bit */ + + if (codec == W_HEVC_ENC && + (!rot_mode || prp_mode == 14)) /* prp_mode 14 : hor_mir && ver_mir && rot_180 */ + return; + + pad_right = aligned_width - src_width; + pad_bot = aligned_height - src_height; + + if (param->conf_win_right > 0) + crop_right = param->conf_win_right + pad_right; + else + crop_right = pad_right; + + if (param->conf_win_bot > 0) + crop_bot = param->conf_win_bot + pad_bot; + else + crop_bot = pad_bot; + + crop_top = param->conf_win_top; + crop_left = param->conf_win_left; + + param->conf_win_top = crop_top; + param->conf_win_left = crop_left; + param->conf_win_bot = crop_bot; + param->conf_win_right = crop_right; + + switch (prp_mode) { + case 0: + return; + case 1: + case 15: + param->conf_win_top = crop_right; + param->conf_win_left = crop_top; + param->conf_win_bot = crop_left; + param->conf_win_right = crop_bot; + break; + case 2: + case 12: + param->conf_win_top = crop_bot; + param->conf_win_left = crop_right; + param->conf_win_bot = crop_top; + param->conf_win_right = crop_left; + break; + case 3: + case 13: + param->conf_win_top = crop_left; + param->conf_win_left = crop_bot; + param->conf_win_bot = crop_right; + param->conf_win_right = crop_top; + break; + case 4: + case 10: + param->conf_win_top = crop_bot; + param->conf_win_bot = crop_top; + break; + case 8: + case 6: + param->conf_win_left = crop_right; + param->conf_win_right = crop_left; + break; + case 5: + case 11: + param->conf_win_top = crop_left; + param->conf_win_left = crop_top; + param->conf_win_bot = crop_right; + param->conf_win_right = crop_bot; + break; + case 7: + case 9: + param->conf_win_top = crop_right; + param->conf_win_left = crop_bot; + param->conf_win_bot = crop_left; + param->conf_win_right = crop_top; + break; + default: + WARN(1, "Invalid prp_mode: %d, must be in range of 1 - 15\n", prp_mode); + } +} + +int wave5_vpu_enc_init_seq(struct vpu_instance *inst) +{ + u32 reg_val = 0, rot_mir_mode, fixed_cu_size_mode = 0x7; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct enc_open_param *p_open_param = &p_enc_info->open_param; + struct enc_wave_param *p_param = &p_open_param->wave_param; + + /* + * OPT_COMMON: + * the last SET_PARAM command should be called with OPT_COMMON + */ + rot_mir_mode = 0; + if (p_enc_info->rotation_enable) { + switch (p_enc_info->rotation_angle) { + case 0: + rot_mir_mode |= NONE_ROTATE; + break; + case 90: + rot_mir_mode |= ROT_CLOCKWISE_90; + break; + case 180: + rot_mir_mode |= ROT_CLOCKWISE_180; + break; + case 270: + rot_mir_mode |= ROT_CLOCKWISE_270; + break; + } + } + + if (p_enc_info->mirror_enable) { + switch (p_enc_info->mirror_direction) { + case MIRDIR_NONE: + rot_mir_mode |= NONE_ROTATE; + break; + case MIRDIR_VER: + rot_mir_mode |= MIR_VER_FLIP; + break; + case MIRDIR_HOR: + rot_mir_mode |= MIR_HOR_FLIP; + break; + case MIRDIR_HOR_VER: + rot_mir_mode |= MIR_HOR_VER_FLIP; + break; + } + } + + wave5_set_enc_crop_info(inst->std, p_param, rot_mir_mode, p_open_param->pic_width, + p_open_param->pic_height); + + /* SET_PARAM + COMMON */ + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SET_PARAM_OPTION, OPT_COMMON); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SRC_SIZE, p_open_param->pic_height << 16 + | p_open_param->pic_width); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN, VDI_LITTLE_ENDIAN); + + reg_val = p_param->profile | + (p_param->level << 3) | + (p_param->internal_bit_depth << 14); + if (inst->std == W_HEVC_ENC) + reg_val |= (p_param->tier << 12) | + (p_param->tmvp_enable << 23) | + (p_param->sao_enable << 24) | + (p_param->skip_intra_trans << 25) | + (p_param->strong_intra_smooth_enable << 27) | + (p_param->en_still_picture << 30); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); + + reg_val = (p_param->lossless_enable) | + (p_param->const_intra_pred_flag << 1) | + (p_param->lf_cross_slice_boundary_enable << 2) | + (p_param->wpp_enable << 4) | + (p_param->disable_deblk << 5) | + ((p_param->beta_offset_div2 & 0xF) << 6) | + ((p_param->tc_offset_div2 & 0xF) << 10) | + ((p_param->chroma_cb_qp_offset & 0x1F) << 14) | + ((p_param->chroma_cr_qp_offset & 0x1F) << 19) | + (p_param->transform8x8_enable << 29) | + (p_param->entropy_coding_mode << 30); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_PPS_PARAM, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_GOP_PARAM, p_param->gop_preset_idx); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp | + ((p_param->intra_period & 0x7ff) << 6) | + ((p_param->avc_idr_period & 0x7ff) << 17)); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, + p_param->decoding_refresh_type | (p_param->intra_qp << 3) | + (p_param->intra_period << 16)); + + reg_val = (p_param->rdo_skip << 2) | + (p_param->lambda_scaling_enable << 3) | + (fixed_cu_size_mode << 5) | + (p_param->intra_nx_n_enable << 8) | + (p_param->max_num_merge << 18); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RDO_PARAM, reg_val); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH, + p_param->intra_mb_refresh_arg << 16 | p_param->intra_mb_refresh_mode); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH, + p_param->intra_refresh_arg << 16 | p_param->intra_refresh_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_FRAME_RATE, p_open_param->frame_rate_info); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_TARGET_RATE, p_open_param->bit_rate); + + reg_val = p_open_param->rc_enable | + (p_param->hvs_qp_enable << 2) | + (p_param->hvs_qp_scale << 4) | + ((p_param->initial_rc_qp & 0x3F) << 14) | + (p_open_param->vbv_buffer_size << 20); + if (inst->std == W_AVC_ENC) + reg_val |= (p_param->mb_level_rc_enable << 1); + else if (inst->std == W_HEVC_ENC) + reg_val |= (p_param->cu_level_rc_enable << 1); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_PARAM, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM, + p_param->rc_weight_buf << 8 | p_param->rc_weight_param); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_MIN_MAX_QP, p_param->min_qp_i | + (p_param->max_qp_i << 6) | (p_param->hvs_max_delta_qp << 12)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP, p_param->min_qp_p | + (p_param->max_qp_p << 6) | (p_param->min_qp_b << 12) | + (p_param->max_qp_b << 18)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT, + p_param->conf_win_bot << 16 | p_param->conf_win_top); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT, + p_param->conf_win_right << 16 | p_param->conf_win_left); + + if (inst->std == W_AVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE, + p_param->avc_slice_arg << 16 | p_param->avc_slice_mode); + else if (inst->std == W_HEVC_ENC) + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE, + p_param->independ_slice_mode_arg << 16 | + p_param->independ_slice_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_TIME_SCALE, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE, 0); + + if (inst->std == W_HEVC_ENC) { + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU04, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU08, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU16, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU32, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU08, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU16, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU32, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_DEPENDENT_SLICE, + p_param->depend_slice_mode_arg << 16 | p_param->depend_slice_mode); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_PARAM, 0); + + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_WEIGHT, + p_param->nr_intra_weight_y | + (p_param->nr_intra_weight_cb << 5) | + (p_param->nr_intra_weight_cr << 10) | + (p_param->nr_inter_weight_y << 15) | + (p_param->nr_inter_weight_cb << 20) | + (p_param->nr_inter_weight_cr << 25)); + } + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_VUI_HRD_PARAM, 0); + + return send_firmware_command(inst, W5_ENC_SET_PARAM, true, NULL, NULL); +} + +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info) +{ + int ret; + u32 reg_val; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + /* send QUERY cmd */ + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: init seq\n", __func__); + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + if (vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS) != 1) { + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO); + ret = -EIO; + } else { + info->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO); + } + + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_ENC_NUM_REQUIRED_FB); + info->min_src_frame_count = vpu_read_reg(inst->dev, W5_RET_ENC_MIN_SRC_BUF_NUM); + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_enc_info->vlc_buf_size = info->vlc_buf_size; + p_enc_info->param_buf_size = info->param_buf_size; + + return ret; +} + +static u32 calculate_luma_stride(u32 width, u32 bit_depth) +{ + return ALIGN(ALIGN(width, 16) * ((bit_depth > 8) ? 5 : 4), 32); +} + +static u32 calculate_chroma_stride(u32 width, u32 bit_depth) +{ + return ALIGN(ALIGN(width / 2, 16) * ((bit_depth > 8) ? 5 : 4), 32); +} + +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret = 0; + u32 stride; + u32 start_no, end_no; + size_t remain, idx, j, i, cnt_8_chunk; + u32 reg_val = 0, pic_size = 0, mv_col_size, fbc_y_tbl_size, fbc_c_tbl_size; + u32 sub_sampled_size = 0; + u32 luma_stride, chroma_stride; + u32 buf_height = 0, buf_width = 0; + u32 bit_depth; + bool avc_encoding = (inst->std == W_AVC_ENC); + struct vpu_buf vb_mv = {0}; + struct vpu_buf vb_fbc_y_tbl = {0}; + struct vpu_buf vb_fbc_c_tbl = {0}; + struct vpu_buf vb_sub_sam_buf = {0}; + struct vpu_buf vb_task = {0}; + struct enc_open_param *p_open_param; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + p_open_param = &p_enc_info->open_param; + mv_col_size = 0; + fbc_y_tbl_size = 0; + fbc_c_tbl_size = 0; + stride = p_enc_info->stride; + bit_depth = p_open_param->wave_param.internal_bit_depth; + + if (avc_encoding) { + buf_width = ALIGN(p_open_param->pic_width, 16); + buf_height = ALIGN(p_open_param->pic_height, 16); + + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + buf_width = ALIGN(p_open_param->pic_width, 16); + buf_height = ALIGN(p_open_param->pic_height, 16); + } + + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) { + buf_width = ALIGN(p_open_param->pic_height, 16); + buf_height = ALIGN(p_open_param->pic_width, 16); + } + } else { + buf_width = ALIGN(p_open_param->pic_width, 8); + buf_height = ALIGN(p_open_param->pic_height, 8); + + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + buf_width = ALIGN(p_open_param->pic_width, 32); + buf_height = ALIGN(p_open_param->pic_height, 32); + } + + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) { + buf_width = ALIGN(p_open_param->pic_height, 32); + buf_height = ALIGN(p_open_param->pic_width, 32); + } + } + + pic_size = (buf_width << 16) | buf_height; + + if (avc_encoding) { + mv_col_size = WAVE5_ENC_AVC_BUF_SIZE(buf_width, buf_height); + vb_mv.daddr = 0; + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + } else { + mv_col_size = WAVE5_ENC_HEVC_BUF_SIZE(buf_width, buf_height); + mv_col_size = ALIGN(mv_col_size, 16); + vb_mv.daddr = 0; + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + } + + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_mv); + if (ret) + return ret; + + p_enc_info->vb_mv = vb_mv; + + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(buf_width, buf_height), 16); + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(buf_width, buf_height), 16); + + vb_fbc_y_tbl.daddr = 0; + vb_fbc_y_tbl.size = ALIGN(fbc_y_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_y_tbl); + if (ret) + goto free_vb_fbc_y_tbl; + + p_enc_info->vb_fbc_y_tbl = vb_fbc_y_tbl; + + vb_fbc_c_tbl.daddr = 0; + vb_fbc_c_tbl.size = ALIGN(fbc_c_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_c_tbl); + if (ret) + goto free_vb_fbc_c_tbl; + + p_enc_info->vb_fbc_c_tbl = vb_fbc_c_tbl; + + if (avc_encoding) + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE_AVC(buf_width, buf_height); + else + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE(buf_width, buf_height); + vb_sub_sam_buf.size = ALIGN(sub_sampled_size * count, BUFFER_MARGIN) + BUFFER_MARGIN; + vb_sub_sam_buf.daddr = 0; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_sub_sam_buf); + if (ret) + goto free_vb_sam_buf; + + p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf; + + vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) + + (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH); + vb_task.daddr = 0; + if (p_enc_info->vb_task.size == 0) { + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task); + if (ret) + goto free_vb_task; + + p_enc_info->vb_task = vb_task; + + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_enc_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_task.size); + } + + /* set sub-sampled buffer base addr */ + vpu_write_reg(inst->dev, W5_ADDR_SUB_SAMPLED_FB_BASE, vb_sub_sam_buf.daddr); + /* set sub-sampled buffer size for one frame */ + vpu_write_reg(inst->dev, W5_SUB_SAMPLED_ONE_FB_SIZE, sub_sampled_size); + + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size); + + /* set stride of luma/chroma for compressed buffer */ + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) && + !(p_enc_info->rotation_angle == 180 && + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) { + luma_stride = calculate_luma_stride(buf_width, bit_depth); + chroma_stride = calculate_chroma_stride(buf_width / 2, bit_depth); + } else { + luma_stride = calculate_luma_stride(p_open_param->pic_width, bit_depth); + chroma_stride = calculate_chroma_stride(p_open_param->pic_width / 2, bit_depth); + } + + vpu_write_reg(inst->dev, W5_FBC_STRIDE, luma_stride << 16 | chroma_stride); + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, stride); + + remain = count; + cnt_8_chunk = DIV_ROUND_UP(count, 8); + idx = 0; + for (j = 0; j < cnt_8_chunk; j++) { + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3); + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val); + start_no = j * 8; + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1; + + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no); + + for (i = 0; i < 8 && i < remain; i++) { + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), fb_arr[i + + start_no].buf_y); + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), + fb_arr[i + start_no].buf_cb); + /* luma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4), + vb_fbc_y_tbl.daddr + idx * fbc_y_tbl_size); + /* chroma FBC offset table */ + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), + vb_fbc_c_tbl.daddr + idx * fbc_c_tbl_size); + + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), + vb_mv.daddr + idx * mv_col_size); + idx++; + } + remain -= i; + + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL); + if (ret) + goto free_vb_mem; + } + + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); + if (ret) + goto free_vb_mem; + + return ret; + +free_vb_mem: + wave5_vdi_free_dma_memory(vpu_dev, &vb_task); +free_vb_task: + wave5_vdi_free_dma_memory(vpu_dev, &vb_sub_sam_buf); +free_vb_sam_buf: + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_c_tbl); +free_vb_fbc_c_tbl: + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_y_tbl); +free_vb_fbc_y_tbl: + wave5_vdi_free_dma_memory(vpu_dev, &vb_mv); + return ret; +} + +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res) +{ + u32 src_frame_format; + u32 reg_val = 0; + u32 src_stride_c = 0; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct frame_buffer *p_src_frame = option->source_frame; + struct enc_open_param *p_open_param = &p_enc_info->open_param; + bool justified = WTL_RIGHT_JUSTIFIED; + u32 format_no = WTL_PIXEL_8BIT; + int ret; + + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_START_ADDR, option->pic_stream_buffer_addr); + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_SIZE, option->pic_stream_buffer_size); + p_enc_info->stream_buf_start_addr = option->pic_stream_buffer_addr; + p_enc_info->stream_buf_size = option->pic_stream_buffer_size; + p_enc_info->stream_buf_end_addr = + option->pic_stream_buffer_addr + option->pic_stream_buffer_size; + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI); + /* secondary AXI */ + reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) | + (p_enc_info->sec_axi_info.use_enc_lf_enable << 15); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0); + + /* + * CODEOPT_ENC_VCL is used to implicitly encode header/headers to generate bitstream. + * (use ENC_PUT_VIDEO_HEADER for give_command to encode only a header) + */ + if (option->code_option.implicit_header_encode) + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION, + CODEOPT_ENC_HEADER_IMPLICIT | CODEOPT_ENC_VCL | + (option->code_option.encode_aud << 5) | + (option->code_option.encode_eos << 6) | + (option->code_option.encode_eob << 7)); + else + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION, + option->code_option.implicit_header_encode | + (option->code_option.encode_vcl << 1) | + (option->code_option.encode_vps << 2) | + (option->code_option.encode_sps << 3) | + (option->code_option.encode_pps << 4) | + (option->code_option.encode_aud << 5) | + (option->code_option.encode_eos << 6) | + (option->code_option.encode_eob << 7)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PIC_PARAM, 0); + + if (option->src_end_flag) + /* no more source images. */ + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, 0xFFFFFFFF); + else + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, option->src_idx); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_Y, p_src_frame->buf_y); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_U, p_src_frame->buf_cb); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_V, p_src_frame->buf_cr); + + switch (p_open_param->src_format) { + case FORMAT_420: + case FORMAT_422: + case FORMAT_YUYV: + case FORMAT_YVYU: + case FORMAT_UYVY: + case FORMAT_VYUY: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_8BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == FORMAT_422) ? src_stride_c * 2 : + src_stride_c; + break; + case FORMAT_420_P10_16BIT_MSB: + case FORMAT_422_P10_16BIT_MSB: + case FORMAT_YUYV_P10_16BIT_MSB: + case FORMAT_YVYU_P10_16BIT_MSB: + case FORMAT_UYVY_P10_16BIT_MSB: + case FORMAT_VYUY_P10_16BIT_MSB: + justified = WTL_RIGHT_JUSTIFIED; + format_no = WTL_PIXEL_16BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_16BIT_MSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_16BIT_LSB: + case FORMAT_422_P10_16BIT_LSB: + case FORMAT_YUYV_P10_16BIT_LSB: + case FORMAT_YVYU_P10_16BIT_LSB: + case FORMAT_UYVY_P10_16BIT_LSB: + case FORMAT_VYUY_P10_16BIT_LSB: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_16BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + (p_src_frame->stride / 2); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_16BIT_LSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_32BIT_MSB: + case FORMAT_422_P10_32BIT_MSB: + case FORMAT_YUYV_P10_32BIT_MSB: + case FORMAT_YVYU_P10_32BIT_MSB: + case FORMAT_UYVY_P10_32BIT_MSB: + case FORMAT_VYUY_P10_32BIT_MSB: + justified = WTL_RIGHT_JUSTIFIED; + format_no = WTL_PIXEL_32BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_32BIT_MSB) ? src_stride_c * 2 : src_stride_c; + break; + case FORMAT_420_P10_32BIT_LSB: + case FORMAT_422_P10_32BIT_LSB: + case FORMAT_YUYV_P10_32BIT_LSB: + case FORMAT_YVYU_P10_32BIT_LSB: + case FORMAT_UYVY_P10_32BIT_LSB: + case FORMAT_VYUY_P10_32BIT_LSB: + justified = WTL_LEFT_JUSTIFIED; + format_no = WTL_PIXEL_32BIT; + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride : + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave); + src_stride_c = (p_open_param->src_format == + FORMAT_422_P10_32BIT_LSB) ? src_stride_c * 2 : src_stride_c; + break; + default: + return -EINVAL; + } + + src_frame_format = (inst->cbcr_interleave << 1) | (inst->nv21); + switch (p_open_param->packed_format) { + case PACKED_YUYV: + src_frame_format = 4; + break; + case PACKED_YVYU: + src_frame_format = 5; + break; + case PACKED_UYVY: + src_frame_format = 6; + break; + case PACKED_VYUY: + src_frame_format = 7; + break; + default: + break; + } + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_STRIDE, + (p_src_frame->stride << 16) | src_stride_c); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_FORMAT, src_frame_format | + (format_no << 3) | (justified << 5) | (PIC_SRC_ENDIANNESS_BIG_ENDIAN << 6)); + + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_LONGTERM_PIC, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PREFIX_SEI_INFO, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SUFFIX_SEI_INFO, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR, 0); + + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, ®_val, fail_res); + if (ret == -ETIMEDOUT) + return ret; + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + if (ret) + return ret; + + return 0; +} + +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result) +{ + int ret; + u32 encoding_success; + u32 reg_val; + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + struct vpu_device *vpu_dev = inst->dev; + + ret = wave5_send_query(inst->dev, inst, GET_RESULT); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: enc pic complete\n", __func__); + + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS); + + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff; + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK); + + encoding_success = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS); + if (!encoding_success) { + result->error_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO); + return -EIO; + } + + result->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO); + + reg_val = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_TYPE); + result->pic_type = reg_val & 0xFFFF; + + result->enc_vcl_nut = vpu_read_reg(inst->dev, W5_RET_ENC_VCL_NUT); + /* + * To get the reconstructed frame use the following index on + * inst->frame_buf + */ + result->recon_frame_index = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_IDX); + result->enc_pic_byte = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_BYTE); + result->enc_src_idx = vpu_read_reg(inst->dev, W5_RET_ENC_USED_SRC_IDX); + p_enc_info->stream_wr_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_WR_PTR); + p_enc_info->stream_rd_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR); + + result->bitstream_buffer = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR); + result->rd_ptr = p_enc_info->stream_rd_ptr; + result->wr_ptr = p_enc_info->stream_wr_ptr; + + /*result for header only(no vcl) encoding */ + if (result->recon_frame_index == RECON_IDX_FLAG_HEADER_ONLY) + result->bitstream_size = result->enc_pic_byte; + else if (result->recon_frame_index < 0) + result->bitstream_size = 0; + else + result->bitstream_size = result->enc_pic_byte; + + result->enc_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_ENC_HOST_CMD_TICK); + result->enc_encode_end_tick = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_END_TICK); + + if (!p_enc_info->first_cycle_check) { + result->frame_cycle = (result->enc_encode_end_tick - result->enc_host_cmd_tick) * + p_enc_info->cycle_per_tick; + p_enc_info->first_cycle_check = true; + } else { + result->frame_cycle = + (result->enc_encode_end_tick - vpu_dev->last_performance_cycles) * + p_enc_info->cycle_per_tick; + if (vpu_dev->last_performance_cycles < result->enc_host_cmd_tick) + result->frame_cycle = (result->enc_encode_end_tick - + result->enc_host_cmd_tick) * p_enc_info->cycle_per_tick; + } + vpu_dev->last_performance_cycles = result->enc_encode_end_tick; + + return 0; +} + +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res) +{ + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res); +} + +static bool wave5_vpu_enc_check_common_param_valid(struct vpu_instance *inst, + struct enc_open_param *open_param) +{ + bool low_delay = true; + struct enc_wave_param *param = &open_param->wave_param; + struct vpu_device *vpu_dev = inst->dev; + struct device *dev = vpu_dev->dev; + u32 num_ctu_row = (open_param->pic_height + 64 - 1) / 64; + u32 num_ctu_col = (open_param->pic_width + 64 - 1) / 64; + u32 ctu_sz = num_ctu_col * num_ctu_row; + + if (inst->std == W_HEVC_ENC && low_delay && + param->decoding_refresh_type == DEC_REFRESH_TYPE_CRA) { + dev_warn(dev, + "dec_refresh_type(CRA) shouldn't be used together with low delay GOP\n"); + dev_warn(dev, "Suggested configuration parameter: decoding refresh type (IDR)\n"); + param->decoding_refresh_type = 2; + } + + if (param->wpp_enable && param->independ_slice_mode) { + unsigned int num_ctb_in_width = ALIGN(open_param->pic_width, 64) >> 6; + + if (param->independ_slice_mode_arg % num_ctb_in_width) { + dev_err(dev, "independ_slice_mode_arg %u must be a multiple of %u\n", + param->independ_slice_mode_arg, num_ctb_in_width); + return false; + } + } + + /* multi-slice & wpp */ + if (param->wpp_enable && param->depend_slice_mode) { + dev_err(dev, "wpp_enable && depend_slice_mode cannot be used simultaneously\n"); + return false; + } + + if (!param->independ_slice_mode && param->depend_slice_mode) { + dev_err(dev, "depend_slice_mode requires independ_slice_mode\n"); + return false; + } else if (param->independ_slice_mode && + param->depend_slice_mode == DEPEND_SLICE_MODE_RECOMMENDED && + param->independ_slice_mode_arg < param->depend_slice_mode_arg) { + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than %u\n", + param->independ_slice_mode_arg, param->depend_slice_mode_arg); + return false; + } + + if (param->independ_slice_mode && param->independ_slice_mode_arg > 65535) { + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than 65535\n", + param->independ_slice_mode_arg); + return false; + } + + if (param->depend_slice_mode && param->depend_slice_mode_arg > 65535) { + dev_err(dev, "depend_slice_mode_arg: %u must be smaller than 65535\n", + param->depend_slice_mode_arg); + return false; + } + + if (param->conf_win_top % 2) { + dev_err(dev, "conf_win_top: %u, must be a multiple of 2\n", param->conf_win_top); + return false; + } + + if (param->conf_win_bot % 2) { + dev_err(dev, "conf_win_bot: %u, must be a multiple of 2\n", param->conf_win_bot); + return false; + } + + if (param->conf_win_left % 2) { + dev_err(dev, "conf_win_left: %u, must be a multiple of 2\n", param->conf_win_left); + return false; + } + + if (param->conf_win_right % 2) { + dev_err(dev, "conf_win_right: %u, Must be a multiple of 2\n", + param->conf_win_right); + return false; + } + + if (param->lossless_enable && open_param->rc_enable) { + dev_err(dev, "option rate_control cannot be used with lossless_coding\n"); + return false; + } + + if (param->lossless_enable && !param->skip_intra_trans) { + dev_err(dev, "option intra_trans_skip must be enabled with lossless_coding\n"); + return false; + } + + /* intra refresh */ + if (param->intra_refresh_mode && param->intra_refresh_arg == 0) { + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u must be > 0\n", + param->intra_refresh_mode, param->intra_refresh_arg); + return false; + } + switch (param->intra_refresh_mode) { + case REFRESH_MODE_CTU_ROWS: + if (param->intra_mb_refresh_arg > num_ctu_row) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTU_COLUMNS: + if (param->intra_refresh_arg > num_ctu_col) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTU_STEP_SIZE: + if (param->intra_refresh_arg > ctu_sz) + goto invalid_refresh_argument; + break; + case REFRESH_MODE_CTUS: + if (param->intra_refresh_arg > ctu_sz) + goto invalid_refresh_argument; + if (param->lossless_enable) { + dev_err(dev, "mode: %u cannot be used lossless_enable", + param->intra_refresh_mode); + return false; + } + }; + return true; + +invalid_refresh_argument: + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u > W(%u)xH(%u)\n", + param->intra_refresh_mode, param->intra_refresh_arg, + num_ctu_row, num_ctu_col); + return false; +} + +static bool wave5_vpu_enc_check_param_valid(struct vpu_device *vpu_dev, + struct enc_open_param *open_param) +{ + struct enc_wave_param *param = &open_param->wave_param; + + if (open_param->rc_enable) { + if (param->min_qp_i > param->max_qp_i || param->min_qp_p > param->max_qp_p || + param->min_qp_b > param->max_qp_b) { + dev_err(vpu_dev->dev, "Configuration failed because min_qp is greater than max_qp\n"); + dev_err(vpu_dev->dev, "Suggested configuration parameters: min_qp = max_qp\n"); + return false; + } + + if (open_param->bit_rate <= (int)open_param->frame_rate_info) { + dev_err(vpu_dev->dev, + "enc_bit_rate: %u must be greater than the frame_rate: %u\n", + open_param->bit_rate, (int)open_param->frame_rate_info); + return false; + } + } + + return true; +} + +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param) +{ + u32 pic_width; + u32 pic_height; + s32 product_id = inst->dev->product; + struct vpu_attr *p_attr = &inst->dev->attr; + struct enc_wave_param *param; + + if (!open_param) + return -EINVAL; + + param = &open_param->wave_param; + pic_width = open_param->pic_width; + pic_height = open_param->pic_height; + + if (inst->id >= MAX_NUM_INSTANCE) { + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n", + inst->id, MAX_NUM_INSTANCE); + return -EOPNOTSUPP; + } + + if (inst->std != W_HEVC_ENC && + !(inst->std == W_AVC_ENC && product_id == PRODUCT_ID_521)) { + dev_err(inst->dev->dev, "Unsupported encoder-codec & product combination\n"); + return -EOPNOTSUPP; + } + + if (param->internal_bit_depth == 10) { + if (inst->std == W_HEVC_ENC && !p_attr->support_hevc10bit_enc) { + dev_err(inst->dev->dev, + "Flag support_hevc10bit_enc must be set to encode 10bit HEVC\n"); + return -EOPNOTSUPP; + } else if (inst->std == W_AVC_ENC && !p_attr->support_avc10bit_enc) { + dev_err(inst->dev->dev, + "Flag support_avc10bit_enc must be set to encode 10bit AVC\n"); + return -EOPNOTSUPP; + } + } + + if (!open_param->frame_rate_info) { + dev_err(inst->dev->dev, "No frame rate information.\n"); + return -EINVAL; + } + + if (open_param->bit_rate > MAX_BIT_RATE) { + dev_err(inst->dev->dev, "Invalid encoding bit-rate: %u (valid: 0-%u)\n", + open_param->bit_rate, MAX_BIT_RATE); + return -EINVAL; + } + + if (pic_width < W5_MIN_ENC_PIC_WIDTH || pic_width > W5_MAX_ENC_PIC_WIDTH || + pic_height < W5_MIN_ENC_PIC_HEIGHT || pic_height > W5_MAX_ENC_PIC_HEIGHT) { + dev_err(inst->dev->dev, "Invalid encoding dimension: %ux%u\n", + pic_width, pic_height); + return -EINVAL; + } + + if (param->profile) { + if (inst->std == W_HEVC_ENC) { + if ((param->profile != HEVC_PROFILE_MAIN || + (param->profile == HEVC_PROFILE_MAIN && + param->internal_bit_depth > 8)) && + (param->profile != HEVC_PROFILE_MAIN10 || + (param->profile == HEVC_PROFILE_MAIN10 && + param->internal_bit_depth < 10)) && + param->profile != HEVC_PROFILE_STILLPICTURE) { + dev_err(inst->dev->dev, + "Invalid HEVC encoding profile: %u (bit-depth: %u)\n", + param->profile, param->internal_bit_depth); + return -EINVAL; + } + } else if (inst->std == W_AVC_ENC) { + if ((param->internal_bit_depth > 8 && + param->profile != H264_PROFILE_HIGH10)) { + dev_err(inst->dev->dev, + "Invalid AVC encoding profile: %u (bit-depth: %u)\n", + param->profile, param->internal_bit_depth); + return -EINVAL; + } + } + } + + if (param->decoding_refresh_type > DEC_REFRESH_TYPE_IDR) { + dev_err(inst->dev->dev, "Invalid decoding refresh type: %u (valid: 0-2)\n", + param->decoding_refresh_type); + return -EINVAL; + } + + if (param->intra_refresh_mode > REFRESH_MODE_CTUS) { + dev_err(inst->dev->dev, "Invalid intra refresh mode: %d (valid: 0-4)\n", + param->intra_refresh_mode); + return -EINVAL; + } + + if (inst->std == W_HEVC_ENC && param->independ_slice_mode && + param->depend_slice_mode > DEPEND_SLICE_MODE_BOOST) { + dev_err(inst->dev->dev, + "Can't combine slice modes: independent and fast dependent for HEVC\n"); + return -EINVAL; + } + + if (!param->disable_deblk) { + if (param->beta_offset_div2 < -6 || param->beta_offset_div2 > 6) { + dev_err(inst->dev->dev, "Invalid beta offset: %d (valid: -6-6)\n", + param->beta_offset_div2); + return -EINVAL; + } + + if (param->tc_offset_div2 < -6 || param->tc_offset_div2 > 6) { + dev_err(inst->dev->dev, "Invalid tc offset: %d (valid: -6-6)\n", + param->tc_offset_div2); + return -EINVAL; + } + } + + if (param->intra_qp > MAX_INTRA_QP) { + dev_err(inst->dev->dev, + "Invalid intra quantization parameter: %u (valid: 0-%u)\n", + param->intra_qp, MAX_INTRA_QP); + return -EINVAL; + } + + if (open_param->rc_enable) { + if (param->min_qp_i > MAX_INTRA_QP || param->max_qp_i > MAX_INTRA_QP || + param->min_qp_p > MAX_INTRA_QP || param->max_qp_p > MAX_INTRA_QP || + param->min_qp_b > MAX_INTRA_QP || param->max_qp_b > MAX_INTRA_QP) { + dev_err(inst->dev->dev, + "Invalid quantization parameter min/max values: " + "I: %u-%u, P: %u-%u, B: %u-%u (valid for each: 0-%u)\n", + param->min_qp_i, param->max_qp_i, param->min_qp_p, param->max_qp_p, + param->min_qp_b, param->max_qp_b, MAX_INTRA_QP); + return -EINVAL; + } + + if (param->hvs_qp_enable && param->hvs_max_delta_qp > MAX_HVS_MAX_DELTA_QP) { + dev_err(inst->dev->dev, + "Invalid HVS max delta quantization parameter: %u (valid: 0-%u)\n", + param->hvs_max_delta_qp, MAX_HVS_MAX_DELTA_QP); + return -EINVAL; + } + + if (open_param->vbv_buffer_size < MIN_VBV_BUFFER_SIZE || + open_param->vbv_buffer_size > MAX_VBV_BUFFER_SIZE) { + dev_err(inst->dev->dev, "VBV buffer size: %u (valid: %u-%u)\n", + open_param->vbv_buffer_size, MIN_VBV_BUFFER_SIZE, + MAX_VBV_BUFFER_SIZE); + return -EINVAL; + } + } + + if (!wave5_vpu_enc_check_common_param_valid(inst, open_param)) + return -EINVAL; + + if (!wave5_vpu_enc_check_param_valid(inst->dev, open_param)) + return -EINVAL; + + if (param->chroma_cb_qp_offset < -12 || param->chroma_cb_qp_offset > 12) { + dev_err(inst->dev->dev, + "Invalid chroma Cb quantization parameter offset: %d (valid: -12-12)\n", + param->chroma_cb_qp_offset); + return -EINVAL; + } + + if (param->chroma_cr_qp_offset < -12 || param->chroma_cr_qp_offset > 12) { + dev_err(inst->dev->dev, + "Invalid chroma Cr quantization parameter offset: %d (valid: -12-12)\n", + param->chroma_cr_qp_offset); + return -EINVAL; + } + + if (param->intra_refresh_mode == REFRESH_MODE_CTU_STEP_SIZE && !param->intra_refresh_arg) { + dev_err(inst->dev->dev, + "Intra refresh mode CTU step-size requires an argument\n"); + return -EINVAL; + } + + if (inst->std == W_HEVC_ENC) { + if (param->nr_intra_weight_y > MAX_INTRA_WEIGHT || + param->nr_intra_weight_cb > MAX_INTRA_WEIGHT || + param->nr_intra_weight_cr > MAX_INTRA_WEIGHT) { + dev_err(inst->dev->dev, + "Invalid intra weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n", + param->nr_intra_weight_y, param->nr_intra_weight_cb, + param->nr_intra_weight_cr, MAX_INTRA_WEIGHT); + return -EINVAL; + } + + if (param->nr_inter_weight_y > MAX_INTER_WEIGHT || + param->nr_inter_weight_cb > MAX_INTER_WEIGHT || + param->nr_inter_weight_cr > MAX_INTER_WEIGHT) { + dev_err(inst->dev->dev, + "Invalid inter weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n", + param->nr_inter_weight_y, param->nr_inter_weight_cb, + param->nr_inter_weight_cr, MAX_INTER_WEIGHT); + return -EINVAL; + } + } + + return 0; +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h new file mode 100644 index 000000000..a15c6b2c3 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h @@ -0,0 +1,732 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - wave5 register definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE5_REGISTER_DEFINE_H__ +#define __WAVE5_REGISTER_DEFINE_H__ + +enum W5_VPU_COMMAND { + W5_INIT_VPU = 0x0001, + W5_WAKEUP_VPU = 0x0002, + W5_SLEEP_VPU = 0x0004, + W5_CREATE_INSTANCE = 0x0008, /* queuing command */ + W5_FLUSH_INSTANCE = 0x0010, + W5_DESTROY_INSTANCE = 0x0020, /* queuing command */ + W5_INIT_SEQ = 0x0040, /* queuing command */ + W5_SET_FB = 0x0080, + W5_DEC_ENC_PIC = 0x0100, /* queuing command */ + W5_ENC_SET_PARAM = 0x0200, /* queuing command */ + W5_QUERY = 0x4000, + W5_UPDATE_BS = 0x8000, + W5_MAX_VPU_COMD = 0x10000, +}; + +enum query_opt { + GET_VPU_INFO = 0, + SET_WRITE_PROT = 1, + GET_RESULT = 2, + UPDATE_DISP_FLAG = 3, + GET_BW_REPORT = 4, + GET_BS_RD_PTR = 5, /* for decoder */ + GET_BS_WR_PTR = 6, /* for encoder */ + GET_SRC_BUF_FLAG = 7, /* for encoder */ + SET_BS_RD_PTR = 8, /* for decoder */ + GET_DEBUG_INFO = 0x61, +}; + +#define W5_REG_BASE 0x00000000 +#define W5_CMD_REG_BASE 0x00000100 +#define W5_CMD_REG_END 0x00000200 + +/* + * COMMON + * + * ---- + * + * Power on configuration + * PO_DEBUG_MODE [0] 1 - power on with debug mode + * USE_PO_CONF [3] 1 - use power-on-configuration + */ +#define W5_PO_CONF (W5_REG_BASE + 0x0000) +#define W5_VCPU_CUR_PC (W5_REG_BASE + 0x0004) +#define W5_VCPU_CUR_LR (W5_REG_BASE + 0x0008) +#define W5_VPU_PDBG_STEP_MASK_V (W5_REG_BASE + 0x000C) +#define W5_VPU_PDBG_CTRL (W5_REG_BASE + 0x0010) /* v_cpu debugger ctrl register */ +#define W5_VPU_PDBG_IDX_REG (W5_REG_BASE + 0x0014) /* v_cpu debugger index register */ +#define W5_VPU_PDBG_WDATA_REG (W5_REG_BASE + 0x0018) /* v_cpu debugger write data reg */ +#define W5_VPU_PDBG_RDATA_REG (W5_REG_BASE + 0x001C) /* v_cpu debugger read data reg */ + +#define W5_VPU_FIO_CTRL_ADDR (W5_REG_BASE + 0x0020) +#define W5_VPU_FIO_DATA (W5_REG_BASE + 0x0024) +#define W5_VPU_VINT_REASON_USR (W5_REG_BASE + 0x0030) +#define W5_VPU_VINT_REASON_CLR (W5_REG_BASE + 0x0034) +#define W5_VPU_HOST_INT_REQ (W5_REG_BASE + 0x0038) +#define W5_VPU_VINT_CLEAR (W5_REG_BASE + 0x003C) +#define W5_VPU_HINT_CLEAR (W5_REG_BASE + 0x0040) +#define W5_VPU_VPU_INT_STS (W5_REG_BASE + 0x0044) +#define W5_VPU_VINT_ENABLE (W5_REG_BASE + 0x0048) +#define W5_VPU_VINT_REASON (W5_REG_BASE + 0x004C) +#define W5_VPU_RESET_REQ (W5_REG_BASE + 0x0050) +#define W5_RST_BLOCK_CCLK(_core) BIT((_core)) +#define W5_RST_BLOCK_CCLK_ALL (0xff) +#define W5_RST_BLOCK_BCLK(_core) (0x100 << (_core)) +#define W5_RST_BLOCK_BCLK_ALL (0xff00) +#define W5_RST_BLOCK_ACLK(_core) (0x10000 << (_core)) +#define W5_RST_BLOCK_ACLK_ALL (0xff0000) +#define W5_RST_BLOCK_VCPU_ALL (0x3f000000) +#define W5_RST_BLOCK_ALL (0x3fffffff) +#define W5_VPU_RESET_STATUS (W5_REG_BASE + 0x0054) + +#define W5_VCPU_RESTART (W5_REG_BASE + 0x0058) +#define W5_VPU_CLK_MASK (W5_REG_BASE + 0x005C) + +/* REMAP_CTRL + * PAGE SIZE: [8:0] 0x001 - 4K + * 0x002 - 8K + * 0x004 - 16K + * ... + * 0x100 - 1M + * REGION ATTR1 [10] 0 - normal + * 1 - make bus error for the region + * REGION ATTR2 [11] 0 - normal + * 1 - bypass region + * REMAP INDEX [15:12] - 0 ~ 3 + * ENDIAN [19:16] - NOTE: Currently not supported in this driver + * AXI-ID [23:20] - upper AXI-ID + * BUS_ERROR [29] 0 - bypass + * 1 - make BUS_ERROR for unmapped region + * BYPASS_ALL [30] 1 - bypass all + * ENABLE [31] 1 - update control register[30:16] + */ +#define W5_VPU_REMAP_CTRL (W5_REG_BASE + 0x0060) +#define W5_VPU_REMAP_VADDR (W5_REG_BASE + 0x0064) +#define W5_VPU_REMAP_PADDR (W5_REG_BASE + 0x0068) +#define W5_VPU_REMAP_CORE_START (W5_REG_BASE + 0x006C) +#define W5_VPU_BUSY_STATUS (W5_REG_BASE + 0x0070) +#define W5_VPU_HALT_STATUS (W5_REG_BASE + 0x0074) +#define W5_VPU_VCPU_STATUS (W5_REG_BASE + 0x0078) +#define W5_VPU_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0094) +/* + * assign vpu_config0 = {conf_map_converter_reg, // [31] + * conf_map_converter_sig, // [30] + * 8'd0, // [29:22] + * conf_std_switch_en, // [21] + * conf_bg_detect, // [20] + * conf_3dnr_en, // [19] + * conf_one_axi_en, // [18] + * conf_sec_axi_en, // [17] + * conf_bus_info, // [16] + * conf_afbc_en, // [15] + * conf_afbc_version_id, // [14:12] + * conf_fbc_en, // [11] + * conf_fbc_version_id, // [10:08] + * conf_scaler_en, // [07] + * conf_scaler_version_id, // [06:04] + * conf_bwb_en, // [03] + * 3'd0}; // [02:00] + */ +#define W5_VPU_RET_VPU_CONFIG0 (W5_REG_BASE + 0x0098) +/* + * assign vpu_config1 = {4'd0, // [31:28] + * conf_perf_timer_en, // [27] + * conf_multi_core_en, // [26] + * conf_gcu_en, // [25] + * conf_cu_report, // [24] + * 4'd0, // [23:20] + * conf_vcore_id_3, // [19] + * conf_vcore_id_2, // [18] + * conf_vcore_id_1, // [17] + * conf_vcore_id_0, // [16] + * conf_bwb_opt, // [15] + * 7'd0, // [14:08] + * conf_cod_std_en_reserved_7, // [7] + * conf_cod_std_en_reserved_6, // [6] + * conf_cod_std_en_reserved_5, // [5] + * conf_cod_std_en_reserved_4, // [4] + * conf_cod_std_en_reserved_3, // [3] + * conf_cod_std_en_reserved_2, // [2] + * conf_cod_std_en_vp9, // [1] + * conf_cod_std_en_hevc}; // [0] + * } + */ +#define W5_VPU_RET_VPU_CONFIG1 (W5_REG_BASE + 0x009C) + +#define W5_VPU_DBG_REG0 (W5_REG_BASE + 0x00f0) +#define W5_VPU_DBG_REG1 (W5_REG_BASE + 0x00f4) +#define W5_VPU_DBG_REG2 (W5_REG_BASE + 0x00f8) +#define W5_VPU_DBG_REG3 (W5_REG_BASE + 0x00fc) + +/************************************************************************/ +/* PRODUCT INFORMATION */ +/************************************************************************/ +#define W5_PRODUCT_NAME (W5_REG_BASE + 0x1040) +#define W5_PRODUCT_NUMBER (W5_REG_BASE + 0x1044) + +/************************************************************************/ +/* DECODER/ENCODER COMMON */ +/************************************************************************/ +#define W5_COMMAND (W5_REG_BASE + 0x0100) +#define W5_COMMAND_OPTION (W5_REG_BASE + 0x0104) +#define W5_QUERY_OPTION (W5_REG_BASE + 0x0104) +#define W5_RET_SUCCESS (W5_REG_BASE + 0x0108) +#define W5_RET_FAIL_REASON (W5_REG_BASE + 0x010C) +#define W5_RET_QUEUE_FAIL_REASON (W5_REG_BASE + 0x0110) +#define W5_CMD_INSTANCE_INFO (W5_REG_BASE + 0x0110) + +#define W5_RET_QUEUE_STATUS (W5_REG_BASE + 0x01E0) +#define W5_RET_BS_EMPTY_INST (W5_REG_BASE + 0x01E4) +#define W5_RET_QUEUE_CMD_DONE_INST (W5_REG_BASE + 0x01E8) +#define W5_RET_STAGE0_INSTANCE_INFO (W5_REG_BASE + 0x01EC) +#define W5_RET_STAGE1_INSTANCE_INFO (W5_REG_BASE + 0x01F0) +#define W5_RET_STAGE2_INSTANCE_INFO (W5_REG_BASE + 0x01F4) + +#define W5_RET_SEQ_DONE_INSTANCE_INFO (W5_REG_BASE + 0x01FC) + +#define W5_BS_OPTION (W5_REG_BASE + 0x0120) + +/* return info when QUERY (GET_RESULT) for en/decoder */ +#define W5_RET_VLC_BUF_SIZE (W5_REG_BASE + 0x01B0) +/* return info when QUERY (GET_RESULT) for en/decoder */ +#define W5_RET_PARAM_BUF_SIZE (W5_REG_BASE + 0x01B4) + +/* set when SET_FB for en/decoder */ +#define W5_CMD_SET_FB_ADDR_TASK_BUF (W5_REG_BASE + 0x01D4) +#define W5_CMD_SET_FB_TASK_BUF_SIZE (W5_REG_BASE + 0x01D8) +/************************************************************************/ +/* INIT_VPU - COMMON */ +/************************************************************************/ +/* note: W5_ADDR_CODE_BASE should be aligned to 4KB */ +#define W5_ADDR_CODE_BASE (W5_REG_BASE + 0x0110) +#define W5_CODE_SIZE (W5_REG_BASE + 0x0114) +#define W5_CODE_PARAM (W5_REG_BASE + 0x0118) +#define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C) +#define W5_TEMP_SIZE (W5_REG_BASE + 0x0120) +#define W5_HW_OPTION (W5_REG_BASE + 0x012C) +#define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180) + +/************************************************************************/ +/* CREATE_INSTANCE - COMMON */ +/************************************************************************/ +#define W5_ADDR_WORK_BASE (W5_REG_BASE + 0x0114) +#define W5_WORK_SIZE (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_BS_START_ADDR (W5_REG_BASE + 0x011C) +#define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120) +#define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124) +#define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130) +#define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134) +#define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138) +#define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C) +#define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140) + +/************************************************************************/ +/* DECODER - INIT_SEQ */ +/************************************************************************/ +#define W5_BS_RD_PTR (W5_REG_BASE + 0x0118) +#define W5_BS_WR_PTR (W5_REG_BASE + 0x011C) +/************************************************************************/ +/* SET_FRAME_BUF */ +/************************************************************************/ +/* SET_FB_OPTION 0x00 REGISTER FRAMEBUFFERS + * 0x01 UPDATE FRAMEBUFFER, just one framebuffer(linear, fbc and mvcol) + */ +#define W5_SFB_OPTION (W5_REG_BASE + 0x0104) +#define W5_COMMON_PIC_INFO (W5_REG_BASE + 0x0118) +#define W5_PIC_SIZE (W5_REG_BASE + 0x011C) +#define W5_SET_FB_NUM (W5_REG_BASE + 0x0120) +#define W5_EXTRA_PIC_INFO (W5_REG_BASE + 0x0124) + +#define W5_ADDR_LUMA_BASE0 (W5_REG_BASE + 0x0134) +#define W5_ADDR_CB_BASE0 (W5_REG_BASE + 0x0138) +#define W5_ADDR_CR_BASE0 (W5_REG_BASE + 0x013C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET0 (W5_REG_BASE + 0x013C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET0 (W5_REG_BASE + 0x0140) +#define W5_ADDR_LUMA_BASE1 (W5_REG_BASE + 0x0144) +#define W5_ADDR_CB_ADDR1 (W5_REG_BASE + 0x0148) +#define W5_ADDR_CR_ADDR1 (W5_REG_BASE + 0x014C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET1 (W5_REG_BASE + 0x014C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET1 (W5_REG_BASE + 0x0150) +#define W5_ADDR_LUMA_BASE2 (W5_REG_BASE + 0x0154) +#define W5_ADDR_CB_ADDR2 (W5_REG_BASE + 0x0158) +#define W5_ADDR_CR_ADDR2 (W5_REG_BASE + 0x015C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET2 (W5_REG_BASE + 0x015C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET2 (W5_REG_BASE + 0x0160) +#define W5_ADDR_LUMA_BASE3 (W5_REG_BASE + 0x0164) +#define W5_ADDR_CB_ADDR3 (W5_REG_BASE + 0x0168) +#define W5_ADDR_CR_ADDR3 (W5_REG_BASE + 0x016C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET3 (W5_REG_BASE + 0x016C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET3 (W5_REG_BASE + 0x0170) +#define W5_ADDR_LUMA_BASE4 (W5_REG_BASE + 0x0174) +#define W5_ADDR_CB_ADDR4 (W5_REG_BASE + 0x0178) +#define W5_ADDR_CR_ADDR4 (W5_REG_BASE + 0x017C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET4 (W5_REG_BASE + 0x017C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET4 (W5_REG_BASE + 0x0180) +#define W5_ADDR_LUMA_BASE5 (W5_REG_BASE + 0x0184) +#define W5_ADDR_CB_ADDR5 (W5_REG_BASE + 0x0188) +#define W5_ADDR_CR_ADDR5 (W5_REG_BASE + 0x018C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET5 (W5_REG_BASE + 0x018C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET5 (W5_REG_BASE + 0x0190) +#define W5_ADDR_LUMA_BASE6 (W5_REG_BASE + 0x0194) +#define W5_ADDR_CB_ADDR6 (W5_REG_BASE + 0x0198) +#define W5_ADDR_CR_ADDR6 (W5_REG_BASE + 0x019C) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET6 (W5_REG_BASE + 0x019C) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET6 (W5_REG_BASE + 0x01A0) +#define W5_ADDR_LUMA_BASE7 (W5_REG_BASE + 0x01A4) +#define W5_ADDR_CB_ADDR7 (W5_REG_BASE + 0x01A8) +#define W5_ADDR_CR_ADDR7 (W5_REG_BASE + 0x01AC) +/* compression offset table for luma */ +#define W5_ADDR_FBC_Y_OFFSET7 (W5_REG_BASE + 0x01AC) +/* compression offset table for chroma */ +#define W5_ADDR_FBC_C_OFFSET7 (W5_REG_BASE + 0x01B0) +#define W5_ADDR_MV_COL0 (W5_REG_BASE + 0x01B4) +#define W5_ADDR_MV_COL1 (W5_REG_BASE + 0x01B8) +#define W5_ADDR_MV_COL2 (W5_REG_BASE + 0x01BC) +#define W5_ADDR_MV_COL3 (W5_REG_BASE + 0x01C0) +#define W5_ADDR_MV_COL4 (W5_REG_BASE + 0x01C4) +#define W5_ADDR_MV_COL5 (W5_REG_BASE + 0x01C8) +#define W5_ADDR_MV_COL6 (W5_REG_BASE + 0x01CC) +#define W5_ADDR_MV_COL7 (W5_REG_BASE + 0x01D0) + +/* UPDATE_FB */ +/* CMD_SET_FB_STRIDE [15:0] - FBC framebuffer stride + * [31:15] - linear framebuffer stride + */ +#define W5_CMD_SET_FB_STRIDE (W5_REG_BASE + 0x0118) +#define W5_CMD_SET_FB_INDEX (W5_REG_BASE + 0x0120) +#define W5_ADDR_LUMA_BASE (W5_REG_BASE + 0x0134) +#define W5_ADDR_CB_BASE (W5_REG_BASE + 0x0138) +#define W5_ADDR_CR_BASE (W5_REG_BASE + 0x013C) +#define W5_ADDR_MV_COL (W5_REG_BASE + 0x0140) +#define W5_ADDR_FBC_Y_BASE (W5_REG_BASE + 0x0144) +#define W5_ADDR_FBC_C_BASE (W5_REG_BASE + 0x0148) +#define W5_ADDR_FBC_Y_OFFSET (W5_REG_BASE + 0x014C) +#define W5_ADDR_FBC_C_OFFSET (W5_REG_BASE + 0x0150) + +/************************************************************************/ +/* DECODER - DEC_PIC */ +/************************************************************************/ +#define W5_CMD_DEC_VCORE_INFO (W5_REG_BASE + 0x0194) +/* sequence change enable mask register + * CMD_SEQ_CHANGE_ENABLE_FLAG [5] profile_idc + * [16] pic_width/height_in_luma_sample + * [19] sps_max_dec_pic_buffering, max_num_reorder, max_latency_increase + */ +#define W5_CMD_SEQ_CHANGE_ENABLE_FLAG (W5_REG_BASE + 0x0128) +#define W5_CMD_DEC_USER_MASK (W5_REG_BASE + 0x012C) +#define W5_CMD_DEC_TEMPORAL_ID_PLUS1 (W5_REG_BASE + 0x0130) +#define W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1 (W5_REG_BASE + 0x0134) +#define W5_USE_SEC_AXI (W5_REG_BASE + 0x0150) + +/************************************************************************/ +/* DECODER - QUERY : GET_VPU_INFO */ +/************************************************************************/ +#define W5_RET_FW_VERSION (W5_REG_BASE + 0x0118) +#define W5_RET_PRODUCT_NAME (W5_REG_BASE + 0x011C) +#define W5_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0120) +#define W5_RET_STD_DEF0 (W5_REG_BASE + 0x0124) +#define W5_RET_STD_DEF1 (W5_REG_BASE + 0x0128) +#define W5_RET_CONF_FEATURE (W5_REG_BASE + 0x012C) +#define W5_RET_CONF_DATE (W5_REG_BASE + 0x0130) +#define W5_RET_CONF_REVISION (W5_REG_BASE + 0x0134) +#define W5_RET_CONF_TYPE (W5_REG_BASE + 0x0138) +#define W5_RET_PRODUCT_ID (W5_REG_BASE + 0x013C) +#define W5_RET_CUSTOMER_ID (W5_REG_BASE + 0x0140) + +/************************************************************************/ +/* DECODER - QUERY : GET_RESULT */ +/************************************************************************/ +#define W5_CMD_DEC_ADDR_REPORT_BASE (W5_REG_BASE + 0x0114) +#define W5_CMD_DEC_REPORT_SIZE (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_REPORT_PARAM (W5_REG_BASE + 0x011C) + +#define W5_RET_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C) +#define W5_RET_DEC_SEQ_PARAM (W5_REG_BASE + 0x0120) +#define W5_RET_DEC_COLOR_SAMPLE_INFO (W5_REG_BASE + 0x0124) +#define W5_RET_DEC_ASPECT_RATIO (W5_REG_BASE + 0x0128) +#define W5_RET_DEC_BIT_RATE (W5_REG_BASE + 0x012C) +#define W5_RET_DEC_FRAME_RATE_NR (W5_REG_BASE + 0x0130) +#define W5_RET_DEC_FRAME_RATE_DR (W5_REG_BASE + 0x0134) +#define W5_RET_DEC_NUM_REQUIRED_FB (W5_REG_BASE + 0x0138) +#define W5_RET_DEC_NUM_REORDER_DELAY (W5_REG_BASE + 0x013C) +#define W5_RET_DEC_SUB_LAYER_INFO (W5_REG_BASE + 0x0140) +#define W5_RET_DEC_NOTIFICATION (W5_REG_BASE + 0x0144) +/* + * USER_DATA_FLAGS for HEVC/H264 only. + * Bits: + * [1] - User data buffer full boolean + * [2] - VUI parameter flag + * [4] - Pic_timing SEI flag + * [5] - 1st user_data_registed_itu_t_t35 prefix SEI flag + * [6] - user_data_unregistered prefix SEI flag + * [7] - 1st user_data_registed_itu_t_t35 suffix SEI flag + * [8] - user_data_unregistered suffix SEI flag + * [10]- mastering_display_color_volume prefix SEI flag + * [11]- chroma_resampling_display_color_volume prefix SEI flag + * [12]- knee_function_info SEI flag + * [13]- tone_mapping_info prefix SEI flag + * [14]- film_grain_characteristics_info prefix SEI flag + * [15]- content_light_level_info prefix SEI flag + * [16]- color_remapping_info prefix SEI flag + * [28]- 2nd user_data_registed_itu_t_t35 prefix SEI flag + * [29]- 3rd user_data_registed_itu_t_t35 prefix SEI flag + * [30]- 2nd user_data_registed_itu_t_t35 suffix SEI flag + * [31]- 3rd user_data_registed_itu_t_t35 suffix SEI flag + */ +#define W5_RET_DEC_USERDATA_IDC (W5_REG_BASE + 0x0148) +#define W5_RET_DEC_PIC_SIZE (W5_REG_BASE + 0x014C) +#define W5_RET_DEC_CROP_TOP_BOTTOM (W5_REG_BASE + 0x0150) +#define W5_RET_DEC_CROP_LEFT_RIGHT (W5_REG_BASE + 0x0154) +/* + * #define W5_RET_DEC_AU_START_POS (W5_REG_BASE + 0x0158) + * => Access unit (AU) Bitstream start position + * #define W5_RET_DEC_AU_END_POS (W5_REG_BASE + 0x015C) + * => Access unit (AU) Bitstream end position + */ + +/* + * Decoded picture type: + * reg_val & 0x7 => picture type + * (reg_val >> 4) & 0x3f => VCL NAL unit type + * (reg_val >> 31) & 0x1 => output_flag + * 16 << ((reg_val >> 10) & 0x3) => ctu_size + */ +#define W5_RET_DEC_PIC_TYPE (W5_REG_BASE + 0x0160) +#define W5_RET_DEC_PIC_POC (W5_REG_BASE + 0x0164) +/* + * #define W5_RET_DEC_RECOVERY_POINT (W5_REG_BASE + 0x0168) + * => HEVC recovery point + * reg_val & 0xff => number of signed recovery picture order counts + * (reg_val >> 16) & 0x1 => exact match flag + * (reg_val >> 17) & 0x1 => broken link flag + * (reg_val >> 18) & 0x1 => exist flag + */ +#define W5_RET_DEC_DEBUG_INDEX (W5_REG_BASE + 0x016C) +#define W5_RET_DEC_DECODED_INDEX (W5_REG_BASE + 0x0170) +#define W5_RET_DEC_DISPLAY_INDEX (W5_REG_BASE + 0x0174) +/* + * #define W5_RET_DEC_REALLOC_INDEX (W5_REG_BASE + 0x0178) + * => display picture index in decoded picture buffer + * reg_val & 0xf => display picture index for FBC buffer (by reordering) + */ +#define W5_RET_DEC_DISP_IDC (W5_REG_BASE + 0x017C) +/* + * #define W5_RET_DEC_ERR_CTB_NUM (W5_REG_BASE + 0x0180) + * => Number of error CTUs + * reg_val >> 16 => erroneous CTUs in bitstream + * reg_val & 0xffff => total CTUs in bitstream + * + * #define W5_RET_DEC_PIC_PARAM (W5_REG_BASE + 0x01A0) + * => Bitstream sequence/picture parameter information (AV1 only) + * reg_val & 0x1 => intrabc tool enable + * (reg_val >> 1) & 0x1 => screen content tools enable + */ +#define W5_RET_DEC_HOST_CMD_TICK (W5_REG_BASE + 0x01B8) +/* + * #define W5_RET_DEC_SEEK_START_TICK (W5_REG_BASE + 0x01BC) + * #define W5_RET_DEC_SEEK_END_TICK (W5_REG_BASE + 0x01C0) + * => Start and end ticks for seeking slices of the picture + * #define W5_RET_DEC_PARSING_START_TICK (W5_REG_BASE + 0x01C4) + * #define W5_RET_DEC_PARSING_END_TICK (W5_REG_BASE + 0x01C8) + * => Start and end ticks for parsing slices of the picture + * #define W5_RET_DEC_DECODING_START_TICK (W5_REG_BASE + 0x01CC) + * => Start tick for decoding slices of the picture + */ +#define W5_RET_DEC_DECODING_ENC_TICK (W5_REG_BASE + 0x01D0) +#define W5_RET_DEC_WARN_INFO (W5_REG_BASE + 0x01D4) +#define W5_RET_DEC_ERR_INFO (W5_REG_BASE + 0x01D8) +#define W5_RET_DEC_DECODING_SUCCESS (W5_REG_BASE + 0x01DC) + +/************************************************************************/ +/* DECODER - FLUSH_INSTANCE */ +/************************************************************************/ +#define W5_CMD_FLUSH_INST_OPT (W5_REG_BASE + 0x104) + +/************************************************************************/ +/* DECODER - QUERY : UPDATE_DISP_FLAG */ +/************************************************************************/ +#define W5_CMD_DEC_SET_DISP_IDC (W5_REG_BASE + 0x0118) +#define W5_CMD_DEC_CLR_DISP_IDC (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* DECODER - QUERY : SET_BS_RD_PTR */ +/************************************************************************/ +#define W5_RET_QUERY_DEC_SET_BS_RD_PTR (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* DECODER - QUERY : GET_BS_RD_PTR */ +/************************************************************************/ +#define W5_RET_QUERY_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C) + +/************************************************************************/ +/* QUERY : GET_DEBUG_INFO */ +/************************************************************************/ +#define W5_RET_QUERY_DEBUG_PRI_REASON (W5_REG_BASE + 0x114) + +/************************************************************************/ +/* GDI register for debugging */ +/************************************************************************/ +#define W5_GDI_BASE 0x8800 +#define W5_GDI_BUS_CTRL (W5_GDI_BASE + 0x0F0) +#define W5_GDI_BUS_STATUS (W5_GDI_BASE + 0x0F4) + +#define W5_BACKBONE_BASE_VCPU 0xFE00 +#define W5_BACKBONE_BUS_CTRL_VCPU (W5_BACKBONE_BASE_VCPU + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCPU (W5_BACKBONE_BASE_VCPU + 0x014) +#define W5_BACKBONE_PROG_AXI_ID (W5_BACKBONE_BASE_VCPU + 0x00C) + +#define W5_BACKBONE_PROC_EXT_ADDR (W5_BACKBONE_BASE_VCPU + 0x0C0) +#define W5_BACKBONE_AXI_PARAM (W5_BACKBONE_BASE_VCPU + 0x0E0) + +#define W5_BACKBONE_BASE_VCORE0 0x8E00 +#define W5_BACKBONE_BUS_CTRL_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x014) + +#define W5_BACKBONE_BASE_VCORE1 0x9E00 /* for dual-core product */ +#define W5_BACKBONE_BUS_CTRL_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x010) +#define W5_BACKBONE_BUS_STATUS_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x014) + +#define W5_COMBINED_BACKBONE_BASE 0xFE00 +#define W5_COMBINED_BACKBONE_BUS_CTRL (W5_COMBINED_BACKBONE_BASE + 0x010) +#define W5_COMBINED_BACKBONE_BUS_STATUS (W5_COMBINED_BACKBONE_BASE + 0x014) + +/************************************************************************/ +/* */ +/* for ENCODER */ +/* */ +/************************************************************************/ +#define W5_RET_STAGE3_INSTANCE_INFO (W5_REG_BASE + 0x1F8) +/************************************************************************/ +/* ENCODER - CREATE_INSTANCE */ +/************************************************************************/ +/* 0x114 ~ 0x124 : defined above (CREATE_INSTANCE COMMON) */ +#define W5_CMD_ENC_VCORE_INFO (W5_REG_BASE + 0x0194) +#define W5_CMD_ENC_SRC_OPTIONS (W5_REG_BASE + 0x0128) + +/************************************************************************/ +/* ENCODER - SET_FB */ +/************************************************************************/ +#define W5_FBC_STRIDE (W5_REG_BASE + 0x128) +#define W5_ADDR_SUB_SAMPLED_FB_BASE (W5_REG_BASE + 0x12C) +#define W5_SUB_SAMPLED_ONE_FB_SIZE (W5_REG_BASE + 0x130) + +/************************************************************************/ +/* ENCODER - ENC_SET_PARAM (COMMON & CHANGE_PARAM) */ +/************************************************************************/ +#define W5_CMD_ENC_SEQ_SET_PARAM_OPTION (W5_REG_BASE + 0x104) +#define W5_CMD_ENC_SEQ_SET_PARAM_ENABLE (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_SEQ_SRC_SIZE (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN (W5_REG_BASE + 0x120) +#define W5_CMD_ENC_SEQ_SPS_PARAM (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_SEQ_PPS_PARAM (W5_REG_BASE + 0x128) +#define W5_CMD_ENC_SEQ_GOP_PARAM (W5_REG_BASE + 0x12C) +#define W5_CMD_ENC_SEQ_INTRA_PARAM (W5_REG_BASE + 0x130) +#define W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT (W5_REG_BASE + 0x134) +#define W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_SEQ_RDO_PARAM (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_SEQ_INDEPENDENT_SLICE (W5_REG_BASE + 0x140) +#define W5_CMD_ENC_SEQ_DEPENDENT_SLICE (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_SEQ_INTRA_REFRESH (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_SEQ_INPUT_SRC_PARAM (W5_REG_BASE + 0x14C) + +#define W5_CMD_ENC_SEQ_RC_FRAME_RATE (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_SEQ_RC_TARGET_RATE (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_SEQ_RC_PARAM (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_SEQ_RC_MIN_MAX_QP (W5_REG_BASE + 0x15C) +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3 (W5_REG_BASE + 0x160) +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7 (W5_REG_BASE + 0x164) +#define W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP (W5_REG_BASE + 0x168) +#define W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM (W5_REG_BASE + 0x16C) + +#define W5_CMD_ENC_SEQ_ROT_PARAM (W5_REG_BASE + 0x170) +#define W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK (W5_REG_BASE + 0x174) +#define W5_CMD_ENC_SEQ_TIME_SCALE (W5_REG_BASE + 0x178) +#define W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE (W5_REG_BASE + 0x17C) + +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU04 (W5_REG_BASE + 0x184) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU08 (W5_REG_BASE + 0x188) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU16 (W5_REG_BASE + 0x18C) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU32 (W5_REG_BASE + 0x190) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU08 (W5_REG_BASE + 0x194) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU16 (W5_REG_BASE + 0x198) +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU32 (W5_REG_BASE + 0x19C) +#define W5_CMD_ENC_SEQ_NR_PARAM (W5_REG_BASE + 0x1A0) +#define W5_CMD_ENC_SEQ_NR_WEIGHT (W5_REG_BASE + 0x1A4) +#define W5_CMD_ENC_SEQ_BG_PARAM (W5_REG_BASE + 0x1A8) +#define W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR (W5_REG_BASE + 0x1AC) +#define W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR (W5_REG_BASE + 0x1B0) +#define W5_CMD_ENC_SEQ_VUI_HRD_PARAM (W5_REG_BASE + 0x180) +#define W5_CMD_ENC_SEQ_VUI_RBSP_ADDR (W5_REG_BASE + 0x1B8) +#define W5_CMD_ENC_SEQ_HRD_RBSP_ADDR (W5_REG_BASE + 0x1BC) + +/************************************************************************/ +/* ENCODER - ENC_SET_PARAM (CUSTOM_GOP) */ +/************************************************************************/ +#define W5_CMD_ENC_CUSTOM_GOP_PARAM (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_0 (W5_REG_BASE + 0x120) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_1 (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_2 (W5_REG_BASE + 0x128) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_3 (W5_REG_BASE + 0x12C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_4 (W5_REG_BASE + 0x130) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_5 (W5_REG_BASE + 0x134) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_6 (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_7 (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_8 (W5_REG_BASE + 0x140) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_9 (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_10 (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_11 (W5_REG_BASE + 0x14C) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_12 (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_13 (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_14 (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_15 (W5_REG_BASE + 0x15C) + +/************************************************************************/ +/* ENCODER - ENC_PIC */ +/************************************************************************/ +#define W5_CMD_ENC_BS_START_ADDR (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_BS_SIZE (W5_REG_BASE + 0x11C) +#define W5_CMD_ENC_PIC_USE_SEC_AXI (W5_REG_BASE + 0x124) +#define W5_CMD_ENC_PIC_REPORT_PARAM (W5_REG_BASE + 0x128) + +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM (W5_REG_BASE + 0x138) +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR (W5_REG_BASE + 0x13C) +#define W5_CMD_ENC_PIC_SRC_PIC_IDX (W5_REG_BASE + 0x144) +#define W5_CMD_ENC_PIC_SRC_ADDR_Y (W5_REG_BASE + 0x148) +#define W5_CMD_ENC_PIC_SRC_ADDR_U (W5_REG_BASE + 0x14C) +#define W5_CMD_ENC_PIC_SRC_ADDR_V (W5_REG_BASE + 0x150) +#define W5_CMD_ENC_PIC_SRC_STRIDE (W5_REG_BASE + 0x154) +#define W5_CMD_ENC_PIC_SRC_FORMAT (W5_REG_BASE + 0x158) +#define W5_CMD_ENC_PIC_SRC_AXI_SEL (W5_REG_BASE + 0x160) +#define W5_CMD_ENC_PIC_CODE_OPTION (W5_REG_BASE + 0x164) +#define W5_CMD_ENC_PIC_PIC_PARAM (W5_REG_BASE + 0x168) +#define W5_CMD_ENC_PIC_LONGTERM_PIC (W5_REG_BASE + 0x16C) +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y (W5_REG_BASE + 0x170) +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C (W5_REG_BASE + 0x174) +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y (W5_REG_BASE + 0x178) +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C (W5_REG_BASE + 0x17C) +#define W5_CMD_ENC_PIC_CF50_Y_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x190) +#define W5_CMD_ENC_PIC_CF50_CB_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x194) +#define W5_CMD_ENC_PIC_CF50_CR_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x198) +#define W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x180) +#define W5_CMD_ENC_PIC_PREFIX_SEI_INFO (W5_REG_BASE + 0x184) +#define W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x188) +#define W5_CMD_ENC_PIC_SUFFIX_SEI_INFO (W5_REG_BASE + 0x18c) + +/************************************************************************/ +/* ENCODER - QUERY (GET_RESULT) */ +/************************************************************************/ +#define W5_RET_ENC_NUM_REQUIRED_FB (W5_REG_BASE + 0x11C) +#define W5_RET_ENC_MIN_SRC_BUF_NUM (W5_REG_BASE + 0x120) +#define W5_RET_ENC_PIC_TYPE (W5_REG_BASE + 0x124) +/* + * #define W5_RET_ENC_PIC_POC (W5_REG_BASE + 0x128) + * => picture order count value of current encoded picture + */ +#define W5_RET_ENC_PIC_IDX (W5_REG_BASE + 0x12C) +/* + * #define W5_RET_ENC_PIC_SLICE_NUM (W5_REG_BASE + 0x130) + * reg_val & 0xffff = total independent slice segment number (16 bits) + * (reg_val >> 16) & 0xffff = total dependent slice segment number (16 bits) + * + * #define W5_RET_ENC_PIC_SKIP (W5_REG_BASE + 0x134) + * reg_val & 0xfe = picture skip flag (7 bits) + * + * #define W5_RET_ENC_PIC_NUM_INTRA (W5_REG_BASE + 0x138) + * => number of intra blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_NUM_MERGE (W5_REG_BASE + 0x13C) + * => number of merge blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_NUM_SKIP (W5_REG_BASE + 0x144) + * => number of skip blocks in 8x8 (32 bits) + * + * #define W5_RET_ENC_PIC_AVG_CTU_QP (W5_REG_BASE + 0x148) + * => Average CTU QP value (32 bits) + */ +#define W5_RET_ENC_PIC_BYTE (W5_REG_BASE + 0x14C) +/* + * #define W5_RET_ENC_GOP_PIC_IDX (W5_REG_BASE + 0x150) + * => picture index in group of pictures + */ +#define W5_RET_ENC_USED_SRC_IDX (W5_REG_BASE + 0x154) +/* + * #define W5_RET_ENC_PIC_NUM (W5_REG_BASE + 0x158) + * => encoded picture number + */ +#define W5_RET_ENC_VCL_NUT (W5_REG_BASE + 0x15C) +/* + * Only for H264: + * #define W5_RET_ENC_PIC_DIST_LOW (W5_REG_BASE + 0x164) + * => lower 32 bits of the sum of squared difference between source Y picture + * and reconstructed Y picture + * #define W5_RET_ENC_PIC_DIST_HIGH (W5_REG_BASE + 0x168) + * => upper 32 bits of the sum of squared difference between source Y picture + * and reconstructed Y picture + */ +#define W5_RET_ENC_PIC_MAX_LATENCY_PICS (W5_REG_BASE + 0x16C) + +#define W5_RET_ENC_HOST_CMD_TICK (W5_REG_BASE + 0x1B8) +/* + * #define W5_RET_ENC_PREPARE_START_TICK (W5_REG_BASE + 0x1BC) + * #define W5_RET_ENC_PREPARE_END_TICK (W5_REG_BASE + 0x1C0) + * => Start and end ticks for preparing slices of the picture + * #define W5_RET_ENC_PROCESSING_START_TICK (W5_REG_BASE + 0x1C4) + * #define W5_RET_ENC_PROCESSING_END_TICK (W5_REG_BASE + 0x1C8) + * => Start and end ticks for processing slices of the picture + * #define W5_RET_ENC_ENCODING_START_TICK (W5_REG_BASE + 0x1CC) + * => Start tick for encoding slices of the picture + */ +#define W5_RET_ENC_ENCODING_END_TICK (W5_REG_BASE + 0x1D0) + +#define W5_RET_ENC_WARN_INFO (W5_REG_BASE + 0x1D4) +#define W5_RET_ENC_ERR_INFO (W5_REG_BASE + 0x1D8) +#define W5_RET_ENC_ENCODING_SUCCESS (W5_REG_BASE + 0x1DC) + +/************************************************************************/ +/* ENCODER - QUERY (GET_BS_WR_PTR) */ +/************************************************************************/ +#define W5_RET_ENC_RD_PTR (W5_REG_BASE + 0x114) +#define W5_RET_ENC_WR_PTR (W5_REG_BASE + 0x118) +#define W5_CMD_ENC_REASON_SEL (W5_REG_BASE + 0x11C) + +/************************************************************************/ +/* ENCODER - QUERY (GET_BW_REPORT) */ +/************************************************************************/ +#define RET_QUERY_BW_PRP_AXI_READ (W5_REG_BASE + 0x118) +#define RET_QUERY_BW_PRP_AXI_WRITE (W5_REG_BASE + 0x11C) +#define RET_QUERY_BW_FBD_Y_AXI_READ (W5_REG_BASE + 0x120) +#define RET_QUERY_BW_FBC_Y_AXI_WRITE (W5_REG_BASE + 0x124) +#define RET_QUERY_BW_FBD_C_AXI_READ (W5_REG_BASE + 0x128) +#define RET_QUERY_BW_FBC_C_AXI_WRITE (W5_REG_BASE + 0x12C) +#define RET_QUERY_BW_PRI_AXI_READ (W5_REG_BASE + 0x130) +#define RET_QUERY_BW_PRI_AXI_WRITE (W5_REG_BASE + 0x134) +#define RET_QUERY_BW_SEC_AXI_READ (W5_REG_BASE + 0x138) +#define RET_QUERY_BW_SEC_AXI_WRITE (W5_REG_BASE + 0x13C) +#define RET_QUERY_BW_PROC_AXI_READ (W5_REG_BASE + 0x140) +#define RET_QUERY_BW_PROC_AXI_WRITE (W5_REG_BASE + 0x144) +#define RET_QUERY_BW_BWB_AXI_WRITE (W5_REG_BASE + 0x148) +#define W5_CMD_BW_OPTION (W5_REG_BASE + 0x14C) + +/************************************************************************/ +/* ENCODER - QUERY (GET_SRC_FLAG) */ +/************************************************************************/ +#define W5_RET_RELEASED_SRC_INSTANCE (W5_REG_BASE + 0x1EC) + +#define W5_ENC_PIC_SUB_FRAME_SYNC_IF (W5_REG_BASE + 0x0300) + +#endif /* __WAVE5_REGISTER_DEFINE_H__ */ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c new file mode 100644 index 000000000..3809f70bc --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - low level access functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include +#include "wave5-vdi.h" +#include "wave5-vpu.h" +#include "wave5-regdefine.h" +#include + +static int wave5_vdi_allocate_common_memory(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + if (!vpu_dev->common_mem.vaddr) { + int ret; + + vpu_dev->common_mem.size = SIZE_COMMON; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem); + if (ret) { + dev_err(dev, "unable to allocate common buffer\n"); + return ret; + } + } + + dev_dbg(dev, "[VDI] common_mem: daddr=%pad size=%zu vaddr=0x%p\n", + &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size, vpu_dev->common_mem.vaddr); + + return 0; +} + +int wave5_vdi_init(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + int ret; + + ret = wave5_vdi_allocate_common_memory(dev); + if (ret < 0) { + dev_err(dev, "[VDI] failed to get vpu common buffer from driver\n"); + return ret; + } + + if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) { + WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code); + return -EOPNOTSUPP; + } + + /* if BIT processor is not running. */ + if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) { + int i; + + for (i = 0; i < 64; i++) + wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0); + } + + dev_dbg(dev, "[VDI] driver initialized successfully\n"); + + return 0; +} + +int wave5_vdi_release(struct device *dev) +{ + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + vpu_dev->vdb_register = NULL; + wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem); + + return 0; +} + +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data) +{ + writel(data, vpu_dev->vdb_register + addr); +} + +unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr) +{ + return readl(vpu_dev->vdb_register + addr); +} + +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + if (!vb || !vb->vaddr) { + dev_err(vpu_dev->dev, "%s: unable to clear unmapped buffer\n", __func__); + return -EINVAL; + } + + memset(vb->vaddr, 0, vb->size); + return vb->size; +} + +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset, + u8 *data, size_t len) +{ + if (!vb || !vb->vaddr) { + dev_err(vpu_dev->dev, "%s: unable to write to unmapped buffer\n", __func__); + return -EINVAL; + } + + if (offset > vb->size || len > vb->size || offset + len > vb->size) { + dev_err(vpu_dev->dev, "%s: buffer too small\n", __func__); + return -ENOSPC; + } + + memcpy(vb->vaddr + offset, data, len); + + return len; +} + +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + void *vaddr; + dma_addr_t daddr; + + if (!vb->size) { + dev_err(vpu_dev->dev, "%s: requested size==0\n", __func__); + return -EINVAL; + } + + vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + vb->vaddr = vaddr; + vb->daddr = daddr; + + return 0; +} + +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb) +{ + if (vb->size == 0) + return -EINVAL; + + if (!vb->vaddr) + dev_err(vpu_dev->dev, "%s: requested free of unmapped buffer\n", __func__); + else + dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr); + + memset(vb, 0, sizeof(*vb)); + + return 0; +} + +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count, + size_t size) +{ + struct vpu_buf vb_buf; + int i, ret = 0; + + vb_buf.size = size; + + for (i = 0; i < count; i++) { + if (array[i].size == size) + continue; + + if (array[i].size != 0) + wave5_vdi_free_dma_memory(vpu_dev, &array[i]); + + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_buf); + if (ret) + return -ENOMEM; + array[i] = vb_buf; + } + + for (i = count; i < MAX_REG_FRAME; i++) + wave5_vdi_free_dma_memory(vpu_dev, &array[i]); + + return 0; +} + +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) +{ + struct vpu_buf *vb = &vpu_dev->sram_buf; + + if (!vpu_dev->sram_pool || !vpu_dev->sram_size) + return; + + if (!vb->vaddr) { + vb->size = vpu_dev->sram_size; + vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, + &vb->daddr); + if (!vb->vaddr) + vb->size = 0; + } + + dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n", + __func__, &vb->daddr, vb->size, vb->vaddr); +} + +void wave5_vdi_free_sram(struct vpu_device *vpu_dev) +{ + struct vpu_buf *vb = &vpu_dev->sram_buf; + + if (!vb->size || !vb->vaddr) + return; + + if (vb->vaddr) + gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, + vb->size); + + memset(vb, 0, sizeof(*vb)); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.h b/drivers/media/platform/chips-media/wave5/wave5-vdi.h new file mode 100644 index 000000000..3984ef3f1 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - low level access functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef _VDI_H_ +#define _VDI_H_ + +#include "wave5-vpuconfig.h" +#include +#include +#include + +/************************************************************************/ +/* COMMON REGISTERS */ +/************************************************************************/ +#define VPU_PRODUCT_CODE_REGISTER 0x1044 + +/* system register write */ +#define vpu_write_reg(VPU_INST, ADDR, DATA) wave5_vdi_write_register(VPU_INST, ADDR, DATA) +/* system register read */ +#define vpu_read_reg(CORE, ADDR) wave5_vdi_read_register(CORE, ADDR) + +struct vpu_buf { + size_t size; + dma_addr_t daddr; + void *vaddr; +}; + +int wave5_vdi_init(struct device *dev); +int wave5_vdi_release(struct device *dev); //this function may be called only at system off. + +#endif //#ifndef _VDI_H_ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c new file mode 100644 index 000000000..ef227af72 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -0,0 +1,1932 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - decoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder" +#define VPU_DEC_DRV_NAME "wave5-dec" + +#define DEFAULT_SRC_SIZE(width, height) ({ \ + (width) * (height) / 8 * 3; \ +}) + +static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = 8192, + .min_width = 32, + .max_height = 4320, + .min_height = 32, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, + .max_width = 8192, + .min_width = 8, + .max_height = 4320, + .min_height = 8, + }, + } +}; + +/* + * Make sure that the state switch is allowed and add logging for debugging + * purposes + */ +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + break; + case VPU_INST_STATE_OPEN: + if (inst->state != VPU_INST_STATE_NONE) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_INIT_SEQ: + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_PIC_RUN: + if (inst->state != VPU_INST_STATE_INIT_SEQ) + goto invalid_state_switch; + goto valid_state_switch; + case VPU_INST_STATE_STOP: + goto valid_state_switch; + } +invalid_state_switch: + WARN(1, "Invalid state switch from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + return -EINVAL; +valid_state_switch: + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + inst->state = state; + return 0; +} + +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst) +{ + int ret; + + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0); + if (ret) { + /* + * To set the EOS flag, a command is sent to the firmware. + * That command may never return (timeout) or may report an error. + */ + dev_err(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); + return ret; + } + return 0; +} + +static bool wave5_last_src_buffer_consumed(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct vpu_src_buffer *vpu_buf; + + if (!m2m_ctx->last_src_buf) + return false; + + vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf); + return vpu_buf->consumed; +} + +static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + size_t consumed_bytes = 0; + + if (rd_ptr >= inst->last_rd_ptr) { + consumed_bytes = rd_ptr - inst->last_rd_ptr; + } else { + size_t rd_offs = rd_ptr - inst->bitstream_vbuf.daddr; + size_t last_rd_offs = inst->last_rd_ptr - inst->bitstream_vbuf.daddr; + + consumed_bytes = rd_offs + (inst->bitstream_vbuf.size - last_rd_offs); + } + + inst->last_rd_ptr = rd_ptr; + consumed_bytes += inst->remaining_consumed_bytes; + + dev_dbg(inst->dev->dev, "%s: %zu bytes of bitstream was consumed", __func__, + consumed_bytes); + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *src_buf = &buf->vb; + size_t src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + if (src_size > consumed_bytes) + break; + + dev_dbg(inst->dev->dev, "%s: removing src buffer %i", + __func__, src_buf->vb2_buf.index); + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + inst->timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + consumed_bytes -= src_size; + + /* Handle the case the last bitstream buffer has been picked */ + if (src_buf == m2m_ctx->last_src_buf) { + int ret; + + m2m_ctx->last_src_buf = NULL; + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + dev_warn(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); + break; + } + } + + inst->remaining_consumed_bytes = consumed_bytes; +} + +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width, + unsigned int height) +{ + switch (pix_mp->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2; + break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height * 2; + break; + case V4L2_PIX_FMT_YUV420M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = width * height / 4; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = width * height / 4; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = width * height / 2; + break; + case V4L2_PIX_FMT_YUV422M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = width * height / 2; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = width * height / 2; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + pix_mp->width = round_up(width, 32); + pix_mp->height = round_up(height, 16); + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = width * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = width * height; + break; + default: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height), + pix_mp->plane_fmt[0].sizeimage); + break; + } +} + +static int start_decode(struct vpu_instance *inst, u32 *fail_res) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + ret = wave5_vpu_dec_start_one_frame(inst, fail_res); + if (ret) { + struct vb2_v4l2_buffer *src_buf; + + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + switch_state(inst, VPU_INST_STATE_STOP); + + dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } + + return ret; +} + +static void flag_last_buffer_done(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *vb; + int i; + + lockdep_assert_held(&inst->state_spinlock); + + vb = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!vb) { + m2m_ctx->is_draining = true; + m2m_ctx->next_buf_last = true; + return; + } + + for (i = 0; i < vb->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&vb->vb2_buf, i, 0); + vb->field = V4L2_FIELD_NONE; + + v4l2_m2m_last_buffer_done(m2m_ctx, vb); +} + +static void send_eos_event(struct vpu_instance *inst) +{ + static const struct v4l2_event vpu_event_eos = { + .type = V4L2_EVENT_EOS + }; + + lockdep_assert_held(&inst->state_spinlock); + + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos); + inst->eos = false; +} + +static int handle_dynamic_resolution_change(struct vpu_instance *inst) +{ + struct v4l2_fh *fh = &inst->v4l2_fh; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + static const struct v4l2_event vpu_event_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info; + + lockdep_assert_held(&inst->state_spinlock); + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad", __func__, &initial_info->rd_ptr); + + dev_dbg(inst->dev->dev, "%s: width: %u height: %u profile: %u | minbuffer: %u\n", + __func__, initial_info->pic_width, initial_info->pic_height, + initial_info->profile, initial_info->min_frame_buffer_count); + + inst->needs_reallocation = true; + inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1; + if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) { + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count); + } + + if (p_dec_info->initial_info_obtained) { + inst->conf_win.left = initial_info->pic_crop_rect.left; + inst->conf_win.top = initial_info->pic_crop_rect.top; + inst->conf_win.width = initial_info->pic_width - + initial_info->pic_crop_rect.left - initial_info->pic_crop_rect.right; + inst->conf_win.height = initial_info->pic_height - + initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom; + + wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width, + initial_info->pic_height); + wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width, + initial_info->pic_height); + } + + v4l2_event_queue_fh(fh, &vpu_event_src_ch); + + return 0; +} + +static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct dec_output_info dec_info; + int ret; + struct vb2_v4l2_buffer *dec_buf = NULL; + struct vb2_v4l2_buffer *disp_buf = NULL; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + struct queue_status_info q_status; + + dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__); + + ret = wave5_vpu_dec_get_output_info(inst, &dec_info); + if (ret) { + dev_warn(inst->dev->dev, "%s: could not get output info.", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &dec_info.rd_ptr, + &dec_info.wr_ptr); + wave5_handle_src_buffer(inst, dec_info.rd_ptr); + + dev_dbg(inst->dev->dev, "%s: dec_info dec_idx %i disp_idx %i", __func__, + dec_info.index_frame_decoded, dec_info.index_frame_display); + + if (!vb2_is_streaming(dst_vq)) { + dev_dbg(inst->dev->dev, "%s: capture is not streaming..", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + /* Remove decoded buffer from the ready queue now that it has been + * decoded. + */ + if (dec_info.index_frame_decoded >= 0) { + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, + dec_info.index_frame_decoded); + if (vb) { + dec_buf = to_vb2_v4l2_buffer(vb); + dec_buf->vb2_buf.timestamp = inst->timestamp; + } else { + dev_warn(inst->dev->dev, "%s: invalid decoded frame index %i", + __func__, dec_info.index_frame_decoded); + } + } + + if (dec_info.index_frame_display >= 0) { + disp_buf = v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, dec_info.index_frame_display); + if (!disp_buf) + dev_warn(inst->dev->dev, "%s: invalid display frame index %i", + __func__, dec_info.index_frame_display); + } + + /* If there is anything to display, do that now */ + if (disp_buf) { + struct vpu_dst_buffer *dst_vpu_buf = wave5_to_vpu_dst_buf(disp_buf); + + if (inst->dst_fmt.num_planes == 1) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + } else if (inst->dst_fmt.num_planes == 2) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + } else if (inst->dst_fmt.num_planes == 3) { + vb2_set_plane_payload(&disp_buf->vb2_buf, 0, + inst->dst_fmt.plane_fmt[0].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 1, + inst->dst_fmt.plane_fmt[1].sizeimage); + vb2_set_plane_payload(&disp_buf->vb2_buf, 2, + inst->dst_fmt.plane_fmt[2].sizeimage); + } + + /* TODO implement interlace support */ + disp_buf->field = V4L2_FIELD_NONE; + dst_vpu_buf->display = true; + v4l2_m2m_buf_done(disp_buf, VB2_BUF_STATE_DONE); + + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u (payload %lu)\n", + __func__, dec_info.frame_cycle, + vb2_get_plane_payload(&disp_buf->vb2_buf, 0)); + } + + if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END || + dec_info.sequence_changed)) { + unsigned long flags; + + spin_lock_irqsave(&inst->state_spinlock, flags); + if (!v4l2_m2m_has_stopped(m2m_ctx)) { + switch_state(inst, VPU_INST_STATE_STOP); + + if (dec_info.sequence_changed) + handle_dynamic_resolution_change(inst); + else + send_eos_event(inst); + + flag_last_buffer_done(inst); + } + spin_unlock_irqrestore(&inst->state_spinlock, flags); + } + + /* + * During a resolution change and while draining, the firmware may flush + * the reorder queue regardless of having a matching decoding operation + * pending. Only terminate the job if there are no more IRQ coming. + */ + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + if (q_status.report_queue_count == 0 && + (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) { + dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } +} + +static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card)); + + return 0; +} + +static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int wave5_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct vpu_format *vpu_fmt; + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + const struct vpu_format *vpu_fmt; + int width, height; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u nm planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + } else { + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt); + + width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = info->mem_planes; + } + + if (p_dec_info->initial_info_obtained) { + width = inst->dst_fmt.width; + height = inst->dst_fmt.height; + } + + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + ret = wave5_vpu_dec_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->dst_fmt.width = f->fmt.pix_mp.width; + inst->dst_fmt.height = f->fmt.pix_mp.height; + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->dst_fmt.field = f->fmt.pix_mp.field; + inst->dst_fmt.flags = f->fmt.pix_mp.flags; + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + inst->output_format = FORMAT_420; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + inst->output_format = FORMAT_420; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + inst->output_format = FORMAT_422; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61 || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + inst->output_format = FORMAT_422; + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422P || + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) { + inst->cbcr_interleave = false; + inst->nv21 = false; + inst->output_format = FORMAT_422; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + inst->output_format = FORMAT_420; + } + + return 0; +} + +static int wave5_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->dst_fmt.width; + f->fmt.pix_mp.height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.field = inst->dst_fmt.field; + f->fmt.pix_mp.flags = inst->dst_fmt.flags; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = 1; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + + return 0; +} + +static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, + "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_dec_try_fmt_out(file, fh, f); + if (ret) + return ret; + + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type); + if (inst->std == STD_UNKNOWN) { + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + + inst->src_fmt.width = f->fmt.pix_mp.width; + inst->src_fmt.height = f->fmt.pix_mp.height; + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->src_fmt.field = f->fmt.pix_mp.field; + inst->src_fmt.flags = f->fmt.pix_mp.flags; + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + inst->colorspace = f->fmt.pix_mp.colorspace; + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + inst->quantization = f->fmt.pix_mp.quantization; + inst->xfer_func = f->fmt.pix_mp.xfer_func; + + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + return 0; +} + +static int wave5_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = 0; + s->r.top = 0; + if (inst->state > VPU_INST_STATE_OPEN) { + s->r = inst->conf_win; + } else { + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_COMPOSE w: %u h: %u\n", + s->r.width, s->r.height); + + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + + return 0; +} + +static int wave5_vpu_dec_stop(struct vpu_instance *inst) +{ + int ret = 0; + unsigned long flags; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + spin_lock_irqsave(&inst->state_spinlock, flags); + + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + + if (inst->state != VPU_INST_STATE_NONE) { + /* + * Temporarily release the state_spinlock so that subsequent + * calls do not block on a mutex while inside this spinlock. + */ + spin_unlock_irqrestore(&inst->state_spinlock, flags); + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + return ret; + + spin_lock_irqsave(&inst->state_spinlock, flags); + /* + * TODO eliminate this check by using a separate check for + * draining triggered by a resolution change. + */ + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + } + + /* + * Used to remember the EOS state after the streamoff/on transition on + * the capture queue. + */ + inst->eos = true; + + if (m2m_ctx->has_stopped) + goto unlock_and_return; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + + /* + * Deferred to device run in case it wasn't in the ring buffer + * yet. In other case, we have to send the EOS signal to the + * firmware so that any pending PIC_RUN ends without new + * bitstream buffer. + */ + if (m2m_ctx->last_src_buf) + goto unlock_and_return; + + if (inst->state == VPU_INST_STATE_NONE) { + send_eos_event(inst); + flag_last_buffer_done(inst); + } + +unlock_and_return: + spin_unlock_irqrestore(&inst->state_spinlock, flags); + return ret; +} + +static int wave5_vpu_dec_start(struct vpu_instance *inst) +{ + int ret = 0; + unsigned long flags; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + + spin_lock_irqsave(&inst->state_spinlock, flags); + + if (m2m_ctx->is_draining) { + ret = -EBUSY; + goto unlock_and_return; + } + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + vb2_clear_last_buffer_dequeued(dst_vq); + inst->eos = false; + +unlock_and_return: + spin_unlock_irqrestore(&inst->state_spinlock, flags); + return ret; +} + +static int wave5_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + + dev_dbg(inst->dev->dev, "decoder command: %u\n", dc->cmd); + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); + if (ret) + return ret; + + switch (dc->cmd) { + case V4L2_DEC_CMD_STOP: + ret = wave5_vpu_dec_stop(inst); + /* Just in case we don't have anything to decode anymore */ + v4l2_m2m_try_schedule(m2m_ctx); + break; + case V4L2_DEC_CMD_START: + ret = wave5_vpu_dec_start(inst); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops wave5_vpu_dec_ioctl_ops = { + .vidioc_querycap = wave5_vpu_dec_querycap, + .vidioc_enum_framesizes = wave5_vpu_dec_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave5_vpu_dec_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_dec_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_dec_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_dec_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave5_vpu_dec_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_dec_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_dec_try_fmt_out, + + .vidioc_g_selection = wave5_vpu_dec_g_selection, + .vidioc_s_selection = wave5_vpu_dec_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + /* + * Firmware does not support CREATE_BUFS for CAPTURE queue. Since + * there is no immediate use-case for supporting CREATE_BUFS on + * just the OUTPUT queue, disable CREATE_BUFS altogether. + */ + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = wave5_vpu_dec_decoder_cmd, + + .vidioc_subscribe_event = wave5_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt; + + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__, + *num_buffers, *num_planes, q->type); + + *num_planes = inst_format.num_planes; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + sizes[0] = inst_format.plane_fmt[0].sizeimage; + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (*num_buffers < inst->fbc_buf_count) + *num_buffers = inst->fbc_buf_count; + + if (*num_planes == 1) { + if (inst->output_format == FORMAT_422) + sizes[0] = inst_format.width * inst_format.height * 2; + else + sizes[0] = inst_format.width * inst_format.height * 3 / 2; + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]); + } else if (*num_planes == 2) { + sizes[0] = inst_format.width * inst_format.height; + if (inst->output_format == FORMAT_422) + sizes[1] = inst_format.width * inst_format.height; + else + sizes[1] = inst_format.width * inst_format.height / 2; + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n", + __func__, sizes[0], sizes[1]); + } else if (*num_planes == 3) { + sizes[0] = inst_format.width * inst_format.height; + if (inst->output_format == FORMAT_422) { + sizes[1] = inst_format.width * inst_format.height / 2; + sizes[2] = inst_format.width * inst_format.height / 2; + } else { + sizes[1] = inst_format.width * inst_format.height / 4; + sizes[2] = inst_format.width * inst_format.height / 4; + } + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n", + __func__, sizes[0], sizes[1], sizes[2]); + } + } + + return 0; +} + +static int wave5_prepare_fb(struct vpu_instance *inst) +{ + int linear_num; + int non_linear_num; + int fb_stride = 0, fb_height = 0; + int luma_size, chroma_size; + int ret, i; + struct v4l2_m2m_buffer *buf, *n; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); + non_linear_num = inst->fbc_buf_count; + + for (i = 0; i < non_linear_num; i++) { + struct frame_buffer *frame = &inst->frame_buf[i]; + struct vpu_buf *vframe = &inst->frame_vbuf[i]; + + fb_stride = inst->dst_fmt.width; + fb_height = ALIGN(inst->dst_fmt.height, 32); + luma_size = fb_stride * fb_height; + + chroma_size = ALIGN(fb_stride / 2, 16) * fb_height; + + if (vframe->size == (luma_size + chroma_size)) + continue; + + if (vframe->size) + wave5_vpu_dec_reset_framebuffer(inst, i); + + vframe->size = luma_size + chroma_size; + ret = wave5_vdi_allocate_dma_memory(inst->dev, vframe); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Allocating FBC buf of size %zu, fail: %d\n", + __func__, vframe->size, ret); + return ret; + } + + frame->buf_y = vframe->daddr; + frame->buf_cb = vframe->daddr + luma_size; + frame->buf_cr = (dma_addr_t)-1; + frame->size = vframe->size; + frame->width = inst->src_fmt.width; + frame->stride = fb_stride; + frame->map_type = COMPRESSED_FRAME_MAP; + frame->update_fb_info = true; + } + /* In case the count has reduced, clean up leftover framebuffer memory */ + for (i = non_linear_num; i < MAX_REG_FRAME; i++) { + ret = wave5_vpu_dec_reset_framebuffer(inst, i); + if (ret) + break; + } + + for (i = 0; i < linear_num; i++) { + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, i); + struct frame_buffer *frame = &inst->frame_buf[non_linear_num + i]; + dma_addr_t buf_addr_y = 0, buf_addr_cb = 0, buf_addr_cr = 0; + u32 buf_size = 0; + u32 fb_stride = inst->dst_fmt.width; + u32 luma_size = fb_stride * inst->dst_fmt.height; + u32 chroma_size; + + if (inst->output_format == FORMAT_422) + chroma_size = fb_stride * inst->dst_fmt.height / 2; + else + chroma_size = fb_stride * inst->dst_fmt.height / 4; + + if (inst->dst_fmt.num_planes == 1) { + buf_size = vb2_plane_size(vb, 0); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = buf_addr_y + luma_size; + buf_addr_cr = buf_addr_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 2) { + buf_size = vb2_plane_size(vb, 0) + + vb2_plane_size(vb, 1); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1); + buf_addr_cr = buf_addr_cb + chroma_size; + } else if (inst->dst_fmt.num_planes == 3) { + buf_size = vb2_plane_size(vb, 0) + + vb2_plane_size(vb, 1) + + vb2_plane_size(vb, 2); + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0); + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1); + buf_addr_cr = vb2_dma_contig_plane_dma_addr(vb, 2); + } + + frame->buf_y = buf_addr_y; + frame->buf_cb = buf_addr_cb; + frame->buf_cr = buf_addr_cr; + frame->size = buf_size; + frame->width = inst->src_fmt.width; + frame->stride = fb_stride; + frame->map_type = LINEAR_FRAME_MAP; + frame->update_fb_info = true; + } + + ret = wave5_vpu_dec_register_frame_buffer_ex(inst, non_linear_num, linear_num, + fb_stride, inst->dst_fmt.height); + if (ret) { + dev_dbg(inst->dev->dev, "%s: vpu_dec_register_frame_buffer_ex fail: %d", + __func__, ret); + return ret; + } + + /* + * Mark all frame buffers as out of display, to avoid using them before + * the application have them queued. + */ + for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) { + ret = wave5_vpu_dec_set_disp_flag(inst, i); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Setting display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + } + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *vbuf = &buf->vb; + + ret = wave5_vpu_dec_clr_disp_flag(inst, vbuf->vb2_buf.index); + if (ret) + dev_dbg(inst->dev->dev, + "%s: Clearing display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + + return 0; +} + +static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t buffer_size, + struct vpu_buf *ring_buffer, dma_addr_t wr_ptr) +{ + size_t size; + size_t offset = wr_ptr - ring_buffer->daddr; + int ret; + + if (wr_ptr + buffer_size > ring_buffer->daddr + ring_buffer->size) { + size = ring_buffer->daddr + ring_buffer->size - wr_ptr; + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, size); + if (ret < 0) + return ret; + + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, 0, (u8 *)buffer + size, + buffer_size - size); + if (ret < 0) + return ret; + } else { + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, + buffer_size); + if (ret < 0) + return ret; + } + + return 0; +} + +static int fill_ringbuffer(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + int ret; + + if (m2m_ctx->last_src_buf) { + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf); + + if (vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "last src buffer already written\n"); + return 0; + } + } + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + struct vb2_v4l2_buffer *vbuf = &buf->vb; + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf; + size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0); + void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + dma_addr_t rd_ptr = 0; + dma_addr_t wr_ptr = 0; + size_t remain_size = 0; + + if (vpu_buf->consumed) { + dev_dbg(inst->dev->dev, "already copied src buf (%u) to the ring buffer\n", + vbuf->vb2_buf.index); + continue; + } + + if (!src_buf) { + dev_dbg(inst->dev->dev, + "%s: Acquiring kernel pointer to src buf (%u), fail\n", + __func__, vbuf->vb2_buf.index); + break; + } + + ret = wave5_vpu_dec_get_bitstream_buffer(inst, &rd_ptr, &wr_ptr, &remain_size); + if (ret) { + /* Unable to acquire the mutex */ + dev_err(inst->dev->dev, "Getting the bitstream buffer, fail: %d\n", + ret); + return ret; + } + + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &rd_ptr, &wr_ptr); + + if (remain_size < src_size) { + dev_dbg(inst->dev->dev, + "%s: remaining size: %zu < source size: %zu for src buf (%u)\n", + __func__, remain_size, src_size, vbuf->vb2_buf.index); + break; + } + + ret = write_to_ringbuffer(inst, src_buf, src_size, ring_buffer, wr_ptr); + if (ret) { + dev_err(inst->dev->dev, "Write src buf (%u) to ring buffer, fail: %d\n", + vbuf->vb2_buf.index, ret); + return ret; + } + + ret = wave5_vpu_dec_update_bitstream_buffer(inst, src_size); + if (ret) { + dev_dbg(inst->dev->dev, + "update_bitstream_buffer fail: %d for src buf (%u)\n", + ret, vbuf->vb2_buf.index); + break; + } + + vpu_buf->consumed = true; + + /* Don't write buffers passed the last one while draining. */ + if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx, vbuf)) { + dev_dbg(inst->dev->dev, "last src buffer written to the ring buffer\n"); + break; + } + } + + return 0; +} + +static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb) +{ + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + + vpu_buf->consumed = false; + vbuf->sequence = inst->queued_src_buf_num++; + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); +} + +static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + vbuf->sequence = inst->queued_dst_buf_num++; + + if (inst->state == VPU_INST_STATE_PIC_RUN) { + struct vpu_dst_buffer *vpu_buf = wave5_to_vpu_dst_buf(vbuf); + int ret; + + /* + * The buffer is already registered just clear the display flag + * to let the firmware know it can be used. + */ + vpu_buf->display = false; + ret = wave5_vpu_dec_clr_disp_flag(inst, vb->index); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: Clearing the display flag of buffer index: %u, fail: %d\n", + __func__, vb->index, ret); + } + } + + if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb2_set_plane_payload(vb, i, 0); + + vbuf->field = V4L2_FIELD_NONE; + + send_eos_event(inst); + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf); + } else { + v4l2_m2m_buf_queue(m2m_ctx, vbuf); + } +} + +static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n", + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + wave5_vpu_dec_buf_queue_src(vb); + else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + wave5_vpu_dec_buf_queue_dst(vb); +} + +static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst) +{ + int ret; + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf; + + ring_buffer->size = ALIGN(inst->src_fmt.plane_fmt[0].sizeimage, 1024) * 4; + ret = wave5_vdi_allocate_dma_memory(inst->dev, ring_buffer); + if (ret) { + dev_dbg(inst->dev->dev, "%s: allocate ring buffer of size %zu fail: %d\n", + __func__, ring_buffer->size, ret); + return ret; + } + + inst->last_rd_ptr = ring_buffer->daddr; + + return 0; +} + +static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + v4l2_m2m_update_start_streaming_state(m2m_ctx, q); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && inst->state == VPU_INST_STATE_NONE) { + struct dec_open_param open_param; + + memset(&open_param, 0, sizeof(struct dec_open_param)); + + ret = wave5_vpu_dec_allocate_ring_buffer(inst); + if (ret) + goto return_buffers; + + open_param.bitstream_buffer = inst->bitstream_vbuf.daddr; + open_param.bitstream_buffer_size = inst->bitstream_vbuf.size; + + ret = wave5_vpu_dec_open(inst, &open_param); + if (ret) { + dev_dbg(inst->dev->dev, "%s: decoder opening, fail: %d\n", + __func__, ret); + goto free_bitstream_vbuf; + } + + ret = switch_state(inst, VPU_INST_STATE_OPEN); + if (ret) + goto free_bitstream_vbuf; + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + struct dec_initial_info *initial_info = + &inst->codec_info->dec_info.initial_info; + + if (inst->state == VPU_INST_STATE_STOP) + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + goto return_buffers; + + if (inst->state == VPU_INST_STATE_INIT_SEQ) { + if (initial_info->luma_bitdepth != 8) { + dev_info(inst->dev->dev, "%s: no support for %d bit depth", + __func__, initial_info->luma_bitdepth); + ret = -EINVAL; + goto return_buffers; + } + } + } + + return ret; + +free_bitstream_vbuf: + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf); +return_buffers: + wave5_return_bufs(q, VB2_BUF_STATE_QUEUED); + return ret; +} + +static int streamoff_output(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + int ret; + dma_addr_t new_rd_ptr; + + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + ret = wave5_vpu_flush_instance(inst); + if (ret) + return ret; + + /* Reset the ring buffer information */ + new_rd_ptr = wave5_vpu_dec_get_rd_ptr(inst); + inst->last_rd_ptr = new_rd_ptr; + 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)) + send_eos_event(inst); + + /* streamoff on output cancels any draining operation */ + inst->eos = false; + + return 0; +} + +static int streamoff_capture(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + unsigned int i; + int ret = 0; + + for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) { + ret = wave5_vpu_dec_set_disp_flag(inst, i); + if (ret) + dev_dbg(inst->dev->dev, + "%s: Setting display flag of buf index: %u, fail: %d\n", + __func__, i, ret); + } + + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) { + u32 plane; + + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + + for (plane = 0; plane < inst->dst_fmt.num_planes; plane++) + vb2_set_plane_payload(&buf->vb2_buf, plane, 0); + + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + if (inst->needs_reallocation) { + wave5_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL); + inst->needs_reallocation = false; + } + + if (v4l2_m2m_has_stopped(m2m_ctx)) { + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + return ret; + } + + return 0; +} + +static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + bool check_cmd = TRUE; + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + while (check_cmd) { + struct queue_status_info q_status; + struct dec_output_info dec_output_info; + + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + + if (q_status.report_queue_count == 0) + break; + + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0) + break; + + if (wave5_vpu_dec_get_output_info(inst, &dec_output_info)) + dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n"); + } + + v4l2_m2m_update_stop_streaming_state(m2m_ctx, q); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + streamoff_output(q); + else + streamoff_capture(q); +} + +static const struct vb2_ops wave5_vpu_dec_vb2_ops = { + .queue_setup = wave5_vpu_dec_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave5_vpu_dec_buf_queue, + .start_streaming = wave5_vpu_dec_start_streaming, + .stop_streaming = wave5_vpu_dec_stop_streaming, +}; + +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt); + + src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->flags = 0; + src_fmt->num_planes = 1; + wave5_update_pix_fmt(src_fmt, 720, 480); + + dst_fmt->pixelformat = dst_pix_fmt; + dst_fmt->field = V4L2_FIELD_NONE; + dst_fmt->flags = 0; + dst_fmt->num_planes = dst_fmt_info->mem_planes; + wave5_update_pix_fmt(dst_fmt, 736, 480); +} + +static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_dec_vb2_ops); +} + +static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = { + .finish_process = wave5_vpu_dec_finish_decode, +}; + +static int initialize_sequence(struct vpu_instance *inst) +{ + struct dec_initial_info initial_info; + int ret = 0; + + memset(&initial_info, 0, sizeof(struct dec_initial_info)); + + ret = wave5_vpu_dec_issue_seq_init(inst); + if (ret) { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_dec_issue_seq_init, fail: %d\n", + __func__, ret); + return ret; + } + + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0) + dev_dbg(inst->dev->dev, "%s: failed to call vpu_wait_interrupt()\n", __func__); + + ret = wave5_vpu_dec_complete_seq_init(inst, &initial_info); + if (ret) { + dev_dbg(inst->dev->dev, "%s: vpu_dec_complete_seq_init, fail: %d, reason: %u\n", + __func__, ret, initial_info.seq_init_err_reason); + wave5_handle_src_buffer(inst, initial_info.rd_ptr); + return ret; + } + + handle_dynamic_resolution_change(inst); + + return 0; +} + +static bool wave5_is_draining_or_eos(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + lockdep_assert_held(&inst->state_spinlock); + return m2m_ctx->is_draining || inst->eos; +} + +static void wave5_vpu_dec_device_run(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct queue_status_info q_status; + u32 fail_res = 0; + int ret = 0; + + dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); + + ret = fill_ringbuffer(inst); + if (ret) { + dev_warn(inst->dev->dev, "Filling ring buffer failed\n"); + goto finish_job_and_return; + } + + switch (inst->state) { + case VPU_INST_STATE_OPEN: + ret = initialize_sequence(inst); + if (ret) { + unsigned long flags; + + spin_lock_irqsave(&inst->state_spinlock, flags); + if (wave5_is_draining_or_eos(inst) && + wave5_last_src_buffer_consumed(m2m_ctx)) { + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); + + switch_state(inst, VPU_INST_STATE_STOP); + + if (vb2_is_streaming(dst_vq)) + send_eos_event(inst); + else + handle_dynamic_resolution_change(inst); + + flag_last_buffer_done(inst); + } + spin_unlock_irqrestore(&inst->state_spinlock, flags); + } else { + switch_state(inst, VPU_INST_STATE_INIT_SEQ); + } + + break; + + case VPU_INST_STATE_INIT_SEQ: + /* + * Do this early, preparing the fb can trigger an IRQ before + * we had a chance to switch, which leads to an invalid state + * change. + */ + switch_state(inst, VPU_INST_STATE_PIC_RUN); + + /* + * During DRC, the picture decoding remains pending, so just leave the job + * active until this decode operation completes. + */ + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + + /* + * The sequence must be analyzed first to calculate the proper + * size of the auxiliary buffers. + */ + ret = wave5_prepare_fb(inst); + if (ret) { + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret); + switch_state(inst, VPU_INST_STATE_STOP); + break; + } + + if (q_status.instance_queue_count) { + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + } + + fallthrough; + case VPU_INST_STATE_PIC_RUN: + ret = start_decode(inst, &fail_res); + if (ret) { + dev_err(inst->dev->dev, + "Frame decoding on m2m context (%p), fail: %d (result: %d)\n", + m2m_ctx, ret, fail_res); + break; + } + /* Return so that we leave this job active */ + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + default: + WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state)); + break; + } + +finish_job_and_return: + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); +} + +static void wave5_vpu_dec_job_abort(void *priv) +{ + struct vpu_instance *inst = priv; + int ret; + + ret = switch_state(inst, VPU_INST_STATE_STOP); + if (ret) + return; + + ret = wave5_vpu_dec_set_eos_on_firmware(inst); + if (ret) + dev_warn(inst->dev->dev, + "Setting EOS for the bitstream, fail: %d\n", ret); +} + +static int wave5_vpu_dec_job_ready(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&inst->state_spinlock, flags); + + switch (inst->state) { + case VPU_INST_STATE_NONE: + dev_dbg(inst->dev->dev, "Decoder must be open to start queueing M2M jobs!\n"); + break; + case VPU_INST_STATE_OPEN: + if (wave5_is_draining_or_eos(inst) || !v4l2_m2m_has_stopped(m2m_ctx) || + v4l2_m2m_num_src_bufs_ready(m2m_ctx) > 0) { + ret = 1; + break; + } + + dev_dbg(inst->dev->dev, + "Decoder must be draining or >= 1 OUTPUT queue buffer must be queued!\n"); + break; + case VPU_INST_STATE_INIT_SEQ: + case VPU_INST_STATE_PIC_RUN: + if (!m2m_ctx->cap_q_ctx.q.streaming) { + dev_dbg(inst->dev->dev, "CAPTURE queue must be streaming to queue jobs!\n"); + break; + } else if (v4l2_m2m_num_dst_bufs_ready(m2m_ctx) < (inst->fbc_buf_count - 1)) { + dev_dbg(inst->dev->dev, + "No capture buffer ready to decode!\n"); + break; + } else if (!wave5_is_draining_or_eos(inst) && + !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, + "No bitstream data to decode!\n"); + break; + } + ret = 1; + break; + case VPU_INST_STATE_STOP: + dev_dbg(inst->dev->dev, "Decoder is stopped, not running.\n"); + break; + } + + spin_unlock_irqrestore(&inst->state_spinlock, flags); + + return ret; +} + +static const struct v4l2_m2m_ops wave5_vpu_dec_m2m_ops = { + .device_run = wave5_vpu_dec_device_run, + .job_abort = wave5_vpu_dec_job_abort, + .job_ready = wave5_vpu_dec_job_ready, +}; + +static int wave5_vpu_open_dec(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + struct v4l2_m2m_ctx *m2m_ctx; + int ret = 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_DEC; + inst->ops = &wave5_vpu_dec_inst_ops; + + spin_lock_init(&inst->state_spinlock); + + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); + if (!inst->codec_info) + return -ENOMEM; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + INIT_LIST_HEAD(&inst->list); + list_add_tail(&inst->list, &dev->instances); + + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev; + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_dec_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto cleanup_inst; + } + m2m_ctx = inst->v4l2_fh.m2m_ctx; + + v4l2_m2m_set_src_buffered(m2m_ctx, true); + v4l2_m2m_set_dst_buffered(m2m_ctx, true); + /* + * We use the M2M job queue to ensure synchronization of steps where + * needed, as IOCTLs can occur at anytime and we need to run commands on + * the firmware in a specified order. + * In order to initialize the sequence on the firmware within an M2M + * job, the M2M framework needs to be able to queue jobs before + * the CAPTURE queue has been started, because we need the results of the + * initialization to properly prepare the CAPTURE queue with the correct + * amount of buffers. + * By setting ignore_cap_streaming to true the m2m framework will call + * job_ready as soon as the OUTPUT queue is streaming, instead of + * waiting until both the CAPTURE and OUTPUT queues are streaming. + */ + m2m_ctx->ignore_cap_streaming = true; + + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10); + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, NULL, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1); + + if (inst->v4l2_ctrl_hdl.error) { + ret = -ENODEV; + goto cleanup_inst; + } + + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl); + + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->colorspace = V4L2_COLORSPACE_REC709; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + init_completion(&inst->irq_done); + + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); + if (inst->id < 0) { + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id); + ret = inst->id; + goto cleanup_inst; + } + + wave5_vdi_allocate_sram(inst->dev); + + return 0; + +cleanup_inst: + wave5_cleanup_instance(inst); + return ret; +} + +static int wave5_vpu_dec_release(struct file *filp) +{ + return wave5_vpu_release_device(filp, wave5_vpu_dec_close, "decoder"); +} + +static const struct v4l2_file_operations wave5_vpu_dec_fops = { + .owner = THIS_MODULE, + .open = wave5_vpu_open_dec, + .release = wave5_vpu_dec_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave5_vpu_dec_register_device(struct vpu_device *dev) +{ + struct video_device *vdev_dec; + int ret; + + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); + if (!vdev_dec) + return -ENOMEM; + + dev->v4l2_m2m_dec_dev = v4l2_m2m_init(&wave5_vpu_dec_m2m_ops); + if (IS_ERR(dev->v4l2_m2m_dec_dev)) { + ret = PTR_ERR(dev->v4l2_m2m_dec_dev); + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret); + return -EINVAL; + } + + dev->video_dev_dec = vdev_dec; + + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name)); + vdev_dec->fops = &wave5_vpu_dec_fops; + vdev_dec->ioctl_ops = &wave5_vpu_dec_ioctl_ops; + vdev_dec->release = video_device_release_empty; + vdev_dec->v4l2_dev = &dev->v4l2_dev; + vdev_dec->vfl_dir = VFL_DIR_M2M; + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_dec->lock = &dev->dev_lock; + + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + video_set_drvdata(vdev_dec, dev); + + return 0; +} + +void wave5_vpu_dec_unregister_device(struct vpu_device *dev) +{ + video_unregister_device(dev->video_dev_dec); + if (dev->v4l2_m2m_dec_dev) + v4l2_m2m_release(dev->v4l2_m2m_dec_dev); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c new file mode 100644 index 000000000..f29cfa3af --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -0,0 +1,1794 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - encoder interface + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include "wave5-helper.h" + +#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder" +#define VPU_ENC_DRV_NAME "wave5-enc" + +static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = { + [VPU_FMT_TYPE_CODEC] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + }, + [VPU_FMT_TYPE_RAW] = { + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, + .max_width = W5_MAX_ENC_PIC_WIDTH, + .min_width = W5_MIN_ENC_PIC_WIDTH, + .max_height = W5_MAX_ENC_PIC_HEIGHT, + .min_height = W5_MIN_ENC_PIC_HEIGHT, + }, + } +}; + +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state) +{ + switch (state) { + case VPU_INST_STATE_NONE: + goto invalid_state_switch; + case VPU_INST_STATE_OPEN: + if (inst->state != VPU_INST_STATE_NONE) + goto invalid_state_switch; + break; + case VPU_INST_STATE_INIT_SEQ: + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP) + goto invalid_state_switch; + break; + case VPU_INST_STATE_PIC_RUN: + if (inst->state != VPU_INST_STATE_INIT_SEQ) + goto invalid_state_switch; + break; + case VPU_INST_STATE_STOP: + break; + }; + + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + inst->state = state; + return 0; + +invalid_state_switch: + WARN(1, "Invalid state switch from %s to %s.\n", + state_to_str(inst->state), state_to_str(state)); + return -EINVAL; +} + +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width, + unsigned int height) +{ + switch (pix_mp->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2; + break; + case V4L2_PIX_FMT_YUV420M: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4; + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2; + pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height; + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32); + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2; + break; + default: + pix_mp->width = width; + pix_mp->height = height; + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3; + break; + } +} + +static int start_encode(struct vpu_instance *inst, u32 *fail_res) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct frame_buffer frame_buf; + struct enc_param pic_param; + u32 stride = ALIGN(inst->dst_fmt.width, 32); + u32 luma_size = (stride * inst->dst_fmt.height); + u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2)); + + memset(&pic_param, 0, sizeof(struct enc_param)); + memset(&frame_buf, 0, sizeof(struct frame_buffer)); + + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); + if (!dst_buf) { + dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__); + return -EAGAIN; + } + + pic_param.pic_stream_buffer_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + pic_param.pic_stream_buffer_size = + vb2_plane_size(&dst_buf->vb2_buf, 0); + + src_buf = v4l2_m2m_next_src_buf(m2m_ctx); + if (!src_buf) { + dev_dbg(inst->dev->dev, "%s: No source buffer found\n", __func__); + if (m2m_ctx->is_draining) + pic_param.src_end_flag = 1; + else + return -EAGAIN; + } else { + if (inst->src_fmt.num_planes == 1) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = frame_buf.buf_y + luma_size; + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 2) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1); + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size; + } else if (inst->src_fmt.num_planes == 3) { + frame_buf.buf_y = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + frame_buf.buf_cb = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1); + frame_buf.buf_cr = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2); + } + frame_buf.stride = stride; + pic_param.src_idx = src_buf->vb2_buf.index; + } + + pic_param.source_frame = &frame_buf; + pic_param.code_option.implicit_header_encode = 1; + pic_param.code_option.encode_aud = inst->encode_aud; + ret = wave5_vpu_enc_start_one_frame(inst, &pic_param, fail_res); + if (ret) { + if (*fail_res == WAVE5_SYSERR_QUEUEING_FAIL) + return -EINVAL; + + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame fail: %d\n", + __func__, ret); + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + if (!src_buf) { + dev_dbg(inst->dev->dev, + "%s: Removing src buf failed, the queue is empty\n", + __func__); + return -EINVAL; + } + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!dst_buf) { + dev_dbg(inst->dev->dev, + "%s: Removing dst buf failed, the queue is empty\n", + __func__); + return -EINVAL; + } + switch_state(inst, VPU_INST_STATE_STOP); + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } else { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", + __func__); + /* + * Remove the source buffer from the ready-queue now and finish + * it in the videobuf2 framework once the index is returned by the + * firmware in finish_encode + */ + if (src_buf) + v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); + } + + return 0; +} + +static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + struct enc_output_info enc_output_info; + struct vb2_v4l2_buffer *src_buf = NULL; + struct vb2_v4l2_buffer *dst_buf = NULL; + + ret = wave5_vpu_enc_get_output_info(inst, &enc_output_info); + if (ret) { + dev_dbg(inst->dev->dev, + "%s: vpu_enc_get_output_info fail: %d reason: %u | info: %u\n", + __func__, ret, enc_output_info.error_reason, enc_output_info.warn_info); + return; + } + + dev_dbg(inst->dev->dev, + "%s: pic_type %i recon_idx %i src_idx %i pic_byte %u pts %llu\n", + __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, + enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); + + /* + * The source buffer will not be found in the ready-queue as it has been + * dropped after sending of the encode firmware command, locate it in + * the videobuf2 queue directly + */ + if (enc_output_info.enc_src_idx >= 0) { + struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), + enc_output_info.enc_src_idx); + if (vb->state != VB2_BUF_STATE_ACTIVE) + dev_warn(inst->dev->dev, + "%s: encoded buffer (%d) was not in ready queue %i.", + __func__, enc_output_info.enc_src_idx, vb->state); + else + src_buf = to_vb2_v4l2_buffer(vb); + + if (src_buf) { + inst->timestamp = src_buf->vb2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } else { + dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", + __func__, enc_output_info.enc_src_idx); + } + } + + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (enc_output_info.recon_frame_index == RECON_IDX_FLAG_ENC_END) { + static const struct v4l2_event vpu_event_eos = { + .type = V4L2_EVENT_EOS + }; + + if (!WARN_ON(!dst_buf)) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->field = V4L2_FIELD_NONE; + v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf); + } + + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos); + + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } else { + if (!dst_buf) { + dev_warn(inst->dev->dev, "No bitstream buffer."); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + return; + } + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_output_info.bitstream_size); + + dst_buf->vb2_buf.timestamp = inst->timestamp; + dst_buf->field = V4L2_FIELD_NONE; + if (enc_output_info.pic_type == PIC_TYPE_I) { + if (enc_output_info.enc_vcl_nut == 19 || + enc_output_info.enc_vcl_nut == 20) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + } else if (enc_output_info.pic_type == PIC_TYPE_P) { + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + } else if (enc_output_info.pic_type == PIC_TYPE_B) { + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME; + } + + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u\n", + __func__, enc_output_info.frame_cycle); + + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + } +} + +static int wave5_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card)); + + return 0; +} + +static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize) +{ + const struct vpu_format *vpu_fmt; + + if (fsize->index) + return -EINVAL; + + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = vpu_fmt->min_width; + fsize->stepwise.max_width = vpu_fmt->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = vpu_fmt->min_height; + fsize->stepwise.max_height = vpu_fmt->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = 1; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_enc_try_fmt_cap(file, fh, f); + if (ret) + return ret; + + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type); + if (inst->std == STD_UNKNOWN) { + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n", + (char *)&f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + + inst->dst_fmt.width = f->fmt.pix_mp.width; + inst->dst_fmt.height = f->fmt.pix_mp.height; + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->dst_fmt.field = f->fmt.pix_mp.field; + inst->dst_fmt.flags = f->fmt.pix_mp.flags; + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->dst_fmt.num_planes; i++) { + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int wave5_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i; + + f->fmt.pix_mp.width = inst->dst_fmt.width; + f->fmt.pix_mp.height = inst->dst_fmt.height; + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat; + f->fmt.pix_mp.field = inst->dst_fmt.field; + f->fmt.pix_mp.flags = inst->dst_fmt.flags; + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage; + } + + f->fmt.pix_mp.colorspace = inst->colorspace; + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc; + f->fmt.pix_mp.quantization = inst->quantization; + f->fmt.pix_mp.xfer_func = inst->xfer_func; + + return 0; +} + +static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index); + + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) + return -EINVAL; + + f->pixelformat = vpu_fmt->v4l2_pix_fmt; + f->flags = 0; + + return 0; +} + +static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + const struct vpu_format *vpu_fmt; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]); + if (!vpu_fmt) { + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat; + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height); + } else { + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width); + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height); + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt); + + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt; + f->fmt.pix_mp.num_planes = info->mem_planes; + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height); + } + + f->fmt.pix_mp.flags = 0; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + + return 0; +} + +static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + int i, ret; + + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n", + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field); + + ret = wave5_vpu_enc_try_fmt_out(file, fh, f); + if (ret) + return ret; + + inst->src_fmt.width = f->fmt.pix_mp.width; + inst->src_fmt.height = f->fmt.pix_mp.height; + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat; + inst->src_fmt.field = f->fmt.pix_mp.field; + inst->src_fmt.flags = f->fmt.pix_mp.flags; + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < inst->src_fmt.num_planes; i++) { + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline; + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { + inst->cbcr_interleave = true; + inst->nv21 = false; + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { + inst->cbcr_interleave = true; + inst->nv21 = true; + } else { + inst->cbcr_interleave = false; + inst->nv21 = false; + } + + inst->colorspace = f->fmt.pix_mp.colorspace; + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + inst->quantization = f->fmt.pix_mp.quantization; + inst->xfer_func = f->fmt.pix_mp.xfer_func; + + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height); + + return 0; +} + +static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->dst_fmt.width; + s->r.height = inst->dst_fmt.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n", + __func__, s->r.width, s->r.height); + + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->src_fmt.width; + s->r.height = inst->src_fmt.height; + + return 0; +} + +static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); + if (ret) + return ret; + + if (!wave5_vpu_both_queues_are_streaming(inst)) + return 0; + + switch (ec->cmd) { + case V4L2_ENC_CMD_STOP: + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + return 0; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + break; + case V4L2_ENC_CMD_START: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wave5_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n", + __func__, a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static int wave5_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vpu_instance *inst = wave5_to_vpu_inst(fh); + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) { + inst->frame_rate = a->parm.output.timeperframe.denominator / + a->parm.output.timeperframe.numerator; + } else { + a->parm.output.timeperframe.numerator = 1; + a->parm.output.timeperframe.denominator = inst->frame_rate; + } + + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n", + __func__, a->parm.output.timeperframe.numerator, + a->parm.output.timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops wave5_vpu_enc_ioctl_ops = { + .vidioc_querycap = wave5_vpu_enc_querycap, + .vidioc_enum_framesizes = wave5_vpu_enc_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = wave5_vpu_enc_enum_fmt_cap, + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_enc_s_fmt_cap, + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_enc_g_fmt_cap, + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_enc_try_fmt_cap, + + .vidioc_enum_fmt_vid_out = wave5_vpu_enc_enum_fmt_out, + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_enc_s_fmt_out, + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out, + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_enc_try_fmt_out, + + .vidioc_g_selection = wave5_vpu_enc_g_selection, + .vidioc_s_selection = wave5_vpu_enc_s_selection, + + .vidioc_g_parm = wave5_vpu_enc_g_parm, + .vidioc_s_parm = wave5_vpu_enc_s_parm, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = wave5_vpu_enc_encoder_cmd, + + .vidioc_subscribe_event = wave5_vpu_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_instance *inst = wave5_ctrl_to_vpu_inst(ctrl); + + dev_dbg(inst->dev->dev, "%s: name: %s | value: %d\n", __func__, ctrl->name, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + inst->encode_aud = ctrl->val; + break; + case V4L2_CID_HFLIP: + inst->mirror_direction |= (ctrl->val << 1); + break; + case V4L2_CID_VFLIP: + inst->mirror_direction |= ctrl->val; + break; + case V4L2_CID_ROTATE: + inst->rot_angle = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + inst->vbv_buf_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: + inst->rc_mode = 0; + break; + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: + inst->rc_mode = 1; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + inst->bit_rate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + inst->enc_param.avc_idr_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + inst->enc_param.independ_slice_mode = ctrl->val; + inst->enc_param.avc_slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + inst->enc_param.independ_slice_mode_arg = ctrl->val; + inst->enc_param.avc_slice_arg = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + inst->rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + inst->enc_param.mb_level_rc_enable = ctrl->val; + inst->enc_param.cu_level_rc_enable = ctrl->val; + inst->enc_param.hvs_qp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + inst->enc_param.profile = HEVC_PROFILE_MAIN; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + inst->enc_param.profile = HEVC_PROFILE_STILLPICTURE; + inst->enc_param.en_still_picture = 1; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + inst->enc_param.profile = HEVC_PROFILE_MAIN10; + inst->bit_depth = 10; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + inst->enc_param.level = 10 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + inst->enc_param.level = 20 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + inst->enc_param.level = 21 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + inst->enc_param.level = 30 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + inst->enc_param.level = 31 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + inst->enc_param.level = 40 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + inst->enc_param.level = 41 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + inst->enc_param.level = 50 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + inst->enc_param.level = 51 * 3; + break; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + inst->enc_param.level = 52 * 3; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + inst->enc_param.min_qp_i = ctrl->val; + inst->enc_param.min_qp_p = ctrl->val; + inst->enc_param.min_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + inst->enc_param.max_qp_i = ctrl->val; + inst->enc_param.max_qp_p = ctrl->val; + inst->enc_param.max_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + inst->enc_param.intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED: + inst->enc_param.disable_deblk = 1; + inst->enc_param.sao_enable = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED: + inst->enc_param.disable_deblk = 0; + inst->enc_param.sao_enable = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + inst->enc_param.disable_deblk = 0; + inst->enc_param.sao_enable = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: + inst->enc_param.beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: + inst->enc_param.tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE: + inst->enc_param.decoding_refresh_type = 0; + break; + case V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA: + inst->enc_param.decoding_refresh_type = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR: + inst->enc_param.decoding_refresh_type = 2; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: + inst->enc_param.intra_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: + inst->enc_param.lossless_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: + inst->enc_param.const_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: + inst->enc_param.wpp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: + inst->enc_param.strong_intra_smooth_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: + inst->enc_param.max_num_merge = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: + inst->enc_param.tmvp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + inst->enc_param.profile = H264_PROFILE_BP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + inst->enc_param.profile = H264_PROFILE_MP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + inst->enc_param.profile = H264_PROFILE_EXTENDED; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + inst->enc_param.profile = H264_PROFILE_HP; + inst->bit_depth = 8; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10: + inst->enc_param.profile = H264_PROFILE_HIGH10; + inst->bit_depth = 10; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422: + inst->enc_param.profile = H264_PROFILE_HIGH422; + inst->bit_depth = 10; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE: + inst->enc_param.profile = H264_PROFILE_HIGH444; + inst->bit_depth = 10; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + inst->enc_param.level = 10; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + inst->enc_param.level = 9; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + inst->enc_param.level = 11; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + inst->enc_param.level = 12; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + inst->enc_param.level = 13; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + inst->enc_param.level = 20; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + inst->enc_param.level = 21; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + inst->enc_param.level = 22; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + inst->enc_param.level = 30; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + inst->enc_param.level = 31; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + inst->enc_param.level = 32; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + inst->enc_param.level = 40; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + inst->enc_param.level = 41; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + inst->enc_param.level = 42; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + inst->enc_param.level = 50; + break; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + inst->enc_param.level = 51; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + inst->enc_param.min_qp_i = ctrl->val; + inst->enc_param.min_qp_p = ctrl->val; + inst->enc_param.min_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + inst->enc_param.max_qp_i = ctrl->val; + inst->enc_param.max_qp_p = ctrl->val; + inst->enc_param.max_qp_b = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + inst->enc_param.intra_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: + inst->enc_param.disable_deblk = 1; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: + inst->enc_param.disable_deblk = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 1; + break; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + inst->enc_param.disable_deblk = 0; + inst->enc_param.lf_cross_slice_boundary_enable = 0; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + inst->enc_param.beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + inst->enc_param.tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + inst->enc_param.transform8x8_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + inst->enc_param.const_intra_pred_flag = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + inst->enc_param.chroma_cb_qp_offset = ctrl->val; + inst->enc_param.chroma_cr_qp_offset = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + inst->enc_param.intra_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + inst->enc_param.entropy_coding_mode = ctrl->val; + break; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wave5_vpu_enc_ctrl_ops = { + .s_ctrl = wave5_vpu_enc_s_ctrl, +}; + +static int wave5_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane inst_format = + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt; + unsigned int i; + + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__, + *num_buffers, *num_planes, q->type); + + if (*num_planes) { + if (inst_format.num_planes != *num_planes) + return -EINVAL; + + for (i = 0; i < *num_planes; i++) { + if (sizes[i] < inst_format.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = inst_format.num_planes; + for (i = 0; i < *num_planes; i++) { + sizes[i] = inst_format.plane_fmt[i].sizeimage; + dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]); + } + } + + dev_dbg(inst->dev->dev, "%s: size: %u\n", __func__, sizes[0]); + + return 0; +} + +static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n", + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf->sequence = inst->queued_src_buf_num++; + else + vbuf->sequence = inst->queued_dst_buf_num++; + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); +} + +static void wave5_set_enc_openparam(struct enc_open_param *open_param, + struct vpu_instance *inst) +{ + struct enc_wave_param input = inst->enc_param; + u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64; + u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16; + + open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE; + open_param->wave_param.hvs_qp_scale = 2; + open_param->wave_param.hvs_max_delta_qp = 10; + open_param->wave_param.skip_intra_trans = 1; + open_param->wave_param.intra_nx_n_enable = 1; + open_param->wave_param.nr_intra_weight_y = 7; + open_param->wave_param.nr_intra_weight_cb = 7; + open_param->wave_param.nr_intra_weight_cr = 7; + open_param->wave_param.nr_inter_weight_y = 4; + open_param->wave_param.nr_inter_weight_cb = 4; + open_param->wave_param.nr_inter_weight_cr = 4; + open_param->wave_param.rdo_skip = 1; + open_param->wave_param.lambda_scaling_enable = 1; + + open_param->line_buf_int_en = true; + open_param->pic_width = inst->dst_fmt.width; + open_param->pic_height = inst->dst_fmt.height; + open_param->frame_rate_info = inst->frame_rate; + open_param->rc_enable = inst->rc_enable; + if (inst->rc_enable) { + open_param->wave_param.initial_rc_qp = -1; + open_param->wave_param.rc_weight_param = 16; + open_param->wave_param.rc_weight_buf = 128; + } + open_param->wave_param.mb_level_rc_enable = input.mb_level_rc_enable; + open_param->wave_param.cu_level_rc_enable = input.cu_level_rc_enable; + open_param->wave_param.hvs_qp_enable = input.hvs_qp_enable; + open_param->bit_rate = inst->bit_rate; + open_param->vbv_buffer_size = inst->vbv_buf_size; + if (inst->rc_mode == 0) + open_param->vbv_buffer_size = 3000; + open_param->wave_param.profile = input.profile; + open_param->wave_param.en_still_picture = input.en_still_picture; + open_param->wave_param.level = input.level; + open_param->wave_param.internal_bit_depth = inst->bit_depth; + open_param->wave_param.intra_qp = input.intra_qp; + open_param->wave_param.min_qp_i = input.min_qp_i; + open_param->wave_param.max_qp_i = input.max_qp_i; + open_param->wave_param.min_qp_p = input.min_qp_p; + open_param->wave_param.max_qp_p = input.max_qp_p; + open_param->wave_param.min_qp_b = input.min_qp_b; + open_param->wave_param.max_qp_b = input.max_qp_b; + open_param->wave_param.disable_deblk = input.disable_deblk; + open_param->wave_param.lf_cross_slice_boundary_enable = + input.lf_cross_slice_boundary_enable; + open_param->wave_param.tc_offset_div2 = input.tc_offset_div2; + open_param->wave_param.beta_offset_div2 = input.beta_offset_div2; + open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type; + open_param->wave_param.intra_period = input.intra_period; + if (inst->std == W_HEVC_ENC) { + if (input.intra_period == 0) { + open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR; + open_param->wave_param.intra_period = input.avc_idr_period; + } + } else { + open_param->wave_param.avc_idr_period = input.avc_idr_period; + } + open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; + open_param->wave_param.lossless_enable = input.lossless_enable; + open_param->wave_param.const_intra_pred_flag = input.const_intra_pred_flag; + open_param->wave_param.wpp_enable = input.wpp_enable; + open_param->wave_param.strong_intra_smooth_enable = input.strong_intra_smooth_enable; + open_param->wave_param.max_num_merge = input.max_num_merge; + open_param->wave_param.tmvp_enable = input.tmvp_enable; + open_param->wave_param.transform8x8_enable = input.transform8x8_enable; + open_param->wave_param.chroma_cb_qp_offset = input.chroma_cb_qp_offset; + open_param->wave_param.chroma_cr_qp_offset = input.chroma_cr_qp_offset; + open_param->wave_param.independ_slice_mode = input.independ_slice_mode; + open_param->wave_param.independ_slice_mode_arg = input.independ_slice_mode_arg; + open_param->wave_param.avc_slice_mode = input.avc_slice_mode; + open_param->wave_param.avc_slice_arg = input.avc_slice_arg; + open_param->wave_param.intra_mb_refresh_mode = input.intra_mb_refresh_mode; + if (input.intra_mb_refresh_mode != REFRESH_MB_MODE_NONE) { + if (num_mb_row >= input.intra_mb_refresh_arg) + open_param->wave_param.intra_mb_refresh_arg = + num_mb_row / input.intra_mb_refresh_arg; + else + open_param->wave_param.intra_mb_refresh_arg = num_mb_row; + } + open_param->wave_param.intra_refresh_mode = input.intra_refresh_mode; + if (input.intra_refresh_mode != 0) { + if (num_ctu_row >= input.intra_refresh_arg) + open_param->wave_param.intra_refresh_arg = + num_ctu_row / input.intra_refresh_arg; + else + open_param->wave_param.intra_refresh_arg = num_ctu_row; + } +} + +static int initialize_sequence(struct vpu_instance *inst) +{ + struct enc_initial_info initial_info; + struct v4l2_ctrl *ctrl; + int ret; + + ret = wave5_vpu_enc_issue_seq_init(inst); + if (ret) { + dev_err(inst->dev->dev, "%s: wave5_vpu_enc_issue_seq_init, fail: %d\n", + __func__, ret); + return ret; + } + + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) { + dev_err(inst->dev->dev, "%s: wave5_vpu_wait_interrupt failed\n", __func__); + return -EINVAL; + } + + ret = wave5_vpu_enc_complete_seq_init(inst, &initial_info); + if (ret) + return ret; + + dev_dbg(inst->dev->dev, "%s: min_frame_buffer: %u | min_source_buffer: %u\n", + __func__, initial_info.min_frame_buffer_count, + initial_info.min_src_frame_count); + inst->min_src_buf_count = initial_info.min_src_frame_count + + COMMAND_QUEUE_DEPTH; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, inst->min_src_buf_count); + + inst->fbc_buf_count = initial_info.min_frame_buffer_count; + + return 0; +} + +static int prepare_fb(struct vpu_instance *inst) +{ + u32 fb_stride = ALIGN(inst->dst_fmt.width, 32); + u32 fb_height = ALIGN(inst->dst_fmt.height, 32); + int i, ret = 0; + + for (i = 0; i < inst->fbc_buf_count; i++) { + u32 luma_size = fb_stride * fb_height; + u32 chroma_size = ALIGN(fb_stride / 2, 16) * fb_height; + + inst->frame_vbuf[i].size = luma_size + chroma_size; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &inst->frame_vbuf[i]); + if (ret < 0) { + dev_err(inst->dev->dev, "%s: failed to allocate FBC buffer %zu\n", + __func__, inst->frame_vbuf[i].size); + goto free_buffers; + } + + inst->frame_buf[i].buf_y = inst->frame_vbuf[i].daddr; + inst->frame_buf[i].buf_cb = (dma_addr_t)-1; + inst->frame_buf[i].buf_cr = (dma_addr_t)-1; + inst->frame_buf[i].update_fb_info = true; + inst->frame_buf[i].size = inst->frame_vbuf[i].size; + } + + ret = wave5_vpu_enc_register_frame_buffer(inst, inst->fbc_buf_count, fb_stride, + fb_height, COMPRESSED_FRAME_MAP); + if (ret) { + dev_err(inst->dev->dev, + "%s: wave5_vpu_enc_register_frame_buffer, fail: %d\n", + __func__, ret); + goto free_buffers; + } + + return 0; +free_buffers: + for (i = 0; i < inst->fbc_buf_count; i++) + wave5_vpu_dec_reset_framebuffer(inst, i); + return ret; +} + +static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + int ret = 0; + + v4l2_m2m_update_start_streaming_state(m2m_ctx, q); + + if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct enc_open_param open_param; + + memset(&open_param, 0, sizeof(struct enc_open_param)); + + wave5_set_enc_openparam(&open_param, inst); + + ret = wave5_vpu_enc_open(inst, &open_param); + if (ret) { + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_open, fail: %d\n", + __func__, ret); + goto return_buffers; + } + + if (inst->mirror_direction) { + wave5_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL); + wave5_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION, + &inst->mirror_direction); + } + if (inst->rot_angle) { + wave5_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL); + wave5_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, &inst->rot_angle); + } + + ret = switch_state(inst, VPU_INST_STATE_OPEN); + if (ret) + goto return_buffers; + } + if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) { + ret = initialize_sequence(inst); + if (ret) { + dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret); + goto return_buffers; + } + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); + if (ret) + goto return_buffers; + /* + * The sequence must be analyzed first to calculate the proper + * size of the auxiliary buffers. + */ + ret = prepare_fb(inst); + if (ret) { + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret); + goto return_buffers; + } + + ret = switch_state(inst, VPU_INST_STATE_PIC_RUN); + } + if (ret) + goto return_buffers; + + return 0; +return_buffers: + wave5_return_bufs(q, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void streamoff_output(struct vpu_instance *inst, struct vb2_queue *q) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } +} + +static void streamoff_capture(struct vpu_instance *inst, struct vb2_queue *q) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) { + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n", + __func__, buf->vb2_buf.type, buf->vb2_buf.index); + vb2_set_plane_payload(&buf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + } + + v4l2_m2m_clear_state(m2m_ctx); +} + +static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q) +{ + struct vpu_instance *inst = vb2_get_drv_priv(q); + bool check_cmd = true; + + /* + * Note that we don't need m2m_ctx->next_buf_last for this driver, so we + * don't call v4l2_m2m_update_stop_streaming_state(). + */ + + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); + + if (wave5_vpu_both_queues_are_streaming(inst)) + switch_state(inst, VPU_INST_STATE_STOP); + + while (check_cmd) { + struct queue_status_info q_status; + struct enc_output_info enc_output_info; + + wave5_vpu_enc_give_command(inst, ENC_GET_QUEUE_STATUS, &q_status); + + if (q_status.report_queue_count == 0) + break; + + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) + break; + + if (wave5_vpu_enc_get_output_info(inst, &enc_output_info)) + dev_dbg(inst->dev->dev, "Getting encoding results from fw, fail\n"); + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + streamoff_output(inst, q); + else + streamoff_capture(inst, q); +} + +static const struct vb2_ops wave5_vpu_enc_vb2_ops = { + .queue_setup = wave5_vpu_enc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = wave5_vpu_enc_buf_queue, + .start_streaming = wave5_vpu_enc_start_streaming, + .stop_streaming = wave5_vpu_enc_stop_streaming, +}; + +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, + struct v4l2_pix_format_mplane *dst_fmt) +{ + unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; + const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt); + + src_fmt->pixelformat = src_pix_fmt; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->flags = 0; + src_fmt->num_planes = src_fmt_info->mem_planes; + wave5_update_pix_fmt(src_fmt, 416, 240); + + dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt; + dst_fmt->field = V4L2_FIELD_NONE; + dst_fmt->flags = 0; + dst_fmt->num_planes = 1; + wave5_update_pix_fmt(dst_fmt, 416, 240); +} + +static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_enc_vb2_ops); +} + +static const struct vpu_instance_ops wave5_vpu_enc_inst_ops = { + .finish_process = wave5_vpu_enc_finish_encode, +}; + +static void wave5_vpu_enc_device_run(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + u32 fail_res = 0; + int ret = 0; + + switch (inst->state) { + case VPU_INST_STATE_PIC_RUN: + ret = start_encode(inst, &fail_res); + if (ret) { + if (ret == -EINVAL) + dev_err(inst->dev->dev, + "Frame encoding on m2m context (%p), fail: %d (res: %d)\n", + m2m_ctx, ret, fail_res); + else if (ret == -EAGAIN) + dev_dbg(inst->dev->dev, "Missing buffers for encode, try again\n"); + break; + } + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); + return; + default: + WARN(1, "Execution of a job in state %s is invalid.\n", + state_to_str(inst->state)); + break; + } + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); +} + +static int wave5_vpu_enc_job_ready(void *priv) +{ + struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + + switch (inst->state) { + case VPU_INST_STATE_NONE: + dev_dbg(inst->dev->dev, "Encoder must be open to start queueing M2M jobs!\n"); + return false; + case VPU_INST_STATE_PIC_RUN: + if (m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, "Encoder ready for a job, state: %s\n", + state_to_str(inst->state)); + return true; + } + fallthrough; + default: + dev_dbg(inst->dev->dev, + "Encoder not ready for a job, state: %s, %s draining, %d src bufs ready\n", + state_to_str(inst->state), m2m_ctx->is_draining ? "is" : "is not", + v4l2_m2m_num_src_bufs_ready(m2m_ctx)); + break; + } + return false; +} + +static const struct v4l2_m2m_ops wave5_vpu_enc_m2m_ops = { + .device_run = wave5_vpu_enc_device_run, + .job_ready = wave5_vpu_enc_job_ready, +}; + +static int wave5_vpu_open_enc(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct vpu_device *dev = video_drvdata(filp); + struct vpu_instance *inst = NULL; + struct v4l2_ctrl_handler *v4l2_ctrl_hdl; + int ret = 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl; + + inst->dev = dev; + inst->type = VPU_INST_TYPE_ENC; + inst->ops = &wave5_vpu_enc_inst_ops; + + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); + if (!inst->codec_info) + return -ENOMEM; + + v4l2_fh_init(&inst->v4l2_fh, vdev); + filp->private_data = &inst->v4l2_fh; + v4l2_fh_add(&inst->v4l2_fh); + + INIT_LIST_HEAD(&inst->list); + list_add_tail(&inst->list, &dev->instances); + + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev; + inst->v4l2_fh.m2m_ctx = + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_enc_queue_init); + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); + goto cleanup_inst; + } + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true); + + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + 0, 63, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + 0, 63, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + 0, 63, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2, + -6, 6, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, 0, + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1, + 1, 2, 1, 2); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION, + 0, 1, 1, 1); + + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0, + V4L2_MPEG_VIDEO_H264_LEVEL_1_0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 63, 1, 8); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 63, 1, 51); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 63, 1, 30); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, + -12, 12, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 2047, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_AU_DELIMITER, + 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_ROTATE, + 0, 270, 90, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, + 10, 3000, 1, 1000); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 700000000, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 2047, 1, 0); + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + 0, 0xFFFF, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + + if (v4l2_ctrl_hdl->error) { + ret = -ENODEV; + goto cleanup_inst; + } + + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); + + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + inst->colorspace = V4L2_COLORSPACE_REC709; + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + inst->quantization = V4L2_QUANTIZATION_DEFAULT; + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; + inst->frame_rate = 30; + + init_completion(&inst->irq_done); + + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); + if (inst->id < 0) { + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id); + ret = inst->id; + goto cleanup_inst; + } + + wave5_vdi_allocate_sram(inst->dev); + + return 0; + +cleanup_inst: + wave5_cleanup_instance(inst); + return ret; +} + +static int wave5_vpu_enc_release(struct file *filp) +{ + return wave5_vpu_release_device(filp, wave5_vpu_enc_close, "encoder"); +} + +static const struct v4l2_file_operations wave5_vpu_enc_fops = { + .owner = THIS_MODULE, + .open = wave5_vpu_open_enc, + .release = wave5_vpu_enc_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +int wave5_vpu_enc_register_device(struct vpu_device *dev) +{ + struct video_device *vdev_enc; + int ret; + + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL); + if (!vdev_enc) + return -ENOMEM; + + dev->v4l2_m2m_enc_dev = v4l2_m2m_init(&wave5_vpu_enc_m2m_ops); + if (IS_ERR(dev->v4l2_m2m_enc_dev)) { + ret = PTR_ERR(dev->v4l2_m2m_enc_dev); + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret); + return -EINVAL; + } + + dev->video_dev_enc = vdev_enc; + + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name)); + vdev_enc->fops = &wave5_vpu_enc_fops; + vdev_enc->ioctl_ops = &wave5_vpu_enc_ioctl_ops; + vdev_enc->release = video_device_release_empty; + vdev_enc->v4l2_dev = &dev->v4l2_dev; + vdev_enc->vfl_dir = VFL_DIR_M2M; + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vdev_enc->lock = &dev->dev_lock; + + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + video_set_drvdata(vdev_enc, dev); + + return 0; +} + +void wave5_vpu_enc_unregister_device(struct vpu_device *dev) +{ + video_unregister_device(dev->video_dev_enc); + if (dev->v4l2_m2m_enc_dev) + v4l2_m2m_release(dev->v4l2_m2m_enc_dev); +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c new file mode 100644 index 000000000..0d90b5820 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - platform driver + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ +#include +#include +#include +#include +#include +#include +#include "wave5-vpu.h" +#include "wave5-regdefine.h" +#include "wave5-vpuconfig.h" +#include "wave5.h" + +#define VPU_PLATFORM_DEVICE_NAME "vdec" +#define VPU_CLK_NAME "vcodec" + +#define WAVE5_IS_ENC BIT(0) +#define WAVE5_IS_DEC BIT(1) + +struct wave5_match_data { + int flags; + const char *fw_name; +}; + +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout) +{ + int ret; + + ret = wait_for_completion_timeout(&inst->irq_done, + msecs_to_jiffies(timeout)); + if (!ret) + return -ETIMEDOUT; + + reinit_completion(&inst->irq_done); + + return 0; +} + +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) +{ + u32 seq_done; + u32 cmd_done; + u32 irq_reason; + struct vpu_instance *inst; + struct vpu_device *dev = dev_id; + + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) { + irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON); + wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason); + wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1); + + list_for_each_entry(inst, &dev->instances, list) { + seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO); + cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST); + + if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) || + irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { + if (seq_done & BIT(inst->id)) { + seq_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, + seq_done); + complete(&inst->irq_done); + } + } + + if (irq_reason & BIT(INT_WAVE5_DEC_PIC) || + irq_reason & BIT(INT_WAVE5_ENC_PIC)) { + if (cmd_done & BIT(inst->id)) { + cmd_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + inst->ops->finish_process(inst); + } + } + + wave5_vpu_clear_interrupt(inst, irq_reason); + } + } + + return IRQ_HANDLED; +} + +static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name, + u32 *revision) +{ + const struct firmware *fw; + int ret; + unsigned int product_id; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + dev_err(dev, "request_firmware, fail: %d\n", ret); + return ret; + } + + ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size); + if (ret) { + dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret); + release_firmware(fw); + return ret; + } + release_firmware(fw); + + ret = wave5_vpu_get_version_info(dev, revision, &product_id); + if (ret) { + dev_err(dev, "vpu_get_version_info fail: %d\n", ret); + return ret; + } + + dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n", + __func__, product_id, *revision); + + return 0; +} + +static int wave5_vpu_probe(struct platform_device *pdev) +{ + int ret; + struct vpu_device *dev; + const struct wave5_match_data *match_data; + u32 fw_revision; + + match_data = device_get_match_data(&pdev->dev); + if (!match_data) { + dev_err(&pdev->dev, "missing device match data\n"); + return -EINVAL; + } + + /* physical addresses limited to 32 bits */ + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret); + return ret; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vdb_register = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->vdb_register)) + return PTR_ERR(dev->vdb_register); + ida_init(&dev->inst_ida); + + mutex_init(&dev->dev_lock); + mutex_init(&dev->hw_lock); + dev_set_drvdata(&pdev->dev, dev); + dev->dev = &pdev->dev; + + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); + + /* continue without clock, assume externally managed */ + if (ret < 0) { + dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret); + ret = 0; + } + dev->num_clks = ret; + + ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks); + if (ret) { + dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "sram-size", + &dev->sram_size); + if (ret) { + dev_warn(&pdev->dev, "sram-size not found\n"); + dev->sram_size = 0; + } + + dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (!dev->sram_pool) + dev_warn(&pdev->dev, "sram node not found\n"); + + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER); + ret = wave5_vdi_init(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret); + goto err_clk_dis; + } + dev->product = wave5_vpu_get_product_id(dev); + + INIT_LIST_HEAD(&dev->instances); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret); + goto err_vdi_release; + } + + if (match_data->flags & WAVE5_IS_DEC) { + ret = wave5_vpu_dec_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret); + goto err_v4l2_unregister; + } + } + if (match_data->flags & WAVE5_IS_ENC) { + ret = wave5_vpu_enc_register_device(dev); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret); + goto err_dec_unreg; + } + } + + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + ret = -ENXIO; + goto err_enc_unreg; + } + + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL, + wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev); + if (ret) { + dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret); + goto err_enc_unreg; + } + + ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision); + if (ret) { + dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret); + goto err_enc_unreg; + } + + dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n", + (match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "", + (match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : ""); + dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code); + dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision); + return 0; + +err_enc_unreg: + if (match_data->flags & WAVE5_IS_ENC) + wave5_vpu_enc_unregister_device(dev); +err_dec_unreg: + if (match_data->flags & WAVE5_IS_DEC) + wave5_vpu_dec_unregister_device(dev); +err_v4l2_unregister: + v4l2_device_unregister(&dev->v4l2_dev); +err_vdi_release: + wave5_vdi_release(&pdev->dev); +err_clk_dis: + clk_bulk_disable_unprepare(dev->num_clks, dev->clks); + + return ret; +} + +static int wave5_vpu_remove(struct platform_device *pdev) +{ + struct vpu_device *dev = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&dev->dev_lock); + mutex_destroy(&dev->hw_lock); + clk_bulk_disable_unprepare(dev->num_clks, dev->clks); + wave5_vpu_enc_unregister_device(dev); + wave5_vpu_dec_unregister_device(dev); + v4l2_device_unregister(&dev->v4l2_dev); + wave5_vdi_release(&pdev->dev); + ida_destroy(&dev->inst_ida); + + return 0; +} + +static const struct wave5_match_data ti_wave521c_data = { + .flags = WAVE5_IS_ENC | WAVE5_IS_DEC, + .fw_name = "cnm/wave521c_k3_codec_fw.bin", +}; + +static const struct of_device_id wave5_dt_ids[] = { + { .compatible = "ti,j721s2-wave521c", .data = &ti_wave521c_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, wave5_dt_ids); + +static struct platform_driver wave5_vpu_driver = { + .driver = { + .name = VPU_PLATFORM_DEVICE_NAME, + .of_match_table = of_match_ptr(wave5_dt_ids), + }, + .probe = wave5_vpu_probe, + .remove = wave5_vpu_remove, +}; + +module_platform_driver(wave5_vpu_driver); +MODULE_DESCRIPTION("chips&media VPU V4L2 driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h new file mode 100644 index 000000000..32b7fd373 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - basic types + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ +#ifndef __VPU_DRV_H__ +#define __VPU_DRV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "wave5-vpuconfig.h" +#include "wave5-vpuapi.h" + +#define VPU_BUF_SYNC_TO_DEVICE 0 +#define VPU_BUF_SYNC_FROM_DEVICE 1 + +struct vpu_src_buffer { + struct v4l2_m2m_buffer v4l2_m2m_buf; + struct list_head list; + bool consumed; +}; + +struct vpu_dst_buffer { + struct v4l2_m2m_buffer v4l2_m2m_buf; + bool display; +}; + +enum vpu_fmt_type { + VPU_FMT_TYPE_CODEC = 0, + VPU_FMT_TYPE_RAW = 1 +}; + +struct vpu_format { + unsigned int v4l2_pix_fmt; + unsigned int max_width; + unsigned int min_width; + unsigned int max_height; + unsigned int min_height; +}; + +static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh) +{ + return container_of(vfh, struct vpu_instance, v4l2_fh); +} + +static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl) +{ + return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl); +} + +static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb); +} + +static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb); +} + +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout); + +int wave5_vpu_dec_register_device(struct vpu_device *dev); +void wave5_vpu_dec_unregister_device(struct vpu_device *dev); +int wave5_vpu_enc_register_device(struct vpu_device *dev); +void wave5_vpu_enc_unregister_device(struct vpu_device *dev); +static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst) +{ + struct vb2_queue *vq_cap = + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + struct vb2_queue *vq_out = + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out); +} + +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c new file mode 100644 index 000000000..1a3efb638 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave5 series multi-standard codec IP - helper functions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#include +#include "wave5-vpuapi.h" +#include "wave5-regdefine.h" +#include "wave5.h" + +#define DECODE_ALL_TEMPORAL_LAYERS 0 +#define DECODE_ALL_SPATIAL_LAYERS 0 + +static int wave5_initialize_vpu(struct device *dev, u8 *code, size_t size) +{ + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (wave5_vpu_is_init(vpu_dev)) { + wave5_vpu_re_init(dev, (void *)code, size); + ret = -EBUSY; + goto err_out; + } + + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT); + if (ret) + goto err_out; + + ret = wave5_vpu_init(dev, (void *)code, size); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + return ret; +} + +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size) +{ + if (!bitcode || size == 0) + return -EINVAL; + + return wave5_initialize_vpu(dev, bitcode, size); +} + +int wave5_vpu_flush_instance(struct vpu_instance *inst) +{ + int ret = 0; + int retry = 0; + + ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (ret) + return ret; + do { + /* + * Repeat the FLUSH command until the firmware reports that the + * VPU isn't running anymore + */ + ret = wave5_vpu_hw_flush_instance(inst); + if (ret < 0 && ret != -EBUSY) { + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d fail: %d\n", + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id, + ret); + mutex_unlock(&inst->dev->hw_lock); + return ret; + } + if (ret == -EBUSY && retry++ >= MAX_FIRMWARE_CALL_RETRY) { + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d timed out!\n", + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id); + mutex_unlock(&inst->dev->hw_lock); + return -ETIMEDOUT; + } + } while (ret != 0); + mutex_unlock(&inst->dev->hw_lock); + + return ret; +} + +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id) +{ + int ret; + struct vpu_device *vpu_dev = dev_get_drvdata(dev); + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + ret = -EINVAL; + goto err_out; + } + + if (product_id) + *product_id = vpu_dev->product; + ret = wave5_vpu_get_version(vpu_dev, revision); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + return ret; +} + +static int wave5_check_dec_open_param(struct vpu_instance *inst, struct dec_open_param *param) +{ + if (inst->id >= MAX_NUM_INSTANCE) { + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n", + inst->id, MAX_NUM_INSTANCE); + return -EOPNOTSUPP; + } + + if (param->bitstream_buffer % 8) { + dev_err(inst->dev->dev, + "Bitstream buffer must be aligned to a multiple of 8\n"); + return -EINVAL; + } + + if (param->bitstream_buffer_size % 1024 || + param->bitstream_buffer_size < MIN_BITSTREAM_BUFFER_SIZE) { + dev_err(inst->dev->dev, + "Bitstream buffer size must be aligned to a multiple of 1024 and have a minimum size of %d\n", + MIN_BITSTREAM_BUFFER_SIZE); + return -EINVAL; + } + + return 0; +} + +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + dma_addr_t buffer_addr; + size_t buffer_size; + + ret = wave5_check_dec_open_param(inst, open_param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + mutex_unlock(&vpu_dev->hw_lock); + return -ENODEV; + } + + p_dec_info = &inst->codec_info->dec_info; + memcpy(&p_dec_info->open_param, open_param, sizeof(struct dec_open_param)); + + buffer_addr = open_param->bitstream_buffer; + buffer_size = open_param->bitstream_buffer_size; + p_dec_info->stream_wr_ptr = buffer_addr; + p_dec_info->stream_rd_ptr = buffer_addr; + p_dec_info->stream_buf_start_addr = buffer_addr; + p_dec_info->stream_buf_size = buffer_size; + p_dec_info->stream_buf_end_addr = buffer_addr + buffer_size; + p_dec_info->reorder_enable = TRUE; + p_dec_info->temp_id_select_mode = TEMPORAL_ID_MODE_ABSOLUTE; + p_dec_info->target_temp_id = DECODE_ALL_TEMPORAL_LAYERS; + p_dec_info->target_spatial_id = DECODE_ALL_SPATIAL_LAYERS; + + ret = wave5_vpu_build_up_dec_param(inst, open_param); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +static int reset_auxiliary_buffers(struct vpu_instance *inst, unsigned int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + + if (index >= MAX_REG_FRAME) + return 1; + + if (p_dec_info->vb_mv[index].size == 0 && p_dec_info->vb_fbc_y_tbl[index].size == 0 && + p_dec_info->vb_fbc_c_tbl[index].size == 0) + return 1; + + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[index]); + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[index]); + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[index]); + + return 0; +} + +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + int retry = 0; + struct vpu_device *vpu_dev = inst->dev; + int i; + + *fail_res = 0; + if (!inst->codec_info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + do { + ret = wave5_vpu_dec_finish_seq(inst, fail_res); + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_warn(inst->dev->dev, "dec_finish_seq timed out\n"); + goto unlock_and_return; + } + + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && + retry++ >= MAX_FIRMWARE_CALL_RETRY) { + ret = -ETIMEDOUT; + goto unlock_and_return; + } + } while (ret != 0); + + dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__); + + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work); + + for (i = 0 ; i < MAX_REG_FRAME; i++) { + ret = reset_auxiliary_buffers(inst, i); + if (ret) { + ret = 0; + break; + } + } + + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task); + +unlock_and_return: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst) +{ + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_dec_init_seq(inst); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_dec_get_seq_info(inst, info); + if (!ret) + p_dec_info->initial_info_obtained = true; + + info->rd_ptr = wave5_dec_get_rd_ptr(inst); + info->wr_ptr = p_dec_info->stream_wr_ptr; + + p_dec_info->initial_info = *info; + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs, + int num_of_display_fbs, int stride, int height) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + struct frame_buffer *fb; + + if (num_of_decoding_fbs >= WAVE5_MAX_FBS || num_of_display_fbs >= WAVE5_MAX_FBS) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + p_dec_info->num_of_decoding_fbs = num_of_decoding_fbs; + p_dec_info->num_of_display_fbs = num_of_display_fbs; + p_dec_info->stride = stride; + + if (!p_dec_info->initial_info_obtained) + return -EINVAL; + + if (stride < p_dec_info->initial_info.pic_width || (stride % 8 != 0) || + height < p_dec_info->initial_info.pic_height) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + fb = inst->frame_buf; + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[p_dec_info->num_of_decoding_fbs], + LINEAR_FRAME_MAP, p_dec_info->num_of_display_fbs); + if (ret) + goto err_out; + + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[0], COMPRESSED_FRAME_MAP, + p_dec_info->num_of_decoding_fbs); + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr, + dma_addr_t *pwr_ptr, size_t *size) +{ + struct dec_info *p_dec_info; + dma_addr_t rd_ptr; + dma_addr_t wr_ptr; + int room; + struct vpu_device *vpu_dev = inst->dev; + int ret; + + p_dec_info = &inst->codec_info->dec_info; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + rd_ptr = wave5_dec_get_rd_ptr(inst); + mutex_unlock(&vpu_dev->hw_lock); + + wr_ptr = p_dec_info->stream_wr_ptr; + + if (wr_ptr < rd_ptr) + room = rd_ptr - wr_ptr; + else + room = (p_dec_info->stream_buf_end_addr - wr_ptr) + + (rd_ptr - p_dec_info->stream_buf_start_addr); + room--; + + if (prd_ptr) + *prd_ptr = rd_ptr; + if (pwr_ptr) + *pwr_ptr = wr_ptr; + if (size) + *size = room; + + return 0; +} + +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size) +{ + struct dec_info *p_dec_info; + dma_addr_t wr_ptr; + dma_addr_t rd_ptr; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (!inst->codec_info) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + wr_ptr = p_dec_info->stream_wr_ptr; + rd_ptr = p_dec_info->stream_rd_ptr; + + if (size > 0) { + if (wr_ptr < rd_ptr && rd_ptr <= wr_ptr + size) + return -EINVAL; + + wr_ptr += size; + + if (wr_ptr > p_dec_info->stream_buf_end_addr) { + u32 room = wr_ptr - p_dec_info->stream_buf_end_addr; + + wr_ptr = p_dec_info->stream_buf_start_addr; + wr_ptr += room; + } else if (wr_ptr == p_dec_info->stream_buf_end_addr) { + wr_ptr = p_dec_info->stream_buf_start_addr; + } + + p_dec_info->stream_wr_ptr = wr_ptr; + p_dec_info->stream_rd_ptr = rd_ptr; + } + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_vpu_dec_set_bitstream_flag(inst, (size == 0)); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (p_dec_info->stride == 0) /* this means frame buffers have not been registered. */ + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_decode(inst, res_fail); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_dec_set_rd_ptr(inst, addr); + + p_dec_info->stream_rd_ptr = addr; + if (update_wr_ptr) + p_dec_info->stream_wr_ptr = addr; + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst) +{ + int ret; + dma_addr_t rd_ptr; + + ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (ret) + return ret; + + rd_ptr = wave5_dec_get_rd_ptr(inst); + + mutex_unlock(&inst->dev->hw_lock); + + return rd_ptr; +} + +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info) +{ + struct dec_info *p_dec_info; + int ret; + struct vpu_rect rect_info; + u32 val; + u32 decoded_index; + u32 disp_idx; + u32 max_dec_index; + struct vpu_device *vpu_dev = inst->dev; + struct dec_output_info *disp_info; + + if (!info) + return -EINVAL; + + p_dec_info = &inst->codec_info->dec_info; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + memset(info, 0, sizeof(*info)); + + ret = wave5_vpu_dec_get_result(inst, info); + if (ret) { + info->rd_ptr = p_dec_info->stream_rd_ptr; + info->wr_ptr = p_dec_info->stream_wr_ptr; + goto err_out; + } + + decoded_index = info->index_frame_decoded; + + /* calculate display frame region */ + val = 0; + rect_info.left = 0; + rect_info.right = 0; + rect_info.top = 0; + rect_info.bottom = 0; + + if (decoded_index < WAVE5_MAX_FBS) { + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) + rect_info = p_dec_info->initial_info.pic_crop_rect; + + if (inst->std == W_HEVC_DEC) + p_dec_info->dec_out_info[decoded_index].decoded_poc = info->decoded_poc; + + p_dec_info->dec_out_info[decoded_index].rc_decoded = rect_info; + } + info->rc_decoded = rect_info; + + disp_idx = info->index_frame_display; + if (info->index_frame_display >= 0 && info->index_frame_display < WAVE5_MAX_FBS) { + disp_info = &p_dec_info->dec_out_info[disp_idx]; + if (info->index_frame_display != info->index_frame_decoded) { + /* + * when index_frame_decoded < 0, and index_frame_display >= 0 + * info->dec_pic_width and info->dec_pic_height are still valid + * but those of p_dec_info->dec_out_info[disp_idx] are invalid in VP9 + */ + info->disp_pic_width = disp_info->dec_pic_width; + info->disp_pic_height = disp_info->dec_pic_height; + } else { + info->disp_pic_width = info->dec_pic_width; + info->disp_pic_height = info->dec_pic_height; + } + + info->rc_display = disp_info->rc_decoded; + + } else { + info->rc_display.left = 0; + info->rc_display.right = 0; + info->rc_display.top = 0; + info->rc_display.bottom = 0; + info->disp_pic_width = 0; + info->disp_pic_height = 0; + } + + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst); + p_dec_info->frame_display_flag = vpu_read_reg(vpu_dev, W5_RET_DEC_DISP_IDC); + + val = p_dec_info->num_of_decoding_fbs; //fb_offset + + max_dec_index = (p_dec_info->num_of_decoding_fbs > p_dec_info->num_of_display_fbs) ? + p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs; + + if (info->index_frame_display >= 0 && + info->index_frame_display < (int)max_dec_index) + info->disp_frame = inst->frame_buf[val + info->index_frame_display]; + + info->rd_ptr = p_dec_info->stream_rd_ptr; + info->wr_ptr = p_dec_info->stream_wr_ptr; + info->frame_display_flag = p_dec_info->frame_display_flag; + + info->sequence_no = p_dec_info->initial_info.sequence_no; + if (decoded_index < WAVE5_MAX_FBS) + p_dec_info->dec_out_info[decoded_index] = *info; + + if (disp_idx < WAVE5_MAX_FBS) + info->disp_frame.sequence_no = info->sequence_no; + + if (info->sequence_changed) { + memcpy((void *)&p_dec_info->initial_info, (void *)&p_dec_info->new_seq_info, + sizeof(struct dec_initial_info)); + p_dec_info->initial_info.sequence_no++; + } + +err_out: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (index >= p_dec_info->num_of_display_fbs) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_dec_clr_disp_flag(inst, index); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret = 0; + struct vpu_device *vpu_dev = inst->dev; + + if (index >= p_dec_info->num_of_display_fbs) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + ret = wave5_dec_set_disp_flag(inst, index); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index) +{ + if (index >= MAX_REG_FRAME) + return -EINVAL; + + if (inst->frame_vbuf[index].size == 0) + return -EINVAL; + + wave5_vdi_free_dma_memory(inst->dev, &inst->frame_vbuf[index]); + + return 0; +} + +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int ret = 0; + + switch (cmd) { + case DEC_GET_QUEUE_STATUS: { + struct queue_status_info *queue_info = parameter; + + queue_info->instance_queue_count = p_dec_info->instance_queue_count; + queue_info->report_queue_count = p_dec_info->report_queue_count; + break; + } + case DEC_RESET_FRAMEBUF_INFO: { + int i; + + for (i = 0; i < MAX_REG_FRAME; i++) { + ret = wave5_vpu_dec_reset_framebuffer(inst, i); + if (ret) + break; + } + + for (i = 0; i < MAX_REG_FRAME; i++) { + ret = reset_auxiliary_buffers(inst, i); + if (ret) + break; + } + + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); + break; + } + case DEC_GET_SEQ_INFO: { + struct dec_initial_info *seq_info = parameter; + + *seq_info = p_dec_info->initial_info; + break; + } + + default: + return -EINVAL; + } + + return ret; +} + +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param) +{ + struct enc_info *p_enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = wave5_vpu_enc_check_open_param(inst, open_param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + if (!wave5_vpu_is_init(vpu_dev)) { + mutex_unlock(&vpu_dev->hw_lock); + return -ENODEV; + } + + p_enc_info = &inst->codec_info->enc_info; + p_enc_info->open_param = *open_param; + + ret = wave5_vpu_build_up_enc_param(vpu_dev->dev, inst, open_param); + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + int retry = 0; + struct vpu_device *vpu_dev = inst->dev; + + *fail_res = 0; + if (!inst->codec_info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + do { + ret = wave5_vpu_enc_finish_seq(inst, fail_res); + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_warn(inst->dev->dev, "enc_finish_seq timed out\n"); + mutex_unlock(&vpu_dev->hw_lock); + return ret; + } + + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && + retry++ >= MAX_FIRMWARE_CALL_RETRY) { + mutex_unlock(&vpu_dev->hw_lock); + return -ETIMEDOUT; + } + } while (ret != 0); + + dev_dbg(inst->dev->dev, "%s: enc_finish_seq complete\n", __func__); + + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work); + + if (inst->std == W_HEVC_ENC || inst->std == W_AVC_ENC) { + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_sub_sam_buf); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_mv); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_y_tbl); + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_c_tbl); + } + + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task); + + mutex_unlock(&vpu_dev->hw_lock); + + return 0; +} + +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num, + unsigned int stride, int height, + enum tiled_map_type map_type) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + unsigned int size_luma, size_chroma; + int i; + + if (p_enc_info->stride) + return -EINVAL; + + if (!p_enc_info->initial_info_obtained) + return -EINVAL; + + if (num < p_enc_info->initial_info.min_frame_buffer_count) + return -EINVAL; + + if (stride == 0 || stride % 8 != 0) + return -EINVAL; + + if (height <= 0) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + p_enc_info->num_frame_buffers = num; + p_enc_info->stride = stride; + + size_luma = stride * height; + size_chroma = ALIGN(stride / 2, 16) * height; + + for (i = 0; i < num; i++) { + if (!inst->frame_buf[i].update_fb_info) + continue; + + inst->frame_buf[i].update_fb_info = false; + inst->frame_buf[i].stride = stride; + inst->frame_buf[i].height = height; + inst->frame_buf[i].map_type = COMPRESSED_FRAME_MAP; + inst->frame_buf[i].buf_y_size = size_luma; + inst->frame_buf[i].buf_cb = inst->frame_buf[i].buf_y + size_luma; + inst->frame_buf[i].buf_cb_size = size_chroma; + inst->frame_buf[i].buf_cr_size = 0; + } + + ret = wave5_vpu_enc_register_framebuffer(inst->dev->dev, inst, &inst->frame_buf[0], + COMPRESSED_FRAME_MAP, + p_enc_info->num_frame_buffers); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +static int wave5_check_enc_param(struct vpu_instance *inst, struct enc_param *param) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + if (!param) + return -EINVAL; + + if (!param->source_frame) + return -EINVAL; + + if (p_enc_info->open_param.bit_rate == 0 && inst->std == W_HEVC_ENC) { + if (param->pic_stream_buffer_addr % 16 || param->pic_stream_buffer_size == 0) + return -EINVAL; + } + if (param->pic_stream_buffer_addr % 8 || param->pic_stream_buffer_size == 0) + return -EINVAL; + + return 0; +} + +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, u32 *fail_res) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + *fail_res = 0; + + if (p_enc_info->stride == 0) /* this means frame buffers have not been registered. */ + return -EINVAL; + + ret = wave5_check_enc_param(inst, param); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + p_enc_info->pts_map[param->src_idx] = param->pts; + + ret = wave5_vpu_encode(inst, param, fail_res); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_get_result(inst, info); + if (ret) { + info->pts = 0; + goto unlock; + } + + if (info->recon_frame_index >= 0) + info->pts = p_enc_info->pts_map[info->enc_src_idx]; + +unlock: + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + + switch (cmd) { + case ENABLE_ROTATION: + p_enc_info->rotation_enable = true; + break; + case ENABLE_MIRRORING: + p_enc_info->mirror_enable = true; + break; + case SET_MIRROR_DIRECTION: { + enum mirror_direction mir_dir; + + mir_dir = *(enum mirror_direction *)parameter; + if (mir_dir != MIRDIR_NONE && mir_dir != MIRDIR_HOR && + mir_dir != MIRDIR_VER && mir_dir != MIRDIR_HOR_VER) + return -EINVAL; + p_enc_info->mirror_direction = mir_dir; + break; + } + case SET_ROTATION_ANGLE: { + int angle; + + angle = *(int *)parameter; + if (angle && angle != 90 && angle != 180 && angle != 270) + return -EINVAL; + if (p_enc_info->initial_info_obtained && (angle == 90 || angle == 270)) + return -EINVAL; + p_enc_info->rotation_angle = angle; + break; + } + case ENC_GET_QUEUE_STATUS: { + struct queue_status_info *queue_info = parameter; + + queue_info->instance_queue_count = p_enc_info->instance_queue_count; + queue_info->report_queue_count = p_enc_info->report_queue_count; + break; + } + default: + return -EINVAL; + } + return 0; +} + +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst) +{ + int ret; + struct vpu_device *vpu_dev = inst->dev; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_init_seq(inst); + + mutex_unlock(&vpu_dev->hw_lock); + + return ret; +} + +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + int ret; + struct vpu_device *vpu_dev = inst->dev; + + if (!info) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret) + return ret; + + ret = wave5_vpu_enc_get_seq_info(inst, info); + if (ret) { + p_enc_info->initial_info_obtained = false; + mutex_unlock(&vpu_dev->hw_lock); + return ret; + } + + p_enc_info->initial_info_obtained = true; + p_enc_info->initial_info = *info; + + mutex_unlock(&vpu_dev->hw_lock); + + return 0; +} diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h new file mode 100644 index 000000000..352f6e904 --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -0,0 +1,870 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - helper definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef VPUAPI_H_INCLUDED +#define VPUAPI_H_INCLUDED + +#include +#include +#include +#include +#include +#include "wave5-vpuerror.h" +#include "wave5-vpuconfig.h" +#include "wave5-vdi.h" + +enum product_id { + PRODUCT_ID_521, + PRODUCT_ID_511, + PRODUCT_ID_517, + PRODUCT_ID_NONE, +}; + +struct vpu_attr; + +enum vpu_instance_type { + VPU_INST_TYPE_DEC = 0, + VPU_INST_TYPE_ENC = 1 +}; + +enum vpu_instance_state { + VPU_INST_STATE_NONE = 0, + VPU_INST_STATE_OPEN = 1, + VPU_INST_STATE_INIT_SEQ = 2, + VPU_INST_STATE_PIC_RUN = 3, + VPU_INST_STATE_STOP = 4 +}; + +/* Maximum available on hardware. */ +#define WAVE5_MAX_FBS 32 + +#define MAX_REG_FRAME (WAVE5_MAX_FBS * 2) + +#define WAVE5_DEC_HEVC_BUF_SIZE(_w, _h) (DIV_ROUND_UP(_w, 64) * DIV_ROUND_UP(_h, 64) * 256 + 64) +#define WAVE5_DEC_AVC_BUF_SIZE(_w, _h) ((((ALIGN(_w, 256) / 16) * (ALIGN(_h, 16) / 16)) + 16) * 80) + +#define WAVE5_FBC_LUMA_TABLE_SIZE(_w, _h) (ALIGN(_h, 64) * ALIGN(_w, 256) / 32) +#define WAVE5_FBC_CHROMA_TABLE_SIZE(_w, _h) (ALIGN((_h), 64) * ALIGN((_w) / 2, 256) / 32) +#define WAVE5_ENC_AVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) * ALIGN(_h, 64) / 32) +#define WAVE5_ENC_HEVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) / 64 * ALIGN(_h, 64) / 64 * 128) + +/* + * common struct and definition + */ +enum cod_std { + STD_AVC = 0, + STD_HEVC = 12, + STD_MAX +}; + +enum wave_std { + W_HEVC_DEC = 0x00, + W_HEVC_ENC = 0x01, + W_AVC_DEC = 0x02, + W_AVC_ENC = 0x03, + STD_UNKNOWN = 0xFF +}; + +enum set_param_option { + OPT_COMMON = 0, /* SET_PARAM command option for encoding sequence */ + OPT_CUSTOM_GOP = 1, /* SET_PARAM command option for setting custom GOP */ + OPT_CUSTOM_HEADER = 2, /* SET_PARAM command option for setting custom VPS/SPS/PPS */ + OPT_VUI = 3, /* SET_PARAM command option for encoding VUI */ + OPT_CHANGE_PARAM = 0x10, +}; + +/************************************************************************/ +/* PROFILE & LEVEL */ +/************************************************************************/ +/* HEVC */ +#define HEVC_PROFILE_MAIN 1 +#define HEVC_PROFILE_MAIN10 2 +#define HEVC_PROFILE_STILLPICTURE 3 +#define HEVC_PROFILE_MAIN10_STILLPICTURE 2 + +/* H.264 profile for encoder*/ +#define H264_PROFILE_BP 1 +#define H264_PROFILE_MP 2 +#define H264_PROFILE_EXTENDED 3 +#define H264_PROFILE_HP 4 +#define H264_PROFILE_HIGH10 5 +#define H264_PROFILE_HIGH422 6 +#define H264_PROFILE_HIGH444 7 + +/************************************************************************/ +/* error codes */ +/************************************************************************/ + +/************************************************************************/ +/* utility macros */ +/************************************************************************/ + +/* Initialize sequence firmware command mode */ +#define INIT_SEQ_NORMAL 1 + +/* Decode firmware command mode */ +#define DEC_PIC_NORMAL 0 + +/* bit_alloc_mode */ +#define BIT_ALLOC_MODE_FIXED_RATIO 2 + +/* bit_rate */ +#define MAX_BIT_RATE 700000000 + +/* decoding_refresh_type */ +#define DEC_REFRESH_TYPE_NON_IRAP 0 +#define DEC_REFRESH_TYPE_CRA 1 +#define DEC_REFRESH_TYPE_IDR 2 + +/* depend_slice_mode */ +#define DEPEND_SLICE_MODE_RECOMMENDED 1 +#define DEPEND_SLICE_MODE_BOOST 2 +#define DEPEND_SLICE_MODE_FAST 3 + +/* hvs_max_delta_qp */ +#define MAX_HVS_MAX_DELTA_QP 51 + +/* intra_refresh_mode */ +#define REFRESH_MODE_CTU_ROWS 1 +#define REFRESH_MODE_CTU_COLUMNS 2 +#define REFRESH_MODE_CTU_STEP_SIZE 3 +#define REFRESH_MODE_CTUS 4 + +/* intra_mb_refresh_mode */ +#define REFRESH_MB_MODE_NONE 0 +#define REFRESH_MB_MODE_CTU_ROWS 1 +#define REFRESH_MB_MODE_CTU_COLUMNS 2 +#define REFRESH_MB_MODE_CTU_STEP_SIZE 3 + +/* intra_qp */ +#define MAX_INTRA_QP 63 + +/* nr_inter_weight_* */ +#define MAX_INTER_WEIGHT 31 + +/* nr_intra_weight_* */ +#define MAX_INTRA_WEIGHT 31 + +/* nr_noise_sigma_* */ +#define MAX_NOISE_SIGMA 255 + +/* bitstream_buffer_size */ +#define MIN_BITSTREAM_BUFFER_SIZE 1024 +#define MIN_BITSTREAM_BUFFER_SIZE_WAVE521 (1024 * 64) + +/* vbv_buffer_size */ +#define MIN_VBV_BUFFER_SIZE 10 +#define MAX_VBV_BUFFER_SIZE 3000 + +#define BUFFER_MARGIN 4096 + +#define MAX_FIRMWARE_CALL_RETRY 10 + +#define VDI_LITTLE_ENDIAN 0x0 + +/* + * Parameters of DEC_SET_SEQ_CHANGE_MASK + */ +#define SEQ_CHANGE_ENABLE_PROFILE BIT(5) +#define SEQ_CHANGE_ENABLE_SIZE BIT(16) +#define SEQ_CHANGE_ENABLE_BITDEPTH BIT(18) +#define SEQ_CHANGE_ENABLE_DPB_COUNT BIT(19) +#define SEQ_CHANGE_ENABLE_ASPECT_RATIO BIT(21) +#define SEQ_CHANGE_ENABLE_VIDEO_SIGNAL BIT(23) +#define SEQ_CHANGE_ENABLE_VUI_TIMING_INFO BIT(29) + +#define SEQ_CHANGE_ENABLE_ALL_HEVC (SEQ_CHANGE_ENABLE_PROFILE | \ + SEQ_CHANGE_ENABLE_SIZE | \ + SEQ_CHANGE_ENABLE_BITDEPTH | \ + SEQ_CHANGE_ENABLE_DPB_COUNT) + +#define SEQ_CHANGE_ENABLE_ALL_AVC (SEQ_CHANGE_ENABLE_SIZE | \ + SEQ_CHANGE_ENABLE_BITDEPTH | \ + SEQ_CHANGE_ENABLE_DPB_COUNT | \ + SEQ_CHANGE_ENABLE_ASPECT_RATIO | \ + SEQ_CHANGE_ENABLE_VIDEO_SIGNAL | \ + SEQ_CHANGE_ENABLE_VUI_TIMING_INFO) + +#define DISPLAY_IDX_FLAG_SEQ_END -1 +#define DISPLAY_IDX_FLAG_NO_FB -3 +#define DECODED_IDX_FLAG_NO_FB -1 +#define DECODED_IDX_FLAG_SKIP -2 + +#define RECON_IDX_FLAG_ENC_END -1 +#define RECON_IDX_FLAG_ENC_DELAY -2 +#define RECON_IDX_FLAG_HEADER_ONLY -3 +#define RECON_IDX_FLAG_CHANGE_PARAM -4 + +enum codec_command { + ENABLE_ROTATION, + ENABLE_MIRRORING, + SET_MIRROR_DIRECTION, + SET_ROTATION_ANGLE, + DEC_GET_QUEUE_STATUS, + ENC_GET_QUEUE_STATUS, + DEC_RESET_FRAMEBUF_INFO, + DEC_GET_SEQ_INFO, +}; + +enum mirror_direction { + MIRDIR_NONE, /* no mirroring */ + MIRDIR_VER, /* vertical mirroring */ + MIRDIR_HOR, /* horizontal mirroring */ + MIRDIR_HOR_VER /* horizontal and vertical mirroring */ +}; + +enum frame_buffer_format { + FORMAT_ERR = -1, + FORMAT_420 = 0, /* 8bit */ + FORMAT_422, /* 8bit */ + FORMAT_224, /* 8bit */ + FORMAT_444, /* 8bit */ + FORMAT_400, /* 8bit */ + + /* little endian perspective */ + /* | addr 0 | addr 1 | */ + FORMAT_420_P10_16BIT_MSB = 5, /* lsb |000000xx|xxxxxxxx | msb */ + FORMAT_420_P10_16BIT_LSB, /* lsb |xxxxxxx |xx000000 | msb */ + FORMAT_420_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */ + FORMAT_420_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */ + + /* 4:2:2 packed format */ + /* little endian perspective */ + /* | addr 0 | addr 1 | */ + FORMAT_422_P10_16BIT_MSB, /* lsb |000000xx |xxxxxxxx | msb */ + FORMAT_422_P10_16BIT_LSB, /* lsb |xxxxxxxx |xx000000 | msb */ + FORMAT_422_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */ + FORMAT_422_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */ + + FORMAT_YUYV, /* 8bit packed format : Y0U0Y1V0 Y2U1Y3V1 ... */ + FORMAT_YUYV_P10_16BIT_MSB, + FORMAT_YUYV_P10_16BIT_LSB, + FORMAT_YUYV_P10_32BIT_MSB, + FORMAT_YUYV_P10_32BIT_LSB, + + FORMAT_YVYU, /* 8bit packed format : Y0V0Y1U0 Y2V1Y3U1 ... */ + FORMAT_YVYU_P10_16BIT_MSB, + FORMAT_YVYU_P10_16BIT_LSB, + FORMAT_YVYU_P10_32BIT_MSB, + FORMAT_YVYU_P10_32BIT_LSB, + + FORMAT_UYVY, /* 8bit packed format : U0Y0V0Y1 U1Y2V1Y3 ... */ + FORMAT_UYVY_P10_16BIT_MSB, + FORMAT_UYVY_P10_16BIT_LSB, + FORMAT_UYVY_P10_32BIT_MSB, + FORMAT_UYVY_P10_32BIT_LSB, + + FORMAT_VYUY, /* 8bit packed format : V0Y0U0Y1 V1Y2U1Y3 ... */ + FORMAT_VYUY_P10_16BIT_MSB, + FORMAT_VYUY_P10_16BIT_LSB, + FORMAT_VYUY_P10_32BIT_MSB, + FORMAT_VYUY_P10_32BIT_LSB, + + FORMAT_MAX, +}; + +enum packed_format_num { + NOT_PACKED = 0, + PACKED_YUYV, + PACKED_YVYU, + PACKED_UYVY, + PACKED_VYUY, +}; + +enum wave5_interrupt_bit { + INT_WAVE5_INIT_VPU = 0, + INT_WAVE5_WAKEUP_VPU = 1, + INT_WAVE5_SLEEP_VPU = 2, + INT_WAVE5_CREATE_INSTANCE = 3, + INT_WAVE5_FLUSH_INSTANCE = 4, + INT_WAVE5_DESTROY_INSTANCE = 5, + INT_WAVE5_INIT_SEQ = 6, + INT_WAVE5_SET_FRAMEBUF = 7, + INT_WAVE5_DEC_PIC = 8, + INT_WAVE5_ENC_PIC = 8, + INT_WAVE5_ENC_SET_PARAM = 9, + INT_WAVE5_DEC_QUERY = 14, + INT_WAVE5_BSBUF_EMPTY = 15, + INT_WAVE5_BSBUF_FULL = 15, +}; + +enum pic_type { + PIC_TYPE_I = 0, + PIC_TYPE_P = 1, + PIC_TYPE_B = 2, + PIC_TYPE_IDR = 5, /* H.264/H.265 IDR (Instantaneous Decoder Refresh) picture */ + PIC_TYPE_MAX /* no meaning */ +}; + +enum sw_reset_mode { + SW_RESET_SAFETY, + SW_RESET_FORCE, + SW_RESET_ON_BOOT +}; + +enum tiled_map_type { + LINEAR_FRAME_MAP = 0, /* linear frame map type */ + COMPRESSED_FRAME_MAP = 17, /* compressed frame map type*/ +}; + +enum temporal_id_mode { + TEMPORAL_ID_MODE_ABSOLUTE, + TEMPORAL_ID_MODE_RELATIVE, +}; + +struct vpu_attr { + u32 product_id; + char product_name[8]; /* product name in ascii code */ + u32 product_version; + u32 fw_version; + u32 customer_id; + u32 support_decoders; /* bitmask */ + u32 support_encoders; /* bitmask */ + u32 support_backbone: 1; + u32 support_avc10bit_enc: 1; + u32 support_hevc10bit_enc: 1; + u32 support_vcore_backbone: 1; + u32 support_vcpu_backbone: 1; +}; + +struct frame_buffer { + dma_addr_t buf_y; + dma_addr_t buf_cb; + dma_addr_t buf_cr; + unsigned int buf_y_size; + unsigned int buf_cb_size; + unsigned int buf_cr_size; + enum tiled_map_type map_type; + unsigned int stride; /* horizontal stride for the given frame buffer */ + unsigned int width; /* width of the given frame buffer */ + unsigned int height; /* height of the given frame buffer */ + size_t size; /* size of the given frame buffer */ + unsigned int sequence_no; + bool update_fb_info; +}; + +struct vpu_rect { + unsigned int left; /* horizontal pixel offset from left edge */ + unsigned int top; /* vertical pixel offset from top edge */ + unsigned int right; /* horizontal pixel offset from right edge */ + unsigned int bottom; /* vertical pixel offset from bottom edge */ +}; + +/* + * decode struct and definition + */ + +struct dec_open_param { + dma_addr_t bitstream_buffer; + size_t bitstream_buffer_size; +}; + +struct dec_initial_info { + u32 pic_width; + u32 pic_height; + struct vpu_rect pic_crop_rect; + u32 min_frame_buffer_count; /* between 1 to 16 */ + + u32 profile; + u32 luma_bitdepth; /* bit-depth of the luma sample */ + u32 chroma_bitdepth; /* bit-depth of the chroma sample */ + u32 seq_init_err_reason; + dma_addr_t rd_ptr; /* read pointer of bitstream buffer */ + dma_addr_t wr_ptr; /* write pointer of bitstream buffer */ + u32 sequence_no; + u32 vlc_buf_size; + u32 param_buf_size; +}; + +struct dec_output_info { + /** + * This is a frame buffer index for the picture to be displayed at the moment + * among frame buffers which are registered using vpu_dec_register_frame_buffer(). + * Frame data that will be displayed is stored in the frame buffer with this index + * When there is no display delay, this index is always the equal to + * index_frame_decoded, however, if displaying is delayed (for display + * reordering in AVC or B-frames in VC1), this index might be different to + * index_frame_decoded. By checking this index, HOST applications can easily figure + * out whether sequence decoding has been finished or not. + * + * -3(0xFFFD) or -2(0xFFFE) : when a display output cannot be given due to picture + * reordering or skip option + * -1(0xFFFF) : when there is no more output for display at the end of sequence + * decoding + */ + s32 index_frame_display; + /** + * This is the frame buffer index of the decoded picture among the frame buffers which were + * registered using vpu_dec_register_frame_buffer(). The currently decoded frame is stored + * into the frame buffer specified by this index. + * + * -2 : indicates that no decoded output is generated because decoder meets EOS + * (end of sequence) or skip + * -1 : indicates that the decoder fails to decode a picture because there is no available + * frame buffer + */ + s32 index_frame_decoded; + s32 index_frame_decoded_for_tiled; + u32 nal_type; + unsigned int pic_type; + struct vpu_rect rc_display; + unsigned int disp_pic_width; + unsigned int disp_pic_height; + struct vpu_rect rc_decoded; + u32 dec_pic_width; + u32 dec_pic_height; + s32 decoded_poc; + int temporal_id; /* temporal ID of the picture */ + dma_addr_t rd_ptr; /* stream buffer read pointer for the current decoder instance */ + dma_addr_t wr_ptr; /* stream buffer write pointer for the current decoder instance */ + struct frame_buffer disp_frame; + u32 frame_display_flag; /* it reports a frame buffer flag to be displayed */ + /** + * this variable reports that sequence has been changed while H.264/AVC stream decoding. + * if it is 1, HOST application can get the new sequence information by calling + * vpu_dec_get_initial_info() or wave5_vpu_dec_issue_seq_init(). + * + * for H.265/HEVC decoder, each bit has a different meaning as follows. + * + * sequence_changed[5] : it indicates that the profile_idc has been changed + * sequence_changed[16] : it indicates that the resolution has been changed + * sequence_changed[19] : it indicates that the required number of frame buffer has + * been changed. + */ + unsigned int frame_cycle; /* reports the number of cycles for processing a frame */ + u32 sequence_no; + + u32 dec_host_cmd_tick; /* tick of DEC_PIC command for the picture */ + u32 dec_decode_end_tick; /* end tick of decoding slices of the picture */ + + u32 sequence_changed; +}; + +struct queue_status_info { + u32 instance_queue_count; + u32 report_queue_count; +}; + +/* + * encode struct and definition + */ + +#define MAX_NUM_TEMPORAL_LAYER 7 +#define MAX_NUM_SPATIAL_LAYER 3 +#define MAX_GOP_NUM 8 + +struct custom_gop_pic_param { + u32 pic_type; /* picture type of nth picture in the custom GOP */ + u32 poc_offset; /* POC of nth picture in the custom GOP */ + u32 pic_qp; /* quantization parameter of nth picture in the custom GOP */ + u32 use_multi_ref_p; /* use multiref pic for P picture. valid only if PIC_TYPE is P */ + u32 ref_poc_l0; /* POC of reference L0 of nth picture in the custom GOP */ + u32 ref_poc_l1; /* POC of reference L1 of nth picture in the custom GOP */ + s32 temporal_id; /* temporal ID of nth picture in the custom GOP */ +}; + +struct enc_wave_param { + /* + * profile indicator (HEVC only) + * + * 0 : the firmware determines a profile according to the internal_bit_depth + * 1 : main profile + * 2 : main10 profile + * 3 : main still picture profile + * In the AVC encoder, a profile cannot be set by the host application. + * The firmware decides it based on internal_bit_depth. + * profile = HIGH (bitdepth 8) profile = HIGH10 (bitdepth 10) + */ + u32 profile; + u32 level; /* level indicator (level * 10) */ + u32 internal_bit_depth: 4; /* 8/10 */ + u32 gop_preset_idx: 4; /* 0 - 9 */ + u32 decoding_refresh_type: 2; /* 0=non-IRAP, 1=CRA, 2=IDR */ + u32 intra_qp; /* quantization parameter of intra picture */ + u32 intra_period; /* period of intra picture in GOP size */ + u32 conf_win_top; /* top offset of conformance window */ + u32 conf_win_bot; /* bottom offset of conformance window */ + u32 conf_win_left; /* left offset of conformance window */ + u32 conf_win_right; /* right offset of conformance window */ + u32 intra_refresh_mode: 3; + /* + * Argument for intra_ctu_refresh_mode. + * + * Depending on intra_refresh_mode, it can mean one of the following: + * - intra_ctu_refresh_mode (1) -> number of consecutive CTU rows + * - intra_ctu_refresh_mode (2) -> the number of consecutive CTU columns + * - intra_ctu_refresh_mode (3) -> step size in CTU + * - intra_ctu_refresh_mode (4) -> number of intra ct_us to be encoded in a picture + */ + u32 intra_refresh_arg; + /* + * 0 : custom setting + * 1 : recommended encoder parameters (slow encoding speed, highest picture quality) + * 2 : boost mode (normal encoding speed, moderate picture quality) + * 3 : fast mode (fast encoding speed, low picture quality) + */ + u32 depend_slice_mode : 2; + u32 depend_slice_mode_arg; + u32 independ_slice_mode : 1; /* 0=no-multi-slice, 1=slice-in-ctu-number*/ + u32 independ_slice_mode_arg; + u32 max_num_merge: 2; + s32 beta_offset_div2: 4; /* sets beta_offset_div2 for deblocking filter */ + s32 tc_offset_div2: 4; /* sets tc_offset_div3 for deblocking filter */ + u32 hvs_qp_scale: 4; /* QP scaling factor for CU QP adjust if hvs_qp_scale_enable is 1 */ + u32 hvs_max_delta_qp; /* maximum delta QP for HVS */ + s32 chroma_cb_qp_offset; /* the value of chroma(cb) QP offset */ + s32 chroma_cr_qp_offset; /* the value of chroma(cr) QP offset */ + s32 initial_rc_qp; + u32 nr_intra_weight_y; + u32 nr_intra_weight_cb; /* weight to cb noise level for intra picture (0 ~ 31) */ + u32 nr_intra_weight_cr; /* weight to cr noise level for intra picture (0 ~ 31) */ + u32 nr_inter_weight_y; + u32 nr_inter_weight_cb; /* weight to cb noise level for inter picture (0 ~ 31) */ + u32 nr_inter_weight_cr; /* weight to cr noise level for inter picture (0 ~ 31) */ + u32 min_qp_i; /* minimum QP of I picture for rate control */ + u32 max_qp_i; /* maximum QP of I picture for rate control */ + u32 min_qp_p; /* minimum QP of P picture for rate control */ + u32 max_qp_p; /* maximum QP of P picture for rate control */ + u32 min_qp_b; /* minimum QP of B picture for rate control */ + u32 max_qp_b; /* maximum QP of B picture for rate control */ + u32 avc_idr_period; /* period of IDR picture (0 ~ 1024). 0 - implies an infinite period */ + u32 avc_slice_arg; /* the number of MB for a slice when avc_slice_mode is set with 1 */ + u32 intra_mb_refresh_mode: 2; /* 0=none, 1=row, 2=column, 3=step-size-in-mb */ + /** + * Argument for intra_mb_refresh_mode. + * + * intra_mb_refresh_mode (1) -> number of consecutive MB rows + * intra_mb_refresh_mode (2) ->the number of consecutive MB columns + * intra_mb_refresh_mode (3) -> step size in MB + */ + u32 intra_mb_refresh_arg; + u32 rc_weight_param; + u32 rc_weight_buf; + + /* flags */ + u32 en_still_picture: 1; /* still picture profile */ + u32 tier: 1; /* 0=main, 1=high */ + u32 avc_slice_mode: 1; /* 0=none, 1=slice-in-mb-number */ + u32 entropy_coding_mode: 1; /* 0=CAVLC, 1=CABAC */ + u32 lossless_enable: 1; /* enable lossless encoding */ + u32 const_intra_pred_flag: 1; /* enable constrained intra prediction */ + u32 tmvp_enable: 1; /* enable temporal motion vector prediction */ + u32 wpp_enable: 1; + u32 disable_deblk: 1; /* disable in-loop deblocking filtering */ + u32 lf_cross_slice_boundary_enable: 1; + u32 skip_intra_trans: 1; + u32 sao_enable: 1; /* enable SAO (sample adaptive offset) */ + u32 intra_nx_n_enable: 1; /* enables intra nx_n p_us */ + u32 cu_level_rc_enable: 1; /* enable CU level rate control */ + u32 hvs_qp_enable: 1; /* enable CU QP adjustment for subjective quality enhancement */ + u32 strong_intra_smooth_enable: 1; /* enable strong intra smoothing */ + u32 rdo_skip: 1; /* skip RDO (rate distortion optimization) */ + u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */ + u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ + u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ +}; + +struct enc_open_param { + dma_addr_t bitstream_buffer; + unsigned int bitstream_buffer_size; + u32 pic_width; /* width of a picture to be encoded in unit of sample */ + u32 pic_height; /* height of a picture to be encoded in unit of sample */ + u32 frame_rate_info;/* desired fps */ + u32 vbv_buffer_size; + u32 bit_rate; /* target bitrate in bps */ + struct enc_wave_param wave_param; + enum packed_format_num packed_format; /* <> */ + enum frame_buffer_format src_format; + bool line_buf_int_en; + u32 rc_enable : 1; /* rate control */ +}; + +struct enc_initial_info { + u32 min_frame_buffer_count; /* minimum number of frame buffers */ + u32 min_src_frame_count; /* minimum number of source buffers */ + u32 seq_init_err_reason; + u32 warn_info; + u32 vlc_buf_size; /* size of task buffer */ + u32 param_buf_size; /* size of task buffer */ +}; + +/* + * Flags to encode NAL units explicitly + */ +struct enc_code_opt { + u32 implicit_header_encode: 1; + u32 encode_vcl: 1; + u32 encode_vps: 1; + u32 encode_sps: 1; + u32 encode_pps: 1; + u32 encode_aud: 1; + u32 encode_eos: 1; + u32 encode_eob: 1; + u32 encode_vui: 1; +}; + +struct enc_param { + struct frame_buffer *source_frame; + u32 pic_stream_buffer_addr; + u64 pic_stream_buffer_size; + u32 src_idx; /* source frame buffer index */ + struct enc_code_opt code_option; + u64 pts; /* presentation timestamp (PTS) of the input source */ + bool src_end_flag; +}; + +struct enc_output_info { + u32 bitstream_buffer; + u32 bitstream_size; /* byte size of encoded bitstream */ + u32 pic_type: 2; /* <> */ + s32 recon_frame_index; + dma_addr_t rd_ptr; + dma_addr_t wr_ptr; + u32 enc_pic_byte; /* number of encoded picture bytes */ + s32 enc_src_idx; /* source buffer index of the currently encoded picture */ + u32 enc_vcl_nut; + u32 error_reason; /* error reason of the currently encoded picture */ + u32 warn_info; /* warning information on the currently encoded picture */ + unsigned int frame_cycle; /* param for reporting the cycle number of encoding one frame*/ + u64 pts; + u32 enc_host_cmd_tick; /* tick of ENC_PIC command for the picture */ + u32 enc_encode_end_tick; /* end tick of encoding slices of the picture */ +}; + +enum enc_pic_code_option { + CODEOPT_ENC_HEADER_IMPLICIT = BIT(0), + CODEOPT_ENC_VCL = BIT(1), /* flag to encode VCL nal unit explicitly */ +}; + +enum gop_preset_idx { + PRESET_IDX_CUSTOM_GOP = 0, /* user defined GOP structure */ + PRESET_IDX_ALL_I = 1, /* all intra, gopsize = 1 */ + PRESET_IDX_IPP = 2, /* consecutive P, cyclic gopsize = 1 */ + PRESET_IDX_IBBB = 3, /* consecutive B, cyclic gopsize = 1 */ + PRESET_IDX_IBPBP = 4, /* gopsize = 2 */ + PRESET_IDX_IBBBP = 5, /* gopsize = 4 */ + PRESET_IDX_IPPPP = 6, /* consecutive P, cyclic gopsize = 4 */ + PRESET_IDX_IBBBB = 7, /* consecutive B, cyclic gopsize = 4 */ + PRESET_IDX_RA_IB = 8, /* random access, cyclic gopsize = 8 */ + PRESET_IDX_IPP_SINGLE = 9, /* consecutive P, cyclic gopsize = 1, with single ref */ +}; + +struct sec_axi_info { + u32 use_ip_enable; + u32 use_bit_enable; + u32 use_lf_row_enable: 1; + u32 use_enc_rdo_enable: 1; + u32 use_enc_lf_enable: 1; +}; + +struct dec_info { + struct dec_open_param open_param; + struct dec_initial_info initial_info; + struct dec_initial_info new_seq_info; /* temporal new sequence information */ + u32 stream_wr_ptr; + u32 stream_rd_ptr; + u32 frame_display_flag; + dma_addr_t stream_buf_start_addr; + dma_addr_t stream_buf_end_addr; + u32 stream_buf_size; + struct vpu_buf vb_mv[MAX_REG_FRAME]; + struct vpu_buf vb_fbc_y_tbl[MAX_REG_FRAME]; + struct vpu_buf vb_fbc_c_tbl[MAX_REG_FRAME]; + unsigned int num_of_decoding_fbs: 7; + unsigned int num_of_display_fbs: 7; + unsigned int stride; + struct sec_axi_info sec_axi_info; + dma_addr_t user_data_buf_addr; + u32 user_data_enable; + u32 user_data_buf_size; + struct vpu_buf vb_work; + struct vpu_buf vb_task; + struct dec_output_info dec_out_info[WAVE5_MAX_FBS]; + u32 seq_change_mask; + enum temporal_id_mode temp_id_select_mode; + u32 target_temp_id; + u32 target_spatial_id; + u32 instance_queue_count; + u32 report_queue_count; + u32 cycle_per_tick; + u32 product_code; + u32 vlc_buf_size; + u32 param_buf_size; + bool initial_info_obtained; + bool reorder_enable; + bool first_cycle_check; + u32 stream_endflag: 1; +}; + +struct enc_info { + struct enc_open_param open_param; + struct enc_initial_info initial_info; + u32 stream_rd_ptr; + u32 stream_wr_ptr; + dma_addr_t stream_buf_start_addr; + dma_addr_t stream_buf_end_addr; + u32 stream_buf_size; + unsigned int num_frame_buffers; + unsigned int stride; + bool rotation_enable; + bool mirror_enable; + enum mirror_direction mirror_direction; + unsigned int rotation_angle; + bool initial_info_obtained; + struct sec_axi_info sec_axi_info; + bool line_buf_int_en; + struct vpu_buf vb_work; + struct vpu_buf vb_mv; /* col_mv buffer */ + struct vpu_buf vb_fbc_y_tbl; /* FBC luma table buffer */ + struct vpu_buf vb_fbc_c_tbl; /* FBC chroma table buffer */ + struct vpu_buf vb_sub_sam_buf; /* sub-sampled buffer for ME */ + struct vpu_buf vb_task; + u64 cur_pts; /* current timestamp in 90_k_hz */ + u64 pts_map[32]; /* PTS mapped with source frame index */ + u32 instance_queue_count; + u32 report_queue_count; + bool first_cycle_check; + u32 cycle_per_tick; + u32 product_code; + u32 vlc_buf_size; + u32 param_buf_size; +}; + +struct vpu_device { + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *v4l2_m2m_dec_dev; + struct v4l2_m2m_dev *v4l2_m2m_enc_dev; + struct list_head instances; + struct video_device *video_dev_dec; + struct video_device *video_dev_enc; + struct mutex dev_lock; /* lock for the src, dst v4l2 queues */ + struct mutex hw_lock; /* lock hw configurations */ + int irq; + enum product_id product; + struct vpu_attr attr; + struct vpu_buf common_mem; + u32 last_performance_cycles; + u32 sram_size; + struct gen_pool *sram_pool; + struct vpu_buf sram_buf; + void __iomem *vdb_register; + u32 product_code; + struct ida inst_ida; + struct clk_bulk_data *clks; + int num_clks; +}; + +struct vpu_instance; + +struct vpu_instance_ops { + void (*finish_process)(struct vpu_instance *inst); +}; + +struct vpu_instance { + struct list_head list; + struct v4l2_fh v4l2_fh; + struct v4l2_m2m_dev *v4l2_m2m_dev; + struct v4l2_ctrl_handler v4l2_ctrl_hdl; + struct vpu_device *dev; + struct completion irq_done; + + struct v4l2_pix_format_mplane src_fmt; + struct v4l2_pix_format_mplane dst_fmt; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + + enum vpu_instance_state state; + enum vpu_instance_type type; + const struct vpu_instance_ops *ops; + spinlock_t state_spinlock; /* This protects the instance state */ + + enum wave_std std; + s32 id; + union { + struct enc_info enc_info; + struct dec_info dec_info; + } *codec_info; + struct frame_buffer frame_buf[MAX_REG_FRAME]; + struct vpu_buf frame_vbuf[MAX_REG_FRAME]; + u32 fbc_buf_count; + u32 queued_src_buf_num; + u32 queued_dst_buf_num; + struct list_head avail_src_bufs; + struct list_head avail_dst_bufs; + struct v4l2_rect conf_win; + u64 timestamp; + enum frame_buffer_format output_format; + bool cbcr_interleave; + bool nv21; + bool eos; + struct vpu_buf bitstream_vbuf; + dma_addr_t last_rd_ptr; + size_t remaining_consumed_bytes; + bool needs_reallocation; + + unsigned int min_src_buf_count; + unsigned int rot_angle; + unsigned int mirror_direction; + unsigned int bit_depth; + unsigned int frame_rate; + unsigned int vbv_buf_size; + unsigned int rc_mode; + unsigned int rc_enable; + unsigned int bit_rate; + unsigned int encode_aud; + struct enc_wave_param enc_param; +}; + +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data); +u32 wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr); +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count, + size_t size); +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset, + u8 *data, size_t len); +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb); +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev); +void wave5_vdi_free_sram(struct vpu_device *vpu_dev); + +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size); +int wave5_vpu_flush_instance(struct vpu_instance *inst); +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id); +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param); +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res); +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst); +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info); +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs, + int num_of_display_fbs, int stride, int height); +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail); +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info); +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr); +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst); +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index); +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter); +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr, + dma_addr_t *pwr_ptr, size_t *size); +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size); +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index); +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index); + +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param); +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res); +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst); +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info); +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num, + unsigned int stride, int height, + enum tiled_map_type map_type); +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, + u32 *fail_res); +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info); +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter); + +#endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h new file mode 100644 index 000000000..d9751eedb --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - product config definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef _VPU_CONFIG_H_ +#define _VPU_CONFIG_H_ + +#define WAVE517_CODE 0x5170 +#define WAVE537_CODE 0x5370 +#define WAVE511_CODE 0x5110 +#define WAVE521_CODE 0x5210 +#define WAVE521C_CODE 0x521c +#define WAVE521C_DUAL_CODE 0x521d // wave521 dual core +#define WAVE521E1_CODE 0x5211 + +#define PRODUCT_CODE_W_SERIES(x) ({ \ + int c = x; \ + ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \ + (c) == WAVE511_CODE || (c) == WAVE521_CODE || \ + (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \ + (c) == WAVE521C_DUAL_CODE); \ +}) + +#define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024) +#define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K +#define WAVE521DEC_WORKBUF_SIZE (1784 * 1024) + +#define MAX_NUM_INSTANCE 32 + +#define W5_MIN_ENC_PIC_WIDTH 256 +#define W5_MIN_ENC_PIC_HEIGHT 128 +#define W5_MAX_ENC_PIC_WIDTH 8192 +#define W5_MAX_ENC_PIC_HEIGHT 8192 + +// application specific configuration +#define VPU_ENC_TIMEOUT 60000 +#define VPU_DEC_TIMEOUT 60000 + +// for WAVE encoder +#define USE_SRC_PRP_AXI 0 +#define USE_SRC_PRI_AXI 1 +#define DEFAULT_SRC_AXI USE_SRC_PRP_AXI + +/************************************************************************/ +/* VPU COMMON MEMORY */ +/************************************************************************/ +#define VLC_BUF_NUM (2) + +#define COMMAND_QUEUE_DEPTH (2) + +#define W5_REMAP_INDEX0 0 +#define W5_REMAP_INDEX1 1 +#define W5_REMAP_MAX_SIZE (1024 * 1024) + +#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) +#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE +#define WAVE5_TEMPBUF_SIZE (1024 * 1024) + +#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) + +//=====4. VPU REPORT MEMORY ======================// + +#define WAVE5_UPPER_PROC_AXI_ID 0x0 + +#define WAVE5_PROC_AXI_ID 0x0 +#define WAVE5_PRP_AXI_ID 0x0 +#define WAVE5_FBD_Y_AXI_ID 0x0 +#define WAVE5_FBC_Y_AXI_ID 0x0 +#define WAVE5_FBD_C_AXI_ID 0x0 +#define WAVE5_FBC_C_AXI_ID 0x0 +#define WAVE5_SEC_AXI_ID 0x0 +#define WAVE5_PRI_AXI_ID 0x0 + +#endif /* _VPU_CONFIG_H_ */ diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h new file mode 100644 index 000000000..905d5c34f --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - error values + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef ERROR_CODE_H_INCLUDED +#define ERROR_CODE_H_INCLUDED + +/* + * WAVE5 + */ + +/************************************************************************/ +/* WAVE5 COMMON SYSTEM ERROR (FAIL_REASON) */ +/************************************************************************/ +#define WAVE5_SYSERR_QUEUEING_FAIL 0x00000001 +#define WAVE5_SYSERR_ACCESS_VIOLATION_HW 0x00000040 +#define WAVE5_SYSERR_BUS_ERROR 0x00000200 +#define WAVE5_SYSERR_DOUBLE_FAULT 0x00000400 +#define WAVE5_SYSERR_RESULT_NOT_READY 0x00000800 +#define WAVE5_SYSERR_VPU_STILL_RUNNING 0x00001000 +#define WAVE5_SYSERR_UNKNOWN_CMD 0x00002000 +#define WAVE5_SYSERR_UNKNOWN_CODEC_STD 0x00004000 +#define WAVE5_SYSERR_UNKNOWN_QUERY_OPTION 0x00008000 +#define WAVE5_SYSERR_VLC_BUF_FULL 0x00010000 +#define WAVE5_SYSERR_WATCHDOG_TIMEOUT 0x00020000 +#define WAVE5_SYSERR_VCPU_TIMEOUT 0x00080000 +#define WAVE5_SYSERR_TEMP_SEC_BUF_OVERFLOW 0x00200000 +#define WAVE5_SYSERR_NEED_MORE_TASK_BUF 0x00400000 +#define WAVE5_SYSERR_PRESCAN_ERR 0x00800000 +#define WAVE5_SYSERR_ENC_GBIN_OVERCONSUME 0x01000000 +#define WAVE5_SYSERR_ENC_MAX_ZERO_DETECT 0x02000000 +#define WAVE5_SYSERR_ENC_LVL_FIRST_ERROR 0x04000000 +#define WAVE5_SYSERR_ENC_EG_RANGE_OVER 0x08000000 +#define WAVE5_SYSERR_ENC_IRB_FRAME_DROP 0x10000000 +#define WAVE5_SYSERR_INPLACE_V 0x20000000 +#define WAVE5_SYSERR_FATAL_VPU_HANGUP 0xf0000000 + +/************************************************************************/ +/* WAVE5 COMMAND QUEUE ERROR (FAIL_REASON) */ +/************************************************************************/ +#define WAVE5_CMDQ_ERR_NOT_QUEABLE_CMD 0x00000001 +#define WAVE5_CMDQ_ERR_SKIP_MODE_ENABLE 0x00000002 +#define WAVE5_CMDQ_ERR_INST_FLUSHING 0x00000003 +#define WAVE5_CMDQ_ERR_INST_INACTIVE 0x00000004 +#define WAVE5_CMDQ_ERR_QUEUE_FAIL 0x00000005 +#define WAVE5_CMDQ_ERR_CMD_BUF_FULL 0x00000006 + +/************************************************************************/ +/* WAVE5 ERROR ON DECODER (ERR_INFO) */ +/************************************************************************/ +// HEVC +#define HEVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000 +#define HEVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001 +#define HEVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002 +#define HEVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003 +#define HEVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004 +#define HEVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005 +#define HEVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006 +#define HEVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007 +#define HEVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008 +#define HEVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009 +#define HEVC_SPSERR_LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 0x0000100A +#define HEVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B +#define HEVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C +#define HEVC_SPSERR_SPS_MAX_LATENCY_INCREASE 0x0000100D +#define HEVC_SPSERR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3 0x0000100E +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE 0x0000100F +#define HEVC_SPSERR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2 0x00001010 +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE 0x00001011 +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER 0x00001012 +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA 0x00001013 +#define HEVC_SPSERR_SCALING_LIST 0x00001014 +#define HEVC_SPSERR_LOG2_DIFF_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3 0x00001015 +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE 0x00001016 +#define HEVC_SPSERR_NUM_SHORT_TERM_REF_PIC_SETS 0x00001017 +#define HEVC_SPSERR_NUM_LONG_TERM_REF_PICS_SPS 0x00001018 +#define HEVC_SPSERR_GBU_PARSING_ERROR 0x00001019 +#define HEVC_SPSERR_EXTENSION_FLAG 0x0000101A +#define HEVC_SPSERR_VUI_ERROR 0x0000101B +#define HEVC_SPSERR_ACTIVATE_SPS 0x0000101C +#define HEVC_SPSERR_PROFILE_SPACE 0x0000101D +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000 +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001 +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002 +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003 +#define HEVC_PPSERR_INIT_QP_MINUS26 0x00002004 +#define HEVC_PPSERR_DIFF_CU_QP_DELTA_DEPTH 0x00002005 +#define HEVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006 +#define HEVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007 +#define HEVC_PPSERR_NUM_TILE_COLUMNS_MINUS1 0x00002008 +#define HEVC_PPSERR_NUM_TILE_ROWS_MINUS1 0x00002009 +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1 0x0000200A +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1 0x0000200B +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2 0x0000200C +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2 0x0000200D +#define HEVC_PPSERR_SCALING_LIST 0x0000200E +#define HEVC_PPSERR_LOG2_PARALLEL_MERGE_LEVEL_MINUS2 0x0000200F +#define HEVC_PPSERR_NUM_TILE_COLUMNS_RANGE_OUT 0x00002010 +#define HEVC_PPSERR_NUM_TILE_ROWS_RANGE_OUT 0x00002011 +#define HEVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012 +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013 +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014 +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015 +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016 +#define HEVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017 +#define HEVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018 +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1_RANGE_OUT 0x00002019 +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1_RANGE_OUT 0x00002020 +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2_RANGE_OUT 0x00002021 +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2_RANGE_OUT 0x00002022 +#define HEVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000 +#define HEVC_SHERR_ACTIVATE_PPS 0x00003001 +#define HEVC_SHERR_ACTIVATE_SPS 0x00003002 +#define HEVC_SHERR_SLICE_TYPE 0x00003003 +#define HEVC_SHERR_FIRST_SLICE_IS_DEPENDENT_SLICE 0x00003004 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_SPS_FLAG 0x00003005 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET 0x00003006 +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_IDX 0x00003007 +#define HEVC_SHERR_NUM_LONG_TERM_SPS 0x00003008 +#define HEVC_SHERR_NUM_LONG_TERM_PICS 0x00003009 +#define HEVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A +#define HEVC_SHERR_DELTA_POC_MSB_CYCLE_LT 0x0000300B +#define HEVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C +#define HEVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D +#define HEVC_SHERR_COLLOCATED_REF_IDX 0x0000300E +#define HEVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F +#define HEVC_SHERR_FIVE_MINUS_MAX_NUM_MERGE_CAND 0x00003010 +#define HEVC_SHERR_SLICE_QP_DELTA 0x00003011 +#define HEVC_SHERR_SLICE_QP_DELTA_IS_OUT_OF_RANGE 0x00003012 +#define HEVC_SHERR_SLICE_CB_QP_OFFSET 0x00003013 +#define HEVC_SHERR_SLICE_CR_QP_OFFSET 0x00003014 +#define HEVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015 +#define HEVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016 +#define HEVC_SHERR_NUM_ENTRY_POINT_OFFSETS 0x00003017 +#define HEVC_SHERR_OFFSET_LEN_MINUS1 0x00003018 +#define HEVC_SHERR_SLICE_SEGMENT_HEADER_EXTENSION_LENGTH 0x00003019 +#define HEVC_SHERR_WRONG_POC_IN_STILL_PICTURE_PROFILE 0x0000301A +#define HEVC_SHERR_SLICE_TYPE_ERROR_IN_STILL_PICTURE_PROFILE 0x0000301B +#define HEVC_SHERR_PPS_ID_NOT_EQUAL_PREV_VALUE 0x0000301C +#define HEVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000 +#define HEVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001 +#define HEVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002 +#define HEVC_SPECERR_OVER_BIT_DEPTH 0x00004003 +#define HEVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004 +#define HEVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005 +#define HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000 +#define HEVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001 +#define HEVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002 +#define HEVC_ETCERR_INPLACE_V 0x0000500F + +// AVC +#define AVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000 +#define AVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001 +#define AVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002 +#define AVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003 +#define AVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004 +#define AVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005 +#define AVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006 +#define AVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007 +#define AVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008 +#define AVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009 +#define AVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B +#define AVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C +#define AVC_SPSERR_SCALING_LIST 0x00001014 +#define AVC_SPSERR_GBU_PARSING_ERROR 0x00001019 +#define AVC_SPSERR_VUI_ERROR 0x0000101B +#define AVC_SPSERR_ACTIVATE_SPS 0x0000101C +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000 +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001 +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002 +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003 +#define AVC_PPSERR_INIT_QP_MINUS26 0x00002004 +#define AVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006 +#define AVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007 +#define AVC_PPSERR_SCALING_LIST 0x0000200E +#define AVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012 +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013 +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014 +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015 +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016 +#define AVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017 +#define AVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018 +#define AVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000 +#define AVC_SHERR_ACTIVATE_PPS 0x00003001 +#define AVC_SHERR_ACTIVATE_SPS 0x00003002 +#define AVC_SHERR_SLICE_TYPE 0x00003003 +#define AVC_SHERR_FIRST_MB_IN_SLICE 0x00003004 +#define AVC_SHERR_RPLM 0x00003006 +#define AVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A +#define AVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C +#define AVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D +#define AVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F +#define AVC_SHERR_SLICE_QP_DELTA 0x00003011 +#define AVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015 +#define AVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016 +#define AVC_SHERR_DISABLE_DEBLOCK_FILTER_IDC 0x00003017 +#define AVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000 +#define AVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001 +#define AVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002 +#define AVC_SPECERR_OVER_BIT_DEPTH 0x00004003 +#define AVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004 +#define AVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005 +#define AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000 +#define AVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001 +#define AVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002 +#define AVC_ETCERR_ASO 0x00005004 +#define AVC_ETCERR_FMO 0x00005005 +#define AVC_ETCERR_INPLACE_V 0x0000500F + +/************************************************************************/ +/* WAVE5 WARNING ON DECODER (WARN_INFO) */ +/************************************************************************/ +// HEVC +#define HEVC_SPSWARN_MAX_SUB_LAYERS_MINUS1 0x00000001 +#define HEVC_SPSWARN_GENERAL_RESERVED_ZERO_44BITS 0x00000002 +#define HEVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004 +#define HEVC_SPSWARN_SUB_LAYER_RESERVED_ZERO_44BITS 0x00000008 +#define HEVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010 +#define HEVC_SPSWARN_SPS_MAX_DEC_PIC_BUFFERING_VALUE_OVER 0x00000020 +#define HEVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040 +#define HEVC_SPSWARN_ST_RPS_UE_ERROR 0x00000080 +#define HEVC_SPSWARN_EXTENSION_FLAG 0x01000000 +#define HEVC_SPSWARN_REPLACED_WITH_PREV_SPS 0x02000000 +#define HEVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100 +#define HEVC_PPSWARN_REPLACED_WITH_PREV_PPS 0x00000200 +#define HEVC_SHWARN_FIRST_SLICE_SEGMENT_IN_PIC_FLAG 0x00001000 +#define HEVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000 +#define HEVC_SHWARN_PIC_OUTPUT_FLAG 0x00004000 +#define HEVC_SHWARN_DUPLICATED_SLICE_SEGMENT 0x00008000 +#define HEVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000 +#define HEVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000 +#define HEVC_ETCWARN_WRONG_TEMPORAL_ID 0x00040000 +#define HEVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000 +#define HEVC_SPECWARN_OVER_PROFILE 0x00100000 +#define HEVC_SPECWARN_OVER_LEVEL 0x00200000 +#define HEVC_PRESWARN_PARSING_ERR 0x04000000 +#define HEVC_PRESWARN_MVD_OUT_OF_RANGE 0x08000000 +#define HEVC_PRESWARN_CU_QP_DELTA_VAL_OUT_OF_RANGE 0x09000000 +#define HEVC_PRESWARN_COEFF_LEVEL_REMAINING_OUT_OF_RANGE 0x0A000000 +#define HEVC_PRESWARN_PCM_ERR 0x0B000000 +#define HEVC_PRESWARN_OVERCONSUME 0x0C000000 +#define HEVC_PRESWARN_END_OF_SUBSET_ONE_BIT_ERR 0x10000000 +#define HEVC_PRESWARN_END_OF_SLICE_SEGMENT_FLAG 0x20000000 + +// AVC +#define AVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004 +#define AVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010 +#define AVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040 +#define AVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100 +#define AVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000 +#define AVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000 +#define AVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000 +#define AVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000 +#define AVC_SPECWARN_OVER_PROFILE 0x00100000 +#define AVC_SPECWARN_OVER_LEVEL 0x00200000 +#define AVC_PRESWARN_MVD_RANGE_OUT 0x00400000 +#define AVC_PRESWARN_MB_QPD_RANGE_OUT 0x00500000 +#define AVC_PRESWARN_COEFF_RANGE_OUT 0x00600000 +#define AVC_PRESWARN_MV_RANGE_OUT 0x00700000 +#define AVC_PRESWARN_MB_SKIP_RUN_RANGE_OUT 0x00800000 +#define AVC_PRESWARN_MB_TYPE_RANGE_OUT 0x00900000 +#define AVC_PRESWARN_SUB_MB_TYPE_RANGE_OUT 0x00A00000 +#define AVC_PRESWARN_CBP_RANGE_OUT 0x00B00000 +#define AVC_PRESWARN_INTRA_CHROMA_PRED_MODE_RANGE_OUT 0x00C00000 +#define AVC_PRESWARN_REF_IDX_RANGE_OUT 0x00D00000 +#define AVC_PRESWARN_COEFF_TOKEN_RANGE_OUT 0x00E00000 +#define AVC_PRESWARN_TOTAL_ZERO_RANGE_OUT 0x00F00000 +#define AVC_PRESWARN_RUN_BEFORE_RANGE_OUT 0x01000000 +#define AVC_PRESWARN_OVERCONSUME 0x01100000 +#define AVC_PRESWARN_MISSING_SLICE 0x01200000 + +/************************************************************************/ +/* WAVE5 ERROR ON ENCODER (ERR_INFO) */ +/************************************************************************/ + +/************************************************************************/ +/* WAVE5 WARNING ON ENCODER (WARN_INFO) */ +/************************************************************************/ +#define WAVE5_ETCWARN_FORCED_SPLIT_BY_CU8X8 0x000000001 + +/************************************************************************/ +/* WAVE5 debug info (PRI_REASON) */ +/************************************************************************/ +#define WAVE5_DEC_VCORE_VCE_HANGUP 0x0001 +#define WAVE5_DEC_VCORE_UNDETECTED_SYNTAX_ERR 0x0002 +#define WAVE5_DEC_VCORE_MIB_BUSY 0x0003 +#define WAVE5_DEC_VCORE_VLC_BUSY 0x0004 + +#endif /* ERROR_CODE_H_INCLUDED */ diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h new file mode 100644 index 000000000..063028ecc --- /dev/null +++ b/drivers/media/platform/chips-media/wave5/wave5.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave5 series multi-standard codec IP - wave5 backend definitions + * + * Copyright (C) 2021-2023 CHIPS&MEDIA INC + */ + +#ifndef __WAVE5_FUNCTION_H__ +#define __WAVE5_FUNCTION_H__ + +#define WAVE5_SUBSAMPLED_ONE_SIZE(_w, _h) (ALIGN((_w) / 4, 16) * ALIGN((_h) / 4, 8)) +#define WAVE5_SUBSAMPLED_ONE_SIZE_AVC(_w, _h) (ALIGN((_w) / 4, 32) * ALIGN((_h) / 4, 4)) + +/* + * Bitstream buffer option: Explicit End + * When set to 1 the VPU assumes that the bitstream has at least one frame and + * will read until the end of the bitstream buffer. + * When set to 0 the VPU will not read the last few bytes. + * This option can be set anytime but cannot be cleared during processing. + * It can be set to force finish decoding even though there is not enough + * bitstream data for a full frame. + */ +#define BSOPTION_ENABLE_EXPLICIT_END BIT(0) +#define BSOPTION_HIGHLIGHT_STREAM_END BIT(1) + +/* + * Currently the driver only supports hardware with little endian but for source + * picture format, the bitstream and the report parameter the hardware works + * with the opposite endianness, thus hard-code big endian for the register + * writes + */ +#define PIC_SRC_ENDIANNESS_BIG_ENDIAN 0xf +#define BITSTREAM_ENDIANNESS_BIG_ENDIAN 0xf +#define REPORT_PARAM_ENDIANNESS_BIG_ENDIAN 0xf + +#define WTL_RIGHT_JUSTIFIED 0 +#define WTL_LEFT_JUSTIFIED 1 +#define WTL_PIXEL_8BIT 0 +#define WTL_PIXEL_16BIT 1 +#define WTL_PIXEL_32BIT 2 + +/* Mirror & rotation modes of the PRP (pre-processing) module */ +#define NONE_ROTATE 0x0 +#define ROT_CLOCKWISE_90 0x3 +#define ROT_CLOCKWISE_180 0x5 +#define ROT_CLOCKWISE_270 0x7 +#define MIR_HOR_FLIP 0x11 +#define MIR_VER_FLIP 0x9 +#define MIR_HOR_VER_FLIP (MIR_HOR_FLIP | MIR_VER_FLIP) + +bool wave5_vpu_is_init(struct vpu_device *vpu_dev); + +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev); + +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision); + +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size); + +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode); + +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, struct dec_open_param *param); + +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos); + +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst); + +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count); + +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size); + +int wave5_vpu_dec_init_seq(struct vpu_instance *inst); + +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info); + +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res); + +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result); + +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res); + +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index); + +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index); + +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags); + +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst); + +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr); + +/***< WAVE5 encoder >******/ + +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, + struct enc_open_param *open_param); + +int wave5_vpu_enc_init_seq(struct vpu_instance *inst); + +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info); + +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst, + struct frame_buffer *fb_arr, enum tiled_map_type map_type, + unsigned int count); + +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res); + +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result); + +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res); + +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param); + +#endif /* __WAVE5_FUNCTION_H__ */ diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index c3456c700..ac48658e2 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -598,12 +598,11 @@ static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) goto end; vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type); - if (buf->index >= vq->num_buffers) { - dev_err(ctx->jpeg->dev, "buffer index out of range\n"); + vb = vb2_get_buffer(vq, buf->index); + if (!vb) { + dev_err(ctx->jpeg->dev, "buffer not found\n"); return -EINVAL; } - - vb = vq->bufs[buf->index]; jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb); jpeg_src_buf->bs_size = buf->m.planes[0].bytesused; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index cc44be10f..94f4ed785 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -208,13 +208,17 @@ static int mdp_probe(struct platform_device *pdev) goto err_destroy_job_wq; } - mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP); - if (WARN_ON(!mm_pdev)) { - dev_err(&pdev->dev, "Could not get scp device\n"); - ret = -ENODEV; - goto err_destroy_clock_wq; + mdp->scp = scp_get(pdev); + if (!mdp->scp) { + mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP); + if (WARN_ON(!mm_pdev)) { + dev_err(&pdev->dev, "Could not get scp device\n"); + ret = -ENODEV; + goto err_destroy_clock_wq; + } + mdp->scp = platform_get_drvdata(mm_pdev); } - mdp->scp = platform_get_drvdata(mm_pdev); + mdp->rproc_handle = scp_get_rproc(mdp->scp); dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle); diff --git a/drivers/media/platform/mediatek/vcodec/Kconfig b/drivers/media/platform/mediatek/vcodec/Kconfig index 74b00eb1b..bc8292232 100644 --- a/drivers/media/platform/mediatek/vcodec/Kconfig +++ b/drivers/media/platform/mediatek/vcodec/Kconfig @@ -24,7 +24,6 @@ config VIDEO_MEDIATEK_VCODEC select V4L2_H264 select V4L2_VP9 select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API help Mediatek video codec driver provides HW capability to encode and decode in a range of video formats on MT8173 diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c index 4c34344dc..d7027d600 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c @@ -50,12 +50,12 @@ static void mtk_vcodec_vpu_reset_dec_handler(void *priv) dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); - mutex_lock(&dev->dev_mutex); + mutex_lock(&dev->dev_ctx_lock); list_for_each_entry(ctx, &dev->ctx_list, list) { ctx->state = MTK_STATE_ABORT; mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); } - mutex_unlock(&dev->dev_mutex); + mutex_unlock(&dev->dev_ctx_lock); } static void mtk_vcodec_vpu_reset_enc_handler(void *priv) @@ -65,12 +65,12 @@ static void mtk_vcodec_vpu_reset_enc_handler(void *priv) dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); - mutex_lock(&dev->dev_mutex); + mutex_lock(&dev->dev_ctx_lock); list_for_each_entry(ctx, &dev->ctx_list, list) { ctx->state = MTK_STATE_ABORT; mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); } - mutex_unlock(&dev->dev_mutex); + mutex_unlock(&dev->dev_ctx_lock); } static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 91ed576d6..ba742f0e3 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -208,36 +208,14 @@ static int vidioc_vdec_dqbuf(struct file *file, void *priv, return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } -static int mtk_vcodec_dec_get_chip_name(void *priv) -{ - struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); - struct device *dev = &ctx->dev->plat_dev->dev; - - if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) - return 8173; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-dec")) - return 8183; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec")) - return 8192; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec")) - return 8195; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) - return 8186; - else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec")) - return 8188; - else - return 8173; -} - static int vidioc_vdec_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; - int platform_name = mtk_vcodec_dec_get_chip_name(priv); strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); - snprintf(cap->card, sizeof(cap->card), "MT%d video decoder", platform_name); + snprintf(cap->card, sizeof(cap->card), "MT%d video decoder", ctx->dev->chip_name); return 0; } diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c index 0a89ce452..2073781cc 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c @@ -268,7 +268,9 @@ static int fops_vcodec_open(struct file *file) ctx->dev->vdec_pdata->init_vdec_params(ctx); + mutex_lock(&dev->dev_ctx_lock); list_add(&ctx->list, &dev->ctx_list); + mutex_unlock(&dev->dev_ctx_lock); mtk_vcodec_dbgfs_create(ctx); mutex_unlock(&dev->dev_mutex); @@ -311,7 +313,9 @@ static int fops_vcodec_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_hdl); mtk_vcodec_dbgfs_remove(dev, ctx->id); + mutex_lock(&dev->dev_ctx_lock); list_del_init(&ctx->list); + mutex_unlock(&dev->dev_ctx_lock); kfree(ctx); mutex_unlock(&dev->dev_mutex); return 0; @@ -326,6 +330,26 @@ static const struct v4l2_file_operations mtk_vcodec_fops = { .mmap = v4l2_m2m_fop_mmap, }; +static void mtk_vcodec_dec_get_chip_name(struct mtk_vcodec_dec_dev *vdec_dev) +{ + struct device *dev = &vdec_dev->plat_dev->dev; + + if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8173; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8183; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8192; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8195; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8186; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec")) + vdec_dev->chip_name = MTK_VDEC_MT8188; + else + vdec_dev->chip_name = MTK_VDEC_INVAL; +} + static int mtk_vcodec_probe(struct platform_device *pdev) { struct mtk_vcodec_dec_dev *dev; @@ -341,6 +365,12 @@ static int mtk_vcodec_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dev->ctx_list); dev->plat_dev = pdev; + mtk_vcodec_dec_get_chip_name(dev); + if (dev->chip_name == MTK_VDEC_INVAL) { + dev_err(&pdev->dev, "Failed to get decoder chip name"); + return -EINVAL; + } + dev->vdec_pdata = of_device_get_match_data(&pdev->dev); if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", &rproc_phandle)) { @@ -378,6 +408,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) for (i = 0; i < MTK_VDEC_HW_MAX; i++) mutex_init(&dev->dec_mutex[i]); mutex_init(&dev->dev_mutex); + mutex_init(&dev->dev_ctx_lock); spin_lock_init(&dev->irqlock); snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h index 7e36b2c69..85b2c0d3d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -18,6 +18,16 @@ #define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) #define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) +enum mtk_vcodec_dec_chip_name { + MTK_VDEC_INVAL = 0, + MTK_VDEC_MT8173 = 8173, + MTK_VDEC_MT8183 = 8183, + MTK_VDEC_MT8186 = 8186, + MTK_VDEC_MT8188 = 8188, + MTK_VDEC_MT8192 = 8192, + MTK_VDEC_MT8195 = 8195, +}; + /* * enum mtk_vdec_format_types - Structure used to get supported * format types according to decoder capability @@ -231,6 +241,7 @@ struct mtk_vcodec_dec_ctx { * * @dec_mutex: decoder hardware lock * @dev_mutex: video_device lock + * @dev_ctx_lock: the lock of context list * @decode_workqueue: decode work queue * * @irqlock: protect data access by irq handler and work thread @@ -249,6 +260,8 @@ struct mtk_vcodec_dec_ctx { * @vdec_racing_info: record register value * @dec_racing_info_mutex: mutex lock used for inner racing mode * @dbgfs: debug log related information + * + * @chip_name: used to distinguish platforms and select the correct codec configuration values */ struct mtk_vcodec_dec_dev { struct v4l2_device v4l2_dev; @@ -270,6 +283,7 @@ struct mtk_vcodec_dec_dev { /* decoder hardware mutex lock */ struct mutex dec_mutex[MTK_VDEC_HW_MAX]; struct mutex dev_mutex; + struct mutex dev_ctx_lock; struct workqueue_struct *decode_workqueue; spinlock_t irqlock; @@ -289,6 +303,8 @@ struct mtk_vcodec_dec_dev { /* Protects access to vdec_racing_info data */ struct mutex dec_racing_info_mutex; struct mtk_vcodec_dbgfs dbgfs; + + enum mtk_vcodec_dec_chip_name chip_name; }; static inline struct mtk_vcodec_dec_ctx *fh_to_dec_ctx(struct v4l2_fh *fh) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index e29c9c58f..d54b38337 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -56,6 +56,15 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { }, .codec_type = V4L2_PIX_FMT_H264_SLICE, }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .def = V4L2_MPEG_VIDEO_H264_LEVEL_4_1, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, { .cfg = { .id = V4L2_CID_STATELESS_H264_DECODE_MODE, @@ -100,7 +109,17 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { .id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, .def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .max = V4L2_MPEG_VIDEO_VP9_PROFILE_3, + .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, + .menu_skip_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_1), + }, + .codec_type = V4L2_PIX_FMT_VP9_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_VP9_LEVEL, + .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, + .def = V4L2_MPEG_VIDEO_VP9_LEVEL_4_0, + .max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_1, }, .codec_type = V4L2_PIX_FMT_VP9_FRAME, }, @@ -138,6 +157,16 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { }, .codec_type = V4L2_PIX_FMT_HEVC_SLICE, }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .def = V4L2_MPEG_VIDEO_HEVC_LEVEL_4, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { .cfg = { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, @@ -519,6 +548,141 @@ static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { .s_ctrl = mtk_vdec_s_ctrl, }; +static void mtk_vcodec_dec_fill_h264_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8192: + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_5_2; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0; + break; + case MTK_VDEC_MT8183: + case MTK_VDEC_MT8186: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + break; + }; +} + +static void mtk_vcodec_dec_fill_h264_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + break; + }; +} + +static void mtk_vcodec_dec_fill_h265_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_HEVC_LEVEL_4; + break; + }; +} + +static void mtk_vcodec_dec_fill_h265_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE; + break; + }; +} + +static void mtk_vcodec_dec_fill_vp9_level(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8192: + case MTK_VDEC_MT8188: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_1; + break; + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_2; + break; + case MTK_VDEC_MT8186: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_1; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_VP9_LEVEL_4_0; + break; + }; +} + +static void mtk_vcodec_dec_fill_vp9_profile(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (ctx->dev->chip_name) { + case MTK_VDEC_MT8188: + case MTK_VDEC_MT8195: + cfg->max = V4L2_MPEG_VIDEO_VP9_PROFILE_2; + break; + default: + cfg->max = V4L2_MPEG_VIDEO_VP9_PROFILE_1; + break; + }; +} + +static void mtk_vcodec_dec_reset_controls(struct v4l2_ctrl_config *cfg, + struct mtk_vcodec_dec_ctx *ctx) +{ + switch (cfg->id) { + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + mtk_vcodec_dec_fill_h264_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h264 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + mtk_vcodec_dec_fill_h265_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h265 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + mtk_vcodec_dec_fill_vp9_level(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "vp9 supported level: %lld %lld", cfg->max, cfg->def); + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + mtk_vcodec_dec_fill_h264_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h264 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + mtk_vcodec_dec_fill_h265_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "h265 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + mtk_vcodec_dec_fill_vp9_profile(cfg, ctx); + mtk_v4l2_vdec_dbg(3, ctx, "vp9 supported profile: %lld %lld", cfg->max, + cfg->menu_skip_mask); + break; + default: + break; + }; +} + static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) { unsigned int i; @@ -532,6 +696,8 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) for (i = 0; i < NUM_CTRLS; i++) { struct v4l2_ctrl_config cfg = mtk_stateless_controls[i].cfg; cfg.ops = &mtk_vcodec_dec_ctrl_ops; + + mtk_vcodec_dec_reset_controls(&cfg, ctx); v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &cfg, NULL); if (ctx->ctrl_hdl.error) { mtk_v4l2_vdec_err(ctx, "Adding control %d failed %d", i, diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c index 06ed47df6..21836dd6e 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c @@ -869,7 +869,6 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx) inst->vpu.codec_type = ctx->current_codec; inst->vpu.capture_type = ctx->capture_fourcc; - ctx->drv_handle = inst; err = vpu_dec_init(&inst->vpu); if (err) { mtk_vdec_err(ctx, "vdec_hevc init err=%d", err); @@ -898,6 +897,7 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx) mtk_vdec_debug(ctx, "lat hevc instance >> %p, codec_type = 0x%x", inst, inst->vpu.codec_type); + ctx->drv_handle = inst; return 0; error_free_inst: kfree(inst); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index e393e3e66..69d37b93b 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -1695,13 +1695,8 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst return -EINVAL; /* update internal buffer's width/height */ - for (i = 0; i < vq->num_buffers; i++) { - if (vb == vq->bufs[i]) { - instance->dpb[i].width = w; - instance->dpb[i].height = h; - break; - } - } + instance->dpb[vb->index].width = w; + instance->dpb[vb->index].height = h; /* * get buffer's width/height from instance diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index 82e57ae98..da6be5567 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -77,12 +77,14 @@ static bool vpu_dec_check_ap_inst(struct mtk_vcodec_dec_dev *dec_dev, struct vde struct mtk_vcodec_dec_ctx *ctx; int ret = false; + mutex_lock(&dec_dev->dev_ctx_lock); list_for_each_entry(ctx, &dec_dev->ctx_list, list) { if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { ret = true; break; } } + mutex_unlock(&dec_dev->dev_ctx_lock); return ret; } diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c index eb381fa6e..181884e79 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -912,7 +912,7 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; err_start_stream: - for (i = 0; i < q->num_buffers; ++i) { + for (i = 0; i < vb2_get_num_buffers(q); ++i) { struct vb2_buffer *buf = vb2_get_buffer(q, i); /* 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 6319f24bc..3cb8a1622 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 @@ -177,7 +177,9 @@ static int fops_vcodec_open(struct file *file) mtk_v4l2_venc_dbg(2, ctx, "Create instance [%d]@%p m2m_ctx=%p ", ctx->id, ctx, ctx->m2m_ctx); + mutex_lock(&dev->dev_ctx_lock); list_add(&ctx->list, &dev->ctx_list); + mutex_unlock(&dev->dev_ctx_lock); mutex_unlock(&dev->dev_mutex); mtk_v4l2_venc_dbg(0, ctx, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), @@ -212,7 +214,9 @@ static int fops_vcodec_release(struct file *file) v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + mutex_lock(&dev->dev_ctx_lock); list_del_init(&ctx->list); + mutex_unlock(&dev->dev_ctx_lock); kfree(ctx); mutex_unlock(&dev->dev_mutex); return 0; @@ -294,6 +298,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mutex_init(&dev->enc_mutex); mutex_init(&dev->dev_mutex); + mutex_init(&dev->dev_ctx_lock); spin_lock_init(&dev->irqlock); snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h index a042f607e..0bd85d0fb 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h @@ -178,6 +178,7 @@ struct mtk_vcodec_enc_ctx { * * @enc_mutex: encoder hardware lock. * @dev_mutex: video_device lock + * @dev_ctx_lock: the lock of context list * @encode_workqueue: encode work queue * * @enc_irq: h264 encoder irq resource @@ -205,6 +206,7 @@ struct mtk_vcodec_enc_dev { /* encoder hardware mutex lock */ struct mutex enc_mutex; struct mutex dev_mutex; + struct mutex dev_ctx_lock; struct workqueue_struct *encode_workqueue; int enc_irq; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c index 84ad1cc6a..51bb7ee14 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c @@ -47,12 +47,14 @@ static bool vpu_enc_check_ap_inst(struct mtk_vcodec_enc_dev *enc_dev, struct ven struct mtk_vcodec_enc_ctx *ctx; int ret = false; + mutex_lock(&enc_dev->dev_ctx_lock); list_for_each_entry(ctx, &enc_dev->ctx_list, list) { if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { ret = true; break; } } + mutex_unlock(&enc_dev->dev_ctx_lock); return ret; } diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index 988c1cc1d..fee73260b 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -232,8 +232,8 @@ static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -281,13 +281,12 @@ static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, req_fmt->format.field = V4L2_FIELD_NONE; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, - sd_state, - CSI2DC_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + CSI2DC_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; /* if we are just trying, we are done */ @@ -436,11 +435,11 @@ static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) return ret; } -static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, - struct v4l2_subdev_state *sd_state) +static int csi2dc_init_state(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); v4l2_try_fmt->height = 480; v4l2_try_fmt->width = 640; @@ -462,7 +461,6 @@ static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { .enum_mbus_code = csi2dc_enum_mbus_code, .set_fmt = csi2dc_set_fmt, .get_fmt = csi2dc_get_fmt, - .init_cfg = csi2dc_init_cfg, }; static const struct v4l2_subdev_video_ops csi2dc_video_ops = { @@ -474,6 +472,10 @@ static const struct v4l2_subdev_ops csi2dc_subdev_ops = { .video = &csi2dc_video_ops, }; +static const struct v4l2_subdev_internal_ops csi2dc_internal_ops = { + .init_state = csi2dc_init_state, +}; + static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) @@ -678,6 +680,7 @@ static int csi2dc_probe(struct platform_device *pdev) } v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); + csi2dc->csi2dc_sd.internal_ops = &csi2dc_internal_ops; csi2dc->csi2dc_sd.owner = THIS_MODULE; csi2dc->csi2dc_sd.dev = dev; diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index 1f8528844..f3a5cbaca 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -851,38 +851,6 @@ static int isc_try_configure_pipeline(struct isc_device *isc) return 0; } -static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_subdev_frame_size_enum fse = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret; - - /* - * If we do not know yet which format the subdev is using, we cannot - * do anything. - */ - if (!isc->config.sd_format) - return; - - fse.code = isc->try_config.sd_format->mbus_code; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - sd_state, &fse); - /* - * Attempt to obtain format size from subdev. If not available, - * just use the maximum ISC can receive. - */ - if (ret) { - sd_state->pads->try_crop.width = isc->max_width; - sd_state->pads->try_crop.height = isc->max_height; - } else { - sd_state->pads->try_crop.width = fse.max_width; - sd_state->pads->try_crop.height = fse.max_height; - } -} - static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f) { struct v4l2_pix_format *pixfmt = &f->fmt.pix; @@ -944,10 +912,6 @@ static int isc_validate(struct isc_device *isc) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = isc->remote_pad, }; - struct v4l2_subdev_pad_config pad_cfg = {}; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, - }; /* Get current format from subdev */ ret = v4l2_subdev_call(isc->current_subdev->sd, pad, get_fmt, NULL, @@ -1008,9 +972,6 @@ static int isc_validate(struct isc_device *isc) if (ret) return ret; - /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_state); - /* Configure ISC pipeline for the config */ ret = isc_try_configure_pipeline(isc); if (ret) @@ -1819,7 +1780,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &isc->lock; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->dev = isc->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c index 0f29a32d1..e83463543 100644 --- a/drivers/media/platform/microchip/microchip-isc-scaler.c +++ b/drivers/media/platform/microchip/microchip-isc-scaler.c @@ -33,8 +33,8 @@ static int isc_scaler_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *v4l2_try_fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - format->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + format->pad); format->format = *v4l2_try_fmt; return 0; @@ -74,12 +74,12 @@ static int isc_scaler_set_fmt(struct v4l2_subdev *sd, req_fmt->format.code = fmt->mbus_code; if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - req_fmt->pad); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + req_fmt->pad); *v4l2_try_fmt = req_fmt->format; /* Trying on the sink pad makes the source pad change too */ - v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, - ISC_SCALER_PAD_SOURCE); + v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state, + ISC_SCALER_PAD_SOURCE); *v4l2_try_fmt = req_fmt->format; v4l_bound_align_image(&v4l2_try_fmt->width, @@ -145,17 +145,17 @@ static int isc_scaler_g_sel(struct v4l2_subdev *sd, return 0; } -static int isc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int isc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *v4l2_try_fmt = - v4l2_subdev_get_try_format(sd, sd_state, 0); + v4l2_subdev_state_get_format(sd_state, 0); struct v4l2_rect *try_crop; struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE]; - try_crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); + try_crop = v4l2_subdev_state_get_crop(sd_state, 0); try_crop->top = 0; try_crop->left = 0; @@ -170,7 +170,6 @@ static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = { .set_fmt = isc_scaler_set_fmt, .get_fmt = isc_scaler_get_fmt, .get_selection = isc_scaler_g_sel, - .init_cfg = isc_scaler_init_cfg, }; static const struct media_entity_operations isc_scaler_entity_ops = { @@ -181,11 +180,16 @@ static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = { .pad = &isc_scaler_pad_ops, }; +static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = { + .init_state = isc_scaler_init_state, +}; + int isc_scaler_init(struct isc_device *isc) { int ret; v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops); + isc->scaler_sd.internal_ops = &isc_scaler_internal_ops; isc->scaler_sd.owner = THIS_MODULE; isc->scaler_sd.dev = isc->dev; diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index b9e6782f5..a1fcb616b 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -120,7 +119,7 @@ struct npcm_video { struct mutex video_lock; /* v4l2 and videobuf2 lock */ struct list_head buffers; - spinlock_t lock; /* buffer list lock */ + struct mutex buffer_lock; /* buffer list lock */ unsigned long flags; unsigned int sequence; @@ -393,7 +392,7 @@ static void npcm_video_free_diff_table(struct npcm_video *video) struct rect_list *tmp; unsigned int i; - for (i = 0; i < video->queue.num_buffers; i++) { + for (i = 0; i < vb2_get_num_buffers(&video->queue); i++) { head = &video->list[i]; list_for_each_safe(pos, nx, head) { tmp = list_entry(pos, struct rect_list, list); @@ -782,7 +781,6 @@ static int npcm_video_start_frame(struct npcm_video *video) { struct npcm_video_buffer *buf; struct regmap *vcd = video->vcd_regmap; - unsigned long flags; unsigned int val; int ret; @@ -798,17 +796,17 @@ static int npcm_video_start_frame(struct npcm_video *video) return -EBUSY; } - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); buf = list_first_entry_or_null(&video->buffers, struct npcm_video_buffer, link); if (!buf) { - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); dev_dbg(video->dev, "No empty buffers; skip capture frame\n"); return 0; } set_bit(VIDEO_CAPTURING, &video->flags); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); npcm_video_vcd_state_machine_reset(video); @@ -834,14 +832,13 @@ static void npcm_video_bufs_done(struct npcm_video *video, enum vb2_buffer_state state) { struct npcm_video_buffer *buf; - unsigned long flags; - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); list_for_each_entry(buf, &video->buffers, link) vb2_buffer_done(&buf->vb.vb2_buf, state); INIT_LIST_HEAD(&video->buffers); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); } static void npcm_video_get_diff_rect(struct npcm_video *video, unsigned int index) @@ -1071,12 +1068,12 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) if (status & VCD_STAT_DONE) { regmap_write(vcd, VCD_INTE, 0); - spin_lock(&video->lock); + mutex_lock(&video->buffer_lock); clear_bit(VIDEO_CAPTURING, &video->flags); buf = list_first_entry_or_null(&video->buffers, struct npcm_video_buffer, link); if (!buf) { - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); return IRQ_NONE; } @@ -1093,7 +1090,7 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) size = npcm_video_hextile(video, index, dma_addr, addr); break; default: - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); return IRQ_NONE; } @@ -1104,7 +1101,7 @@ static irqreturn_t npcm_video_irq(int irq, void *arg) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); list_del(&buf->link); - spin_unlock(&video->lock); + mutex_unlock(&video->buffer_lock); if (npcm_video_start_frame(video)) dev_err(video->dev, "Failed to capture next frame\n"); @@ -1508,13 +1505,12 @@ static void npcm_video_buf_queue(struct vb2_buffer *vb) struct npcm_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct npcm_video_buffer *nvb = to_npcm_video_buffer(vbuf); - unsigned long flags; bool empty; - spin_lock_irqsave(&video->lock, flags); + mutex_lock(&video->buffer_lock); empty = list_empty(&video->buffers); list_add_tail(&nvb->link, &video->buffers); - spin_unlock_irqrestore(&video->lock, flags); + mutex_unlock(&video->buffer_lock); if (test_bit(VIDEO_STREAMING, &video->flags) && !test_bit(VIDEO_CAPTURING, &video->flags) && empty) { @@ -1616,7 +1612,7 @@ static int npcm_video_setup_video(struct npcm_video *video) vbq->drv_priv = video; vbq->buf_struct_size = sizeof(struct npcm_video_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = 3; + vbq->min_queued_buffers = 3; rc = vb2_queue_init(vbq); if (rc) { @@ -1744,8 +1740,8 @@ static int npcm_video_probe(struct platform_device *pdev) return -ENOMEM; video->dev = &pdev->dev; - spin_lock_init(&video->lock); mutex_init(&video->video_lock); + mutex_init(&video->buffer_lock); INIT_LIST_HEAD(&video->buffers); regs = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig index f7454823b..2fe13f39c 100644 --- a/drivers/media/platform/nvidia/tegra-vde/Kconfig +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -6,7 +6,6 @@ config VIDEO_TEGRA_VDE select DMA_SHARED_BUFFER select IOMMU_IOVA select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API select SRAM select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c index bd8c207d5..0f48ce6f2 100644 --- a/drivers/media/platform/nvidia/tegra-vde/v4l2.c +++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c @@ -813,7 +813,7 @@ static int tegra_open(struct file *file) struct tegra_ctx *ctx; int err; - ctx = kzalloc(offsetof(struct tegra_ctx, ctrls[ARRAY_SIZE(ctrl_cfgs)]), + ctx = kzalloc(struct_size(ctx, ctrls, ARRAY_SIZE(ctrl_cfgs)), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index b08f6d2e7..db8ff5f5c 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -954,7 +954,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) state = v4l2_subdev_lock_and_get_active_state(sd); - format = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK); csis_fmt = find_csis_format(format->code); ret = mipi_csis_calculate_params(csis, csis_fmt); @@ -1002,7 +1002,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -1069,7 +1069,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, &sdformat->format.height, 1, CSIS_MAX_PIX_HEIGHT, 0, 0); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csis_fmt->code; fmt->width = sdformat->format.width; @@ -1083,7 +1083,7 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, CSIS_PAD_SOURCE); *fmt = sdformat->format; /* The format on the source pad might change due to unpacking. */ @@ -1104,7 +1104,7 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_get_pad_format(sd, state, CSIS_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(state, CSIS_PAD_SOURCE); csis_fmt = find_csis_format(fmt->code); v4l2_subdev_unlock_state(state); @@ -1122,8 +1122,8 @@ static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return 0; } -static int mipi_csis_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int mipi_csis_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = CSIS_PAD_SINK, @@ -1163,7 +1163,6 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { }; static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { - .init_cfg = mipi_csis_init_cfg, .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mipi_csis_set_fmt, @@ -1176,6 +1175,10 @@ static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { .pad = &mipi_csis_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = { + .init_state = mipi_csis_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -1350,6 +1353,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) int ret; v4l2_subdev_init(sd, &mipi_csis_subdev_ops); + sd->internal_ops = &mipi_csis_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "csis-%s", dev_name(csis->dev)); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 15049c6aa..9566ff738 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -3,31 +3,46 @@ * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / i.MX7 SOC * * Copyright (c) 2019 Linaro Ltd - * */ #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include #include -#include #include #include +#include #include -#include -#include #include -#include +#include #include #include +#include +#include #include +#include +#include +#include +#include +#include #include -#include +#include #include #include #include +#include #include +#include #define IMX7_CSI_PAD_SINK 0 #define IMX7_CSI_PAD_SRC 1 @@ -542,8 +557,8 @@ static void imx7_csi_configure(struct imx7_csi *csi, } else { const struct v4l2_mbus_framefmt *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&csi->sd, sd_state, - IMX7_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SINK); cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; @@ -1245,6 +1260,7 @@ static int imx7_csi_video_queue_setup(struct vb2_queue *vq, struct device *alloc_devs[]) { struct imx7_csi *csi = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); struct v4l2_pix_format *pix = &csi->vdev_fmt; unsigned int count = *nbuffers; @@ -1254,14 +1270,14 @@ static int imx7_csi_video_queue_setup(struct vb2_queue *vq, if (*nplanes) { if (*nplanes != 1 || sizes[0] < pix->sizeimage) return -EINVAL; - count += vq->num_buffers; + count += q_num_bufs; } count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count); if (*nplanes) - *nbuffers = (count < vq->num_buffers) ? 0 : - count - vq->num_buffers; + *nbuffers = (count < q_num_bufs) ? 0 : + count - q_num_bufs; else *nbuffers = count; @@ -1675,7 +1691,7 @@ static int imx7_csi_video_init(struct imx7_csi *csi) vq->mem_ops = &vb2_dma_contig_memops; vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->lock = &csi->vdev_mutex; - vq->min_buffers_needed = 2; + vq->min_queued_buffers = 2; vq->dev = csi->dev; ret = vb2_queue_init(vq); @@ -1728,8 +1744,8 @@ out_unlock: return ret; } -static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx7_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { const struct imx7_csi_pixfmt *cc; int i; @@ -1738,7 +1754,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd, for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { struct v4l2_mbus_framefmt *mf = - v4l2_subdev_get_pad_format(sd, sd_state, i); + v4l2_subdev_state_get_format(sd_state, i); mf->code = IMX7_CSI_DEF_MBUS_CODE; mf->width = IMX7_CSI_DEF_PIX_WIDTH; @@ -1762,7 +1778,7 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; int ret = 0; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (code->pad) { case IMX7_CSI_PAD_SINK: @@ -1841,7 +1857,7 @@ static void imx7_csi_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *in_fmt; u32 code; - in_fmt = v4l2_subdev_get_pad_format(sd, sd_state, IMX7_CSI_PAD_SINK); + in_fmt = v4l2_subdev_state_get_format(sd_state, IMX7_CSI_PAD_SINK); switch (sdformat->pad) { case IMX7_CSI_PAD_SRC: @@ -1891,7 +1907,7 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, imx7_csi_try_fmt(sd, sd_state, sdformat, &cc); - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *fmt = sdformat->format; @@ -1902,8 +1918,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, format.format = sdformat->format; imx7_csi_try_fmt(sd, sd_state, &format, &outcc); - outfmt = v4l2_subdev_get_pad_format(sd, sd_state, - IMX7_CSI_PAD_SRC); + outfmt = v4l2_subdev_state_get_format(sd_state, + IMX7_CSI_PAD_SRC); *outfmt = format.format; } @@ -2005,7 +2021,6 @@ static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { - .init_cfg = imx7_csi_init_cfg, .enum_mbus_code = imx7_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx7_csi_set_fmt, @@ -2018,6 +2033,7 @@ static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { }; static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { + .init_state = imx7_csi_init_state, .registered = imx7_csi_registered, .unregistered = imx7_csi_unregistered, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index c9a4d091b..93a55c97c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -58,7 +58,7 @@ static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, return -EINVAL; } - fmt = v4l2_subdev_state_get_stream_format(state, port, 0); + fmt = v4l2_subdev_state_get_format(state, port, 0); if (!fmt) return -EINVAL; @@ -175,8 +175,8 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, return sd; } -static int mxc_isi_crossbar_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); struct v4l2_subdev_krouting routing = { }; @@ -280,8 +280,7 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, * Set the format on the sink stream and propagate it to the source * streams. */ - sink_fmt = v4l2_subdev_state_get_stream_format(state, fmt->pad, - fmt->stream); + sink_fmt = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream); if (!sink_fmt) return -EINVAL; @@ -295,8 +294,9 @@ static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd, route->sink_stream != fmt->stream) continue; - source_fmt = v4l2_subdev_state_get_stream_format(state, route->source_pad, - route->source_stream); + source_fmt = v4l2_subdev_state_get_format(state, + route->source_pad, + route->source_stream); if (!source_fmt) return -EINVAL; @@ -403,7 +403,6 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { - .init_cfg = mxc_isi_crossbar_init_cfg, .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_crossbar_set_fmt, @@ -416,6 +415,10 @@ static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = { .pad = &mxc_isi_crossbar_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_crossbar_internal_ops = { + .init_state = mxc_isi_crossbar_init_state, +}; + static const struct media_entity_operations mxc_isi_cross_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -437,6 +440,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) xbar->isi = isi; v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops); + sd->internal_ops = &mxc_isi_crossbar_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, "crossbar", sizeof(sd->name)); sd->dev = isi->dev; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c index 6709ab7ea..5e8a177da 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c @@ -22,10 +22,11 @@ static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg) static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) { #define MXC_ISI_DEBUG_REG(name) { name, #name } - static const struct { + struct debug_regs { u32 offset; const char * const name; - } registers[] = { + }; + static const struct debug_regs registers[] = { MXC_ISI_DEBUG_REG(CHNL_CTRL), MXC_ISI_DEBUG_REG(CHNL_IMG_CTRL), MXC_ISI_DEBUG_REG(CHNL_OUT_BUF_CTRL), @@ -67,6 +68,16 @@ static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) MXC_ISI_DEBUG_REG(CHNL_SCL_IMG_CFG), MXC_ISI_DEBUG_REG(CHNL_FLOW_CTRL), }; + /* These registers contain the upper 4 bits of 36-bit DMA addresses. */ + static const struct debug_regs registers_36bit_dma[] = { + MXC_ISI_DEBUG_REG(CHNL_Y_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_U_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_V_BUF1_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_Y_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_U_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_V_BUF2_XTND_ADDR), + MXC_ISI_DEBUG_REG(CHNL_IN_BUF_XTND_ADDR), + }; struct mxc_isi_pipe *pipe = m->private; unsigned int i; @@ -77,10 +88,20 @@ static int mxc_isi_debug_dump_regs_show(struct seq_file *m, void *p) seq_printf(m, "--- ISI pipe %u registers ---\n", pipe->id); for (i = 0; i < ARRAY_SIZE(registers); ++i) - seq_printf(m, "%20s[0x%02x]: 0x%08x\n", + seq_printf(m, "%21s[0x%02x]: 0x%08x\n", registers[i].name, registers[i].offset, mxc_isi_read(pipe, registers[i].offset)); + if (pipe->isi->pdata->has_36bit_dma) { + for (i = 0; i < ARRAY_SIZE(registers_36bit_dma); ++i) { + const struct debug_regs *reg = ®isters_36bit_dma[i]; + + seq_printf(m, "%21s[0x%02x]: 0x%08x\n", + reg->name, reg->offset, + mxc_isi_read(pipe, reg->offset)); + } + } + pm_runtime_put(pipe->isi->dev); return 0; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 65d20e9ba..d76eb58de 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -263,10 +263,10 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) /* Configure the pipeline. */ state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); - compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); - crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); + compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK); + crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, MXC_ISI_PIPE_PAD_SINK); @@ -322,7 +322,7 @@ mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_format(&pipe->sd, state, pad); + return v4l2_subdev_state_get_format(state, pad); } static struct v4l2_rect * @@ -330,7 +330,7 @@ mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); + return v4l2_subdev_state_get_crop(state, pad); } static struct v4l2_rect * @@ -338,11 +338,11 @@ mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, struct v4l2_subdev_state *state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); + return v4l2_subdev_state_get_compose(state, pad); } -static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct mxc_isi_pipe *pipe = to_isi_pipe(sd); struct v4l2_mbus_framefmt *fmt_source; @@ -682,7 +682,6 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { - .init_cfg = mxc_isi_pipe_init_cfg, .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_pipe_set_fmt, @@ -694,6 +693,10 @@ static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { .pad = &mxc_isi_pipe_subdev_pad_ops, }; +static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = { + .init_state = mxc_isi_pipe_init_state, +}; + /* ----------------------------------------------------------------------------- * IRQ handling */ @@ -767,6 +770,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) sd = &pipe->sd; v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); + sd->internal_ops = &mxc_isi_pipe_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); sd->dev = isi->dev; @@ -832,8 +836,8 @@ int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, int ret; state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); - src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); v4l2_subdev_unlock_state(state); sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, 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 10840c9a0..4091f1c0e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -713,7 +713,7 @@ static int mxc_isi_video_validate_format(struct mxc_isi_video *video) info = mxc_isi_format_by_fourcc(video->pix.pixelformat, MXC_ISI_VIDEO_CAP); - format = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE); if (format->code != info->mbus_code || format->width != video->pix.width || @@ -1453,7 +1453,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_buffers_needed = 2; + q->min_queued_buffers = 2; 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 ed048f73c..ba2e81f24 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -296,7 +296,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, /* Calculate the line rate from the pixel rate. */ - fmt = v4l2_subdev_get_pad_format(&state->sd, sd_state, MIPI_CSI2_PAD_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); csi2_fmt = find_csi2_format(fmt->code); link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, @@ -437,14 +437,15 @@ unlock: return ret; } -static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int imx8mq_mipi_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt_sink; struct v4l2_mbus_framefmt *fmt_source; - fmt_sink = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SINK); - fmt_source = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt_sink = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK); + fmt_source = v4l2_subdev_state_get_format(sd_state, + MIPI_CSI2_PAD_SOURCE); fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; @@ -477,7 +478,7 @@ static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, code->pad); + fmt = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = fmt->code; return 0; } @@ -514,7 +515,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, if (!csi2_fmt) csi2_fmt = &imx8mq_mipi_csi_formats[0]; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad); fmt->code = csi2_fmt->code; fmt->width = sdformat->format.width; @@ -523,7 +524,7 @@ static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, sdformat->format = *fmt; /* Propagate the format from sink to source. */ - fmt = v4l2_subdev_get_pad_format(sd, sd_state, MIPI_CSI2_PAD_SOURCE); + fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SOURCE); *fmt = sdformat->format; return 0; @@ -534,7 +535,6 @@ static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = { }; static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = { - .init_cfg = imx8mq_mipi_csi_init_cfg, .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx8mq_mipi_csi_set_fmt, @@ -545,6 +545,10 @@ static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = { .pad = &imx8mq_mipi_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops imx8mq_mipi_csi_internal_ops = { + .init_state = imx8mq_mipi_csi_init_state, +}; + /* ----------------------------------------------------------------------------- * Media entity operations */ @@ -759,6 +763,7 @@ static int imx8mq_mipi_csi_subdev_init(struct csi_state *state) int ret; v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops); + sd->internal_ops = &imx8mq_mipi_csi_internal_ops; sd->owner = THIS_MODULE; snprintf(sd->name, sizeof(sd->name), "%s %s", MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 05ff5fa80..b11de4797 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -21,7 +21,6 @@ * interface support. As a result of that it has an * alternate register layout. */ -#define IS_LITE (csid->id >= 2 ? 1 : 0) #define CSID_HW_VERSION 0x0 #define HW_VERSION_STEPPING 0 @@ -35,13 +34,13 @@ #define CSID_CSI2_RX_IRQ_MASK 0x24 #define CSID_CSI2_RX_IRQ_CLEAR 0x28 -#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((IS_LITE ? 0x30 : 0x40) \ +#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((csid_is_lite(csid) ? 0x30 : 0x40) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((IS_LITE ? 0x34 : 0x44) \ +#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((csid_is_lite(csid) ? 0x34 : 0x44) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((IS_LITE ? 0x38 : 0x48) \ +#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((csid_is_lite(csid) ? 0x38 : 0x48) \ + 0x10 * (rdi)) -#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((IS_LITE ? 0x3C : 0x4C) \ +#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((csid_is_lite(csid) ? 0x3C : 0x4C) \ + 0x10 * (rdi)) #define CSID_TOP_IRQ_STATUS 0x70 @@ -73,7 +72,7 @@ #define CGC_MODE_DYNAMIC_GATING 0 #define CGC_MODE_ALWAYS_ON 1 -#define CSID_RDI_CFG0(rdi) ((IS_LITE ? 0x200 : 0x300) \ +#define CSID_RDI_CFG0(rdi) ((csid_is_lite(csid) ? 0x200 : 0x300) \ + 0x100 * (rdi)) #define RDI_CFG0_BYTE_CNTR_EN 0 #define RDI_CFG0_FORMAT_MEASURE_EN 1 @@ -98,32 +97,32 @@ #define RDI_CFG0_PACKING_FORMAT 30 #define RDI_CFG0_ENABLE 31 -#define CSID_RDI_CFG1(rdi) ((IS_LITE ? 0x204 : 0x304)\ +#define CSID_RDI_CFG1(rdi) ((csid_is_lite(csid) ? 0x204 : 0x304)\ + 0x100 * (rdi)) #define RDI_CFG1_TIMESTAMP_STB_SEL 0 -#define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\ +#define CSID_RDI_CTRL(rdi) ((csid_is_lite(csid) ? 0x208 : 0x308)\ + 0x100 * (rdi)) #define RDI_CTRL_HALT_CMD 0 #define HALT_CMD_HALT_AT_FRAME_BOUNDARY 0 #define HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 #define RDI_CTRL_HALT_MODE 2 -#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\ +#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x20C : 0x30C)\ + 0x100 * (rdi)) -#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((IS_LITE ? 0x210 : 0x310)\ +#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x210 : 0x310)\ + 0x100 * (rdi)) -#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((IS_LITE ? 0x214 : 0x314)\ +#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((csid_is_lite(csid) ? 0x214 : 0x314)\ + 0x100 * (rdi)) -#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((IS_LITE ? 0x218 : 0x318)\ +#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((csid_is_lite(csid) ? 0x218 : 0x318)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((IS_LITE ? 0x224 : 0x324)\ +#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x224 : 0x324)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((IS_LITE ? 0x228 : 0x328)\ +#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x228 : 0x328)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((IS_LITE ? 0x22C : 0x32C)\ +#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((csid_is_lite(csid) ? 0x22C : 0x32C)\ + 0x100 * (rdi)) -#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((IS_LITE ? 0x230 : 0x330)\ +#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((csid_is_lite(csid) ? 0x230 : 0x330)\ + 0x100 * (rdi)) #define CSID_TPG_CTRL 0x600 diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 95873f988..eb27d69e8 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -263,7 +263,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) /* * __csid_get_format - Get pointer to format structure * @csid: CSID device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -276,8 +276,7 @@ __csid_get_format(struct csid_device *csid, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csid->fmt[pad]; } @@ -285,7 +284,7 @@ __csid_get_format(struct csid_device *csid, /* * csid_try_format - Handle try format by pad subdev method * @csid: CSID device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -353,7 +352,7 @@ static void csid_try_format(struct csid_device *csid, /* * csid_enum_mbus_code - Handle pixel format enumeration * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -394,7 +393,7 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, /* * csid_enum_frame_size - Handle frame size enumeration * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -431,7 +430,7 @@ static int csid_enum_frame_size(struct v4l2_subdev *sd, /* * csid_get_format - Handle get format by pads subdev method * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -455,7 +454,7 @@ static int csid_get_format(struct v4l2_subdev *sd, /* * csid_set_format - Handle set format by pads subdev method * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -897,3 +896,8 @@ void msm_csid_unregister_entity(struct csid_device *csid) media_entity_cleanup(&csid->subdev.entity); v4l2_ctrl_handler_free(&csid->ctrls); } + +inline bool csid_is_lite(struct csid_device *csid) +{ + return csid->camss->res->csid_res[csid->id].is_lite; +} diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 30d94eb2e..fddccb69d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -215,5 +215,12 @@ extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; extern const struct csid_hw_ops csid_ops_gen2; +/* + * csid_is_lite - Check if CSID is CSID lite. + * @csid: CSID Device + * + * Return whether CSID is CSID lite + */ +bool csid_is_lite(struct csid_device *csid); #endif /* QC_MSM_CAMSS_CSID_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index edd573606..264c99efe 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -312,7 +312,7 @@ static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) /* * __csiphy_get_format - Get pointer to format structure * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -325,8 +325,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &csiphy->fmt[pad]; } @@ -334,7 +333,7 @@ __csiphy_get_format(struct csiphy_device *csiphy, /* * csiphy_try_format - Handle try format by pad subdev method * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -381,7 +380,7 @@ static void csiphy_try_format(struct csiphy_device *csiphy, /* * csiphy_enum_mbus_code - Handle pixel format enumeration * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -414,7 +413,7 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, /* * csiphy_enum_frame_size - Handle frame size enumeration * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -451,7 +450,7 @@ static int csiphy_enum_frame_size(struct v4l2_subdev *sd, /* * csiphy_get_format - Handle get format by pads subdev method * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -475,7 +474,7 @@ static int csiphy_get_format(struct v4l2_subdev *sd, /* * csiphy_set_format - Handle set format by pads subdev method * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index be9d2f0a1..a12dcc7ff 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -270,7 +270,7 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) unsigned long time; u32 val; - if (vfe_id > camss->res->vfe_num - 1) { + if (vfe_id >= camss->res->vfe_num) { dev_err(camss->dev, "Error: asked reset for invalid VFE%d\n", vfe_id); return -ENOENT; @@ -866,7 +866,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) /* * __ispif_get_format - Get pointer to format structure * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -879,8 +879,7 @@ __ispif_get_format(struct ispif_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -888,7 +887,7 @@ __ispif_get_format(struct ispif_line *line, /* * ispif_try_format - Handle try format by pad subdev method * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -936,7 +935,7 @@ static void ispif_try_format(struct ispif_line *line, /* * ispif_enum_mbus_code - Handle pixel format enumeration * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -969,7 +968,7 @@ static int ispif_enum_mbus_code(struct v4l2_subdev *sd, /* * ispif_enum_frame_size - Handle frame size enumeration * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * return -EINVAL or zero on success */ @@ -1006,7 +1005,7 @@ static int ispif_enum_frame_size(struct v4l2_subdev *sd, /* * ispif_get_format - Handle get format by pads subdev method * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1030,7 +1029,7 @@ static int ispif_get_format(struct v4l2_subdev *sd, /* * ispif_set_format - Handle set format by pads subdev method * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c index 0b211fed1..795ac3815 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c @@ -627,42 +627,6 @@ out_unlock: spin_unlock_irqrestore(&vfe->output_lock, flags); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - if (vfe->id >= camss->res->vfe_num) - return; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - if (id >= camss->res->vfe_num) - return 0; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[id]) - return -EINVAL; - - return 0; -} - /* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 2911e4126..ef6b34c91 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -936,7 +936,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) * vfe_pm_domain_off - Disable power domains specific to this VFE. * @vfe: VFE Device */ -static void vfe_pm_domain_off(struct vfe_device *vfe) +static void vfe_4_1_pm_domain_off(struct vfe_device *vfe) { /* nop */ } @@ -945,7 +945,7 @@ static void vfe_pm_domain_off(struct vfe_device *vfe) * vfe_pm_domain_on - Enable power domains specific to this VFE. * @vfe: VFE Device */ -static int vfe_pm_domain_on(struct vfe_device *vfe) +static int vfe_4_1_pm_domain_on(struct vfe_device *vfe) { return 0; } @@ -999,8 +999,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = { .hw_version = vfe_hw_version, .isr_read = vfe_isr_read, .isr = vfe_isr, - .pm_domain_off = vfe_pm_domain_off, - .pm_domain_on = vfe_pm_domain_on, + .pm_domain_off = vfe_4_1_pm_domain_off, + .pm_domain_on = vfe_4_1_pm_domain_on, .reg_update_clear = vfe_reg_update_clear, .reg_update = vfe_reg_update, .subdev_init = vfe_subdev_init, diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index b65ed0fef..7655d22a9 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -1103,42 +1103,6 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss; - - if (!vfe) - return; - - camss = vfe->camss; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); - - if (!camss->genpd_link[id]) { - dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); - return -EINVAL; - } - - return 0; -} - static void vfe_violation_read(struct vfe_device *vfe) { u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c index 7b3805177..f52fa30f3 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -1093,37 +1093,6 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); - - if (!camss->genpd_link[id]) { - dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); - return -EINVAL; - } - - return 0; -} - static void vfe_violation_read(struct vfe_device *vfe) { u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c index f2368b77f..dc2735476 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-480.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c @@ -15,31 +15,28 @@ #include "camss.h" #include "camss-vfe.h" -/* VFE 2/3 are lite and have a different register layout */ -#define IS_LITE (vfe->id >= 2 ? 1 : 0) - #define VFE_HW_VERSION (0x00) -#define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c) -#define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0)) +#define VFE_GLOBAL_RESET_CMD (vfe_is_lite(vfe) ? 0x0c : 0x1c) +#define GLOBAL_RESET_HW_AND_REG (vfe_is_lite(vfe) ? BIT(1) : BIT(0)) -#define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34) +#define VFE_REG_UPDATE_CMD (vfe_is_lite(vfe) ? 0x20 : 0x34) static inline int reg_update_rdi(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(n) : BIT(1 + (n)); + return vfe_is_lite(vfe) ? BIT(n) : BIT(1 + (n)); } #define REG_UPDATE_RDI reg_update_rdi -#define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38) +#define VFE_IRQ_CMD (vfe_is_lite(vfe) ? 0x24 : 0x38) #define IRQ_CMD_GLOBAL_CLEAR BIT(0) -#define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4) -#define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0)) -#define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7)) -#define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4) -#define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4) +#define VFE_IRQ_MASK(n) ((vfe_is_lite(vfe) ? 0x28 : 0x3c) + (n) * 4) +#define IRQ_MASK_0_RESET_ACK (vfe_is_lite(vfe) ? BIT(17) : BIT(0)) +#define IRQ_MASK_0_BUS_TOP_IRQ (vfe_is_lite(vfe) ? BIT(4) : BIT(7)) +#define VFE_IRQ_CLEAR(n) ((vfe_is_lite(vfe) ? 0x34 : 0x48) + (n) * 4) +#define VFE_IRQ_STATUS(n) ((vfe_is_lite(vfe) ? 0x40 : 0x54) + (n) * 4) -#define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00) +#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x1a00 : 0xaa00) #define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08) #define WM_CGC_OVERRIDE_ALL (0x3FFFFFF) @@ -49,13 +46,13 @@ static inline int reg_update_rdi(struct vfe_device *vfe, int n) #define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4) static inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(n) : BIT(3 + (n)); + return vfe_is_lite(vfe) ? BIT(n) : BIT(3 + (n)); } #define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) { - return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n)); + return vfe_is_lite(vfe) ? BIT(4 + (n)) : BIT(6 + (n)); } #define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done @@ -90,8 +87,8 @@ static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) /* for titan 480, each bus client is hardcoded to a specific path * and each bus client is part of a hardcoded "comp group" */ -#define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n)) -#define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n)) +#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 23) + (n)) +#define RDI_COMP_GROUP(n) ((vfe_is_lite(vfe) ? 0 : 11) + (n)) #define MAX_VFE_OUTPUT_LINES 4 @@ -452,42 +449,6 @@ out_unlock: spin_unlock_irqrestore(&vfe->output_lock, flags); } -/* - * vfe_pm_domain_off - Disable power domains specific to this VFE. - * @vfe: VFE Device - */ -static void vfe_pm_domain_off(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - - if (vfe->id >= camss->res->vfe_num) - return; - - device_link_del(camss->genpd_link[vfe->id]); -} - -/* - * vfe_pm_domain_on - Enable power domains specific to this VFE. - * @vfe: VFE Device - */ -static int vfe_pm_domain_on(struct vfe_device *vfe) -{ - struct camss *camss = vfe->camss; - enum vfe_line_id id = vfe->id; - - if (id >= camss->res->vfe_num) - return 0; - - camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[id]) - return -EINVAL; - - return 0; -} - /* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 4839e2ced..2062be668 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -474,6 +475,40 @@ void vfe_isr_reset_ack(struct vfe_device *vfe) complete(&vfe->reset_complete); } +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +void vfe_pm_domain_off(struct vfe_device *vfe) +{ + if (!vfe->genpd) + return; + + device_link_del(vfe->genpd_link); + vfe->genpd_link = NULL; +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +int vfe_pm_domain_on(struct vfe_device *vfe) +{ + struct camss *camss = vfe->camss; + + if (!vfe->genpd) + return 0; + + vfe->genpd_link = device_link_add(camss->dev, vfe->genpd, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!vfe->genpd_link) + return -EINVAL; + + return 0; +} + static int vfe_match_clock_names(struct vfe_device *vfe, struct camss_clock *clock) { @@ -815,7 +850,7 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) /* * __vfe_get_format - Get pointer to format structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad from which format is requested * @which: TRY or ACTIVE format * @@ -828,8 +863,7 @@ __vfe_get_format(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); return &line->fmt[pad]; } @@ -837,7 +871,7 @@ __vfe_get_format(struct vfe_line *line, /* * __vfe_get_compose - Get pointer to compose selection structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which: TRY or ACTIVE format * * Return pointer to TRY or ACTIVE compose rectangle structure @@ -848,8 +882,8 @@ __vfe_get_compose(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, sd_state, - MSM_VFE_PAD_SINK); + return v4l2_subdev_state_get_compose(sd_state, + MSM_VFE_PAD_SINK); return &line->compose; } @@ -857,7 +891,7 @@ __vfe_get_compose(struct vfe_line *line, /* * __vfe_get_crop - Get pointer to crop selection structure * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which: TRY or ACTIVE format * * Return pointer to TRY or ACTIVE crop rectangle structure @@ -868,8 +902,7 @@ __vfe_get_crop(struct vfe_line *line, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, sd_state, - MSM_VFE_PAD_SRC); + return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC); return &line->crop; } @@ -877,7 +910,7 @@ __vfe_get_crop(struct vfe_line *line, /* * vfe_try_format - Handle try format by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad on which format is requested * @fmt: pointer to v4l2 format structure * @which: wanted subdev format @@ -938,7 +971,7 @@ static void vfe_try_format(struct vfe_line *line, /* * vfe_try_compose - Handle try compose selection by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @rect: pointer to v4l2 rect structure * @which: wanted subdev format */ @@ -977,7 +1010,7 @@ static void vfe_try_compose(struct vfe_line *line, /* * vfe_try_crop - Handle try crop selection by pad subdev method * @line: VFE line - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @rect: pointer to v4l2 rect structure * @which: wanted subdev format */ @@ -1020,7 +1053,7 @@ static void vfe_try_crop(struct vfe_line *line, /* * vfe_enum_mbus_code - Handle pixel format enumeration * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: pointer to v4l2_subdev_mbus_code_enum structure * * return -EINVAL or zero on success @@ -1054,7 +1087,7 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, /* * vfe_enum_frame_size - Handle frame size enumeration * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: pointer to v4l2_subdev_frame_size_enum structure * * Return -EINVAL or zero on success @@ -1092,7 +1125,7 @@ static int vfe_enum_frame_size(struct v4l2_subdev *sd, /* * vfe_get_format - Handle get format by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1120,7 +1153,7 @@ static int vfe_set_selection(struct v4l2_subdev *sd, /* * vfe_set_format - Handle set format by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * * Return -EINVAL or zero on success @@ -1171,7 +1204,7 @@ static int vfe_set_format(struct v4l2_subdev *sd, /* * vfe_get_selection - Handle get selection by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: pointer to v4l2 subdev selection structure * * Return -EINVAL or zero on success @@ -1241,7 +1274,7 @@ static int vfe_get_selection(struct v4l2_subdev *sd, /* * vfe_set_selection - Handle set selection by pads subdev method * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: pointer to v4l2 subdev selection structure * * Return -EINVAL or zero on success @@ -1347,6 +1380,34 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, if (!res->line_num) return -EINVAL; + /* Power domain */ + + if (res->pd_name) { + vfe->genpd = dev_pm_domain_attach_by_name(camss->dev, + res->pd_name); + if (IS_ERR(vfe->genpd)) { + ret = PTR_ERR(vfe->genpd); + return ret; + } + } + + if (!vfe->genpd && res->has_pd) { + /* + * Legacy magic index. + * Requires + * power-domain = , + * , + * + * id must correspondng to the index of the VFE which must + * come before the TOP GDSC. VFE Lite has no individually + * collapasible domain which is why id < vfe_num is a valid + * check. + */ + vfe->genpd = dev_pm_domain_attach_by_id(camss->dev, id); + if (IS_ERR(vfe->genpd)) + return PTR_ERR(vfe->genpd); + } + vfe->line_num = res->line_num; vfe->ops->subdev_init(dev, vfe); @@ -1469,6 +1530,19 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, return 0; } +/* + * msm_vfe_genpd_cleanup - Cleanup VFE genpd linkages + * @vfe: VFE device + */ +void msm_vfe_genpd_cleanup(struct vfe_device *vfe) +{ + if (vfe->genpd_link) + device_link_del(vfe->genpd_link); + + if (vfe->genpd) + dev_pm_domain_detach(vfe->genpd, true); +} + /* * vfe_link_setup - Setup VFE connections * @entity: Pointer to media entity structure @@ -1663,3 +1737,8 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) media_entity_cleanup(&sd->entity); } } + +bool vfe_is_lite(struct vfe_device *vfe) +{ + return vfe->camss->res->vfe_res[vfe->id].is_lite; +} diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 09baded0d..0572c9b08 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -150,6 +150,8 @@ struct vfe_device { const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; struct camss_video_ops video_ops; + struct device *genpd; + struct device_link *genpd_link; }; struct camss_subdev_resources; @@ -157,6 +159,8 @@ struct camss_subdev_resources; int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, const struct camss_subdev_resources *res, u8 id); +void msm_vfe_genpd_cleanup(struct vfe_device *vfe); + int msm_vfe_register_entities(struct vfe_device *vfe, struct v4l2_device *v4l2_dev); @@ -201,6 +205,18 @@ int vfe_reset(struct vfe_device *vfe); */ int vfe_disable(struct vfe_line *line); +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +void vfe_pm_domain_off(struct vfe_device *vfe); + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +int vfe_pm_domain_on(struct vfe_device *vfe); + extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; @@ -210,4 +226,14 @@ extern const struct vfe_hw_ops vfe_ops_480; int vfe_get(struct vfe_device *vfe); void vfe_put(struct vfe_device *vfe); +/* + * vfe_is_lite - Return if VFE is VFE lite. + * @vfe: VFE Device + * + * Some VFE lites have a different register layout. + * + * Return whether VFE is VFE lite + */ +bool vfe_is_lite(struct vfe_device *vfe); + #endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8e78dd8d5..58f4be660 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -278,6 +278,7 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_7 }, @@ -298,6 +299,7 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_7 } }; @@ -468,6 +470,7 @@ static const struct camss_subdev_resources vfe_res_660[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_8 }, @@ -491,6 +494,7 @@ static const struct camss_subdev_resources vfe_res_660[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 3, + .has_pd = true, .ops = &vfe_ops_4_8 } }; @@ -634,6 +638,7 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, + .is_lite = true, .ops = &csid_ops_gen2 } }; @@ -658,6 +663,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { .reg = { "vfe0" }, .interrupt = { "vfe0" }, .line_num = 4, + .has_pd = true, .ops = &vfe_ops_170 }, @@ -680,6 +686,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { .reg = { "vfe1" }, .interrupt = { "vfe1" }, .line_num = 4, + .has_pd = true, .ops = &vfe_ops_170 }, @@ -700,6 +707,7 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe_lite" }, .interrupt = { "vfe_lite" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_170 } @@ -805,6 +813,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, + .is_lite = true, .ops = &csid_ops_gen2 }, /* CSID3 */ @@ -817,6 +826,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, + .is_lite = true, .ops = &csid_ops_gen2 } }; @@ -839,7 +849,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, + .pd_name = "ife0", .line_num = 3, + .has_pd = true, .ops = &vfe_ops_480 }, /* VFE1 */ @@ -859,7 +871,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, + .pd_name = "ife1", .line_num = 3, + .has_pd = true, .ops = &vfe_ops_480 }, /* VFE2 (lite) */ @@ -878,6 +892,7 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_480 }, @@ -897,6 +912,7 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, + .is_lite = true, .line_num = 4, .ops = &vfe_ops_480 }, @@ -1196,7 +1212,7 @@ static int camss_init_subdevices(struct camss *camss) } /* note: SM8250 requires VFE to be initialized before CSID */ - for (i = 0; i < camss->vfe_total_num; i++) { + for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_subdev_init(camss, &camss->vfe[i], &res->vfe_res[i], i); if (ret < 0) { @@ -1268,7 +1284,7 @@ static int camss_register_entities(struct camss *camss) goto err_reg_ispif; } - for (i = 0; i < camss->vfe_total_num; i++) { + for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_register_entities(&camss->vfe[i], &camss->v4l2_dev); if (ret < 0) { @@ -1340,7 +1356,7 @@ static int camss_register_entities(struct camss *camss) } } else { for (i = 0; i < camss->res->csid_num; i++) - for (k = 0; k < camss->vfe_total_num; k++) + for (k = 0; k < camss->res->vfe_num; k++) for (j = 0; j < camss->vfe[k].line_num; j++) { struct v4l2_subdev *csid = &camss->csid[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1364,7 +1380,7 @@ static int camss_register_entities(struct camss *camss) return 0; err_link: - i = camss->vfe_total_num; + i = camss->res->vfe_num; err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -1403,7 +1419,7 @@ static void camss_unregister_entities(struct camss *camss) msm_ispif_unregister_entities(camss->ispif); - for (i = 0; i < camss->vfe_total_num; i++) + for (i = 0; i < camss->res->vfe_num; i++) msm_vfe_unregister_entities(&camss->vfe[i]); } @@ -1478,7 +1494,9 @@ static const struct media_device_ops camss_media_ops = { static int camss_configure_pd(struct camss *camss) { + const struct camss_resources *res = camss->res; struct device *dev = camss->dev; + int vfepd_num; int i; int ret; @@ -1498,45 +1516,60 @@ static int camss_configure_pd(struct camss *camss) if (camss->genpd_num == 1) return 0; - camss->genpd = devm_kmalloc_array(dev, camss->genpd_num, - sizeof(*camss->genpd), GFP_KERNEL); - if (!camss->genpd) - return -ENOMEM; + /* count the # of VFEs which have flagged power-domain */ + for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) { + if (res->vfe_res[i].has_pd) + vfepd_num++; + } - camss->genpd_link = devm_kmalloc_array(dev, camss->genpd_num, - sizeof(*camss->genpd_link), - GFP_KERNEL); - if (!camss->genpd_link) - return -ENOMEM; + /* + * If the number of power-domains is greater than the number of VFEs + * then the additional power-domain is for the entire CAMSS block. + */ + if (!(camss->genpd_num > vfepd_num)) + return 0; /* - * VFE power domains are in the beginning of the list, and while all - * power domains should be attached, only if TITAN_TOP power domain is - * found in the list, it should be linked over here. + * If a power-domain name is defined try to use it. + * It is possible we are running a new kernel with an old dtb so + * fallback to indexes even if a pd_name is defined but not found. */ - for (i = 0; i < camss->genpd_num; i++) { - camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i); - if (IS_ERR(camss->genpd[i])) { - ret = PTR_ERR(camss->genpd[i]); + if (camss->res->pd_name) { + camss->genpd = dev_pm_domain_attach_by_name(camss->dev, + camss->res->pd_name); + if (IS_ERR(camss->genpd)) { + ret = PTR_ERR(camss->genpd); goto fail_pm; } } - if (i > camss->res->vfe_num) { - camss->genpd_link[i - 1] = device_link_add(camss->dev, camss->genpd[i - 1], - DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!camss->genpd_link[i - 1]) { - ret = -EINVAL; - goto fail_pm; - } + if (!camss->genpd) { + /* + * Legacy magic index. TITAN_TOP GDSC must be the last + * item in the power-domain list. + */ + camss->genpd = dev_pm_domain_attach_by_id(camss->dev, + camss->genpd_num - 1); + } + if (IS_ERR_OR_NULL(camss->genpd)) { + if (!camss->genpd) + ret = -ENODEV; + else + ret = PTR_ERR(camss->genpd); + goto fail_pm; + } + camss->genpd_link = device_link_add(camss->dev, camss->genpd, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!camss->genpd_link) { + ret = -EINVAL; + goto fail_pm; } return 0; fail_pm: - for (--i ; i >= 0; i--) - dev_pm_domain_detach(camss->genpd[i], true); + dev_pm_domain_detach(camss->genpd, true); return ret; } @@ -1558,18 +1591,25 @@ static int camss_icc_get(struct camss *camss) return 0; } -static void camss_genpd_cleanup(struct camss *camss) +static void camss_genpd_subdevice_cleanup(struct camss *camss) { int i; + for (i = 0; i < camss->res->vfe_num; i++) + msm_vfe_genpd_cleanup(&camss->vfe[i]); +} + +static void camss_genpd_cleanup(struct camss *camss) +{ if (camss->genpd_num == 1) return; - if (camss->genpd_num > camss->res->vfe_num) - device_link_del(camss->genpd_link[camss->genpd_num - 1]); + camss_genpd_subdevice_cleanup(camss); + + if (camss->genpd_link) + device_link_del(camss->genpd_link); - for (i = 0; i < camss->genpd_num; i++) - dev_pm_domain_detach(camss->genpd[i], true); + dev_pm_domain_detach(camss->genpd, true); } /* @@ -1612,8 +1652,7 @@ static int camss_probe(struct platform_device *pdev) return -ENOMEM; } - camss->vfe_total_num = camss->res->vfe_num + camss->res->vfe_lite_num; - camss->vfe = devm_kcalloc(dev, camss->vfe_total_num, + camss->vfe = devm_kcalloc(dev, camss->res->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); if (!camss->vfe) return -ENOMEM; @@ -1771,12 +1810,12 @@ static const struct camss_resources sdm845_resources = { .vfe_res = vfe_res_845, .csiphy_num = ARRAY_SIZE(csiphy_res_845), .csid_num = ARRAY_SIZE(csid_res_845), - .vfe_num = 2, - .vfe_lite_num = 1, + .vfe_num = ARRAY_SIZE(vfe_res_845), }; static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, + .pd_name = "top", .csiphy_res = csiphy_res_8250, .csid_res = csid_res_8250, .vfe_res = vfe_res_8250, @@ -1784,8 +1823,7 @@ static const struct camss_resources sm8250_resources = { .icc_path_num = ARRAY_SIZE(icc_res_sm8250), .csiphy_num = ARRAY_SIZE(csiphy_res_8250), .csid_num = ARRAY_SIZE(csid_res_8250), - .vfe_num = 2, - .vfe_lite_num = 2, + .vfe_num = ARRAY_SIZE(vfe_res_8250), }; static const struct of_device_id camss_dt_match[] = { diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 8acad7321..a0c2dcc77 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -48,7 +48,10 @@ struct camss_subdev_resources { u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; char *reg[CAMSS_RES_MAX]; char *interrupt[CAMSS_RES_MAX]; + char *pd_name; u8 line_num; + bool has_pd; + bool is_lite; const void *ops; }; @@ -83,6 +86,7 @@ enum icc_count { struct camss_resources { enum camss_version version; + const char *pd_name; const struct camss_subdev_resources *csiphy_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; @@ -92,7 +96,6 @@ struct camss_resources { const unsigned int csiphy_num; const unsigned int csid_num; const unsigned int vfe_num; - const unsigned int vfe_lite_num; }; struct camss { @@ -106,11 +109,10 @@ struct camss { struct vfe_device *vfe; atomic_t ref_count; int genpd_num; - struct device **genpd; - struct device_link **genpd_link; + struct device *genpd; + struct device_link *genpd_link; struct icc_path *icc_path[ICC_SM8250_COUNT]; const struct camss_resources *res; - unsigned int vfe_total_num; }; struct camss_camera_interface { diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 9cffe9755..a712dd4f0 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -881,6 +881,10 @@ static const struct venus_resources sc7280_res = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, .fwname = "qcom/vpu-2.0/venus.mbn", }; diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index dbf305cec..29130a944 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1641,7 +1641,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 0; + src_vq->min_queued_buffers = 0; src_vq->dev = inst->core->dev; src_vq->lock = &inst->ctx_q_lock; ret = vb2_queue_init(src_vq); @@ -1656,7 +1656,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 0; + dst_vq->min_queued_buffers = 0; dst_vq->dev = inst->core->dev; dst_vq->lock = &inst->ctx_q_lock; return vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 44b13696c..3ec2fb8d9 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1398,7 +1398,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->dev = inst->core->dev; src_vq->lock = &inst->ctx_q_lock; if (inst->core->res->hfi_version == HFI_VERSION_1XX) @@ -1415,7 +1415,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 1; + dst_vq->min_queued_buffers = 1; dst_vq->dev = inst->core->dev; dst_vq->lock = &inst->ctx_q_lock; return vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 19a005d83..530d65fc5 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -282,7 +282,7 @@ static int risp_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isp->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -302,7 +302,7 @@ static int risp_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = isp->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&isp->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 66fe553a0..582d5e35d 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -1185,7 +1185,7 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { priv->mf = format->format; } else { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format; } @@ -1205,7 +1205,7 @@ static int rcsi2_get_pad_format(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) format->format = priv->mf; else - format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); + format->format = *v4l2_subdev_state_get_format(sd_state, 0); mutex_unlock(&priv->lock); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 2a77353f1..e2c40abc6 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1559,7 +1559,7 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 4; + q->min_queued_buffers = 4; q->dev = vin->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 292c5bf9e..f21d05054 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -424,10 +424,11 @@ static int rcar_drif_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); /* Need at least 16 buffers */ - if (vq->num_buffers + *num_buffers < 16) - *num_buffers = 16 - vq->num_buffers; + if (q_num_bufs + *num_buffers < 16) + *num_buffers = 16 - q_num_bufs; *num_planes = 1; sizes[0] = PAGE_ALIGN(sdr->fmt->buffersize); diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 2562b30ac..167760276 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -1399,7 +1399,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct ceu_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &ceudev->mlock; q->dev = ceudev->v4l2_dev.dev; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index ad2bd7103..d20f4eff9 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -250,7 +250,7 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) } state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev); - fmt = v4l2_subdev_get_pad_format(&csi2->subdev, state, RZG2L_CSI2_SINK); + fmt = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); format = rzg2l_csi2_code_to_fmt(fmt->code); v4l2_subdev_unlock_state(state); @@ -500,13 +500,13 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SOURCE); if (fmt->pad == RZG2L_CSI2_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CSI2_SINK); + sink_format = v4l2_subdev_state_get_format(state, RZG2L_CSI2_SINK); if (!rzg2l_csi2_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_csi2_formats[0].code; @@ -530,8 +530,8 @@ static int rzg2l_csi2_set_format(struct v4l2_subdev *sd, return 0; } -static int rzg2l_csi2_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CSI2_SINK, }; @@ -582,7 +582,6 @@ static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_mbus_code = rzg2l_csi2_enum_mbus_code, - .init_cfg = rzg2l_csi2_init_config, .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, @@ -593,6 +592,10 @@ static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { .pad = &rzg2l_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_csi2_internal_ops = { + .init_state = rzg2l_csi2_init_state, +}; + /* ----------------------------------------------------------------------------- * Async handling and registration of subdevices and links. */ @@ -777,6 +780,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); + csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), "csi-%s", dev_name(&pdev->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 4dcd2faff..9f351a058 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -39,7 +39,7 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) struct v4l2_mbus_framefmt *fmt; state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); - fmt = v4l2_subdev_get_pad_format(&cru->ip.subdev, state, 1); + fmt = v4l2_subdev_state_get_format(state, 1); v4l2_subdev_unlock_state(state); return fmt; @@ -108,13 +108,13 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; - src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CRU_IP_SOURCE); + src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); if (fmt->pad == RZG2L_CRU_IP_SOURCE) { fmt->format = *src_format; return 0; } - sink_format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); + sink_format = v4l2_subdev_state_get_format(state, fmt->pad); if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) sink_format->code = rzg2l_cru_ip_formats[0].code; @@ -168,8 +168,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rzg2l_cru_ip_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; @@ -192,7 +192,6 @@ static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, .enum_frame_size = rzg2l_cru_ip_enum_frame_size, - .init_cfg = rzg2l_cru_ip_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rzg2l_cru_ip_set_format, }; @@ -202,6 +201,10 @@ static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { .pad = &rzg2l_cru_ip_pad_ops, }; +static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { + .init_state = rzg2l_cru_ip_init_state, +}; + static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -213,6 +216,7 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.dev = cru->dev; v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); + ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; v4l2_set_subdevdata(&ip->subdev, cru); snprintf(ip->subdev.name, sizeof(ip->subdev.name), "cru-ip-%s", dev_name(cru->dev)); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index e6eedd65b..d0ffa90bc 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -767,7 +767,7 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) q->ops = &rzg2l_cru_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 4; + q->min_queued_buffers = 4; q->dev = cru->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index f792aedc9..1e74dd601 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -1297,7 +1297,7 @@ static int sh_vou_probe(struct platform_device *pdev) q->ops = &sh_vou_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &vou_dev->fop_lock; q->dev = &pdev->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 89385b4ca..a8535c6e2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -100,7 +100,7 @@ static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad); + return v4l2_subdev_state_get_compose(sd_state, pad); } static void brx_try_format(struct vsp1_brx *brx, @@ -136,29 +136,28 @@ static int brx_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - brx_try_format(brx, config, fmt->pad, &fmt->format); + brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, config, fmt->pad); + compose = brx_get_compose(brx, state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -171,7 +170,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, for (i = 0; i <= brx->entity.source_pad; ++i) { format = vsp1_entity_get_pad_format(&brx->entity, - config, i); + state, i); format->code = fmt->format.code; } } @@ -186,7 +185,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -200,13 +199,13 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return 0; case V4L2_SEL_TGT_COMPOSE: - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - sel->which); - if (!config) + state = vsp1_entity_get_state(&brx->entity, sd_state, + sel->which); + if (!state) return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, config, sel->pad); + sel->r = *brx_get_compose(brx, state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -220,7 +219,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_brx *brx = to_brx(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; int ret = 0; @@ -233,9 +232,8 @@ static int brx_set_selection(struct v4l2_subdev *subdev, mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&brx->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -244,7 +242,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, config, + format = vsp1_entity_get_pad_format(&brx->entity, state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -253,11 +251,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad); + format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, config, sel->pad); + compose = brx_get_compose(brx, state, sel->pad); *compose = sel->r; done: @@ -266,7 +264,6 @@ done: } static const struct v4l2_subdev_pad_ops brx_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = brx_enum_mbus_code, .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -293,7 +290,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config, + format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, brx->entity.source_pad); /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index c5217fee2..625776a9b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -155,7 +155,6 @@ static int clu_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops clu_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = clu_enum_mbus_code, .enum_frame_size = clu_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -182,8 +181,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, - clu->entity.config, + format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index c31f05a80..0a5a7f9cc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -101,27 +101,26 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, */ /** - * vsp1_entity_get_pad_config - Get the pad configuration for an entity + * vsp1_entity_get_state - Get the subdev state for an entity * @entity: the entity * @sd_state: the TRY state - * @which: configuration selector (ACTIVE or TRY) + * @which: state selector (ACTIVE or TRY) * * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold * the entity lock to access the returned configuration. * - * Return the pad configuration requested by the which argument. The TRY - * configuration is passed explicitly to the function through the cfg argument - * and simply returned when requested. The ACTIVE configuration comes from the - * entity structure. + * Return the subdev state requested by the which argument. The TRY state is + * passed explicitly to the function through the sd_state argument and simply + * returned when requested. The ACTIVE state comes from the entity structure. */ struct v4l2_subdev_state * -vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) +vsp1_entity_get_state(struct vsp1_entity *entity, + struct v4l2_subdev_state *sd_state, + enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_ACTIVE: - return entity->config; + return entity->state; case V4L2_SUBDEV_FORMAT_TRY: default: return sd_state; @@ -142,7 +141,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad) { - return v4l2_subdev_get_try_format(&entity->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } /** @@ -163,46 +162,18 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, { switch (target) { case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_get_try_compose(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_compose(sd_state, pad); case V4L2_SEL_TGT_CROP: - return v4l2_subdev_get_try_crop(&entity->subdev, sd_state, - pad); + return v4l2_subdev_state_get_crop(sd_state, pad); default: return NULL; } } -/* - * vsp1_entity_init_cfg - Initialize formats on all pads - * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * - * Initialize all pad formats with default values in the given pad config. This - * function can be used as a handler for the subdev pad::init_cfg operation. - */ -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) -{ - unsigned int pad; - - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - struct v4l2_subdev_format format = { - .pad = pad, - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); - } - - return 0; -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: V4L2 subdev format * * This function implements the subdev get_fmt pad operation. It can be used as @@ -213,14 +184,14 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; - config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, fmt->which); + if (!state) return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); + fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -229,7 +200,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, /* * vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code: Media bus code enumeration * @codes: Array of supported media bus codes * @ncodes: Number of supported media bus codes @@ -252,7 +223,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; /* @@ -262,13 +233,12 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - config = vsp1_entity_get_pad_config(entity, sd_state, - code->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, code->which); + if (!state) return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, config, 0); + format = vsp1_entity_get_pad_format(entity, state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -279,7 +249,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, /* * vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fse: Frame size enumeration * @min_width: Minimum image width * @min_height: Minimum image height @@ -298,15 +268,15 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(entity, sd_state, fse->which); - if (!config) + state = vsp1_entity_get_state(entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, config, fse->pad); + format = vsp1_entity_get_pad_format(entity, state, fse->pad); mutex_lock(&entity->lock); @@ -339,7 +309,7 @@ done: /* * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler * @subdev: V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: V4L2 subdev format * @codes: Array of supported media bus codes * @ncodes: Number of supported media bus codes @@ -362,7 +332,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, unsigned int max_width, unsigned int max_height) { struct vsp1_entity *entity = to_vsp1_entity(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; @@ -370,13 +340,13 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, mutex_lock(&entity->lock); - config = vsp1_entity_get_pad_config(entity, sd_state, fmt->which); - if (!config) { + state = vsp1_entity_get_state(entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - format = vsp1_entity_get_pad_format(entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(entity, state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -404,18 +374,18 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, config, entity->source_pad); + format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, V4L2_SEL_TGT_CROP); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, V4L2_SEL_TGT_COMPOSE); selection->left = 0; selection->top = 0; @@ -427,6 +397,29 @@ done: return ret; } +static int vsp1_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + unsigned int pad; + + /* Initialize all pad formats with default values. */ + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); + } + + return 0; +} + +static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { + .init_state = vsp1_entity_init_state, +}; + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -661,6 +654,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, /* Initialize the V4L2 subdev. */ subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->internal_ops = &vsp1_entity_internal_ops; subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; @@ -669,21 +663,21 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); - vsp1_entity_init_cfg(subdev, NULL); + vsp1_entity_init_state(subdev, NULL); /* - * Allocate the pad configuration to store formats and selection + * Allocate the subdev state to store formats and selection * rectangles. */ /* * FIXME: Drop this call, drivers are not supposed to use * __v4l2_subdev_state_alloc(). */ - entity->config = __v4l2_subdev_state_alloc(&entity->subdev, - "vsp1:config->lock", &key); - if (IS_ERR(entity->config)) { + entity->state = __v4l2_subdev_state_alloc(&entity->subdev, + "vsp1:state->lock", &key); + if (IS_ERR(entity->state)) { media_entity_cleanup(&entity->subdev.entity); - return PTR_ERR(entity->config); + return PTR_ERR(entity->state); } return 0; @@ -695,6 +689,6 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); - __v4l2_subdev_state_free(entity->config); + __v4l2_subdev_state_free(entity->state); media_entity_cleanup(&entity->subdev.entity); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 17f98a6a9..735f32dde 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -115,9 +115,9 @@ struct vsp1_entity { unsigned int sink_pad; struct v4l2_subdev subdev; - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; - struct mutex lock; /* Protects the pad config */ + struct mutex lock; /* Protects the state */ }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -135,9 +135,9 @@ int vsp1_entity_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags); struct v4l2_subdev_state * -vsp1_entity_get_pad_config(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which); +vsp1_entity_get_state(struct vsp1_entity *entity, + struct v4l2_subdev_state *sd_state, + enum v4l2_subdev_format_whence which); struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, @@ -146,8 +146,6 @@ struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, unsigned int pad, unsigned int target); -int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index e6492deb0..40c571a98 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -140,9 +140,9 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->config, + compose = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index aa1c718e0..8281b8687 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -139,9 +139,9 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->config, + compose = vsp1_entity_get_pad_selection(entity, entity->state, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index f22449dd6..71155282c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -203,7 +203,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -213,9 +213,8 @@ static int histo_get_selection(struct v4l2_subdev *subdev, mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -223,7 +222,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, config, + crop = vsp1_entity_get_pad_selection(&histo->entity, state, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); sel->r.left = 0; @@ -234,7 +233,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, config, + format = vsp1_entity_get_pad_format(&histo->entity, state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -244,7 +243,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, config, + sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, sel->pad, sel->target); break; @@ -346,7 +345,7 @@ static int histo_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; int ret; if (sel->pad != HISTO_PAD_SINK) @@ -354,17 +353,16 @@ static int histo_set_selection(struct v4l2_subdev *subdev, mutex_lock(&histo->entity.lock); - config = vsp1_entity_get_pad_config(&histo->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } if (sel->target == V4L2_SEL_TGT_CROP) - ret = histo_set_crop(subdev, config, sel); + ret = histo_set_crop(subdev, state, sel); else if (sel->target == V4L2_SEL_TGT_COMPOSE) - ret = histo_set_compose(subdev, config, sel); + ret = histo_set_compose(subdev, state, sel); else ret = -EINVAL; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 361a87038..bc1299c29 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -66,20 +66,19 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&hsit->entity.lock); - config = vsp1_entity_get_pad_config(&hsit->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -102,7 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, config, + format = vsp1_entity_get_pad_format(&hsit->entity, state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 @@ -114,7 +113,6 @@ done: } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 0ab2e0c70..b1d21a548 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -68,7 +68,6 @@ static int lif_set_format(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops lif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -94,7 +93,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index ac6802a32..451d24ab0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -131,7 +131,6 @@ static int lut_set_format(struct v4l2_subdev *subdev, */ static const struct v4l2_subdev_pad_ops lut_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index ea12c3f12..c47579efc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -81,10 +81,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity, /* Format */ sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.config, + rpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.config, + rpf->entity.state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM @@ -158,7 +158,7 @@ static void rpf_configure_stream(struct vsp1_entity *entity, const struct v4l2_rect *compose; compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->config, + pipe->brx->state, rpf->brx_input, V4L2_SEL_TGT_COMPOSE); left = compose->left; @@ -302,7 +302,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); /* * Partition Algorithm Control diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index e0f87c810..09fb6ffa1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -19,8 +19,7 @@ struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_state *sd_state) { - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state, - RWPF_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); } /* ----------------------------------------------------------------------------- @@ -62,15 +61,14 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } @@ -81,7 +79,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -107,7 +105,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, config); + crop = vsp1_rwpf_get_crop(rwpf, state); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -115,7 +113,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SOURCE); *format = fmt->format; @@ -134,7 +132,7 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -147,20 +145,19 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, config); + sel->r = *vsp1_rwpf_get_crop(rwpf, state); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -183,7 +180,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; int ret = 0; @@ -200,15 +197,14 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, mutex_lock(&rwpf->entity.lock); - config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SINK); /* @@ -229,11 +225,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, config); + crop = vsp1_rwpf_get_crop(rwpf, state); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, config, + format = vsp1_entity_get_pad_format(&rwpf->entity, state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; @@ -244,7 +240,6 @@ done: } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index b614a2aea..11e008aa9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -123,16 +123,15 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&sru->entity, sd_state, - fse->which); - if (!config) + state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK); + format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -221,31 +220,30 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&sru->entity.lock); - config = vsp1_entity_get_pad_config(&sru->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - sru_try_format(sru, config, fmt->pad, &fmt->format); + sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, config, + format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SOURCE); *format = fmt->format; - sru_try_format(sru, config, SRU_PAD_SOURCE, format); + sru_try_format(sru, state, SRU_PAD_SOURCE, format); } done: @@ -254,7 +252,6 @@ done: } static const struct v4l2_subdev_pad_ops sru_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -280,9 +277,9 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) @@ -310,9 +307,9 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); /* @@ -336,9 +333,9 @@ static void sru_partition(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 1c290cda0..d89f1197b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -128,17 +128,15 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; - config = vsp1_entity_get_pad_config(&uds->entity, sd_state, - fse->which); - if (!config) + state = vsp1_entity_get_state(&uds->entity, sd_state, fse->which); + if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, config, - UDS_PAD_SINK); + format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -205,31 +203,30 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&uds->entity.lock); - config = vsp1_entity_get_pad_config(&uds->entity, sd_state, - fmt->which); - if (!config) { + state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which); + if (!state) { ret = -EINVAL; goto done; } - uds_try_format(uds, config, fmt->pad, &fmt->format); + uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, config, + format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SOURCE); *format = fmt->format; - uds_try_format(uds, config, UDS_PAD_SOURCE, format); + uds_try_format(uds, state, UDS_PAD_SOURCE, format); } done: @@ -242,7 +239,6 @@ done: */ static const struct v4l2_subdev_pad_ops uds_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -269,9 +265,9 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); @@ -314,7 +310,7 @@ static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_partition *partition = pipe->partition; const struct v4l2_mbus_framefmt *output; - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); /* Input size clipping. */ @@ -339,9 +335,9 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); hscale = output->width / input->width; @@ -381,9 +377,9 @@ static void uds_partition(struct vsp1_entity *entity, partition->uds_sink = *window; partition->uds_source = *window; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 83d7f17df..f66936a28 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -86,7 +86,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; int ret = 0; @@ -95,9 +95,8 @@ static int uif_get_selection(struct v4l2_subdev *subdev, mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } @@ -105,7 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, config, + format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; @@ -114,7 +113,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config, + sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, sel->pad, sel->target); break; @@ -133,7 +132,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_uif *uif = to_uif(subdev); - struct v4l2_subdev_state *config; + struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; int ret = 0; @@ -144,15 +143,14 @@ static int uif_set_selection(struct v4l2_subdev *subdev, mutex_lock(&uif->entity.lock); - config = vsp1_entity_get_pad_config(&uif->entity, sd_state, - sel->which); - if (!config) { + state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); + if (!state) { ret = -EINVAL; goto done; } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, config, UIF_PAD_SINK); + format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -162,7 +160,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, config, + selection = vsp1_entity_get_pad_selection(&uif->entity, state, sel->pad, V4L2_SEL_TGT_CROP); *selection = sel->r; @@ -176,7 +174,6 @@ done: */ static const struct v4l2_subdev_pad_ops uif_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uif_enum_mbus_code, .enum_frame_size = uif_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, @@ -206,7 +203,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->config, + crop = vsp1_entity_get_pad_selection(entity, entity->state, UIF_PAD_SINK, V4L2_SEL_TGT_CROP); left = crop->left; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index e9d502776..5a9cb0e56 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -198,7 +198,7 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, * at the WPF sink. */ format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.config, + pipe->output->entity.state, RWPF_PAD_SINK); /* A single partition simply processes the output size in full. */ @@ -263,7 +263,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) * at the WPF sink. */ format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.config, + pipe->output->entity.state, RWPF_PAD_SINK); div_size = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index cab4445ec..9693aeab1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -66,10 +66,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) } sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -246,10 +246,10 @@ static void wpf_configure_stream(struct vsp1_entity *entity, int ret; sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SOURCE); /* Format */ @@ -384,7 +384,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int i; sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, + wpf->entity.state, RWPF_PAD_SINK); width = sink_format->width; height = sink_format->height; diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 81508ed5a..662c81b6d 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -5,7 +5,9 @@ */ #include +#include +#include #include #include #include @@ -15,6 +17,26 @@ #include "rga-hw.h" #include "rga.h" +static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc, + struct sg_table *sgt) +{ + struct sg_dma_page_iter iter; + struct rga_dma_desc *tmp = desc; + size_t n_desc = 0; + dma_addr_t addr; + + for_each_sgtable_dma_page(sgt, &iter, 0) { + if (n_desc > max_desc) + return -EINVAL; + addr = sg_page_iter_dma_address(&iter); + tmp->addr = lower_32_bits(addr); + tmp++; + n_desc++; + } + + return n_desc; +} + static int rga_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -22,28 +44,105 @@ rga_queue_setup(struct vb2_queue *vq, { struct rga_ctx *ctx = vb2_get_drv_priv(vq); struct rga_frame *f = rga_get_frame(ctx, vq->type); + const struct v4l2_pix_format_mplane *pix_fmt; + int i; if (IS_ERR(f)) return PTR_ERR(f); - if (*nplanes) - return sizes[0] < f->size ? -EINVAL : 0; + pix_fmt = &f->pix; + + if (*nplanes) { + if (*nplanes != pix_fmt->num_planes) + return -EINVAL; + + for (i = 0; i < pix_fmt->num_planes; i++) + if (sizes[i] < pix_fmt->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + *nplanes = pix_fmt->num_planes; + + for (i = 0; i < pix_fmt->num_planes; i++) + sizes[i] = pix_fmt->plane_fmt[i].sizeimage; + + return 0; +} + +static int rga_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); + struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct rockchip_rga *rga = ctx->rga; + struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); + size_t n_desc = 0; - sizes[0] = f->size; - *nplanes = 1; + n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); + + rbuf->n_desc = n_desc; + rbuf->dma_desc = dma_alloc_coherent(rga->dev, + rbuf->n_desc * sizeof(*rbuf->dma_desc), + &rbuf->dma_desc_pa, GFP_KERNEL); + if (!rbuf->dma_desc) + return -ENOMEM; return 0; } +static int get_plane_offset(struct rga_frame *f, int plane) +{ + if (plane == 0) + return 0; + if (plane == 1) + return f->width * f->height; + if (plane == 2) + return f->width * f->height + (f->width * f->height / f->fmt->uv_factor); + + return -EINVAL; +} + static int rga_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); + ssize_t n_desc = 0; + size_t curr_desc = 0; + int i; + const struct v4l2_format_info *info; + unsigned int offsets[VIDEO_MAX_PLANES]; if (IS_ERR(f)) return PTR_ERR(f); - vb2_set_plane_payload(vb, 0, f->size); + for (i = 0; i < vb->num_planes; i++) { + vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage); + + /* Create local MMU table for RGA */ + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], + rbuf->n_desc - curr_desc, + vb2_dma_sg_plane_desc(vb, i)); + if (n_desc < 0) { + v4l2_err(&ctx->rga->v4l2_dev, + "Failed to map video buffer to RGA\n"); + return n_desc; + } + offsets[i] = curr_desc << PAGE_SHIFT; + curr_desc += n_desc; + } + + /* Fill the remaining planes */ + info = v4l2_format_info(f->fmt->fourcc); + for (i = info->mem_planes; i < info->comp_planes; i++) + offsets[i] = get_plane_offset(f, i); + + rbuf->offset.y_off = offsets[0]; + rbuf->offset.u_off = offsets[1]; + rbuf->offset.v_off = offsets[2]; return 0; } @@ -56,6 +155,17 @@ static void rga_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static void rga_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rga_vb_buffer *rbuf = vb_to_rga(vbuf); + struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct rockchip_rga *rga = ctx->rga; + + dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc), + rbuf->dma_desc, rbuf->dma_desc_pa); +} + static void rga_buf_return_buffers(struct vb2_queue *q, enum vb2_buffer_state state) { @@ -99,50 +209,12 @@ static void rga_buf_stop_streaming(struct vb2_queue *q) const struct vb2_ops rga_qops = { .queue_setup = rga_queue_setup, + .buf_init = rga_buf_init, .buf_prepare = rga_buf_prepare, .buf_queue = rga_buf_queue, + .buf_cleanup = rga_buf_cleanup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .start_streaming = rga_buf_start_streaming, .stop_streaming = rga_buf_stop_streaming, }; - -/* RGA MMU is a 1-Level MMU, so it can't be used through the IOMMU API. - * We use it more like a scatter-gather list. - */ -void rga_buf_map(struct vb2_buffer *vb) -{ - struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct rockchip_rga *rga = ctx->rga; - struct sg_table *sgt; - struct scatterlist *sgl; - unsigned int *pages; - unsigned int address, len, i, p; - unsigned int mapped_size = 0; - - if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - pages = rga->src_mmu_pages; - else - pages = rga->dst_mmu_pages; - - /* Create local MMU table for RGA */ - sgt = vb2_plane_cookie(vb, 0); - - for_each_sg(sgt->sgl, sgl, sgt->nents, i) { - len = sg_dma_len(sgl) >> PAGE_SHIFT; - address = sg_phys(sgl); - - for (p = 0; p < len; p++) { - dma_addr_t phys = address + - ((dma_addr_t)p << PAGE_SHIFT); - - pages[mapped_size + p] = phys; - } - - mapped_size += len; - } - - /* sync local MMU table for RGA */ - dma_sync_single_for_device(rga->dev, virt_to_phys(pages), - 8 * PAGE_SIZE, DMA_BIDIRECTIONAL); -} diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index aaa96f256..11c3d7234 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -16,12 +16,6 @@ enum e_rga_start_pos { RB = 3, }; -struct rga_addr_offset { - unsigned int y_off; - unsigned int u_off; - unsigned int v_off; -}; - struct rga_corners_addr_offset { struct rga_addr_offset left_top; struct rga_addr_offset right_top; @@ -43,13 +37,13 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) } static struct rga_corners_addr_offset -rga_get_addr_offset(struct rga_frame *frm, unsigned int x, unsigned int y, - unsigned int w, unsigned int h) +rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, + unsigned int x, unsigned int y, unsigned int w, unsigned int h) { struct rga_corners_addr_offset offsets; struct rga_addr_offset *lt, *lb, *rt, *rb; unsigned int x_div = 0, - y_div = 0, uv_stride = 0, pixel_width = 0, uv_factor = 0; + y_div = 0, uv_stride = 0, pixel_width = 0; lt = &offsets.left_top; lb = &offsets.left_bottom; @@ -58,14 +52,12 @@ rga_get_addr_offset(struct rga_frame *frm, unsigned int x, unsigned int y, x_div = frm->fmt->x_div; y_div = frm->fmt->y_div; - uv_factor = frm->fmt->uv_factor; uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->width; - lt->y_off = y * frm->stride + x * pixel_width; - lt->u_off = - frm->width * frm->height + (y / y_div) * uv_stride + x / x_div; - lt->v_off = lt->u_off + frm->width * frm->height / uv_factor; + lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; + lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; + lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div; lb->y_off = lt->y_off + (h - 1) * frm->stride; lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride; @@ -119,40 +111,40 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct return NULL; } -static void rga_cmd_set_src_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7; } -static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7 << 4; } -static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, void *mmu_pages) +static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG; - dest[reg >> 2] = virt_to_phys(mmu_pages) >> 4; + dest[reg >> 2] = dma_addr >> 4; reg = RGA_MMU_CTRL1 - RGA_MODE_BASE_REG; dest[reg >> 2] |= 0x7 << 8; @@ -163,7 +155,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) struct rockchip_rga *rga = ctx->rga; u32 *dest = rga->cmdbuf_virt; unsigned int scale_dst_w, scale_dst_h; - unsigned int src_h, src_w, src_x, src_y, dst_h, dst_w, dst_x, dst_y; + unsigned int src_h, src_w, dst_h, dst_w; union rga_src_info src_info; union rga_dst_info dst_info; union rga_src_x_factor x_factor; @@ -173,18 +165,10 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; - struct rga_addr_offset *dst_offset; - struct rga_corners_addr_offset offsets; - struct rga_corners_addr_offset src_offsets; - src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; - src_x = ctx->in.crop.left; - src_y = ctx->in.crop.top; dst_h = ctx->out.crop.height; dst_w = ctx->out.crop.width; - dst_x = ctx->out.crop.left; - dst_y = ctx->out.crop.top; src_info.val = dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2]; dst_info.val = dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2]; @@ -312,18 +296,37 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_act_info.data.act_height = dst_h - 1; dst_act_info.data.act_width = dst_w - 1; - /* - * Calculate the source framebuffer base address with offset pixel. - */ - src_offsets = rga_get_addr_offset(&ctx->in, src_x, src_y, - src_w, src_h); + dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2] = x_factor.val; + dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2] = y_factor.val; + dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = src_vir_info.val; + dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = src_act_info.val; + + dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2] = src_info.val; + + dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = dst_vir_info.val; + dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = dst_act_info.val; + + dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2] = dst_info.val; +} + +static void rga_cmd_set_src_info(struct rga_ctx *ctx, + struct rga_addr_offset *offset) +{ + struct rga_corners_addr_offset src_offsets; + struct rockchip_rga *rga = ctx->rga; + u32 *dest = rga->cmdbuf_virt; + unsigned int src_h, src_w, src_x, src_y; + + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + src_x = ctx->in.crop.left; + src_y = ctx->in.crop.top; /* - * Configure the dest framebuffer base address with pixel offset. + * Calculate the source framebuffer base address with offset pixel. */ - offsets = rga_get_addr_offset(&ctx->out, dst_x, dst_y, dst_w, dst_h); - dst_offset = rga_lookup_draw_pos(&offsets, src_info.data.rot_mode, - src_info.data.mir_mode); + src_offsets = rga_get_addr_offset(&ctx->in, offset, + src_x, src_y, src_w, src_h); dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = src_offsets.left_top.y_off; @@ -331,13 +334,49 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) src_offsets.left_top.u_off; dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = src_offsets.left_top.v_off; +} - dest[(RGA_SRC_X_FACTOR - RGA_MODE_BASE_REG) >> 2] = x_factor.val; - dest[(RGA_SRC_Y_FACTOR - RGA_MODE_BASE_REG) >> 2] = y_factor.val; - dest[(RGA_SRC_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = src_vir_info.val; - dest[(RGA_SRC_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = src_act_info.val; +static void rga_cmd_set_dst_info(struct rga_ctx *ctx, + struct rga_addr_offset *offset) +{ + struct rga_addr_offset *dst_offset; + struct rga_corners_addr_offset offsets; + struct rockchip_rga *rga = ctx->rga; + u32 *dest = rga->cmdbuf_virt; + unsigned int dst_h, dst_w, dst_x, dst_y; + unsigned int mir_mode = 0; + unsigned int rot_mode = 0; - dest[(RGA_SRC_INFO - RGA_MODE_BASE_REG) >> 2] = src_info.val; + dst_h = ctx->out.crop.height; + dst_w = ctx->out.crop.width; + dst_x = ctx->out.crop.left; + dst_y = ctx->out.crop.top; + + if (ctx->vflip) + mir_mode |= RGA_SRC_MIRR_MODE_X; + if (ctx->hflip) + mir_mode |= RGA_SRC_MIRR_MODE_Y; + + switch (ctx->rotate) { + case 90: + rot_mode = RGA_SRC_ROT_MODE_90_DEGREE; + break; + case 180: + rot_mode = RGA_SRC_ROT_MODE_180_DEGREE; + break; + case 270: + rot_mode = RGA_SRC_ROT_MODE_270_DEGREE; + break; + default: + rot_mode = RGA_SRC_ROT_MODE_0_DEGREE; + break; + } + + /* + * Configure the dest framebuffer base address with pixel offset. + */ + offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h); + dst_offset = rga_lookup_draw_pos(&offsets, mir_mode, rot_mode); dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = dst_offset->y_off; @@ -345,11 +384,6 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_offset->u_off; dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = dst_offset->v_off; - - dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2] = dst_vir_info.val; - dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2] = dst_act_info.val; - - dest[(RGA_DST_INFO - RGA_MODE_BASE_REG) >> 2] = dst_info.val; } static void rga_cmd_set_mode(struct rga_ctx *ctx) @@ -375,22 +409,25 @@ static void rga_cmd_set_mode(struct rga_ctx *ctx) dest[(RGA_MODE_CTRL - RGA_MODE_BASE_REG) >> 2] = mode.val; } -static void rga_cmd_set(struct rga_ctx *ctx) +static void rga_cmd_set(struct rga_ctx *ctx, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rockchip_rga *rga = ctx->rga; memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); - rga_cmd_set_src_addr(ctx, rga->src_mmu_pages); + rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* * Due to hardware bug, * src1 mmu also should be configured when using alpha blending. */ - rga_cmd_set_src1_addr(ctx, rga->dst_mmu_pages); + rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_dst_addr(ctx, rga->dst_mmu_pages); + rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); rga_cmd_set_mode(ctx); + rga_cmd_set_src_info(ctx, &src->offset); + rga_cmd_set_dst_info(ctx, &dst->offset); rga_cmd_set_trans_info(ctx); rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy); @@ -400,11 +437,12 @@ static void rga_cmd_set(struct rga_ctx *ctx) PAGE_SIZE, DMA_BIDIRECTIONAL); } -void rga_hw_start(struct rockchip_rga *rga) +void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rga_ctx *ctx = rga->curr; - rga_cmd_set(ctx); + rga_cmd_set(ctx, src, dst); rga_write(rga, RGA_SYS_CTRL, 0x00); diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 25f5b5eeb..00fdfa9e1 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -45,10 +45,7 @@ static void device_run(void *prv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_buf_map(&src->vb2_buf); - rga_buf_map(&dst->vb2_buf); - - rga_hw_start(rga); + rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); spin_unlock_irqrestore(&rga->ctrl_lock, flags); } @@ -96,12 +93,13 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) struct rga_ctx *ctx = priv; int ret; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; src_vq->mem_ops = &vb2_dma_sg_memops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->gfp_flags = __GFP_DMA32; + src_vq->buf_struct_size = sizeof(struct rga_vb_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->rga->mutex; src_vq->dev = ctx->rga->v4l2_dev.dev; @@ -110,12 +108,13 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) if (ret) return ret; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &rga_qops; dst_vq->mem_ops = &vb2_dma_sg_memops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->gfp_flags = __GFP_DMA32; + dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->rga->mutex; dst_vq->dev = ctx->rga->v4l2_dev.dev; @@ -281,6 +280,15 @@ static struct rga_fmt formats[] = { .y_div = 2, .x_div = 1, }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .uv_factor = 4, + .y_div = 2, + .x_div = 1, + }, { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, @@ -321,12 +329,12 @@ static struct rga_fmt formats[] = { #define NUM_FORMATS ARRAY_SIZE(formats) -static struct rga_fmt *rga_fmt_find(struct v4l2_format *f) +static struct rga_fmt *rga_fmt_find(u32 pixelformat) { unsigned int i; for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].fourcc == f->fmt.pix.pixelformat) + if (formats[i].fourcc == pixelformat) return &formats[i]; } return NULL; @@ -345,14 +353,11 @@ static struct rga_frame def_frame = { struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (V4L2_TYPE_IS_OUTPUT(type)) return &ctx->in; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (V4L2_TYPE_IS_CAPTURE(type)) return &ctx->out; - default: - return ERR_PTR(-EINVAL); - } + return ERR_PTR(-EINVAL); } static int rga_open(struct file *file) @@ -369,6 +374,11 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; + v4l2_fill_pixfmt_mp(&ctx->in.pix, + ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height); + v4l2_fill_pixfmt_mp(&ctx->out.pix, + ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height); + if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); return -ERESTARTSYS; @@ -449,6 +459,7 @@ static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = prv; struct vb2_queue *vq; struct rga_frame *frm; @@ -460,58 +471,43 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); - f->fmt.pix.width = frm->width; - f->fmt.pix.height = frm->height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = frm->fmt->fourcc; - f->fmt.pix.bytesperline = frm->stride; - f->fmt.pix.sizeimage = frm->size; - f->fmt.pix.colorspace = frm->colorspace; + v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height); + + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = frm->colorspace; return 0; } static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_fmt *fmt; - fmt = rga_fmt_find(f); - if (!fmt) { + fmt = rga_fmt_find(pix_fmt->pixelformat); + if (!fmt) fmt = &formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - f->fmt.pix.field = V4L2_FIELD_NONE; + pix_fmt->width = clamp(pix_fmt->width, + (u32)MIN_WIDTH, (u32)MAX_WIDTH); + pix_fmt->height = clamp(pix_fmt->height, + (u32)MIN_HEIGHT, (u32)MAX_HEIGHT); - if (f->fmt.pix.width > MAX_WIDTH) - f->fmt.pix.width = MAX_WIDTH; - if (f->fmt.pix.height > MAX_HEIGHT) - f->fmt.pix.height = MAX_HEIGHT; - - if (f->fmt.pix.width < MIN_WIDTH) - f->fmt.pix.width = MIN_WIDTH; - if (f->fmt.pix.height < MIN_HEIGHT) - f->fmt.pix.height = MIN_HEIGHT; - - if (fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) - f->fmt.pix.bytesperline = f->fmt.pix.width; - else - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - - f->fmt.pix.sizeimage = - f->fmt.pix.height * (f->fmt.pix.width * fmt->depth) >> 3; + v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); + pix_fmt->field = V4L2_FIELD_NONE; return 0; } static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) { + struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = prv; struct rockchip_rga *rga = ctx->rga; struct vb2_queue *vq; struct rga_frame *frm; - struct rga_fmt *fmt; int ret = 0; + int i; /* Adjust all values accordingly to the hardware capabilities * and chosen format. @@ -527,15 +523,14 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - fmt = rga_fmt_find(f); - if (!fmt) - return -EINVAL; - frm->width = f->fmt.pix.width; - frm->height = f->fmt.pix.height; - frm->size = f->fmt.pix.sizeimage; - frm->fmt = fmt; - frm->stride = f->fmt.pix.bytesperline; - frm->colorspace = f->fmt.pix.colorspace; + frm->width = pix_fmt->width; + frm->height = pix_fmt->height; + frm->size = 0; + for (i = 0; i < pix_fmt->num_planes; i++) + frm->size += pix_fmt->plane_fmt[i].sizeimage; + frm->fmt = rga_fmt_find(pix_fmt->pixelformat); + frm->stride = pix_fmt->plane_fmt[0].bytesperline; + frm->colorspace = pix_fmt->colorspace; /* Reset crop settings */ frm->crop.left = 0; @@ -543,6 +538,21 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) frm->crop.width = frm->width; frm->crop.height = frm->height; + frm->pix = *pix_fmt; + + v4l2_dbg(debug, 1, &rga->v4l2_dev, + "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", + &frm->fmt->fourcc, frm->width, frm->height, + frm->stride, frm->size); + + for (i = 0; i < pix_fmt->num_planes; i++) { + v4l2_dbg(debug, 1, &rga->v4l2_dev, + "plane[%d]: size %d, bytesperline %d\n", + i, pix_fmt->plane_fmt[i].sizeimage, + pix_fmt->plane_fmt[i].bytesperline); + } + return 0; } @@ -560,21 +570,21 @@ static int vidioc_g_selection(struct file *file, void *prv, switch (s->target) { case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_COMPOSE: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; use_frame = true; break; case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; use_frame = true; break; @@ -612,7 +622,7 @@ static int vidioc_s_selection(struct file *file, void *prv, * COMPOSE target is only valid for capture buffer type, return * error for output buffer type */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!V4L2_TYPE_IS_CAPTURE(s->type)) return -EINVAL; break; case V4L2_SEL_TGT_CROP: @@ -620,7 +630,7 @@ static int vidioc_s_selection(struct file *file, void *prv, * CROP target is only valid for output buffer type, return * error for capture buffer type */ - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (!V4L2_TYPE_IS_OUTPUT(s->type)) return -EINVAL; break; /* @@ -653,14 +663,14 @@ static const struct v4l2_ioctl_ops rga_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, - .vidioc_g_fmt_vid_out = vidioc_g_fmt, - .vidioc_try_fmt_vid_out = vidioc_try_fmt, - .vidioc_s_fmt_vid_out = vidioc_s_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, @@ -687,7 +697,7 @@ static const struct video_device rga_videodev = { .minor = -1, .release = video_device_release, .vfl_dir = VFL_DIR_M2M, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; static int rga_enable_clocks(struct rockchip_rga *rga) @@ -827,6 +837,12 @@ static int rga_probe(struct platform_device *pdev) goto err_put_clk; } + ret = dma_set_mask_and_coherent(rga->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(rga->dev, "32-bit DMA not supported"); + goto err_put_clk; + } + ret = v4l2_device_register(&pdev->dev, &rga->v4l2_dev); if (ret) goto err_put_clk; @@ -872,26 +888,13 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - rga->src_mmu_pages = - (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); - if (!rga->src_mmu_pages) { - ret = -ENOMEM; - goto free_dma; - } - rga->dst_mmu_pages = - (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); - if (!rga->dst_mmu_pages) { - ret = -ENOMEM; - goto free_src_pages; - } - def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; def_frame.size = def_frame.stride * def_frame.height; ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto free_dst_pages; + goto free_dma; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -899,10 +902,6 @@ static int rga_probe(struct platform_device *pdev) return 0; -free_dst_pages: - free_pages((unsigned long)rga->dst_mmu_pages, 3); -free_src_pages: - free_pages((unsigned long)rga->src_mmu_pages, 3); free_dma: dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); @@ -925,9 +924,6 @@ static void rga_remove(struct platform_device *pdev) dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); - free_pages((unsigned long)rga->src_mmu_pages, 3); - free_pages((unsigned long)rga->dst_mmu_pages, 3); - v4l2_info(&rga->v4l2_dev, "Removing\n"); v4l2_m2m_release(rga->m2m_dev); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5fa9d2f36..3502dff60 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -34,12 +34,17 @@ struct rga_frame { /* Image format */ struct rga_fmt *fmt; + struct v4l2_pix_format_mplane pix; /* Variables that can calculated once and reused */ u32 stride; u32 size; }; +struct rga_dma_desc { + u32 addr; +}; + struct rockchip_rga_version { u32 major; u32 minor; @@ -81,15 +86,36 @@ struct rockchip_rga { struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; - unsigned int *src_mmu_pages; - unsigned int *dst_mmu_pages; }; +struct rga_addr_offset { + unsigned int y_off; + unsigned int u_off; + unsigned int v_off; +}; + +struct rga_vb_buffer { + struct vb2_v4l2_buffer vb_buf; + struct list_head queue; + + /* RGA MMU mapping for this buffer */ + struct rga_dma_desc *dma_desc; + dma_addr_t dma_desc_pa; + size_t n_desc; + + /* Plane offsets of this buffer into the mapping */ + struct rga_addr_offset offset; +}; + +static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct rga_vb_buffer, vb_buf); +} + struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type); /* RGA Buffers Manage */ extern const struct vb2_ops rga_qops; -void rga_buf_map(struct vb2_buffer *vb); /* RGA Hardware */ static inline void rga_write(struct rockchip_rga *rga, u32 reg, u32 value) @@ -110,6 +136,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; -void rga_hw_start(struct rockchip_rga *rga); +void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst); #endif diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 3752b702e..c381c2213 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -1434,7 +1434,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct rkisp1_buffer); - q->min_buffers_needed = RKISP1_MIN_BUFFERS_NEEDED; + q->min_queued_buffers = RKISP1_MIN_BUFFERS_NEEDED; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; q->dev = cap->rkisp1->dev; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index a4e272adc..b757f75ed 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -425,6 +425,7 @@ struct rkisp1_debug { unsigned long stats_error; unsigned long stop_timeout[2]; unsigned long frame_drop[2]; + unsigned long complete_frames; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 7320c1c72..4202642e0 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -257,8 +257,8 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, if (code->index) return -EINVAL; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_CSI_PAD_SINK); code->code = sink_fmt->code; return 0; @@ -285,15 +285,13 @@ static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_csi_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_CSI_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; @@ -316,7 +314,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == RKISP1_CSI_PAD_SRC) return v4l2_subdev_get_fmt(sd, sd_state, fmt); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); sink_fmt->code = fmt->format.code; @@ -336,7 +334,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; /* Propagate the format to the source pad. */ - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SRC); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC); *src_fmt = *sink_fmt; return 0; @@ -389,7 +387,7 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) return -EINVAL; sd_state = v4l2_subdev_lock_and_get_active_state(sd); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, RKISP1_CSI_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK); format = rkisp1_mbus_info_get_by_code(sink_fmt->code); v4l2_subdev_unlock_state(sd_state); @@ -422,7 +420,6 @@ static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { .enum_mbus_code = rkisp1_csi_enum_mbus_code, - .init_cfg = rkisp1_csi_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_csi_set_fmt, }; @@ -432,6 +429,10 @@ static const struct v4l2_subdev_ops rkisp1_csi_ops = { .pad = &rkisp1_csi_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_csi_internal_ops = { + .init_state = rkisp1_csi_init_state, +}; + int rkisp1_csi_register(struct rkisp1_device *rkisp1) { struct rkisp1_csi *csi = &rkisp1->csi; @@ -443,6 +444,7 @@ int rkisp1_csi_register(struct rkisp1_device *rkisp1) sd = &csi->sd; v4l2_subdev_init(sd, &rkisp1_csi_ops); + sd->internal_ops = &rkisp1_csi_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_csi_media_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c index 71df3dc95..79cda589d 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c @@ -92,6 +92,10 @@ static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p) RKISP1_DEBUG_REG(ISP_FLAGS_SHD), RKISP1_DEBUG_REG(ISP_RIS), RKISP1_DEBUG_REG(ISP_ERR), + RKISP1_DEBUG_SHD_REG(ISP_IS_H_OFFS), + RKISP1_DEBUG_SHD_REG(ISP_IS_V_OFFS), + RKISP1_DEBUG_SHD_REG(ISP_IS_H_SIZE), + RKISP1_DEBUG_SHD_REG(ISP_IS_V_SIZE), { /* Sentinel */ }, }; struct rkisp1_device *rkisp1 = m->private; @@ -217,6 +221,8 @@ void rkisp1_debug_init(struct rkisp1_device *rkisp1) &debug->frame_drop[RKISP1_MAINPATH]); debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir, &debug->frame_drop[RKISP1_SELFPATH]); + debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir, + &debug->complete_frames); debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1, &rkisp1_debug_input_status_fops); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index caffea6a4..78a1f7a14 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -66,8 +66,8 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = - v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; @@ -102,12 +102,12 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, const struct v4l2_mbus_framefmt *sink_frm; const struct v4l2_rect *sink_crop; - sink_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code); src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code); @@ -201,8 +201,8 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_frm = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat, src_frm->quantization, src_frm->ycbcr_enc); @@ -332,8 +332,8 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp, RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); src_info = rkisp1_mbus_info_get_by_code(src_fmt->code); if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER) @@ -423,15 +423,15 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int rkisp1_isp_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_isp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop, *src_crop; /* Video. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -441,15 +441,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - sink_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_fmt = *sink_fmt; src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; src_fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -457,15 +457,15 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - src_crop = v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); *src_crop = *sink_crop; /* Parameters and statistics. */ - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SINK_PARAMS); - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_ISP_PAD_SOURCE_STATS); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_PARAMS); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_STATS); sink_fmt->width = 0; sink_fmt->height = 0; sink_fmt->field = V4L2_FIELD_NONE; @@ -486,12 +486,12 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, const struct v4l2_rect *src_crop; bool set_csc; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); /* * Media bus code. The ISP can operate in pass-through mode (Bayer in, @@ -584,10 +584,10 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, const struct v4l2_rect *sink_crop; struct v4l2_rect *src_crop; - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); src_crop->left = ALIGN(r->left, 2); src_crop->width = ALIGN(r->width, 2); @@ -598,8 +598,8 @@ static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp, *r = *src_crop; /* Propagate to out format */ - src_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt); } @@ -610,10 +610,10 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop, *src_crop; const struct v4l2_mbus_framefmt *sink_fmt; - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_crop->left = ALIGN(r->left, 2); sink_crop->width = ALIGN(r->width, 2); @@ -624,8 +624,8 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, *r = *sink_crop; /* Propagate to out crop */ - src_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SOURCE_VIDEO); + src_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SOURCE_VIDEO); rkisp1_isp_set_src_crop(isp, sd_state, src_crop); } @@ -638,8 +638,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); sink_fmt->code = format->code; mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { @@ -687,8 +687,8 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, *format = *sink_fmt; /* Propagate to in crop */ - sink_crop = v4l2_subdev_get_pad_crop(&isp->sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sink_crop = v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop); } @@ -703,7 +703,8 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format); else - fmt->format = *v4l2_subdev_get_pad_format(sd, sd_state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(sd_state, + fmt->pad); return 0; } @@ -723,19 +724,19 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, sd_state, sel->pad); + fmt = v4l2_subdev_state_get_format(sd_state, sel->pad); sel->r.height = fmt->height; sel->r.width = fmt->width; sel->r.left = 0; sel->r.top = 0; } else { - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_ISP_PAD_SINK_VIDEO); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_ISP_PAD_SINK_VIDEO); } break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; default: @@ -782,7 +783,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, - .init_cfg = rkisp1_isp_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_isp_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -893,6 +893,10 @@ static const struct v4l2_subdev_ops rkisp1_isp_ops = { .pad = &rkisp1_isp_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = { + .init_state = rkisp1_isp_init_state, +}; + int rkisp1_isp_register(struct rkisp1_device *rkisp1) { struct rkisp1_isp *isp = &rkisp1->isp; @@ -903,6 +907,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) isp->rkisp1 = rkisp1; v4l2_subdev_init(sd, &rkisp1_isp_ops); + sd->internal_ops = &rkisp1_isp_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; @@ -1007,6 +1012,8 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) if (status & RKISP1_CIF_ISP_FRAME) { u32 isp_ris; + rkisp1->debug.complete_frames++; + /* New frame from the sensor received */ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS); if (isp_ris & RKISP1_STATS_MEAS_MASK) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index 350f452e6..bea69a0d7 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -172,12 +172,9 @@ #define RKISP1_CIF_MI_FRAME(stream) BIT((stream)->id) #define RKISP1_CIF_MI_MBLK_LINE BIT(2) #define RKISP1_CIF_MI_FILL_MP_Y BIT(3) -#define RKISP1_CIF_MI_WRAP_MP_Y BIT(4) -#define RKISP1_CIF_MI_WRAP_MP_CB BIT(5) -#define RKISP1_CIF_MI_WRAP_MP_CR BIT(6) -#define RKISP1_CIF_MI_WRAP_SP_Y BIT(7) -#define RKISP1_CIF_MI_WRAP_SP_CB BIT(8) -#define RKISP1_CIF_MI_WRAP_SP_CR BIT(9) +#define RKISP1_CIF_MI_WRAP_Y(stream) BIT(4 + (stream)->id * 3) +#define RKISP1_CIF_MI_WRAP_CB(stream) BIT(5 + (stream)->id * 3) +#define RKISP1_CIF_MI_WRAP_CR(stream) BIT(6 + (stream)->id * 3) #define RKISP1_CIF_MI_DMA_READY BIT(11) /* MI_STATUS */ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 6297870ee..a8e377701 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -142,10 +142,8 @@ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; u32 dc_ctrl; - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); if (sink_crop->width == sink_fmt->width && sink_crop->height == sink_fmt->height && @@ -275,10 +273,8 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, struct v4l2_area src_y, src_c; struct v4l2_rect sink_c; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code); src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code); @@ -292,8 +288,7 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, return; } - sink_y = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_y = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_c.width = sink_y->width / sink_yuv_info->hdiv; sink_c.height = sink_y->height / sink_yuv_info->vdiv; @@ -381,14 +376,13 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } -static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int rkisp1_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_fmt->width = RKISP1_DEFAULT_WIDTH; sink_fmt->height = RKISP1_DEFAULT_HEIGHT; sink_fmt->field = V4L2_FIELD_NONE; @@ -398,15 +392,13 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd, sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - sink_crop = v4l2_subdev_get_try_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); sink_crop->width = RKISP1_DEFAULT_WIDTH; sink_crop->height = RKISP1_DEFAULT_HEIGHT; sink_crop->left = 0; sink_crop->top = 0; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); *src_fmt = *sink_fmt; /* NOTE: there is no crop in the source pad, only in the sink */ @@ -421,10 +413,8 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -451,10 +441,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); /* Not crop for MP bayer raw data */ mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); @@ -488,12 +476,9 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_rect *sink_crop; bool is_yuv; - sink_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); - src_fmt = v4l2_subdev_get_pad_format(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SRC); - sink_crop = v4l2_subdev_get_pad_crop(&rsz->sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC); + sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); if (rsz->id == RKISP1_SELFPATH) sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; @@ -583,8 +568,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - mf_sink = v4l2_subdev_get_pad_format(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + mf_sink = v4l2_subdev_state_get_format(sd_state, + RKISP1_RSZ_PAD_SINK); sel->r.height = mf_sink->height; sel->r.width = mf_sink->width; sel->r.left = 0; @@ -592,8 +577,8 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_CROP: - sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, - RKISP1_RSZ_PAD_SINK); + sel->r = *v4l2_subdev_state_get_crop(sd_state, + RKISP1_RSZ_PAD_SINK); break; default: @@ -630,7 +615,6 @@ static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = { .enum_mbus_code = rkisp1_rsz_enum_mbus_code, .get_selection = rkisp1_rsz_get_selection, .set_selection = rkisp1_rsz_set_selection, - .init_cfg = rkisp1_rsz_init_config, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = rkisp1_rsz_set_fmt, .link_validate = v4l2_subdev_link_validate_default, @@ -677,6 +661,10 @@ static const struct v4l2_subdev_ops rkisp1_rsz_ops = { .pad = &rkisp1_rsz_pad_ops, }; +static const struct v4l2_subdev_internal_ops rkisp1_rsz_internal_ops = { + .init_state = rkisp1_rsz_init_state, +}; + static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) { if (!rsz->rkisp1) @@ -706,6 +694,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) } v4l2_subdev_init(sd, &rkisp1_rsz_ops); + sd->internal_ops = &rkisp1_rsz_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.ops = &rkisp1_rsz_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h index 1ea5fa1bf..b9777e07f 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h @@ -26,7 +26,6 @@ #include "gsc-regs.h" -#define CONFIG_VB2_GSC_DMA_CONTIG 1 #define GSC_MODULE_NAME "exynos-gsc" #define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index a0d43bf89..05cafba1c 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -1479,7 +1479,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1534,7 +1534,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; return 0; } @@ -1604,10 +1604,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: @@ -1651,10 +1651,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); + try_sel = v4l2_subdev_state_get_compose(sd_state, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index 97908778e..0be687b01 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -814,7 +814,7 @@ err: fimc_clk_put(fimc); dev_err(&fimc->pdev->dev, "failed to get clock: %s\n", fimc_clocks[i]); - return -ENXIO; + return ret; } #ifdef CONFIG_PM diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c index bef6e9b4a..44363c424 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c @@ -57,7 +57,6 @@ static int fimc_is_i2c_probe(struct platform_device *pdev) strscpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name)); i2c_adap->owner = THIS_MODULE; i2c_adap->algo = &fimc_is_i2c_algorithm; - i2c_adap->class = I2C_CLASS_SPD; platform_set_drvdata(pdev, isp_i2c); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c index b85986e50..3c5d7bee2 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c @@ -126,7 +126,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *mf = *v4l2_subdev_state_get_format(sd_state, fmt->pad); return 0; } @@ -172,9 +172,8 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(&isp->subdev, - sd_state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, + FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -207,7 +206,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __isp_subdev_try_format(isp, sd_state, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -220,8 +219,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, format.pad = pad; __isp_subdev_try_format(isp, sd_state, &format); - mf = v4l2_subdev_get_try_format(sd, sd_state, - pad); + mf = v4l2_subdev_state_get_format(sd_state, + pad); *mf = format.format; } } @@ -374,18 +373,17 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, .field = V4L2_FIELD_NONE, }; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_state_get_format(fh->state, FIMC_ISP_SD_PAD_SINK); *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(sd, fh->state, - FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_state_get_format(fh->state, + FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 9396b10b5..7898c9beb 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -574,16 +574,14 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, + FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(&fimc->subdev, - sd_state, - FLITE_SD_PAD_SINK); + rect = v4l2_subdev_state_get_crop(sd_state, + FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; mf->colorspace = sink->fmt->colorspace; @@ -1021,7 +1019,7 @@ static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, @@ -1129,7 +1127,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1166,7 +1164,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 686ca8753..aae8a8b2c 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -569,8 +569,7 @@ static struct v4l2_mbus_framefmt *__s5pcsis_get_format( enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&state->sd, - sd_state, 0) : NULL; + return sd_state ? v4l2_subdev_state_get_format(sd_state, 0) : NULL; return &state->format; } diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 0f5b3845d..be58260ea 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -1216,7 +1216,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); fmt->format = *mf; return 0; } @@ -1305,7 +1305,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1357,7 +1357,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); return 0; } @@ -1445,7 +1445,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h new file mode 100644 index 000000000..24e669d8e --- /dev/null +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Register definition file for Samsung MFC V12.x Interface (FIMV) driver + * + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + */ + +#ifndef _REGS_MFC_V12_H +#define _REGS_MFC_V12_H + +#include +#include "regs-mfc-v10.h" + +/* MFCv12 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V12 (30 * SZ_1K) +#define MFC_H264_DEC_CTX_BUF_SIZE_V12 (2 * SZ_1M) +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V12 (30 * SZ_1K) +#define MFC_H264_ENC_CTX_BUF_SIZE_V12 (100 * SZ_1K) +#define MFC_HEVC_ENC_CTX_BUF_SIZE_V12 (40 * SZ_1K) +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V12 (25 * SZ_1K) + +/* MFCv12 variant defines */ +#define MAX_FW_SIZE_V12 (SZ_1M) +#define MAX_CPB_SIZE_V12 (7 * SZ_1M) +#define MFC_VERSION_V12 0xC0 +#define MFC_NUM_PORTS_V12 1 +#define S5P_FIMV_CODEC_VP9_ENC 27 +#define MFC_CHROMA_PAD_BYTES_V12 256 +#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V12 256 + +/* Encoder buffer size for MFCv12 */ +#define ENC_V120_BASE_SIZE(x, y) \ + ((((x) + 3) * ((y) + 3) * 8) \ + + ((((y) * 64) + 2304) * ((x) + 7) / 8)) + +#define ENC_V120_H264_ME_SIZE(x, y) \ + ALIGN((ENC_V120_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP((x) * (y), 64) * 32)), 256) + +#define ENC_V120_MPEG4_ME_SIZE(x, y) \ + ALIGN((ENC_V120_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP((x) * (y), 128) * 16)), 256) + +#define ENC_V120_VP8_ME_SIZE(x, y) \ + ALIGN(ENC_V120_BASE_SIZE((x), (y)), 256) + +#define ENC_V120_HEVC_ME_SIZE(x, y) \ + ALIGN(((((x) + 3) * ((y) + 3) * 32) \ + + ((((y) * 128) + 2304) * ((x) + 3) / 4)), 256) + +#endif /*_REGS_MFC_V12_H*/ diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h index 4a7adfdaa..50f9bf060 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h @@ -24,6 +24,7 @@ #define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70 #define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74 +#define S5P_FIMV_E_ENCODED_SOURCE_THIRD_ADDR_V7 0xfa78 #define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0 #define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4 diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h index 162e3c7e9..0ef9eb2df 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h @@ -17,13 +17,16 @@ #define S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8 0xf108 #define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144 #define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148 +#define S5P_FIMV_D_THIRD_PLANE_DPB_SIZE_V8 0xf14C #define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150 #define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8 0xf138 #define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8 0xf13c +#define S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE_V8 0xf140 #define S5P_FIMV_D_FIRST_PLANE_DPB_V8 0xf160 #define S5P_FIMV_D_SECOND_PLANE_DPB_V8 0xf260 +#define S5P_FIMV_D_THIRD_PLANE_DPB_V8 0xf360 #define S5P_FIMV_D_MV_BUFFER_V8 0xf460 #define S5P_FIMV_D_NUM_MV_V8 0xf134 diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index e30e54935..fbb047ead 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -604,6 +604,8 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, s5p_mfc_clock_off(); wake_up(&ctx->queue); + if (ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) + set_work_bit_irqsave(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); @@ -790,6 +792,8 @@ static int s5p_mfc_open(struct file *file) INIT_LIST_HEAD(&ctx->dst_queue); ctx->src_queue_cnt = 0; ctx->dst_queue_cnt = 0; + ctx->is_422 = 0; + ctx->is_10bit = 0; /* Get context number */ ctx->num = 0; while (dev->ctx[ctx->num]) { @@ -863,7 +867,7 @@ static int s5p_mfc_open(struct file *file) q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops = get_enc_queue_ops(); } else { ret = -ENOENT; @@ -890,7 +894,7 @@ static int s5p_mfc_open(struct file *file) q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->ops = get_enc_queue_ops(); } else { ret = -ENOENT; @@ -1660,6 +1664,31 @@ static struct s5p_mfc_variant mfc_drvdata_v10 = { .fw_name[0] = "s5p-mfc-v10.fw", }; +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v12 = { + .dev_ctx = MFC_CTX_BUF_SIZE_V12, + .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V12, + .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V12, + .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V12, + .hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V12, + .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V12, +}; + +static struct s5p_mfc_buf_size buf_size_v12 = { + .fw = MAX_FW_SIZE_V12, + .cpb = MAX_CPB_SIZE_V12, + .priv = &mfc_buf_size_v12, +}; + +static struct s5p_mfc_variant mfc_drvdata_v12 = { + .version = MFC_VERSION_V12, + .version_bit = MFC_V12_BIT, + .port_num = MFC_NUM_PORTS_V12, + .buf_size = &buf_size_v12, + .fw_name[0] = "s5p-mfc-v12.fw", + .clk_names = {"mfc"}, + .num_clocks = 1, +}; + static const struct of_device_id exynos_mfc_match[] = { { .compatible = "samsung,mfc-v5", @@ -1682,6 +1711,9 @@ static const struct of_device_id exynos_mfc_match[] = { }, { .compatible = "samsung,mfc-v10", .data = &mfc_drvdata_v10, + }, { + .compatible = "tesla,fsd-mfc", + .data = &mfc_drvdata_v12, }, {}, }; diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h index 5304f42c8..59450b324 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h @@ -19,7 +19,7 @@ #include #include #include "regs-mfc.h" -#include "regs-mfc-v10.h" +#include "regs-mfc-v12.h" #define S5P_MFC_NAME "s5p-mfc" @@ -56,6 +56,7 @@ #define MFC_NO_INSTANCE_SET -1 #define MFC_ENC_CAP_PLANE_COUNT 1 #define MFC_ENC_OUT_PLANE_COUNT 2 +#define VB2_MAX_PLANE_COUNT 3 #define STUFF_BYTE 4 #define MFC_MAX_CTRLS 128 @@ -181,6 +182,7 @@ struct s5p_mfc_buf { struct { size_t luma; size_t chroma; + size_t chroma_1; } raw; size_t stream; } cookie; @@ -621,6 +623,10 @@ struct s5p_mfc_codec_ops { * v4l2 control framework * @ctrl_handler: handler for v4l2 framework * @scratch_buf_size: scratch buffer size + * @is_10bit: state to check 10bit support + * @is_422: state to check YUV422 10bit format + * @chroma_size_1: size of a chroma third plane + * @stride: size of stride for all planes */ struct s5p_mfc_ctx { struct s5p_mfc_dev *dev; @@ -657,6 +663,7 @@ struct s5p_mfc_ctx { int luma_size; int chroma_size; + int chroma_size_1; int mv_size; unsigned long consumed_stream; @@ -720,6 +727,9 @@ struct s5p_mfc_ctx { struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS]; struct v4l2_ctrl_handler ctrl_handler; size_t scratch_buf_size; + int is_10bit; + int is_422; + int stride[VB2_MAX_PLANE_COUNT]; }; /* @@ -771,22 +781,27 @@ void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq); #define HAS_PORTNUM(dev) (dev ? (dev->variant ? \ (dev->variant->port_num ? 1 : 0) : 0) : 0) #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) -#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) -#define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0) -#define IS_MFCV8_PLUS(dev) (dev->variant->version >= 0x80 ? 1 : 0) -#define IS_MFCV10(dev) (dev->variant->version >= 0xA0 ? 1 : 0) -#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10(dev)) +#define IS_MFCV6_PLUS(dev) ((dev)->variant->version >= 0x60) +#define IS_MFCV7_PLUS(dev) ((dev)->variant->version >= 0x70) +#define IS_MFCV8_PLUS(dev) ((dev)->variant->version >= 0x80) +#define IS_MFCV10_PLUS(dev) ((dev)->variant->version >= 0xA0) +#define IS_MFCV12(dev) ((dev)->variant->version >= 0xC0) +#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10_PLUS(dev)) #define MFC_V5_BIT BIT(0) #define MFC_V6_BIT BIT(1) #define MFC_V7_BIT BIT(2) #define MFC_V8_BIT BIT(3) #define MFC_V10_BIT BIT(5) +#define MFC_V12_BIT BIT(7) #define MFC_V5PLUS_BITS (MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | \ - MFC_V8_BIT | MFC_V10_BIT) + MFC_V8_BIT | MFC_V10_BIT | MFC_V12_BIT) #define MFC_V6PLUS_BITS (MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT | \ - MFC_V10_BIT) -#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT) + MFC_V10_BIT | MFC_V12_BIT) +#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT | \ + MFC_V12_BIT) + +#define MFC_V10PLUS_BITS (MFC_V10_BIT | MFC_V12_BIT) #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c index 6d3c92045..503487f34 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c @@ -51,8 +51,14 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) * into kernel. */ mfc_debug_enter(); - if (dev->fw_get_done) - return 0; + /* In case of MFC v12, RET_SYS_INIT response from hardware fails due to + * incorrect firmware transfer and therefore it is not able to initialize + * the hardware. This causes failed response for SYS_INIT command when + * MFC runs for second time. So, load the MFC v12 firmware for each run. + */ + if (!IS_MFCV12(dev)) + if (dev->fw_get_done) + return 0; for (i = MFC_FW_MAX_VERSIONS - 1; i >= 0; i--) { if (!dev->variant->fw_name[i]) @@ -130,7 +136,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev) mfc_write(dev, 0, S5P_FIMV_REG_CLEAR_BEGIN_V6 + (i*4)); /* check bus reset control before reset */ - if (dev->risc_on) + if (dev->risc_on && !IS_MFCV12(dev)) if (s5p_mfc_bus_reset(dev)) return -EIO; /* Reset @@ -236,7 +242,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) else mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10); mfc_debug(2, "Will now wait for completion of firmware transfer\n"); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c index 268ffe4da..3957f28d4 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c @@ -56,6 +56,20 @@ static struct s5p_mfc_fmt formats[] = { .num_planes = 2, .versions = MFC_V6PLUS_BITS, }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT + }, { .fourcc = V4L2_PIX_FMT_H264, .codec_mode = S5P_MFC_CODEC_H264_DEC, @@ -146,7 +160,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_HEVC_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, @@ -155,7 +169,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_VP9_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, }; @@ -355,14 +369,19 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_mp->width = ctx->buf_width; pix_mp->height = ctx->buf_height; pix_mp->field = V4L2_FIELD_NONE; - pix_mp->num_planes = 2; + pix_mp->num_planes = ctx->dst_fmt->num_planes; /* Set pixelformat to the format in which MFC outputs the decoded frame */ pix_mp->pixelformat = ctx->dst_fmt->fourcc; - pix_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_mp->plane_fmt[1].bytesperline = ctx->stride[1]; pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size; + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { /* This is run on OUTPUT The buffer contains compressed image @@ -920,6 +939,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; + const struct v4l2_format_info *format; /* Video output for decoding (source) * this can be set after getting an instance */ @@ -936,7 +956,13 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, } else if (ctx->state == MFCINST_HEAD_PARSED && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { /* Output plane count is 2 - one for Y and one for CbCr */ - *plane_count = 2; + format = v4l2_format_info(ctx->dst_fmt->fourcc); + if (!format) { + mfc_err("invalid format\n"); + return -EINVAL; + } + *plane_count = format->comp_planes; + /* Setup buffer count */ if (*buf_count < ctx->pb_count) *buf_count = ctx->pb_count; @@ -955,14 +981,18 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; - + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + psize[2] = ctx->chroma_size_1; if (IS_MFCV6_PLUS(dev)) alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; else alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_L_CTX]; - } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - ctx->state == MFCINST_INIT) { + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + alloc_devs[2] = ctx->dev->mem_dev[BANK_L_CTX]; + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && ctx->state == MFCINST_INIT) { psize[0] = ctx->dec_src_buf_size; alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; } else { @@ -994,12 +1024,24 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) mfc_err("Plane buffer (CAPTURE) is too small\n"); return -EINVAL; } + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + if (vb2_plane_size(vb, 2) < ctx->chroma_size_1) { + mfc_err("Plane buffer (CAPTURE) is too small\n"); + return -EINVAL; + } + } i = vb->index; ctx->dst_bufs[i].b = vbuf; ctx->dst_bufs[i].cookie.raw.luma = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs[i].cookie.raw.chroma = vb2_dma_contig_plane_dma_addr(vb, 1); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + ctx->dst_bufs[i].cookie.raw.chroma_1 = + vb2_dma_contig_plane_dma_addr(vb, 2); + } ctx->dst_bufs_cnt++; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (IS_ERR_OR_NULL(ERR_PTR( diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c index 4b4c129c0..ef8bb40b9 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c @@ -59,6 +59,20 @@ static struct s5p_mfc_fmt formats[] = { .num_planes = 2, .versions = MFC_V6PLUS_BITS, }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .codec_mode = S5P_MFC_CODEC_NONE, + .type = MFC_FMT_RAW, + .num_planes = 3, + .versions = MFC_V12_BIT, + }, { .fourcc = V4L2_PIX_FMT_H264, .codec_mode = S5P_MFC_CODEC_H264_ENC, @@ -92,7 +106,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_FIMV_CODEC_HEVC_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V10_BIT, + .versions = MFC_V10PLUS_BITS, }, }; @@ -1150,7 +1164,6 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_buf *dst_mb; - unsigned int enc_pb_count; if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) { if (!list_empty(&ctx->dst_queue)) { @@ -1172,14 +1185,12 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) set_work_bit_irqsave(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { - enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops, - get_enc_dpb_count, dev); - if (ctx->pb_count < enc_pb_count) - ctx->pb_count = enc_pb_count; + ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_enc_dpb_count, dev); if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) { ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, get_e_min_scratch_buf_size, dev); - ctx->bank1.size += ctx->scratch_buf_size; + if (!IS_MFCV12(dev)) + ctx->bank1.size += ctx->scratch_buf_size; } ctx->state = MFCINST_HEAD_PRODUCED; } @@ -1192,14 +1203,20 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_mb; struct s5p_mfc_buf *src_mb; - unsigned long src_y_addr, src_c_addr, dst_addr; + unsigned long src_y_addr, src_c_addr, src_c_1_addr, dst_addr; unsigned int dst_size; src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + src_c_1_addr = + vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 2); + else + src_c_1_addr = 0; s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx, - src_y_addr, src_c_addr); + src_y_addr, src_c_addr, src_c_1_addr); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); @@ -1214,8 +1231,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *mb_entry; - unsigned long enc_y_addr = 0, enc_c_addr = 0; - unsigned long mb_y_addr, mb_c_addr; + unsigned long enc_y_addr = 0, enc_c_addr = 0, enc_c_1_addr = 0; + unsigned long mb_y_addr, mb_c_addr, mb_c_1_addr; int slice_type; unsigned int strm_size; bool src_ready; @@ -1228,18 +1245,26 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT)); if (slice_type >= 0) { s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx, - &enc_y_addr, &enc_c_addr); + &enc_y_addr, &enc_c_addr, &enc_c_1_addr); list_for_each_entry(mb_entry, &ctx->src_queue, list) { mb_y_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 0); mb_c_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 1); - if ((enc_y_addr == mb_y_addr) && - (enc_c_addr == mb_c_addr)) { + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + mb_c_1_addr = vb2_dma_contig_plane_dma_addr + (&mb_entry->b->vb2_buf, 2); + else + mb_c_1_addr = 0; + if (enc_y_addr == mb_y_addr && enc_c_addr == mb_c_addr && enc_c_1_addr + == mb_c_1_addr) { list_del(&mb_entry->list); ctx->src_queue_cnt--; vb2_buffer_done(&mb_entry->b->vb2_buf, - VB2_BUF_STATE_DONE); + VB2_BUF_STATE_DONE); break; } } @@ -1248,20 +1273,27 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) &mb_entry->b->vb2_buf, 0); mb_c_addr = vb2_dma_contig_plane_dma_addr( &mb_entry->b->vb2_buf, 1); - if ((enc_y_addr == mb_y_addr) && - (enc_c_addr == mb_c_addr)) { + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) + mb_c_1_addr = vb2_dma_contig_plane_dma_addr(& + mb_entry->b->vb2_buf, 2); + else + mb_c_1_addr = 0; + if (enc_y_addr == mb_y_addr && enc_c_addr == mb_c_addr && enc_c_1_addr + == mb_c_1_addr) { list_del(&mb_entry->list); ctx->ref_queue_cnt--; vb2_buffer_done(&mb_entry->b->vb2_buf, - VB2_BUF_STATE_DONE); + VB2_BUF_STATE_DONE); break; } } } if (ctx->src_queue_cnt > 0 && (ctx->state == MFCINST_RUNNING || - ctx->state == MFCINST_FINISHING)) { + ctx->state == MFCINST_FINISHING)) { mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, - list); + list); if (mb_entry->flags & MFC_BUF_FLAG_USED) { list_del(&mb_entry->list); ctx->src_queue_cnt--; @@ -1380,10 +1412,15 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc; pix_fmt_mp->num_planes = ctx->src_fmt->num_planes; - pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[1].bytesperline = ctx->stride[1]; pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size; + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_fmt_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_fmt_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } } else { mfc_err("invalid buf type\n"); return -EINVAL; @@ -1420,9 +1457,12 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; } - - v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1, - &pix_fmt_mp->height, 4, 1080, 1, 0); + if (IS_MFCV12(dev)) + v4l_bound_align_image(&pix_fmt_mp->width, 8, 3840, 1, &pix_fmt_mp + ->height, 4, 2160, 1, 0); + else + v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1, &pix_fmt_mp + ->height, 4, 1080, 1, 0); } else { mfc_err("invalid buf type\n"); return -EINVAL; @@ -1467,9 +1507,14 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx); pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size; - pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[0].bytesperline = ctx->stride[0]; pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size; - pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width; + pix_fmt_mp->plane_fmt[1].bytesperline = ctx->stride[1]; + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + pix_fmt_mp->plane_fmt[2].bytesperline = ctx->stride[2]; + pix_fmt_mp->plane_fmt[2].sizeimage = ctx->chroma_size_1; + } ctx->src_bufs_cnt = 0; ctx->output_state = QUEUE_FREE; @@ -1489,9 +1534,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - /* if memory is not mmp or userptr return error */ + /* if memory is not mmp or userptr or dmabuf return error */ if ((reqbufs->memory != V4L2_MEMORY_MMAP) && - (reqbufs->memory != V4L2_MEMORY_USERPTR)) + (reqbufs->memory != V4L2_MEMORY_USERPTR) && + (reqbufs->memory != V4L2_MEMORY_DMABUF)) return -EINVAL; if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (reqbufs->count == 0) { @@ -1514,14 +1560,6 @@ static int vidioc_reqbufs(struct file *file, void *priv, } ctx->capture_state = QUEUE_BUFS_REQUESTED; - ret = s5p_mfc_hw_call(ctx->dev->mfc_ops, - alloc_codec_buffers, ctx); - if (ret) { - mfc_err("Failed to allocate encoding buffers\n"); - reqbufs->count = 0; - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - return -ENOMEM; - } } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (reqbufs->count == 0) { mfc_debug(2, "Freeing buffers\n"); @@ -1537,15 +1575,13 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (IS_MFCV6_PLUS(dev)) { + if (IS_MFCV6_PLUS(dev) && (!IS_MFCV12(dev))) { /* Check for min encoder buffers */ if (ctx->pb_count && (reqbufs->count < ctx->pb_count)) { reqbufs->count = ctx->pb_count; mfc_debug(2, "Minimum %d output buffers needed\n", ctx->pb_count); - } else { - ctx->pb_count = reqbufs->count; } } @@ -1568,9 +1604,10 @@ static int vidioc_querybuf(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - /* if memory is not mmp or userptr return error */ + /* if memory is not mmp or userptr or dmabuf return error */ if ((buf->memory != V4L2_MEMORY_MMAP) && - (buf->memory != V4L2_MEMORY_USERPTR)) + (buf->memory != V4L2_MEMORY_USERPTR) && + (buf->memory != V4L2_MEMORY_DMABUF)) return -EINVAL; if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (ctx->state != MFCINST_GOT_INST) { @@ -2413,10 +2450,18 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; + if (ctx->src_fmt && (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M)) + psize[2] = ctx->chroma_size_1; if (IS_MFCV6_PLUS(dev)) { alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_L_CTX]; + if (ctx->src_fmt && (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M)) + alloc_devs[2] = ctx->dev->mem_dev[BANK_L_CTX]; } else { alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX]; alloc_devs[1] = ctx->dev->mem_dev[BANK_R_CTX]; @@ -2455,6 +2500,11 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs[i].cookie.raw.chroma = vb2_dma_contig_plane_dma_addr(vb, 1); + if (ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + ctx->src_bufs[i].cookie.raw.chroma_1 = + vb2_dma_contig_plane_dma_addr(vb, 2); ctx->src_bufs_cnt++; } else { mfc_err("invalid queue type: %d\n", vq->type); @@ -2492,6 +2542,12 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb) mfc_err("plane size is too small for output\n"); return -EINVAL; } + if ((ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || + ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) && + (vb2_plane_size(vb, 2) < ctx->chroma_size_1)) { + mfc_err("plane size is too small for output\n"); + return -EINVAL; + } } else { mfc_err("invalid queue type: %d\n", vq->type); return -EINVAL; @@ -2513,11 +2569,11 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) S5P_MFC_R2H_CMD_SEQ_DONE_RET, 0); } - - if (ctx->src_bufs_cnt < ctx->pb_count) { - mfc_err("Need minimum %d OUTPUT buffers\n", - ctx->pb_count); - return -ENOBUFS; + if (q->memory != V4L2_MEMORY_DMABUF) { + if (ctx->src_bufs_cnt < ctx->pb_count) { + mfc_err("Need minimum %d OUTPUT buffers\n", ctx->pb_count); + return -ENOBUFS; + } } } diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h index b9831275f..7c5e851c8 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h @@ -166,9 +166,9 @@ struct s5p_mfc_regs { void __iomem *d_decoded_third_addr;/* only v7 */ void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */ void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */ - void __iomem *d_min_scratch_buffer_size; /* v10 */ - void __iomem *d_static_buffer_addr; /* v10 */ - void __iomem *d_static_buffer_size; /* v10 */ + void __iomem *d_min_scratch_buffer_size; /* v10 and v12 */ + void __iomem *d_static_buffer_addr; /* v10 and v12 */ + void __iomem *d_static_buffer_size; /* v10 and v12 */ /* encoder registers */ void __iomem *e_frame_width; @@ -268,7 +268,7 @@ struct s5p_mfc_regs { void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ - void __iomem *e_min_scratch_buffer_size; /* v10 */ + void __iomem *e_min_scratch_buffer_size; /* v10 and v12 */ void __iomem *e_num_t_layer; /* v10 */ void __iomem *e_hier_qp_layer0; /* v10 */ void __iomem *e_hier_bit_rate_layer0; /* v10 */ @@ -293,9 +293,11 @@ struct s5p_mfc_hw_ops { int (*set_enc_stream_buffer)(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size); void (*set_enc_frame_buffer)(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr); + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr); void (*get_enc_frame_buffer)(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr); + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr); void (*try_run)(struct s5p_mfc_dev *dev); void (*clear_int_flags)(struct s5p_mfc_dev *dev); int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c index 28a06dc34..fcfaf125a 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c @@ -516,7 +516,8 @@ static int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr) + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -525,7 +526,8 @@ static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr) + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1210,7 +1212,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) if (list_empty(&ctx->src_queue)) { /* send null frame */ s5p_mfc_set_enc_frame_buffer_v5(ctx, dev->dma_base[BANK_R_CTX], - dev->dma_base[BANK_R_CTX]); + dev->dma_base[BANK_R_CTX], 0); src_mb = NULL; } else { src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, @@ -1220,7 +1222,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) /* send null frame */ s5p_mfc_set_enc_frame_buffer_v5(ctx, dev->dma_base[BANK_R_CTX], - dev->dma_base[BANK_R_CTX]); + dev->dma_base[BANK_R_CTX], 0); ctx->state = MFCINST_FINISHING; } else { src_y_addr = vb2_dma_contig_plane_dma_addr( @@ -1228,7 +1230,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) src_c_addr = vb2_dma_contig_plane_dma_addr( &src_mb->b->vb2_buf, 1); s5p_mfc_set_enc_frame_buffer_v5(ctx, src_y_addr, - src_c_addr); + src_c_addr, 0); if (src_mb->flags & MFC_BUF_FLAG_EOS) ctx->state = MFCINST_FINISHING; } diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c index c0df5ac9f..fd945211d 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c @@ -60,21 +60,23 @@ static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - unsigned int mb_width, mb_height; + unsigned int mb_width, mb_height, width64, height32; unsigned int lcu_width = 0, lcu_height = 0; int ret; mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); + width64 = ALIGN(ctx->img_width, 64); + height32 = ALIGN(ctx->img_height, 32); if (ctx->type == MFCINST_DECODER) { mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n", ctx->luma_size, ctx->chroma_size, ctx->mv_size); mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count); } else if (ctx->type == MFCINST_ENCODER) { - if (IS_MFCV10(dev)) { + if (IS_MFCV10_PLUS(dev)) ctx->tmv_buffer_size = 0; - } else if (IS_MFCV8_PLUS(dev)) + else if (IS_MFCV8_PLUS(dev)) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); @@ -82,7 +84,39 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width); + lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height); + if (ctx->codec_mode == S5P_FIMV_CODEC_HEVC_ENC && ctx->is_10bit) { + ctx->luma_dpb_size = + width64 * height32 + + ALIGN(DIV_ROUND_UP(lcu_width * 32, 4), 16) * height32 + 128; + if (ctx->is_422) + ctx->chroma_dpb_size = + ctx->luma_dpb_size; + else + ctx->chroma_dpb_size = + width64 * height32 / 2 + + ALIGN(DIV_ROUND_UP(lcu_width * + 32, 4), 16) * height32 / 2 + 128; + } else if (ctx->codec_mode == S5P_FIMV_CODEC_VP9_ENC && ctx->is_10bit) { + ctx->luma_dpb_size = + ALIGN(ctx->img_width * 2, 128) * height32 + 64; + ctx->chroma_dpb_size = + ALIGN(ctx->img_width * 2, 128) * height32 / 2 + 64; + } else { + ctx->luma_dpb_size = + width64 * height32 + 64; + if (ctx->is_422) + ctx->chroma_dpb_size = + ctx->luma_dpb_size; + else + ctx->chroma_dpb_size = + width64 * height32 / 2 + 64; + } + ctx->luma_dpb_size = ALIGN(ctx->luma_dpb_size + 256, SZ_2K); + ctx->chroma_dpb_size = ALIGN(ctx->chroma_dpb_size + 256, SZ_2K); + } else if (IS_MFCV10_PLUS(dev)) { lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width); lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height); if (ctx->codec_mode != S5P_FIMV_CODEC_HEVC_ENC) { @@ -133,7 +167,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: case S5P_MFC_CODEC_H264_MVC_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = @@ -152,7 +186,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) (ctx->mv_count * ctx->mv_size); break; case S5P_MFC_CODEC_MPEG4_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV7_PLUS(dev)) { ctx->scratch_buf_size = @@ -172,7 +206,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else ctx->scratch_buf_size = @@ -189,7 +223,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else ctx->scratch_buf_size = @@ -201,7 +235,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: - if (IS_MFCV10(dev)) + if (IS_MFCV10_PLUS(dev)) mfc_debug(2, "Use min scratch buffer size\n"); else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = @@ -230,7 +264,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) DEC_VP9_STATIC_BUFFER_SIZE; break; case S5P_MFC_CODEC_H264_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_H264_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), 16); @@ -254,7 +292,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_MPEG4_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, @@ -265,7 +307,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_width, mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, - S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); + S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->pb_count * (ctx->luma_dpb_size + @@ -273,7 +315,11 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VP8_ENC: - if (IS_MFCV10(dev)) { + if (IS_MFCV12(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ENC_V120_VP8_ME_SIZE(mb_width, mb_height); + } else if (IS_MFCV10_PLUS(dev)) { mfc_debug(2, "Use min scratch buffer size\n"); ctx->me_buffer_size = ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), @@ -297,9 +343,13 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_HEVC_ENC: + if (IS_MFCV12(dev)) + ctx->me_buffer_size = + ENC_V120_HEVC_ME_SIZE(lcu_width, lcu_height); + else + ctx->me_buffer_size = + ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16); mfc_debug(2, "Use min scratch buffer size\n"); - ctx->me_buffer_size = - ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256); ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + @@ -438,30 +488,48 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6); + ctx->chroma_size_1 = 0; mfc_debug(2, "SEQ Done: Movie dimensions %dx%d,\n" "buffer dimensions: %dx%d\n", ctx->img_width, ctx->img_height, ctx->buf_width, ctx->buf_height); - ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height); - ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1)); + switch (ctx->dst_fmt->fourcc) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->luma_size = calc_plane(ctx->stride[0], ctx->img_height); + ctx->chroma_size = calc_plane(ctx->stride[1], (ctx->img_height / 2)); + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->stride[2] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12MT_HALIGN_V6); + ctx->luma_size = calc_plane(ctx->stride[0], ctx->img_height); + ctx->chroma_size = calc_plane(ctx->stride[1], (ctx->img_height / 2)); + ctx->chroma_size_1 = calc_plane(ctx->stride[2], (ctx->img_height / 2)); + break; + } + if (IS_MFCV8_PLUS(ctx->dev)) { /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/ ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; + ctx->chroma_size_1 += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { - if (IS_MFCV10(dev)) { - ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V10(ctx->img_width, - ctx->img_height); - } else { - ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, - ctx->img_height); - } + if (IS_MFCV12(dev)) + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 1024); + else if (IS_MFCV10_PLUS(dev)) + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 512); + else + ctx->mv_size = S5P_MFC_DEC_MV_SIZE(ctx->img_width, ctx->img_height, 128); + } else if (ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) { - ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width, - ctx->img_height); + ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width, ctx->img_height); ctx->mv_size = ALIGN(ctx->mv_size, 32); } else { ctx->mv_size = 0; @@ -475,14 +543,40 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); - ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); - ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256); - ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); - - /* MFCv7 needs pad bytes for Luma and Chroma */ - if (IS_MFCV7_PLUS(ctx->dev)) { + if (IS_MFCV12(ctx->dev)) { + switch (ctx->src_fmt->fourcc) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16); + ctx->chroma_size = ctx->stride[0] * ALIGN(ctx->img_height / 2, 16); + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[1] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[2] = ALIGN(ctx->img_width / 2, S5P_FIMV_NV12M_HALIGN_V6); + ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16); + ctx->chroma_size = ctx->stride[1] * ALIGN(ctx->img_height / 2, 16); + ctx->chroma_size_1 = ctx->stride[2] * ALIGN(ctx->img_height / 2, 16); + break; + } ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; - ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7; + ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V12; + ctx->chroma_size_1 += MFC_CHROMA_PAD_BYTES_V12; + } else { + ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); + ctx->stride[0] = ctx->buf_width; + ctx->stride[1] = ctx->buf_width; + ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256); + ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); + ctx->chroma_size_1 = 0; + /* MFCv7 needs pad bytes for Luma and Chroma */ + if (IS_MFCV7_PLUS(ctx->dev)) { + ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; + ctx->chroma_size += MFC_LUMA_PAD_BYTES_V7; + } } } @@ -529,15 +623,18 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) writel(ctx->total_dpb_count, mfc_regs->d_num_dpb); writel(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); writel(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); - + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->chroma_size_1, mfc_regs->d_third_plane_dpb_size); writel(buf_addr1, mfc_regs->d_scratch_buffer_addr); writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); if (IS_MFCV8_PLUS(dev)) { - writel(ctx->img_width, - mfc_regs->d_first_plane_dpb_stride_size); - writel(ctx->img_width, - mfc_regs->d_second_plane_dpb_stride_size); + writel(ctx->stride[0], mfc_regs->d_first_plane_dpb_stride_size); + writel(ctx->stride[1], mfc_regs->d_second_plane_dpb_stride_size); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->stride[2], mfc_regs->d_third_plane_dpb_stride_size); } buf_addr1 += ctx->scratch_buf_size; @@ -566,6 +663,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) ctx->dst_bufs[i].cookie.raw.chroma); writel(ctx->dst_bufs[i].cookie.raw.chroma, mfc_regs->d_second_plane_dpb + i * 4); + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->dst_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) { + mfc_debug(2, "\tChroma_1 %d: %zx\n", i, ctx + ->dst_bufs[i].cookie.raw.chroma_1); + writel(ctx->dst_bufs[i].cookie.raw.chroma_1, mfc_regs->d_third_plane_dpb + + i * 4); + } } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC || @@ -624,20 +728,24 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, } static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, - unsigned long y_addr, unsigned long c_addr) + unsigned long y_addr, unsigned long c_addr, + unsigned long c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; writel(y_addr, mfc_regs->e_source_first_plane_addr); writel(c_addr, mfc_regs->e_source_second_plane_addr); + writel(c_1_addr, mfc_regs->e_source_third_plane_addr); mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr); mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); + mfc_debug(2, "enc src cr buf addr: 0x%08lx\n", c_1_addr); } static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, - unsigned long *y_addr, unsigned long *c_addr) + unsigned long *y_addr, unsigned long *c_addr, + unsigned long *c_1_addr) { struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; @@ -645,12 +753,17 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, *y_addr = readl(mfc_regs->e_encoded_source_first_plane_addr); *c_addr = readl(mfc_regs->e_encoded_source_second_plane_addr); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + *c_1_addr = readl(mfc_regs->e_encoded_source_third_plane_addr); + else + *c_1_addr = 0; enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr); enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr); mfc_debug(2, "recon y addr: 0x%08lx y_addr: 0x%08lx\n", enc_recon_y_addr, *y_addr); - mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); + mfc_debug(2, "recon c addr: 0x%08lx c_addr: 0x%08lx\n", enc_recon_c_addr, *c_addr); } /* Set encoding ref & codec buffer */ @@ -668,7 +781,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); - if (IS_MFCV10(dev)) { + if (IS_MFCV10_PLUS(dev)) { /* start address of per buffer is aligned */ for (i = 0; i < ctx->pb_count; i++) { writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); @@ -827,6 +940,20 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) writel(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ writel(0x0, mfc_regs->pixel_format); + } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YVU420M) { + /* 0: Linear, 1: 2D tiled*/ + reg = readl(mfc_regs->e_enc_options); + reg &= ~(0x1 << 7); + writel(reg, mfc_regs->e_enc_options); + /* 2: YV12(CrCb), 3: I420(CrCb) */ + writel(0x2, mfc_regs->pixel_format); + } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M) { + /* 0: Linear, 1: 2D tiled*/ + reg = readl(mfc_regs->e_enc_options); + reg &= ~(0x1 << 7); + writel(reg, mfc_regs->e_enc_options); + /* 2: YV12(CrCb), 3: I420(CrCb) */ + writel(0x3, mfc_regs->pixel_format); } /* memory structure recon. frame */ @@ -865,10 +992,24 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* reaction coefficient */ if (p->rc_frame) { - if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */ - writel(1, mfc_regs->e_rc_mode); - else /* loose CBR */ - writel(2, mfc_regs->e_rc_mode); + if (IS_MFCV12(dev)) { + /* loose CBR */ + if (p->rc_reaction_coeff < LOOSE_CBR_MAX) + writel(1, mfc_regs->e_rc_mode); + /* tight CBR */ + else if (p->rc_reaction_coeff < TIGHT_CBR_MAX) + writel(0, mfc_regs->e_rc_mode); + /* VBR */ + else + writel(2, mfc_regs->e_rc_mode); + } else { + /* tight CBR */ + if (p->rc_reaction_coeff < TIGHT_CBR_MAX) + writel(1, mfc_regs->e_rc_mode); + /* loose CBR */ + else + writel(2, mfc_regs->e_rc_mode); + } } /* seq header ctrl */ @@ -930,6 +1071,18 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p->num_b_frame & 0x3) << 16); writel(reg, mfc_regs->e_gop_config); + /* UHD encoding case */ + if (ctx->img_width == 3840 && ctx->img_height == 2160) { + if (p_h264->level < 51) { + mfc_debug(2, "Set Level 5.1 for UHD\n"); + p_h264->level = 51; + } + if (p_h264->profile != 0x2) { + mfc_debug(2, "Set High profile for UHD\n"); + p_h264->profile = 0x2; + } + } + /* profile & level */ reg = 0; /** level */ @@ -1637,8 +1790,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) else writel(reg, mfc_regs->d_dec_options); - /* 0: NV12(CbCr), 1: NV21(CrCb) */ - if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) + /* 0: NV12(CbCr), 1: NV21(CrCb), 2: YV12(CrCb), 3: I420(CbCr) */ + if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420M) + writel(0x3, mfc_regs->pixel_format); + else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YVU420M) + writel(0x2, mfc_regs->pixel_format); + else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) writel(0x1, mfc_regs->pixel_format); else writel(0x0, mfc_regs->pixel_format); @@ -1722,8 +1879,11 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) /* Set stride lengths for v7 & above */ if (IS_MFCV7_PLUS(dev)) { - writel(ctx->img_width, mfc_regs->e_source_first_plane_stride); - writel(ctx->img_width, mfc_regs->e_source_second_plane_stride); + writel(ctx->stride[0], mfc_regs->e_source_first_plane_stride); + writel(ctx->stride[1], mfc_regs->e_source_second_plane_stride); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + writel(ctx->stride[2], mfc_regs->e_source_third_plane_stride); } writel(ctx->inst_no, mfc_regs->instance_id); @@ -1832,7 +1992,7 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_mb; struct s5p_mfc_buf *src_mb; - unsigned long src_y_addr, src_c_addr, dst_addr; + unsigned long src_y_addr, src_c_addr, src_c_1_addr, dst_addr; /* unsigned int src_y_size, src_c_size; */ @@ -1850,22 +2010,28 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) if (list_empty(&ctx->src_queue)) { /* send null frame */ - s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0, 0); src_mb = NULL; } else { src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_mb->flags |= MFC_BUF_FLAG_USED; if (src_mb->b->vb2_buf.planes[0].bytesused == 0) { - s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0, 0); ctx->state = MFCINST_FINISHING; } else { src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); + if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420M || ctx->src_fmt->fourcc == + V4L2_PIX_FMT_YVU420M) + src_c_1_addr = vb2_dma_contig_plane_dma_addr + (&src_mb->b->vb2_buf, 2); + else + src_c_1_addr = 0; mfc_debug(2, "enc src y addr: 0x%08lx\n", src_y_addr); mfc_debug(2, "enc src c addr: 0x%08lx\n", src_c_addr); - s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr); + s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr, src_c_1_addr); if (src_mb->flags & MFC_BUF_FLAG_EOS) ctx->state = MFCINST_FINISHING; } @@ -1944,6 +2110,13 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx) struct s5p_mfc_dev *dev = ctx->dev; int ret; + ret = s5p_mfc_hw_call(ctx->dev->mfc_ops, alloc_codec_buffers, ctx); + if (ret) { + mfc_err("Failed to allocate encoding buffers\n"); + return -ENOMEM; + } + mfc_debug(2, "Allocated Internal Encoding Buffers\n"); + dev->curr_ctx = ctx->num; ret = s5p_mfc_set_enc_ref_buffer_v6(ctx); if (ret) { @@ -2387,10 +2560,9 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7); - R(e_encoded_source_first_plane_addr, - S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); - R(e_encoded_source_second_plane_addr, - S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + R(e_encoded_source_first_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); + R(e_encoded_source_second_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + R(e_encoded_source_third_plane_addr, S5P_FIMV_E_ENCODED_SOURCE_THIRD_ADDR_V7); R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7); if (!IS_MFCV8_PLUS(dev)) @@ -2405,16 +2577,17 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8); R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8); R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8); + R(d_third_plane_dpb_size, S5P_FIMV_D_THIRD_PLANE_DPB_SIZE_V8); R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8); R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8); - R(d_first_plane_dpb_stride_size, - S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); - R(d_second_plane_dpb_stride_size, - S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); + R(d_first_plane_dpb_stride_size, S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); + R(d_second_plane_dpb_stride_size, S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); + R(d_third_plane_dpb_stride_size, S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE_V8); R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8); R(d_num_mv, S5P_FIMV_D_NUM_MV_V8); R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8); R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8); + R(d_third_plane_dpb, S5P_FIMV_D_THIRD_PLANE_DPB_V8); R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8); R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8); R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8); @@ -2455,7 +2628,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8); R(e_min_scratch_buffer_size, S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8); - if (!IS_MFCV10(dev)) + if (!IS_MFCV10_PLUS(dev)) goto done; /* Initialize registers used in MFC v10 only. diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h index e4dd03c54..94ecb0e6e 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h @@ -19,10 +19,8 @@ #define MB_WIDTH(x_size) DIV_ROUND_UP(x_size, 16) #define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, 16) -#define S5P_MFC_DEC_MV_SIZE_V6(x, y) (MB_WIDTH(x) * \ - (((MB_HEIGHT(y)+1)/2)*2) * 64 + 128) -#define S5P_MFC_DEC_MV_SIZE_V10(x, y) (MB_WIDTH(x) * \ - (((MB_HEIGHT(y)+1)/2)*2) * 64 + 512) +#define S5P_MFC_DEC_MV_SIZE(x, y, offset) (MB_WIDTH(x) * \ + (((MB_HEIGHT(y) + 1) / 2) * 2) * 64 + (offset)) #define S5P_MFC_LCU_WIDTH(x_size) DIV_ROUND_UP(x_size, 32) #define S5P_MFC_LCU_HEIGHT(y_size) DIV_ROUND_UP(y_size, 32) @@ -42,6 +40,7 @@ #define ENC_H264_LEVEL_MAX 42 #define ENC_MPEG4_VOP_TIME_RES_MAX ((1 << 16) - 1) #define FRAME_DELTA_H264_H263 1 +#define LOOSE_CBR_MAX 5 #define TIGHT_CBR_MAX 10 #define ENC_HEVC_RC_FRAME_RATE_MAX ((1 << 16) - 1) #define ENC_HEVC_QP_INDEX_MIN -12 diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index 3a848ca32..161a5c0fb 100644 --- a/drivers/media/platform/st/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c @@ -569,14 +569,11 @@ static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) struct vb2_buffer *vb2_buf; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type); - - if (buf->index >= vq->num_buffers) { - dev_dbg(dev, "%s buffer index %d out of range (%d)\n", - ctx->name, buf->index, vq->num_buffers); + vb2_buf = vb2_get_buffer(vq, buf->index); + if (!vb2_buf) { + dev_dbg(dev, "%s buffer index %d not found\n", ctx->name, buf->index); return -EINVAL; } - - vb2_buf = vb2_get_buffer(vq, buf->index); stream = to_hva_stream(to_vb2_v4l2_buffer(vb2_buf)); stream->bytesused = buf->bytesused; } @@ -1145,7 +1142,7 @@ static int hva_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->buf_struct_size = sizeof(struct hva_frame); - src_vq->min_buffers_needed = MIN_FRAMES; + src_vq->min_queued_buffers = MIN_FRAMES; src_vq->dev = ctx->hva_dev->dev; ret = queue_init(ctx, src_vq); @@ -1154,7 +1151,7 @@ static int hva_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->buf_struct_size = sizeof(struct hva_stream); - dst_vq->min_buffers_needed = MIN_STREAMS; + dst_vq->min_queued_buffers = MIN_STREAMS; dst_vq->dev = ctx->hva_dev->dev; return queue_init(ctx, dst_vq); diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig index b22dd4753..9df9a2a17 100644 --- a/drivers/media/platform/st/stm32/Kconfig +++ b/drivers/media/platform/st/stm32/Kconfig @@ -16,6 +16,22 @@ config VIDEO_STM32_DCMI To compile this driver as a module, choose M here: the module will be called stm32-dcmi. +config VIDEO_STM32_DCMIPP + tristate "STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STM32 || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This module makes the STM32 Digital Camera Memory Interface + Pixel Processor (DCMIPP) available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-dcmipp. + # Mem2mem drivers config VIDEO_STM32_DMA2D tristate "STM32 Chrom-Art Accelerator (DMA2D)" diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 896ef98a7..7ed8297b9 100644 --- a/drivers/media/platform/st/stm32/Makefile +++ b/drivers/media/platform/st/stm32/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o +obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp/ stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 8cb4fdcae..c4610e305 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -1890,7 +1889,6 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) static int dcmi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match = NULL; struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; struct stm32_dcmi *dcmi; struct vb2_queue *q; @@ -1899,12 +1897,6 @@ static int dcmi_probe(struct platform_device *pdev) struct clk *mclk; int ret = 0; - match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Could not find a match in devicetree\n"); - return -ENODEV; - } - dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL); if (!dcmi) return -ENOMEM; @@ -2039,7 +2031,7 @@ static int dcmi_probe(struct platform_device *pdev) q->ops = &dcmi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->allow_cache_hints = 1; q->dev = &pdev->dev; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile new file mode 100644 index 000000000..8920d9388 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o + +obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c new file mode 100644 index 000000000..9f768f011 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include +#include +#include +#include +#include + +#include "dcmipp-common.h" + +#define DCMIPP_PRSR 0x1f8 +#define DCMIPP_CMIER 0x3f0 +#define DCMIPP_CMIER_P0FRAMEIE BIT(9) +#define DCMIPP_CMIER_P0VSYNCIE BIT(10) +#define DCMIPP_CMIER_P0OVRIE BIT(15) +#define DCMIPP_CMIER_P0ALL (DCMIPP_CMIER_P0VSYNCIE |\ + DCMIPP_CMIER_P0FRAMEIE |\ + DCMIPP_CMIER_P0OVRIE) +#define DCMIPP_CMSR1 0x3f4 +#define DCMIPP_CMSR2 0x3f8 +#define DCMIPP_CMSR2_P0FRAMEF BIT(9) +#define DCMIPP_CMSR2_P0VSYNCF BIT(10) +#define DCMIPP_CMSR2_P0OVRF BIT(15) +#define DCMIPP_CMFCR 0x3fc +#define DCMIPP_P0FSCR 0x404 +#define DCMIPP_P0FSCR_PIPEN BIT(31) +#define DCMIPP_P0FCTCR 0x500 +#define DCMIPP_P0FCTCR_CPTREQ BIT(3) +#define DCMIPP_P0DCCNTR 0x5b0 +#define DCMIPP_P0DCLMTR 0x5b4 +#define DCMIPP_P0DCLMTR_ENABLE BIT(31) +#define DCMIPP_P0DCLMTR_LIMIT_MASK GENMASK(23, 0) +#define DCMIPP_P0PPM0AR1 0x5c4 +#define DCMIPP_P0SR 0x5f8 +#define DCMIPP_P0SR_CPTACT BIT(23) + +struct dcmipp_bytecap_pix_map { + unsigned int code; + u32 pixelformat; +}; + +#define PIXMAP_MBUS_PFMT(mbus, fmt) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .pixelformat = V4L2_PIX_FMT_##fmt \ + } + +static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { + PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), + PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), + PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), + PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY), + PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), + PIXMAP_MBUS_PFMT(Y8_1X8, GREY), + PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8), + PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8), + PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8), + PIXMAP_MBUS_PFMT(SRGGB8_1X8, SRGGB8), + PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG), +}; + +static const struct dcmipp_bytecap_pix_map * +dcmipp_bytecap_pix_map_by_pixelformat(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + if (dcmipp_bytecap_pix_map_list[i].pixelformat == pixelformat) + return &dcmipp_bytecap_pix_map_list[i]; + } + + return NULL; +} + +struct dcmipp_buf { + struct vb2_v4l2_buffer vb; + bool prepared; + dma_addr_t addr; + size_t size; + struct list_head list; +}; + +enum dcmipp_state { + DCMIPP_STOPPED = 0, + DCMIPP_WAIT_FOR_BUFFER, + DCMIPP_RUNNING, +}; + +struct dcmipp_bytecap_device { + struct dcmipp_ent_device ved; + struct video_device vdev; + struct device *dev; + struct v4l2_pix_format format; + struct vb2_queue queue; + struct list_head buffers; + /* + * Protects concurrent calls of buf queue / irq handler + * and buffer handling related variables / lists + */ + spinlock_t irqlock; + /* mutex used as vdev and queue lock */ + struct mutex lock; + u32 sequence; + struct media_pipeline pipe; + struct v4l2_subdev *s_subdev; + + enum dcmipp_state state; + + /* + * DCMIPP driver is handling 2 buffers + * active: buffer into which DCMIPP is currently writing into + * next: buffer given to the DCMIPP and which will become + * automatically active on next VSYNC + */ + struct dcmipp_buf *active, *next; + + void __iomem *regs; + + u32 cmier; + u32 cmsr2; + + struct { + u32 errors; + u32 limit; + u32 overrun; + u32 buffers; + u32 vsync; + u32 frame; + u32 it; + u32 underrun; + u32 nactive; + } count; +}; + +static const struct v4l2_pix_format fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .pixelformat = V4L2_PIX_FMT_RGB565, + .field = V4L2_FIELD_NONE, + .bytesperline = DCMIPP_FMT_WIDTH_DEFAULT * 2, + .sizeimage = DCMIPP_FMT_WIDTH_DEFAULT * DCMIPP_FMT_HEIGHT_DEFAULT * 2, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_bytecap_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, DCMIPP_PDEV_NAME, sizeof(cap->driver)); + strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); + + return 0; +} + +static int dcmipp_bytecap_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + + f->fmt.pix = vcap->format; + + return 0; +} + +static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct dcmipp_bytecap_pix_map *vpix; + u32 in_w, in_h; + + /* Don't accept a pixelformat that is not on the table */ + vpix = dcmipp_bytecap_pix_map_by_pixelformat(format->pixelformat); + if (!vpix) + format->pixelformat = fmt_default.pixelformat; + + /* Adjust width & height */ + in_w = format->width; + in_h = format->height; + v4l_bound_align_image(&format->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH, 0, &format->height, + DCMIPP_FRAME_MIN_HEIGHT, DCMIPP_FRAME_MAX_HEIGHT, + 0, 0); + if (format->width != in_w || format->height != in_h) + dev_dbg(vcap->dev, "resolution updated: %dx%d -> %dx%d\n", + in_w, in_h, format->width, format->height); + + if (format->pixelformat == V4L2_PIX_FMT_JPEG) { + format->bytesperline = format->width; + format->sizeimage = format->bytesperline * format->height; + } else { + v4l2_fill_pixfmt(format, format->pixelformat, + format->width, format->height); + } + + if (format->field == V4L2_FIELD_ANY) + format->field = fmt_default.field; + + dcmipp_colorimetry_clamp(format); + + return 0; +} + +static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dcmipp_bytecap_device *vcap = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&vcap->queue)) + return -EBUSY; + + ret = dcmipp_bytecap_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(vcap->dev, "%s: format update: old:%ux%u (0x%p4cc, %u, %u, %u, %u) new:%ux%d (0x%p4cc, %u, %u, %u, %u)\n", + vcap->vdev.name, + /* old */ + vcap->format.width, vcap->format.height, + &vcap->format.pixelformat, vcap->format.colorspace, + vcap->format.quantization, vcap->format.xfer_func, + vcap->format.ycbcr_enc, + /* new */ + f->fmt.pix.width, f->fmt.pix.height, + &f->fmt.pix.pixelformat, f->fmt.pix.colorspace, + f->fmt.pix.quantization, f->fmt.pix.xfer_func, + f->fmt.pix.ycbcr_enc); + + vcap->format = f->fmt.pix; + + return 0; +} + +static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct dcmipp_bytecap_pix_map *vpix; + unsigned int index = f->index; + unsigned int i; + + if (f->mbus_code) { + /* + * If a media bus code is specified, only enumerate formats + * compatible with it. + */ + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + vpix = &dcmipp_bytecap_pix_map_list[i]; + if (vpix->code != f->mbus_code) + continue; + + if (index == 0) + break; + + index--; + } + + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + } else { + /* Otherwise, enumerate all formats. */ + if (f->index >= ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + + vpix = &dcmipp_bytecap_pix_map_list[f->index]; + } + + f->pixelformat = vpix->pixelformat; + + return 0; +} + +static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct dcmipp_bytecap_pix_map *vpix; + + if (fsize->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_bytecap_pix_map_by_pixelformat(fsize->pixel_format); + if (!vpix) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = DCMIPP_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = DCMIPP_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = DCMIPP_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = DCMIPP_FRAME_MAX_HEIGHT; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_file_operations dcmipp_bytecap_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = { + .vidioc_querycap = dcmipp_bytecap_querycap, + + .vidioc_g_fmt_vid_cap = dcmipp_bytecap_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = dcmipp_bytecap_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = dcmipp_bytecap_try_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = dcmipp_bytecap_enum_fmt_vid_cap, + .vidioc_enum_framesizes = dcmipp_bytecap_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static int dcmipp_pipeline_s_stream(struct dcmipp_bytecap_device *vcap, + int state) +{ + struct media_pad *pad; + int ret; + + /* + * Get source subdev - since link is IMMUTABLE, pointer is cached + * within the dcmipp_bytecap_device structure + */ + if (!vcap->s_subdev) { + pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); + } + + ret = v4l2_subdev_call(vcap->s_subdev, video, s_stream, state); + if (ret < 0) { + dev_err(vcap->dev, "failed to %s streaming (%d)\n", + state ? "start" : "stop", ret); + return ret; + } + + return 0; +} + +static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap, + struct dcmipp_buf *buf) +{ + /* Set buffer address */ + reg_write(vcap, DCMIPP_P0PPM0AR1, buf->addr); + + /* Set buffer size */ + reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE | + ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK)); + + /* Capture request */ + reg_set(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); +} + +static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap, + enum vb2_buffer_state state) +{ + struct dcmipp_buf *buf, *node; + + list_for_each_entry_safe(buf, node, &vcap->buffers, list) { + list_del_init(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + struct media_entity *entity = &vcap->vdev.entity; + struct dcmipp_buf *buf; + int ret; + + vcap->sequence = 0; + memset(&vcap->count, 0, sizeof(vcap->count)); + + ret = pm_runtime_resume_and_get(vcap->dev); + if (ret < 0) { + dev_err(vcap->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", + __func__, ret); + goto err_buffer_done; + } + + ret = media_pipeline_start(entity->pads, &vcap->pipe); + if (ret) { + dev_dbg(vcap->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n", + __func__, ret); + goto err_pm_put; + } + + ret = dcmipp_pipeline_s_stream(vcap, 1); + if (ret) + goto err_media_pipeline_stop; + + spin_lock_irq(&vcap->irqlock); + + /* Enable pipe at the end of programming */ + reg_set(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN); + + /* + * vb2 framework guarantee that we have at least 'min_queued_buffers' + * buffers in the list at this moment + */ + vcap->next = list_first_entry(&vcap->buffers, typeof(*buf), list); + dev_dbg(vcap->dev, "Start with next [%d] %p phy=%pad\n", + vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr); + + dcmipp_start_capture(vcap, vcap->next); + + /* Enable interruptions */ + vcap->cmier |= DCMIPP_CMIER_P0ALL; + reg_set(vcap, DCMIPP_CMIER, vcap->cmier); + + vcap->state = DCMIPP_RUNNING; + + spin_unlock_irq(&vcap->irqlock); + + return 0; + +err_media_pipeline_stop: + media_pipeline_stop(entity->pads); +err_pm_put: + pm_runtime_put(vcap->dev); +err_buffer_done: + spin_lock_irq(&vcap->irqlock); + /* + * Return all buffers to vb2 in QUEUED state. + * This will give ownership back to userspace + */ + dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED); + vcap->active = NULL; + spin_unlock_irq(&vcap->irqlock); + + return ret; +} + +static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap) +{ + struct device *dev = vcap->dev; + + dev_dbg(dev, "[DCMIPP_PRSR] =%#10.8x\n", reg_read(vcap, DCMIPP_PRSR)); + dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_P0SR)); + dev_dbg(dev, "[DCMIPP_P0DCCNTR]=%#10.8x\n", + reg_read(vcap, DCMIPP_P0DCCNTR)); + dev_dbg(dev, "[DCMIPP_CMSR1] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR1)); + dev_dbg(dev, "[DCMIPP_CMSR2] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR2)); +} + +/* + * Stop the stream engine. Any remaining buffers in the stream queue are + * dequeued and passed on to the vb2 framework marked as STATE_ERROR. + */ +static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + int ret; + u32 status; + + dcmipp_pipeline_s_stream(vcap, 0); + + /* Stop the media pipeline */ + media_pipeline_stop(vcap->vdev.entity.pads); + + /* Disable interruptions */ + reg_clear(vcap, DCMIPP_CMIER, vcap->cmier); + + /* Stop capture */ + reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + + /* Wait until CPTACT become 0 */ + ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_P0SR, status, + !(status & DCMIPP_P0SR_CPTACT), + 20 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + dev_warn(vcap->dev, "Timeout when stopping\n"); + + /* Disable pipe */ + reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN); + + spin_lock_irq(&vcap->irqlock); + + /* Return all queued buffers to vb2 in ERROR state */ + dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&vcap->buffers); + + vcap->active = NULL; + vcap->state = DCMIPP_STOPPED; + + spin_unlock_irq(&vcap->irqlock); + + dcmipp_dump_status(vcap); + + pm_runtime_put(vcap->dev); + + if (vcap->count.errors) + dev_warn(vcap->dev, "Some errors found while streaming: errors=%d (overrun=%d, limit=%d, nactive=%d), underrun=%d, buffers=%d\n", + vcap->count.errors, vcap->count.overrun, + vcap->count.limit, vcap->count.nactive, + vcap->count.underrun, vcap->count.buffers); +} + +static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + unsigned long size; + + size = vcap->format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(vcap->dev, "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + if (!buf->prepared) { + /* Get memory addresses */ + buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); + buf->prepared = true; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + + dev_dbg(vcap->dev, "Setup [%d] phy=%pad size=%zu\n", + vb->index, &buf->addr, buf->size); + } + + return 0; +} + +static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf) +{ + struct dcmipp_bytecap_device *vcap = + vb2_get_drv_priv(vb2_buf->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2_buf); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + + dev_dbg(vcap->dev, "Queue [%d] %p phy=%pad\n", buf->vb.vb2_buf.index, + buf, &buf->addr); + + spin_lock_irq(&vcap->irqlock); + list_add_tail(&buf->list, &vcap->buffers); + + if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) { + vcap->next = buf; + dev_dbg(vcap->dev, "Restart with next [%d] %p phy=%pad\n", + buf->vb.vb2_buf.index, buf, &buf->addr); + + dcmipp_start_capture(vcap, buf); + + vcap->state = DCMIPP_RUNNING; + } + + spin_unlock_irq(&vcap->irqlock); +} + +static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); + unsigned int size; + + size = vcap->format.sizeimage; + + /* Make sure the image size is large enough */ + if (*nplanes) + return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = vcap->format.sizeimage; + + dev_dbg(vcap->dev, "Setup queue, count=%d, size=%d\n", + *nbuffers, size); + + return 0; +} + +static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb); + + INIT_LIST_HEAD(&buf->list); + + return 0; +} + +static const struct vb2_ops dcmipp_bytecap_qops = { + .start_streaming = dcmipp_bytecap_start_streaming, + .stop_streaming = dcmipp_bytecap_stop_streaming, + .buf_init = dcmipp_bytecap_buf_init, + .buf_prepare = dcmipp_bytecap_buf_prepare, + .buf_queue = dcmipp_bytecap_buf_queue, + .queue_setup = dcmipp_bytecap_queue_setup, + /* + * Since q->lock is set we can use the standard + * vb2_ops_wait_prepare/finish helper functions. + */ + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static void dcmipp_bytecap_release(struct video_device *vdev) +{ + struct dcmipp_bytecap_device *vcap = + container_of(vdev, struct dcmipp_bytecap_device, vdev); + + dcmipp_pads_cleanup(vcap->ved.pads); + mutex_destroy(&vcap->lock); + + kfree(vcap); +} + +void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_bytecap_device *vcap = + container_of(ved, struct dcmipp_bytecap_device, ved); + + media_entity_cleanup(ved->ent); + vb2_video_unregister_device(&vcap->vdev); +} + +static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap, + struct dcmipp_buf *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + list_del_init(&buf->list); + + vbuf = &buf->vb; + + vbuf->sequence = vcap->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev_dbg(vcap->dev, "Done [%d] %p phy=%pad\n", buf->vb.vb2_buf.index, + buf, &buf->addr); + vcap->count.buffers++; +} + +/* irqlock must be held */ +static void +dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap) +{ + if (!vcap->next && list_is_singular(&vcap->buffers)) { + /* + * If there is no available buffer (none or a single one in the + * list while two are expected), stop the capture (effective + * for next frame). On-going frame capture will continue until + * FRAME END but no further capture will be done. + */ + reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + + dev_dbg(vcap->dev, "Capture restart is deferred to next buffer queueing\n"); + vcap->next = NULL; + vcap->state = DCMIPP_WAIT_FOR_BUFFER; + return; + } + + /* If we don't have buffer yet, pick the one after active */ + if (!vcap->next) + vcap->next = list_next_entry(vcap->active, list); + + /* + * Set buffer address + * This register is shadowed and will be taken into + * account on next VSYNC (start of next frame) + */ + reg_write(vcap, DCMIPP_P0PPM0AR1, vcap->next->addr); + dev_dbg(vcap->dev, "Write [%d] %p phy=%pad\n", + vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr); +} + +/* irqlock must be held */ +static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap, + size_t bytesused) +{ + int err = 0; + struct dcmipp_buf *buf = vcap->active; + + if (!buf) { + vcap->count.nactive++; + vcap->count.errors++; + return; + } + + if (bytesused > buf->size) { + dev_dbg(vcap->dev, "frame larger than expected (%zu > %zu)\n", + bytesused, buf->size); + /* Clip to buffer size and return buffer to V4L2 in error */ + bytesused = buf->size; + vcap->count.limit++; + vcap->count.errors++; + err = -EOVERFLOW; + } + + dcmipp_buffer_done(vcap, buf, bytesused, err); + vcap->active = NULL; +} + +static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg) +{ + struct dcmipp_bytecap_device *vcap = + container_of(arg, struct dcmipp_bytecap_device, ved); + size_t bytesused = 0; + u32 cmsr2; + + spin_lock_irq(&vcap->irqlock); + + cmsr2 = vcap->cmsr2 & vcap->cmier; + + /* + * If we have an overrun, a frame-end will probably not be generated, + * in that case the active buffer will be recycled as next buffer by + * the VSYNC handler + */ + if (cmsr2 & DCMIPP_CMSR2_P0OVRF) { + vcap->count.errors++; + vcap->count.overrun++; + } + + if (cmsr2 & DCMIPP_CMSR2_P0FRAMEF) { + vcap->count.frame++; + + /* Read captured buffer size */ + bytesused = reg_read(vcap, DCMIPP_P0DCCNTR); + dcmipp_bytecap_process_frame(vcap, bytesused); + } + + if (cmsr2 & DCMIPP_CMSR2_P0VSYNCF) { + vcap->count.vsync++; + if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) { + vcap->count.underrun++; + goto out; + } + + /* + * On VSYNC, the previously set next buffer is going to become + * active thanks to the shadowing mechanism of the DCMIPP. In + * most of the cases, since a FRAMEEND has already come, + * pointer next is NULL since active is reset during the + * FRAMEEND handling. However, in case of framerate adjustment, + * there are more VSYNC than FRAMEEND. Thus we recycle the + * active (but not used) buffer and put it back into next. + */ + swap(vcap->active, vcap->next); + dcmipp_bytecap_set_next_frame_or_stop(vcap); + } + +out: + spin_unlock_irq(&vcap->irqlock); + return IRQ_HANDLED; +} + +static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg) +{ + struct dcmipp_bytecap_device *vcap = + container_of(arg, struct dcmipp_bytecap_device, ved); + + /* Store interrupt status register */ + vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & vcap->cmier; + vcap->count.it++; + + /* Clear interrupt */ + reg_write(vcap, DCMIPP_CMFCR, vcap->cmsr2); + + return IRQ_WAKE_THREAD; +} + +static int dcmipp_bytecap_link_validate(struct media_link *link) +{ + struct media_entity *entity = link->sink->entity; + struct video_device *vd = media_entity_to_video_device(entity); + struct dcmipp_bytecap_device *vcap = container_of(vd, + struct dcmipp_bytecap_device, vdev); + struct v4l2_subdev *source_sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct v4l2_subdev_format source_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = link->source->index, + }; + const struct dcmipp_bytecap_pix_map *vpix; + int ret; + + ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt); + if (ret < 0) + return 0; + + if (source_fmt.format.width != vcap->format.width || + source_fmt.format.height != vcap->format.height) { + dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n", + vcap->format.width, vcap->format.height, + source_fmt.format.width, source_fmt.format.height); + return -EINVAL; + } + + vpix = dcmipp_bytecap_pix_map_by_pixelformat(vcap->format.pixelformat); + if (source_fmt.format.code != vpix->code) { + dev_err(vcap->dev, "Wrong mbus_code 0x%x, (0x%x expected)\n", + vpix->code, source_fmt.format.code); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations dcmipp_bytecap_entity_ops = { + .link_validate = dcmipp_bytecap_link_validate, +}; + +struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_bytecap_device *vcap; + struct video_device *vdev; + struct vb2_queue *q; + const unsigned long pad_flag = MEDIA_PAD_FL_SINK; + int ret = 0; + + /* Allocate the dcmipp_bytecap_device struct */ + vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); + if (!vcap) + return ERR_PTR(-ENOMEM); + + /* Allocate the pads */ + vcap->ved.pads = dcmipp_pads_init(1, &pad_flag); + if (IS_ERR(vcap->ved.pads)) { + ret = PTR_ERR(vcap->ved.pads); + goto err_free_vcap; + } + + /* Initialize the media entity */ + vcap->vdev.entity.name = entity_name; + vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + vcap->vdev.entity.ops = &dcmipp_bytecap_entity_ops; + ret = media_entity_pads_init(&vcap->vdev.entity, 1, vcap->ved.pads); + if (ret) + goto err_clean_pads; + + /* Initialize the lock */ + mutex_init(&vcap->lock); + + /* Initialize the vb2 queue */ + q = &vcap->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->lock = &vcap->lock; + q->drv_priv = vcap; + q->buf_struct_size = sizeof(struct dcmipp_buf); + q->ops = &dcmipp_bytecap_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_queued_buffers = 1; + q->dev = dev; + + /* DCMIPP requires 16 bytes aligned buffers */ + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f); + if (ret) { + dev_err(dev, "Failed to set DMA mask\n"); + goto err_mutex_destroy; + } + + ret = vb2_queue_init(q); + if (ret) { + dev_err(dev, "%s: vb2 queue init failed (err=%d)\n", + entity_name, ret); + goto err_clean_m_ent; + } + + /* Initialize buffer list and its lock */ + INIT_LIST_HEAD(&vcap->buffers); + spin_lock_init(&vcap->irqlock); + + /* Set default frame format */ + vcap->format = fmt_default; + + /* Fill the dcmipp_ent_device struct */ + vcap->ved.ent = &vcap->vdev.entity; + vcap->ved.handler = dcmipp_bytecap_irq_callback; + vcap->ved.thread_fn = dcmipp_bytecap_irq_thread; + vcap->dev = dev; + vcap->regs = regs; + + /* Initialize the video_device struct */ + vdev = &vcap->vdev; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->release = dcmipp_bytecap_release; + vdev->fops = &dcmipp_bytecap_fops; + vdev->ioctl_ops = &dcmipp_bytecap_ioctl_ops; + vdev->lock = &vcap->lock; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + strscpy(vdev->name, entity_name, sizeof(vdev->name)); + video_set_drvdata(vdev, &vcap->ved); + + /* Register the video_device with the v4l2 and the media framework */ + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(dev, "%s: video register failed (err=%d)\n", + vcap->vdev.name, ret); + goto err_clean_m_ent; + } + + return &vcap->ved; + +err_clean_m_ent: + media_entity_cleanup(&vcap->vdev.entity); +err_mutex_destroy: + mutex_destroy(&vcap->lock); +err_clean_pads: + dcmipp_pads_cleanup(vcap->ved.pads); +err_free_vcap: + kfree(vcap); + + return ERR_PTR(ret); +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c new file mode 100644 index 000000000..5a361ad6b --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include +#include +#include + +#include "dcmipp-common.h" + +#define DCMIPP_P0FCTCR 0x500 +#define DCMIPP_P0FCTCR_FRATE_MASK GENMASK(1, 0) +#define DCMIPP_P0SCSTR 0x504 +#define DCMIPP_P0SCSTR_HSTART_SHIFT 0 +#define DCMIPP_P0SCSTR_VSTART_SHIFT 16 +#define DCMIPP_P0SCSZR 0x508 +#define DCMIPP_P0SCSZR_ENABLE BIT(31) +#define DCMIPP_P0SCSZR_HSIZE_SHIFT 0 +#define DCMIPP_P0SCSZR_VSIZE_SHIFT 16 +#define DCMIPP_P0PPCR 0x5c0 +#define DCMIPP_P0PPCR_BSM_1_2 0x1 +#define DCMIPP_P0PPCR_BSM_1_4 0x2 +#define DCMIPP_P0PPCR_BSM_2_4 0x3 +#define DCMIPP_P0PPCR_BSM_MASK GENMASK(8, 7) +#define DCMIPP_P0PPCR_BSM_SHIFT 0x7 +#define DCMIPP_P0PPCR_LSM BIT(10) +#define DCMIPP_P0PPCR_OELS BIT(11) + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_byteproc_pix_map { + unsigned int code; + unsigned int bpp; +}; + +#define PIXMAP_MBUS_BPP(mbus, byteperpixel) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .bpp = byteperpixel, \ + } +static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { + PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), + PIXMAP_MBUS_BPP(YUYV8_2X8, 2), + PIXMAP_MBUS_BPP(YVYU8_2X8, 2), + PIXMAP_MBUS_BPP(UYVY8_2X8, 2), + PIXMAP_MBUS_BPP(VYUY8_2X8, 2), + PIXMAP_MBUS_BPP(Y8_1X8, 1), + PIXMAP_MBUS_BPP(SBGGR8_1X8, 1), + PIXMAP_MBUS_BPP(SGBRG8_1X8, 1), + PIXMAP_MBUS_BPP(SGRBG8_1X8, 1), + PIXMAP_MBUS_BPP(SRGGB8_1X8, 1), + PIXMAP_MBUS_BPP(JPEG_1X8, 1), +}; + +static const struct dcmipp_byteproc_pix_map * +dcmipp_byteproc_pix_map_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_byteproc_pix_map_list); i++) { + if (dcmipp_byteproc_pix_map_list[i].code == code) + return &dcmipp_byteproc_pix_map_list[i]; + } + + return NULL; +} + +struct dcmipp_byteproc_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; + bool streaming; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static const struct v4l2_rect crop_min = { + .width = DCMIPP_FRAME_MIN_WIDTH, + .height = DCMIPP_FRAME_MIN_HEIGHT, + .top = 0, + .left = 0, +}; + +static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r, + struct v4l2_rect *compose) +{ + /* Disallow rectangles smaller than the minimal one. */ + v4l2_rect_set_min_size(r, &crop_min); + v4l2_rect_map_inside(r, compose); +} + +static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r, + const struct v4l2_mbus_framefmt *fmt) +{ + r->top = 0; + r->left = 0; + + /* Compose is not possible for JPEG or Bayer formats */ + if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 || + fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || + fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 || + fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 || + fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) { + r->width = fmt->width; + r->height = fmt->height; + return; + } + + /* Adjust height - we can only perform 1/2 decimation */ + if (r->height <= (fmt->height / 2)) + r->height = fmt->height / 2; + else + r->height = fmt->height; + + /* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */ + if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4)) + r->width = fmt->width / 4; + else if (r->width <= (fmt->width / 2)) + r->width = fmt->width / 2; + else + r->width = fmt->width; +} + +static void dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt *fmt) +{ + const struct dcmipp_byteproc_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_byteproc_pix_map_by_code(fmt->code); + if (!vpix) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_byteproc_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *r; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + + if (IS_SINK(i)) + r = v4l2_subdev_state_get_compose(sd_state, i); + else + r = v4l2_subdev_state_get_crop(sd_state, i); + + r->top = 0; + r->left = 0; + r->width = DCMIPP_FMT_WIDTH_DEFAULT; + r->height = DCMIPP_FMT_HEIGHT_DEFAULT; + } + + return 0; +} + +static int +dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_byteproc_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + + if (IS_SINK(code->pad)) { + if (code->index >= ARRAY_SIZE(dcmipp_byteproc_pix_map_list)) + return -EINVAL; + vpix = &dcmipp_byteproc_pix_map_list[code->index]; + code->code = vpix->code; + } else { + /* byteproc doesn't support transformation on format */ + if (code->index > 0) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + code->code = sink_fmt->code; + } + + return 0; +} + +static int +dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_rect *compose; + + if (fse->index) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + + if (IS_SINK(fse->pad)) { + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + } else { + compose = v4l2_subdev_state_get_compose(sd_state, 0); + fse->max_width = compose->width; + fse->max_height = compose->height; + } + + return 0; +} + +static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop, *compose; + + if (byteproc->streaming) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + if (IS_SRC(fmt->pad)) { + fmt->format = *v4l2_subdev_state_get_format(sd_state, 0); + fmt->format.width = crop->width; + fmt->format.height = crop->height; + } else { + dcmipp_byteproc_adjust_fmt(&fmt->format); + crop->top = 0; + crop->left = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + *compose = *crop; + /* Set the same format on SOURCE pad as well */ + *v4l2_subdev_state_get_format(sd_state, 1) = fmt->format; + } + *mf = fmt->format; + + return 0; +} + +static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *s) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *crop, *compose; + + /* + * In the HW, the decimation block is located prior to the crop hence: + * Compose is done on the sink pad + * Crop is done on the src pad + */ + if (IS_SINK(s->pad) && + (s->target == V4L2_SEL_TGT_CROP || + s->target == V4L2_SEL_TGT_CROP_BOUNDS || + s->target == V4L2_SEL_TGT_CROP_DEFAULT)) + return -EINVAL; + + if (IS_SRC(s->pad) && + (s->target == V4L2_SEL_TGT_COMPOSE || + s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS || + s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT)) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + s->r = *crop; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r = *compose; + break; + case V4L2_SEL_TGT_COMPOSE: + s->r = *compose; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.top = 0; + s->r.left = 0; + s->r.width = sink_fmt->width; + s->r.height = sink_fmt->height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *s) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop, *compose; + + /* + * In the HW, the decimation block is located prior to the crop hence: + * Compose is done on the sink pad + * Crop is done on the src pad + */ + if ((s->target == V4L2_SEL_TGT_CROP || + s->target == V4L2_SEL_TGT_CROP_BOUNDS || + s->target == V4L2_SEL_TGT_CROP_DEFAULT) && IS_SINK(s->pad)) + return -EINVAL; + + if ((s->target == V4L2_SEL_TGT_COMPOSE || + s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS || + s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT) && IS_SRC(s->pad)) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, 1); + compose = v4l2_subdev_state_get_compose(sd_state, 0); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + dcmipp_byteproc_adjust_crop(&s->r, compose); + + *crop = s->r; + mf = v4l2_subdev_state_get_format(sd_state, 1); + mf->width = s->r.width; + mf->height = s->r.height; + + dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n", + crop->width, crop->height, crop->left, crop->top); + break; + case V4L2_SEL_TGT_COMPOSE: + mf = v4l2_subdev_state_get_format(sd_state, 0); + dcmipp_byteproc_adjust_compose(&s->r, mf); + *compose = s->r; + *crop = s->r; + + mf = v4l2_subdev_state_get_format(sd_state, 1); + mf->width = s->r.width; + mf->height = s->r.height; + + dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n", + compose->width, compose->height, + compose->left, compose->top); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { + .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, + .enum_frame_size = dcmipp_byteproc_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_byteproc_set_fmt, + .get_selection = dcmipp_byteproc_get_selection, + .set_selection = dcmipp_byteproc_set_selection, +}; + +static int dcmipp_byteproc_configure_scale_crop + (struct dcmipp_byteproc_device *byteproc) +{ + const struct dcmipp_byteproc_pix_map *vpix; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *sink_fmt; + u32 hprediv, vprediv; + struct v4l2_rect *compose, *crop; + u32 val = 0; + + state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + crop = v4l2_subdev_state_get_crop(state, 1); + v4l2_subdev_unlock_state(state); + + /* find output format bpp */ + vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code); + if (!vpix) + return -EINVAL; + + /* clear decimation/crop */ + reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK); + reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM); + reg_write(byteproc, DCMIPP_P0SCSTR, 0); + reg_write(byteproc, DCMIPP_P0SCSZR, 0); + + /* Ignore decimation/crop with JPEG */ + if (vpix->code == MEDIA_BUS_FMT_JPEG_1X8) + return 0; + + /* decimation */ + hprediv = sink_fmt->width / compose->width; + if (hprediv == 4) + val |= DCMIPP_P0PPCR_BSM_1_4 << DCMIPP_P0PPCR_BSM_SHIFT; + else if ((vpix->code == MEDIA_BUS_FMT_Y8_1X8) && (hprediv == 2)) + val |= DCMIPP_P0PPCR_BSM_1_2 << DCMIPP_P0PPCR_BSM_SHIFT; + else if (hprediv == 2) + val |= DCMIPP_P0PPCR_BSM_2_4 << DCMIPP_P0PPCR_BSM_SHIFT; + + vprediv = sink_fmt->height / compose->height; + if (vprediv == 2) + val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS; + + /* decimate using bytes and lines skipping */ + if (val) { + reg_set(byteproc, DCMIPP_P0PPCR, val); + + dev_dbg(byteproc->dev, "decimate to %dx%d [prediv=%dx%d]\n", + compose->width, compose->height, + hprediv, vprediv); + } + + dev_dbg(byteproc->dev, "crop to %dx%d\n", crop->width, crop->height); + + /* expressed in 32-bits words on X axis, lines on Y axis */ + reg_write(byteproc, DCMIPP_P0SCSTR, + (((crop->left * vpix->bpp) / 4) << + DCMIPP_P0SCSTR_HSTART_SHIFT) | + (crop->top << DCMIPP_P0SCSTR_VSTART_SHIFT)); + reg_write(byteproc, DCMIPP_P0SCSZR, + DCMIPP_P0SCSZR_ENABLE | + (((crop->width * vpix->bpp) / 4) << + DCMIPP_P0SCSZR_HSIZE_SHIFT) | + (crop->height << DCMIPP_P0SCSZR_VSIZE_SHIFT)); + + return 0; +} + +static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_subdev *s_subdev; + struct media_pad *pad; + int ret = 0; + + /* Get source subdev */ + pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(pad->entity); + + if (enable) { + ret = dcmipp_byteproc_configure_scale_crop(byteproc); + if (ret) + return ret; + + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", + ret); + return ret; + } + } else { + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to stop source subdev streaming (%d)\n", + ret); + return ret; + } + } + + byteproc->streaming = enable; + + return 0; +} + +static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = { + .s_stream = dcmipp_byteproc_s_stream, +}; + +static const struct v4l2_subdev_ops dcmipp_byteproc_ops = { + .pad = &dcmipp_byteproc_pad_ops, + .video = &dcmipp_byteproc_video_ops, +}; + +static void dcmipp_byteproc_release(struct v4l2_subdev *sd) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + + kfree(byteproc); +} + +static const struct v4l2_subdev_internal_ops dcmipp_byteproc_int_ops = { + .init_state = dcmipp_byteproc_init_state, + .release = dcmipp_byteproc_release, +}; + +void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_byteproc_device *byteproc = + container_of(ved, struct dcmipp_byteproc_device, ved); + + dcmipp_ent_sd_unregister(ved, &byteproc->sd); +} + +struct dcmipp_ent_device * +dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs) +{ + struct dcmipp_byteproc_device *byteproc; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the byteproc struct */ + byteproc = kzalloc(sizeof(*byteproc), GFP_KERNEL); + if (!byteproc) + return ERR_PTR(-ENOMEM); + + byteproc->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd, + v4l2_dev, entity_name, + MEDIA_ENT_F_PROC_VIDEO_SCALER, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_byteproc_int_ops, + &dcmipp_byteproc_ops, + NULL, NULL); + if (ret) { + kfree(byteproc); + return ERR_PTR(ret); + } + + byteproc->dev = dev; + + return &byteproc->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c new file mode 100644 index 000000000..562933e08 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include + +#include "dcmipp-common.h" + +/* Helper function to allocate and initialize pads */ +struct media_pad *dcmipp_pads_init(u16 num_pads, const unsigned long *pads_flags) +{ + struct media_pad *pads; + unsigned int i; + + /* Allocate memory for the pads */ + pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); + if (!pads) + return ERR_PTR(-ENOMEM); + + /* Initialize the pads */ + for (i = 0; i < num_pads; i++) { + pads[i].index = i; + pads[i].flags = pads_flags[i]; + } + + return pads; +} + +static const struct media_entity_operations dcmipp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int dcmipp_ent_sd_register(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_internal_ops *sd_int_ops, + const struct v4l2_subdev_ops *sd_ops, + irq_handler_t handler, + irq_handler_t thread_fn) +{ + int ret; + + /* Allocate the pads. Should be released from the sd_int_op release */ + ved->pads = dcmipp_pads_init(num_pads, pads_flag); + if (IS_ERR(ved->pads)) + return PTR_ERR(ved->pads); + + /* Fill the dcmipp_ent_device struct */ + ved->ent = &sd->entity; + + /* Initialize the subdev */ + v4l2_subdev_init(sd, sd_ops); + sd->internal_ops = sd_int_ops; + sd->entity.function = function; + sd->entity.ops = &dcmipp_entity_ops; + sd->owner = THIS_MODULE; + strscpy(sd->name, name, sizeof(sd->name)); + v4l2_set_subdevdata(sd, ved); + + /* Expose this subdev to user space */ + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (sd->ctrl_handler) + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + + /* Initialize the media entity */ + ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); + if (ret) + goto err_clean_pads; + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) + goto err_clean_m_ent; + + /* Register the subdev with the v4l2 and the media framework */ + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev register failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + + ved->handler = handler; + ved->thread_fn = thread_fn; + + return 0; + +err_clean_m_ent: + media_entity_cleanup(&sd->entity); +err_clean_pads: + dcmipp_pads_cleanup(ved->pads); + return ret; +} + +void +dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, struct v4l2_subdev *sd) +{ + media_entity_cleanup(ved->ent); + v4l2_device_unregister_subdev(sd); +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h new file mode 100644 index 000000000..7a7cf43ba --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#ifndef _DCMIPP_COMMON_H_ +#define _DCMIPP_COMMON_H_ + +#include +#include +#include +#include +#include + +#define DCMIPP_PDEV_NAME "dcmipp" + +#define DCMIPP_FRAME_MAX_WIDTH 4096 +#define DCMIPP_FRAME_MAX_HEIGHT 2160 +#define DCMIPP_FRAME_MIN_WIDTH 16 +#define DCMIPP_FRAME_MIN_HEIGHT 16 + +#define DCMIPP_FMT_WIDTH_DEFAULT 640 +#define DCMIPP_FMT_HEIGHT_DEFAULT 480 + +#define DCMIPP_COLORSPACE_DEFAULT V4L2_COLORSPACE_REC709 +#define DCMIPP_YCBCR_ENC_DEFAULT V4L2_YCBCR_ENC_DEFAULT +#define DCMIPP_QUANTIZATION_DEFAULT V4L2_QUANTIZATION_DEFAULT +#define DCMIPP_XFER_FUNC_DEFAULT V4L2_XFER_FUNC_DEFAULT + +/** + * dcmipp_colorimetry_clamp() - Adjust colorimetry parameters + * + * @fmt: the pointer to struct v4l2_pix_format or + * struct v4l2_mbus_framefmt + * + * Entities must check if colorimetry given by the userspace is valid, if not + * then set them as DEFAULT + */ +#define dcmipp_colorimetry_clamp(fmt) \ +do { \ + if ((fmt)->colorspace == V4L2_COLORSPACE_DEFAULT || \ + (fmt)->colorspace > V4L2_COLORSPACE_DCI_P3) { \ + (fmt)->colorspace = DCMIPP_COLORSPACE_DEFAULT; \ + (fmt)->ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT; \ + (fmt)->quantization = DCMIPP_QUANTIZATION_DEFAULT; \ + (fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \ + } \ + if ((fmt)->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) \ + (fmt)->ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT; \ + if ((fmt)->quantization > V4L2_QUANTIZATION_LIM_RANGE) \ + (fmt)->quantization = DCMIPP_QUANTIZATION_DEFAULT; \ + if ((fmt)->xfer_func > V4L2_XFER_FUNC_SMPTE2084) \ + (fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \ +} while (0) + +/** + * struct dcmipp_ent_device - core struct that represents a node in the topology + * + * @ent: the pointer to struct media_entity for the node + * @pads: the list of pads of the node + * @bus: struct v4l2_mbus_config_parallel describing input bus + * @bus_type: type of input bus (parallel or BT656) + * @handler: irq handler dedicated to the subdev + * @handler_ret: value returned by the irq handler + * @thread_fn: threaded irq handler + * + * The DCMIPP provides a single IRQ line and a IRQ status registers for all + * subdevs, hence once the main irq handler (registered at probe time) is + * called, it will chain calls to the irq handler of each the subdevs of the + * pipelines, using the handler/handler_ret/thread_fn variables. + * + * Each node of the topology must create a dcmipp_ent_device struct. + * Depending on the node it will be of an instance of v4l2_subdev or + * video_device struct where both contains a struct media_entity. + * Those structures should embedded the dcmipp_ent_device struct through + * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the + * dcmipp_ent_device struct to be retrieved from the corresponding struct + * media_entity + */ +struct dcmipp_ent_device { + struct media_entity *ent; + struct media_pad *pads; + + /* Parallel input device */ + struct v4l2_mbus_config_parallel bus; + enum v4l2_mbus_type bus_type; + irq_handler_t handler; + irqreturn_t handler_ret; + irq_handler_t thread_fn; +}; + +/** + * dcmipp_pads_init - initialize pads + * + * @num_pads: number of pads to initialize + * @pads_flags: flags to use in each pad + * + * Helper functions to allocate/initialize pads + */ +struct media_pad *dcmipp_pads_init(u16 num_pads, + const unsigned long *pads_flags); + +/** + * dcmipp_pads_cleanup - free pads + * + * @pads: pointer to the pads + * + * Helper function to free the pads initialized with dcmipp_pads_init + */ +static inline void dcmipp_pads_cleanup(struct media_pad *pads) +{ + kfree(pads); +} + +/** + * dcmipp_ent_sd_register - initialize and register a subdev node + * + * @ved: the dcmipp_ent_device struct to be initialize + * @sd: the v4l2_subdev struct to be initialize and registered + * @v4l2_dev: the v4l2 device to register the v4l2_subdev + * @name: name of the sub-device. Please notice that the name must be + * unique. + * @function: media entity function defined by MEDIA_ENT_F_* macros + * @num_pads: number of pads to initialize + * @pads_flag: flags to use in each pad + * @sd_int_ops: pointer to &struct v4l2_subdev_internal_ops + * @sd_ops: pointer to &struct v4l2_subdev_ops. + * @handler: func pointer of the irq handler + * @thread_fn: func pointer of the threaded irq handler + * + * Helper function initialize and register the struct dcmipp_ent_device and + * struct v4l2_subdev which represents a subdev node in the topology + */ +int dcmipp_ent_sd_register(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_internal_ops *sd_int_ops, + const struct v4l2_subdev_ops *sd_ops, + irq_handler_t handler, + irq_handler_t thread_fn); + +/** + * dcmipp_ent_sd_unregister - cleanup and unregister a subdev node + * + * @ved: the dcmipp_ent_device struct to be cleaned up + * @sd: the v4l2_subdev struct to be unregistered + * + * Helper function cleanup and unregister the struct dcmipp_ent_device and + * struct v4l2_subdev which represents a subdev node in the topology + */ +void dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, + struct v4l2_subdev *sd); + +#define reg_write(device, reg, val) \ + (__reg_write((device)->dev, (device)->regs, (reg), (val))) +#define reg_read(device, reg) \ + (__reg_read((device)->dev, (device)->regs, (reg))) +#define reg_set(device, reg, mask) \ + (__reg_set((device)->dev, (device)->regs, (reg), (mask))) +#define reg_clear(device, reg, mask) \ + (__reg_clear((device)->dev, (device)->regs, (reg), (mask))) + +static inline u32 __reg_read(struct device *dev, void __iomem *base, u32 reg) +{ + u32 val = readl_relaxed(base + reg); + + dev_dbg(dev, "RD 0x%x %#10.8x\n", reg, val); + return val; +} + +static inline void __reg_write(struct device *dev, void __iomem *base, u32 reg, + u32 val) +{ + dev_dbg(dev, "WR 0x%x %#10.8x\n", reg, val); + writel_relaxed(val, base + reg); +} + +static inline void __reg_set(struct device *dev, void __iomem *base, u32 reg, + u32 mask) +{ + dev_dbg(dev, "SET 0x%x %#10.8x\n", reg, mask); + __reg_write(dev, base, reg, readl_relaxed(base + reg) | mask); +} + +static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, + u32 mask) +{ + dev_dbg(dev, "CLR 0x%x %#10.8x\n", reg, mask); + __reg_write(dev, base, reg, readl_relaxed(base + reg) & ~mask); +} + +/* DCMIPP subdev init / release entry points */ +struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs); +void dcmipp_par_ent_release(struct dcmipp_ent_device *ved); +struct dcmipp_ent_device * +dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs); +void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved); +struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs); +void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved); + +#endif diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c new file mode 100644 index 000000000..32c6619be --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dcmipp-common.h" + +#define DCMIPP_MDEV_MODEL_NAME "DCMIPP MDEV" + +#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ + .src_ent = src, \ + .src_pad = srcpad, \ + .sink_ent = sink, \ + .sink_pad = sinkpad, \ + .flags = link_flags, \ +} + +struct dcmipp_device { + /* The platform device */ + struct platform_device pdev; + struct device *dev; + + /* Hardware resources */ + void __iomem *regs; + struct clk *kclk; + + /* The pipeline configuration */ + const struct dcmipp_pipeline_config *pipe_cfg; + + /* The Associated media_device parent */ + struct media_device mdev; + + /* Internal v4l2 parent device*/ + struct v4l2_device v4l2_dev; + + /* Entities */ + struct dcmipp_ent_device **entity; + + struct v4l2_async_notifier notifier; +}; + +static inline struct dcmipp_device * +notifier_to_dcmipp(struct v4l2_async_notifier *n) +{ + return container_of(n, struct dcmipp_device, notifier); +} + +/* Structure which describes individual configuration for each entity */ +struct dcmipp_ent_config { + const char *name; + struct dcmipp_ent_device *(*init) + (struct device *dev, const char *entity_name, + struct v4l2_device *v4l2_dev, void __iomem *regs); + void (*release)(struct dcmipp_ent_device *ved); +}; + +/* Structure which describes links between entities */ +struct dcmipp_ent_link { + unsigned int src_ent; + u16 src_pad; + unsigned int sink_ent; + u16 sink_pad; + u32 flags; +}; + +/* Structure which describes the whole topology */ +struct dcmipp_pipeline_config { + const struct dcmipp_ent_config *ents; + size_t num_ents; + const struct dcmipp_ent_link *links; + size_t num_links; +}; + +/* -------------------------------------------------------------------------- + * Topology Configuration + */ + +static const struct dcmipp_ent_config stm32mp13_ent_config[] = { + { + .name = "dcmipp_parallel", + .init = dcmipp_par_ent_init, + .release = dcmipp_par_ent_release, + }, + { + .name = "dcmipp_dump_postproc", + .init = dcmipp_byteproc_ent_init, + .release = dcmipp_byteproc_ent_release, + }, + { + .name = "dcmipp_dump_capture", + .init = dcmipp_bytecap_ent_init, + .release = dcmipp_bytecap_ent_release, + }, +}; + +#define ID_PARALLEL 0 +#define ID_DUMP_BYTEPROC 1 +#define ID_DUMP_CAPTURE 2 + +static const struct dcmipp_ent_link stm32mp13_ent_links[] = { + DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), +}; + +static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = { + .ents = stm32mp13_ent_config, + .num_ents = ARRAY_SIZE(stm32mp13_ent_config), + .links = stm32mp13_ent_links, + .num_links = ARRAY_SIZE(stm32mp13_ent_links) +}; + +#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ + (f) == MEDIA_LNK_FL_ENABLED ? "ENABLED" :\ + (f) == MEDIA_LNK_FL_IMMUTABLE ? "IMMUTABLE" :\ + (f) == (MEDIA_LNK_FL_ENABLED |\ + MEDIA_LNK_FL_IMMUTABLE) ?\ + "ENABLED, IMMUTABLE" :\ + "UNKNOWN") + +static int dcmipp_create_links(struct dcmipp_device *dcmipp) +{ + unsigned int i; + int ret; + + /* Initialize the links between entities */ + for (i = 0; i < dcmipp->pipe_cfg->num_links; i++) { + const struct dcmipp_ent_link *link = + &dcmipp->pipe_cfg->links[i]; + struct dcmipp_ent_device *ved_src = + dcmipp->entity[link->src_ent]; + struct dcmipp_ent_device *ved_sink = + dcmipp->entity[link->sink_ent]; + + dev_dbg(dcmipp->dev, "Create link \"%s\":%d -> %d:\"%s\" [%s]\n", + dcmipp->pipe_cfg->ents[link->src_ent].name, + link->src_pad, link->sink_pad, + dcmipp->pipe_cfg->ents[link->sink_ent].name, + LINK_FLAG_TO_STR(link->flags)); + + ret = media_create_pad_link(ved_src->ent, link->src_pad, + ved_sink->ent, link->sink_pad, + link->flags); + if (ret) + return ret; + } + + return 0; +} + +static int dcmipp_graph_init(struct dcmipp_device *dcmipp); + +static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) +{ + int ret, i; + + /* Call all subdev inits */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + const char *name = dcmipp->pipe_cfg->ents[i].name; + + dev_dbg(dcmipp->dev, "add subdev %s\n", name); + dcmipp->entity[i] = + dcmipp->pipe_cfg->ents[i].init(dcmipp->dev, name, + &dcmipp->v4l2_dev, + dcmipp->regs); + if (IS_ERR(dcmipp->entity[i])) { + dev_err(dcmipp->dev, "failed to init subdev %s\n", + name); + ret = PTR_ERR(dcmipp->entity[i]); + goto err_init_entity; + } + } + + /* Initialize links */ + ret = dcmipp_create_links(dcmipp); + if (ret) + goto err_init_entity; + + ret = dcmipp_graph_init(dcmipp); + if (ret < 0) + goto err_init_entity; + + return 0; + +err_init_entity: + while (i > 0) + dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); + return ret; +} + +static const struct of_device_id dcmipp_of_match[] = { + { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, dcmipp_of_match); + +static irqreturn_t dcmipp_irq_thread(int irq, void *arg) +{ + struct dcmipp_device *dcmipp = arg; + struct dcmipp_ent_device *ved; + unsigned int i; + + /* Call irq thread of each entities of pipeline */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + ved = dcmipp->entity[i]; + if (ved->thread_fn && ved->handler_ret == IRQ_WAKE_THREAD) + ved->thread_fn(irq, ved); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dcmipp_irq_callback(int irq, void *arg) +{ + struct dcmipp_device *dcmipp = arg; + struct dcmipp_ent_device *ved; + irqreturn_t ret = IRQ_HANDLED; + unsigned int i; + + /* Call irq handler of each entities of pipeline */ + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { + ved = dcmipp->entity[i]; + if (ved->handler) + ved->handler_ret = ved->handler(irq, ved); + else if (ved->thread_fn) + ved->handler_ret = IRQ_WAKE_THREAD; + else + ved->handler_ret = IRQ_HANDLED; + if (ved->handler_ret != IRQ_HANDLED) + ret = ved->handler_ret; + } + + return ret; +} + +static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + unsigned int ret; + int src_pad; + struct dcmipp_ent_device *sink; + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct fwnode_handle *ep; + + dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); + + /* + * Link this sub-device to DCMIPP, it could be + * a parallel camera sensor or a CSI-2 to parallel bridge + */ + src_pad = media_entity_get_fwnode_pad(&subdev->entity, + subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + + /* Get bus characteristics from devicetree */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dcmipp->dev, "Could not find the endpoint\n"); + return -ENODEV; + } + + /* Check for parallel bus-type first, then bt656 */ + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + vep.bus_type = V4L2_MBUS_BT656; + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + dev_err(dcmipp->dev, "Could not parse the endpoint\n"); + fwnode_handle_put(ep); + return ret; + } + } + + fwnode_handle_put(ep); + + if (vep.bus.parallel.bus_width == 0) { + dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); + return -ENODEV; + } + + /* Only 8 bits bus width supported with BT656 bus */ + if (vep.bus_type == V4L2_MBUS_BT656 && + vep.bus.parallel.bus_width != 8) { + dev_err(dcmipp->dev, "BT656 bus conflicts with %u bits bus width (8 bits required)\n", + vep.bus.parallel.bus_width); + return -ENODEV; + } + + /* Parallel input device detected, connect it to parallel subdev */ + sink = dcmipp->entity[ID_PARALLEL]; + sink->bus.flags = vep.bus.parallel.flags; + sink->bus.bus_width = vep.bus.parallel.bus_width; + sink->bus.data_shift = vep.bus.parallel.data_shift; + sink->bus_type = vep.bus_type; + ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(dcmipp->dev, "Failed to create media pad link with subdev \"%s\"\n", + subdev->name); + return ret; + } + + dev_dbg(dcmipp->dev, "DCMIPP is now linked to \"%s\"\n", subdev->name); + + return 0; +} + +static void dcmipp_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asd) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + + dev_dbg(dcmipp->dev, "Removing %s\n", sd->name); +} + +static int dcmipp_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); + int ret; + + /* Register the media device */ + ret = media_device_register(&dcmipp->mdev); + if (ret) { + dev_err(dcmipp->mdev.dev, + "media device register failed (err=%d)\n", ret); + return ret; + } + + /* Expose all subdev's nodes*/ + ret = v4l2_device_register_subdev_nodes(&dcmipp->v4l2_dev); + if (ret) { + dev_err(dcmipp->mdev.dev, + "dcmipp subdev nodes registration failed (err=%d)\n", + ret); + media_device_unregister(&dcmipp->mdev); + return ret; + } + + dev_dbg(dcmipp->dev, "Notify complete !\n"); + + return 0; +} + +static const struct v4l2_async_notifier_operations dcmipp_graph_notify_ops = { + .bound = dcmipp_graph_notify_bound, + .unbind = dcmipp_graph_notify_unbind, + .complete = dcmipp_graph_notify_complete, +}; + +static int dcmipp_graph_init(struct dcmipp_device *dcmipp) +{ + struct v4l2_async_connection *asd; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dcmipp->dev, "Failed to get next endpoint\n"); + return -EINVAL; + } + + v4l2_async_nf_init(&dcmipp->notifier, &dcmipp->v4l2_dev); + + asd = v4l2_async_nf_add_fwnode_remote(&dcmipp->notifier, ep, + struct v4l2_async_connection); + + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + dev_err(dcmipp->dev, "Failed to add fwnode remote subdev\n"); + return PTR_ERR(asd); + } + + dcmipp->notifier.ops = &dcmipp_graph_notify_ops; + + ret = v4l2_async_nf_register(&dcmipp->notifier); + if (ret < 0) { + dev_err(dcmipp->dev, "Failed to register notifier\n"); + v4l2_async_nf_cleanup(&dcmipp->notifier); + return ret; + } + + return 0; +} + +static int dcmipp_probe(struct platform_device *pdev) +{ + struct dcmipp_device *dcmipp; + struct clk *kclk; + const struct dcmipp_pipeline_config *pipe_cfg; + struct reset_control *rstc; + int irq; + int ret; + + dcmipp = devm_kzalloc(&pdev->dev, sizeof(*dcmipp), GFP_KERNEL); + if (!dcmipp) + return -ENOMEM; + + dcmipp->dev = &pdev->dev; + + pipe_cfg = device_get_match_data(dcmipp->dev); + if (!pipe_cfg) { + dev_err(&pdev->dev, "Can't get device data\n"); + return -ENODEV; + } + dcmipp->pipe_cfg = pipe_cfg; + + platform_set_drvdata(pdev, dcmipp); + + /* Get hardware resources from devicetree */ + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Could not get reset control\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not get irq\n"); + return irq ? irq : -ENXIO; + } + + dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(dcmipp->regs)) { + dev_err(&pdev->dev, "Could not map registers\n"); + return PTR_ERR(dcmipp->regs); + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, dcmipp_irq_callback, + dcmipp_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), dcmipp); + if (ret) { + dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + return ret; + } + + /* Reset device */ + ret = reset_control_assert(rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to assert the reset line\n"); + return ret; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(rstc); + if (ret) { + dev_err(&pdev->dev, "Failed to deassert the reset line\n"); + return ret; + } + + kclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(kclk), + "Unable to get kclk\n"); + dcmipp->kclk = kclk; + + dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, + sizeof(*dcmipp->entity), GFP_KERNEL); + if (!dcmipp->entity) + return -ENOMEM; + + /* Register the v4l2 struct */ + ret = v4l2_device_register(&pdev->dev, &dcmipp->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, + "v4l2 device register failed (err=%d)\n", ret); + return ret; + } + + /* Link the media device within the v4l2_device */ + dcmipp->v4l2_dev.mdev = &dcmipp->mdev; + + /* Initialize media device */ + strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, + sizeof(dcmipp->mdev.model)); + dcmipp->mdev.dev = &pdev->dev; + media_device_init(&dcmipp->mdev); + + /* Initialize subdevs */ + ret = dcmipp_create_subdevs(dcmipp); + if (ret) { + media_device_cleanup(&dcmipp->mdev); + v4l2_device_unregister(&dcmipp->v4l2_dev); + return ret; + } + + pm_runtime_enable(dcmipp->dev); + + dev_info(&pdev->dev, "Probe done"); + + return 0; +} + +static int dcmipp_remove(struct platform_device *pdev) +{ + struct dcmipp_device *dcmipp = platform_get_drvdata(pdev); + unsigned int i; + + pm_runtime_disable(&pdev->dev); + + v4l2_async_nf_unregister(&dcmipp->notifier); + v4l2_async_nf_cleanup(&dcmipp->notifier); + + for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) + dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); + + media_device_unregister(&dcmipp->mdev); + media_device_cleanup(&dcmipp->mdev); + + v4l2_device_unregister(&dcmipp->v4l2_dev); + + return 0; +} + +static int dcmipp_runtime_suspend(struct device *dev) +{ + struct dcmipp_device *dcmipp = dev_get_drvdata(dev); + + clk_disable_unprepare(dcmipp->kclk); + + return 0; +} + +static int dcmipp_runtime_resume(struct device *dev) +{ + struct dcmipp_device *dcmipp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dcmipp->kclk); + if (ret) + dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); + + return ret; +} + +static int dcmipp_suspend(struct device *dev) +{ + /* disable clock */ + pm_runtime_force_suspend(dev); + + /* change pinctrl state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int dcmipp_resume(struct device *dev) +{ + /* restore pinctl default state */ + pinctrl_pm_select_default_state(dev); + + /* clock enable */ + pm_runtime_force_resume(dev); + + return 0; +} + +static const struct dev_pm_ops dcmipp_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dcmipp_suspend, dcmipp_resume) + RUNTIME_PM_OPS(dcmipp_runtime_suspend, dcmipp_runtime_resume, NULL) +}; + +static struct platform_driver dcmipp_pdrv = { + .probe = dcmipp_probe, + .remove = dcmipp_remove, + .driver = { + .name = DCMIPP_PDEV_NAME, + .of_match_table = dcmipp_of_match, + .pm = pm_ptr(&dcmipp_pm_ops), + }, +}; + +module_platform_driver(dcmipp_pdrv); + +MODULE_AUTHOR("Hugues Fruchet "); +MODULE_AUTHOR("Alain Volmat "); +MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface with Pixel Processor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c new file mode 100644 index 000000000..62c5c3331 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include +#include + +#include "dcmipp-common.h" + +#define DCMIPP_PRCR 0x104 +#define DCMIPP_PRCR_FORMAT_SHIFT 16 +#define DCMIPP_PRCR_FORMAT_YUV422 0x1e +#define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RAW8 0x2a +#define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a +#define DCMIPP_PRCR_ESS BIT(4) +#define DCMIPP_PRCR_PCKPOL BIT(5) +#define DCMIPP_PRCR_HSPOL BIT(6) +#define DCMIPP_PRCR_VSPOL BIT(7) +#define DCMIPP_PRCR_ENABLE BIT(14) +#define DCMIPP_PRCR_SWAPCYCLES BIT(25) + +#define DCMIPP_PRESCR 0x108 +#define DCMIPP_PRESUR 0x10c + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_par_pix_map { + unsigned int code_sink; + unsigned int code_src; + u8 prcr_format; + u8 prcr_swapcycles; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + } +static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index + (unsigned int index, + unsigned int pad) +{ + unsigned int i = 0; + u32 prev_code = 0, cur_code; + + while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_par_pix_map_list[i].code_src; + else + cur_code = dcmipp_par_pix_map_list[i].code_sink; + + if (cur_code == prev_code) { + i++; + continue; + } + prev_code = cur_code; + + if (index == 0) + break; + i++; + index--; + } + + if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list)) + return NULL; + + return &dcmipp_par_pix_map_list[i]; +} + +static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) { + if ((dcmipp_par_pix_map_list[i].code_sink == code_sink && + dcmipp_par_pix_map_list[i].code_src == code_src) || + (dcmipp_par_pix_map_list[i].code_sink == code_src && + dcmipp_par_pix_map_list[i].code_src == code_sink) || + (dcmipp_par_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_par_pix_map_list[i].code_src == code_src)) + return &dcmipp_par_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_par_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; + bool streaming; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_par_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + } + + return 0; +} + +static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_par_pix_map *vpix = + dcmipp_par_pix_map_by_index(code->index, code->pad); + + if (!vpix) + return -EINVAL; + + code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; + + return 0; +} + +static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_par_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, + IS_SRC(fse->pad) ? fse->code : 0); + if (!vpix) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + + return 0; +} + +static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_par_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, + IS_SRC(pad) ? fmt->code : 0); + if (!vpix) + fmt->code = fmt_default.code; + + /* Exclude JPEG if BT656 bus is selected */ + if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && + par->ved.bus_type == V4L2_MBUS_BT656) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_par_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_par_device *par = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (par->streaming) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad); + + dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + par->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *mf = fmt->format; + + /* When setting the sink format, report that format on the src pad */ + if (IS_SINK(fmt->pad)) { + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = fmt->format; + dcmipp_par_adjust_fmt(par, mf, 1); + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = { + .enum_mbus_code = dcmipp_par_enum_mbus_code, + .enum_frame_size = dcmipp_par_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_par_set_fmt, +}; + +static int dcmipp_par_configure(struct dcmipp_par_device *par) +{ + u32 val = 0; + const struct dcmipp_par_pix_map *vpix; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= DCMIPP_PRCR_PCKPOL; + + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (par->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + state = v4l2_subdev_lock_and_get_active_state(&par->sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + v4l2_subdev_unlock_state(state); + + vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(par->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; + + /* swap cycles */ + if (vpix->prcr_swapcycles) + val |= DCMIPP_PRCR_SWAPCYCLES; + + reg_write(par, DCMIPP_PRCR, val); + + return 0; +} + +static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct dcmipp_par_device *par = + container_of(sd, struct dcmipp_par_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *pad; + int ret = 0; + + /* Get source subdev */ + pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(pad->entity); + + if (enable) { + ret = dcmipp_par_configure(par); + if (ret) + return ret; + + /* Enable parallel interface */ + reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(par->dev, + "failed to start source subdev streaming (%d)\n", + ret); + return ret; + } + } else { + ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); + if (ret < 0) { + dev_err(par->dev, + "failed to stop source subdev streaming (%d)\n", + ret); + return ret; + } + + /* Disable parallel interface */ + reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + } + + par->streaming = enable; + + return ret; +} + +static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = { + .s_stream = dcmipp_par_s_stream, +}; + +static const struct v4l2_subdev_ops dcmipp_par_ops = { + .pad = &dcmipp_par_pad_ops, + .video = &dcmipp_par_video_ops, +}; + +static void dcmipp_par_release(struct v4l2_subdev *sd) +{ + struct dcmipp_par_device *par = + container_of(sd, struct dcmipp_par_device, sd); + + kfree(par); +} + +static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = { + .init_state = dcmipp_par_init_state, + .release = dcmipp_par_release, +}; + +void dcmipp_par_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_par_device *par = + container_of(ved, struct dcmipp_par_device, ved); + + dcmipp_ent_sd_unregister(ved, &par->sd); +} + +struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_par_device *par; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the par struct */ + par = kzalloc(sizeof(*par), GFP_KERNEL); + if (!par) + return ERR_PTR(-ENOMEM); + + par->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_par_int_ops, &dcmipp_par_ops, + NULL, NULL); + if (ret) { + kfree(par); + return ERR_PTR(ret); + } + + par->dev = dev; + + return &par->ved; +} diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index ad13d447d..097a3a08e 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -211,6 +211,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) /* Initialize subdev */ v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops); + subdev->internal_ops = &sun4i_csi_subdev_internal_ops; subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 8eeed87bf..4e0c2df45 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -89,6 +89,7 @@ enum csi_subdev_pads { }; extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops; +extern const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops; struct sun4i_csi_format { u32 mbus; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 95b5633b7..d1371e130 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -411,7 +411,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) for (i = 0; i < CSI_MAX_BUFFER; i++) csi->current_buf[i] = NULL; - q->min_buffers_needed = 3; + q->min_queued_buffers = 3; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->io_modes = VB2_MMAP | VB2_DMABUF; q->lock = &csi->lock; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 48702134c..744197b0f 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -266,12 +266,12 @@ static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state) +static int sun4i_csi_subdev_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK); + fmt = v4l2_subdev_state_get_format(sd_state, CSI_SUBDEV_SINK); *fmt = sun4i_csi_pad_fmt_default; return 0; @@ -285,8 +285,7 @@ static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -303,8 +302,7 @@ static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *subdev_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state, - fmt->pad); + subdev_fmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); else subdev_fmt = &csi->subdev_fmt; @@ -336,7 +334,6 @@ sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .init_cfg = sun4i_csi_subdev_init_cfg, .get_fmt = sun4i_csi_subdev_get_fmt, .set_fmt = sun4i_csi_subdev_set_fmt, .enum_mbus_code = sun4i_csi_subdev_enum_mbus_code, @@ -346,6 +343,10 @@ const struct v4l2_subdev_ops sun4i_csi_subdev_ops = { .pad = &sun4i_csi_subdev_pad_ops, }; +const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops = { + .init_state = sun4i_csi_subdev_init_state, +}; + int sun4i_csi_v4l2_register(struct sun4i_csi *csi) { struct video_device *vdev = &csi->vdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index e57341312..d006d9dd0 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -501,13 +501,13 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi_dev->bridge.lock; mutex_lock(lock); @@ -547,8 +547,8 @@ static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi_dev->bridge.mbus_format; @@ -570,7 +570,7 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, sun6i_csi_bridge_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi_dev->bridge.mbus_format = *mbus_format; @@ -581,7 +581,6 @@ static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { - .init_cfg = sun6i_csi_bridge_init_cfg, .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, .get_fmt = sun6i_csi_bridge_get_fmt, .set_fmt = sun6i_csi_bridge_set_fmt, @@ -592,6 +591,10 @@ static const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = { .pad = &sun6i_csi_bridge_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_csi_bridge_internal_ops = { + .init_state = sun6i_csi_bridge_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_csi_bridge_entity_ops = { @@ -782,6 +785,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); + subdev->internal_ops = &sun6i_csi_bridge_internal_ops; strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c index cf6aadbc1..14c0dc827 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c @@ -1010,7 +1010,7 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev) queue->buf_struct_size = sizeof(struct sun6i_csi_buffer); queue->ops = &sun6i_csi_capture_queue_ops; queue->mem_ops = &vb2_dma_contig_memops; - queue->min_buffers_needed = 2; + queue->min_queued_buffers = 2; queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; queue->lock = &capture->lock; queue->dev = csi_dev->dev; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index 08d86c17b..f9d4dc45b 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -305,13 +305,13 @@ sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun6i_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -351,8 +351,8 @@ static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -374,7 +374,7 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun6i_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -385,7 +385,6 @@ static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = { - .init_cfg = sun6i_mipi_csi2_init_cfg, .enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code, .get_fmt = sun6i_mipi_csi2_get_fmt, .set_fmt = sun6i_mipi_csi2_set_fmt, @@ -396,6 +395,10 @@ static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = { .pad = &sun6i_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun6i_mipi_csi2_internal_ops = { + .init_state = sun6i_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { @@ -504,6 +507,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun6i_mipi_csi2_internal_ops; strscpy(subdev->name, SUN6I_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index 14a184481..4a5698eb1 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -338,14 +338,14 @@ sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; } -static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *state) +static int sun8i_a83t_mipi_csi2_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) { struct sun8i_a83t_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; struct v4l2_mbus_framefmt *mbus_format = - v4l2_subdev_get_try_format(subdev, state, pad); + v4l2_subdev_state_get_format(state, pad); struct mutex *lock = &csi2_dev->bridge.lock; mutex_lock(lock); @@ -387,8 +387,8 @@ static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, mutex_lock(lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *mbus_format = *v4l2_subdev_get_try_format(subdev, state, - format->pad); + *mbus_format = *v4l2_subdev_state_get_format(state, + format->pad); else *mbus_format = csi2_dev->bridge.mbus_format; @@ -411,7 +411,7 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) - *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *v4l2_subdev_state_get_format(state, format->pad) = *mbus_format; else csi2_dev->bridge.mbus_format = *mbus_format; @@ -422,7 +422,6 @@ static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, } static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { - .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, @@ -433,6 +432,10 @@ static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { .pad = &sun8i_a83t_mipi_csi2_pad_ops, }; +static const struct v4l2_subdev_internal_ops sun8i_a83t_mipi_csi2_internal_ops = { + .init_state = sun8i_a83t_mipi_csi2_init_state, +}; + /* Media Entity */ static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { @@ -542,6 +545,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* V4L2 Subdev */ v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); + subdev->internal_ops = &sun8i_a83t_mipi_csi2_internal_ops; strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->owner = THIS_MODULE; diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index f7ff09378..a1c35a2b6 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -673,7 +673,7 @@ static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -688,7 +688,7 @@ static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->min_buffers_needed = 2; + dst_vq->min_queued_buffers = 2; dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index 0b025ec91..a12323ca8 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -536,7 +536,7 @@ static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->min_buffers_needed = 1; + src_vq->min_queued_buffers = 1; src_vq->ops = &rotate_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -551,7 +551,7 @@ static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->min_buffers_needed = 2; + dst_vq->min_queued_buffers = 2; dst_vq->ops = &rotate_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 5fa2ea902..77e12457d 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -1771,9 +1771,10 @@ static int vpfe_queue_setup(struct vb2_queue *vq, { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); unsigned size = vpfe->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (sizes[0] < size) @@ -2233,7 +2234,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) q->buf_struct_size = sizeof(struct vpfe_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &vpfe->lock; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->dev = vpfe->pdev; err = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 1a4273bbe..4afc2ad00 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -57,7 +57,7 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) state = v4l2_subdev_get_locked_active_state(&phy->subdev); - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) @@ -621,8 +621,6 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; @@ -630,8 +628,8 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); code->code = fmt->code; } else { if (code->index >= cal_num_formats) @@ -656,8 +654,8 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_SINK); if (fse->code != fmt->code) return -EINVAL; @@ -713,18 +711,18 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); *fmt = format->format; - fmt = v4l2_subdev_get_pad_format(sd, state, - CAL_CAMERARX_PAD_FIRST_SOURCE); + fmt = v4l2_subdev_state_get_format(state, + CAL_CAMERARX_PAD_FIRST_SOURCE); *fmt = format->format; return 0; } -static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { .which = state ? V4L2_SUBDEV_FORMAT_TRY @@ -784,7 +782,6 @@ static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { }; static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { - .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, @@ -797,6 +794,10 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; +static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { + .init_state = cal_camerarx_sd_init_state, +}; + static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; @@ -848,6 +849,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->internal_ops = &cal_camerarx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index a8abcd0fe..e1ba5dfc2 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -603,9 +603,10 @@ static int cal_queue_setup(struct vb2_queue *vq, { struct cal_ctx *ctx = vb2_get_drv_priv(vq); unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; if (*nplanes) { if (sizes[0] < size) @@ -697,7 +698,7 @@ static int cal_video_check_format(struct cal_ctx *ctx) state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); - format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index); + format = v4l2_subdev_state_get_format(state, remote_pad->index); if (!format) { ret = -EINVAL; goto out; @@ -1009,7 +1010,7 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; - q->min_buffers_needed = 3; + q->min_queued_buffers = 3; q->dev = ctx->cal->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 99fae8830..c31a5566f 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -113,6 +113,7 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; unsigned size = common->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); vpif_dbg(2, debug, "vpif_buffer_setup\n"); @@ -122,8 +123,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, size = sizes[0]; } - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; *nplanes = 1; sizes[0] = size; @@ -1428,7 +1429,7 @@ static int vpif_probe_complete(void) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &common->lock; q->dev = vpif_dev; diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index f8ec2991c..02ede1fe1 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -115,6 +115,7 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; unsigned size = common->fmt.fmt.pix.sizeimage; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); if (*nplanes) { if (sizes[0] < size) @@ -122,8 +123,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, size = sizes[0]; } - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 3) + *nbuffers = 3 - q_num_bufs; *nplanes = 1; sizes[0] = size; @@ -1168,7 +1169,7 @@ static int vpif_probe_complete(void) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_disp_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &common->lock; q->dev = vpif_dev; err = vb2_queue_init(q); diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index ada61391c..59b30fc43 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -873,7 +873,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->dev = dmaengine_get_dma_device(csi->dma.chan); q->lock = &csi->mutex; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/ti/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c index 414327408..1c56b6a87 100644 --- a/drivers/media/platform/ti/omap/omap_vout.c +++ b/drivers/media/platform/ti/omap/omap_vout.c @@ -944,10 +944,11 @@ static int omap_vout_vb2_queue_setup(struct vb2_queue *vq, struct device *alloc_devs[]) { struct omap_vout_device *vout = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); int size = vout->pix.sizeimage; - if (is_rotation_enabled(vout) && vq->num_buffers + *nbufs > VRFB_NUM_BUFS) { - *nbufs = VRFB_NUM_BUFS - vq->num_buffers; + if (is_rotation_enabled(vout) && q_num_bufs + *nbufs > VRFB_NUM_BUFS) { + *nbufs = VRFB_NUM_BUFS - q_num_bufs; if (*nbufs == 0) return -EINVAL; } @@ -1403,7 +1404,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) vq->ops = &omap_vout_vb2_ops; vq->mem_ops = &vb2_dma_contig_memops; vq->lock = &vout->lock; - vq->min_buffers_needed = 1; + vq->min_queued_buffers = 1; vfd->queue = vq; ret = vb2_queue_init(vq); diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 2fe42aa91..dd375c4e1 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1948,8 +1948,7 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccdc->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccdc->formats[pad]; } @@ -1960,8 +1959,8 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&ccdc->subdev, sd_state, - CCDC_PAD_SOURCE_OF); + return v4l2_subdev_state_get_crop(sd_state, + CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1969,7 +1968,7 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device - * @cfg : V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: Pad number * @fmt: Format */ @@ -2127,7 +2126,7 @@ static void ccdc_try_crop(struct isp_ccdc_device *ccdc, /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg : V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -2230,7 +2229,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, /* * ccdc_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the output formatter @@ -2274,7 +2273,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, /* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output @@ -2322,7 +2321,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond @@ -2346,7 +2345,7 @@ static int ccdc_get_format(struct v4l2_subdev *sd, /* * ccdc_set_format - Set the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index da5f0176e..1204ee221 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -614,7 +614,7 @@ static const unsigned int ccp2_fmts[] = { /* * __ccp2_get_format - helper function for getting ccp2 format * @ccp2 : Pointer to ISP CCP2 device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad number * @which : wanted subdev format * return format structure or NULL on error @@ -625,8 +625,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &ccp2->formats[pad]; } @@ -634,7 +633,7 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, /* * ccp2_try_format - Handle try format by pad subdev method * @ccp2 : Pointer to ISP CCP2 device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad num * @fmt : pointer to v4l2 mbus format structure * @which : wanted subdev format @@ -689,7 +688,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, /* * ccp2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -750,7 +749,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, /* * ccp2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -772,7 +771,7 @@ static int ccp2_get_format(struct v4l2_subdev *sd, /* * ccp2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * returns zero */ diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 0f9a54b11..ae574e1b6 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -834,8 +834,7 @@ __csi2_get_format(struct isp_csi2_device *csi2, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &csi2->formats[pad]; } @@ -894,7 +893,7 @@ csi2_try_format(struct isp_csi2_device *csi2, /* * csi2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -968,7 +967,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, /* * csi2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -990,7 +989,7 @@ static int csi2_get_format(struct v4l2_subdev *sd, /* * csi2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index 53aedec79..e383a5765 100644 --- a/drivers/media/platform/ti/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c @@ -1684,8 +1684,7 @@ __preview_get_format(struct isp_prev_device *prev, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&prev->subdev, sd_state, - pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &prev->formats[pad]; } @@ -1696,8 +1695,7 @@ __preview_get_crop(struct isp_prev_device *prev, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&prev->subdev, sd_state, - PREV_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, PREV_PAD_SINK); else return &prev->crop; } @@ -1724,7 +1722,7 @@ static const unsigned int preview_output_fmts[] = { /* * preview_try_format - Validate a format * @prev: ISP preview engine - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad: pad number * @fmt: format to be validated * @which: try/active format selector @@ -1863,7 +1861,7 @@ static void preview_try_crop(struct isp_prev_device *prev, /* * preview_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -1924,7 +1922,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, /* * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1967,7 +1965,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, /* * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -2015,7 +2013,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, /* * preview_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -2037,7 +2035,7 @@ static int preview_get_format(struct v4l2_subdev *sd, /* * preview_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index ed2fb0c7a..87d821b02 100644 --- a/drivers/media/platform/ti/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c @@ -109,7 +109,7 @@ static const struct isprsz_coef filter_coefs = { * __resizer_get_format - helper function for getting resizer format * @res : pointer to resizer private structure * @pad : pad number - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which : wanted subdev format * return zero */ @@ -119,7 +119,7 @@ __resizer_get_format(struct isp_res_device *res, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&res->subdev, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &res->formats[pad]; } @@ -127,7 +127,7 @@ __resizer_get_format(struct isp_res_device *res, /* * __resizer_get_crop - helper function for getting resizer crop rectangle * @res : pointer to resizer private structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @which : wanted subdev crop rectangle */ static struct v4l2_rect * @@ -136,8 +136,7 @@ __resizer_get_crop(struct isp_res_device *res, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&res->subdev, sd_state, - RESZ_PAD_SINK); + return v4l2_subdev_state_get_crop(sd_state, RESZ_PAD_SINK); else return &res->crop.request; } @@ -1215,7 +1214,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, /* * resizer_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1265,7 +1264,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, /* * resizer_set_selection - Set a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1369,7 +1368,7 @@ static unsigned int resizer_max_in_width(struct isp_res_device *res) /* * resizer_try_format - Handle try format by pad subdev method * @res : ISP resizer device - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @pad : pad num * @fmt : pointer to v4l2 format structure * @which : wanted subdev format @@ -1413,7 +1412,7 @@ static void resizer_try_format(struct isp_res_device *res, /* * resizer_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ @@ -1474,7 +1473,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ @@ -1496,7 +1495,7 @@ static int resizer_get_format(struct v4l2_subdev *sd, /* * resizer_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration + * @sd_state: V4L2 subdev state * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index e65b836b9..24b927d8f 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -8,7 +8,6 @@ config VIDEO_HANTRO depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index 77aee9489..6f5eb975d 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -328,6 +328,8 @@ struct hantro_vp9_decoded_buffer_info { /* Info needed when the decoded frame serves as a reference frame. */ unsigned short width; unsigned short height; + size_t chroma_offset; + size_t mv_offset; u32 bit_depth : 4; }; @@ -469,11 +471,14 @@ hantro_get_dst_buf(struct hantro_ctx *ctx) bool hantro_needs_postproc(const struct hantro_ctx *ctx, const struct hantro_fmt *fmt); +dma_addr_t +hantro_postproc_get_dec_buf_addr(struct hantro_ctx *ctx, int index); + static inline dma_addr_t hantro_get_dec_buf_addr(struct hantro_ctx *ctx, struct vb2_buffer *vb) { if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - return ctx->postproc.dec_q[vb->index].dma; + return hantro_postproc_get_dec_buf_addr(ctx, vb->index); return vb2_dma_contig_plane_dma_addr(vb, 0); } @@ -485,8 +490,8 @@ vb2_to_hantro_decoded_buf(struct vb2_buffer *buf) void hantro_postproc_disable(struct hantro_ctx *ctx); void hantro_postproc_enable(struct hantro_ctx *ctx); +int hantro_postproc_init(struct hantro_ctx *ctx); void hantro_postproc_free(struct hantro_ctx *ctx); -int hantro_postproc_alloc(struct hantro_ctx *ctx); int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize); diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 3a2a0f28c..db3df6cc4 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -235,8 +235,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) * The Kernel needs access to the JPEG destination buffer for the * JPEG encoder to fill in the JPEG headers. */ - if (!ctx->is_encoder) + if (!ctx->is_encoder) { dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; + dst_vq->max_num_buffers = MAX_POSTPROC_BUFFERS; + } dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index ee5f14c5f..b880a6849 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -8,6 +8,8 @@ #include "hantro_hw.h" #include "hantro_g2_regs.h" +#define G2_ALIGN 16 + void hantro_g2_check_idle(struct hantro_dev *vpu) { int i; @@ -42,3 +44,15 @@ irqreturn_t hantro_g2_irq(int irq, void *dev_id) return IRQ_HANDLED; } + +size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx) +{ + return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; +} + +size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) +{ + size_t cr_offset = hantro_g2_chroma_offset(ctx); + + return ALIGN((cr_offset * 3) / 2, G2_ALIGN); +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index a9d4ac84a..d3f8c33eb 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -8,20 +8,6 @@ #include "hantro_hw.h" #include "hantro_g2_regs.h" -#define G2_ALIGN 16 - -static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx) -{ - return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; -} - -static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx) -{ - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - static void prepare_tile_info_buffer(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; @@ -384,8 +370,8 @@ static int set_ref(struct hantro_ctx *ctx) struct hantro_dev *vpu = ctx->dev; struct vb2_v4l2_buffer *vb2_dst; struct hantro_decoded_buffer *dst; - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - size_t mv_offset = hantro_hevc_motion_vectors_offset(ctx); + size_t cr_offset = hantro_g2_chroma_offset(ctx); + size_t mv_offset = hantro_g2_motion_vectors_offset(ctx); u32 max_ref_frames; u16 dpb_longterm_e; static const struct hantro_reg cur_poc[] = { diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c index 6fc4b5555..342e543de 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c @@ -16,8 +16,6 @@ #include "hantro_vp9.h" #include "hantro_g2_regs.h" -#define G2_ALIGN 16 - enum hantro_ref_frames { INTRA_FRAME = 0, LAST_FRAME = 1, @@ -90,22 +88,6 @@ static int start_prepare_run(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_ return 0; } -static size_t chroma_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2; - - return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel; -} - -static size_t mv_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - size_t cr_offset = chroma_offset(ctx, dec_params); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - static struct hantro_decoded_buffer * get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) { @@ -156,11 +138,13 @@ static void config_output(struct hantro_ctx *ctx, luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); hantro_write_addr(ctx->dev, G2_OUT_LUMA_ADDR, luma_addr); - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + chroma_addr = luma_addr + hantro_g2_chroma_offset(ctx); hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr); + dst->vp9.chroma_offset = hantro_g2_chroma_offset(ctx); - mv_addr = luma_addr + mv_offset(ctx, dec_params); + mv_addr = luma_addr + hantro_g2_motion_vectors_offset(ctx); hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr); + dst->vp9.mv_offset = hantro_g2_motion_vectors_offset(ctx); } struct hantro_vp9_ref_reg { @@ -195,7 +179,7 @@ static void config_ref(struct hantro_ctx *ctx, luma_addr = hantro_get_dec_buf_addr(ctx, &buf->base.vb.vb2_buf); hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr); - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + chroma_addr = luma_addr + buf->vp9.chroma_offset; hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr); } @@ -238,7 +222,7 @@ static void config_ref_registers(struct hantro_ctx *ctx, config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts); mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) + - mv_offset(ctx, dec_params); + mv_ref->vp9.mv_offset; hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr); hantro_reg_write(ctx->dev, &vp9_last_sign_bias, diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 7f33f7b07..9aec8a79a 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -40,6 +40,8 @@ #define AV1_MAX_FRAME_BUF_COUNT (V4L2_AV1_TOTAL_REFS_PER_FRAME + 1) +#define MAX_POSTPROC_BUFFERS 64 + struct hantro_dev; struct hantro_ctx; struct hantro_buf; @@ -336,7 +338,7 @@ struct hantro_av1_dec_hw_ctx { * @dec_q: References buffers, in decoder format. */ struct hantro_postproc_ctx { - struct hantro_aux_buf dec_q[VB2_MAX_FRAME]; + struct hantro_aux_buf dec_q[MAX_POSTPROC_BUFFERS]; }; /** @@ -519,6 +521,9 @@ hantro_av1_mv_size(unsigned int width, unsigned int height) return ALIGN(num_sbs * 384, 16) * 2 + 512; } +size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx); +size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx); + int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); void hantro_mpeg2_dec_copy_qtable(u8 *qtable, diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 64d6fb852..41e931763 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -177,9 +177,11 @@ static int hantro_postproc_g2_enum_framesizes(struct hantro_ctx *ctx, void hantro_postproc_free(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *queue = &m2m_ctx->cap_q_ctx.q; unsigned int i; - for (i = 0; i < VB2_MAX_FRAME; ++i) { + for (i = 0; i < queue->max_num_buffers; ++i) { struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; if (priv->cpu) { @@ -190,20 +192,17 @@ void hantro_postproc_free(struct hantro_ctx *ctx) } } -int hantro_postproc_alloc(struct hantro_ctx *ctx) +static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx) { - struct hantro_dev *vpu = ctx->dev; - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; - unsigned int num_buffers = cap_queue->num_buffers; struct v4l2_pix_format_mplane pix_mp; const struct hantro_fmt *fmt; - unsigned int i, buf_size; + unsigned int buf_size; /* this should always pick native format */ fmt = hantro_get_default_fmt(ctx, false, ctx->bit_depth, HANTRO_AUTO_POSTPROC); if (!fmt) - return -EINVAL; + return 0; + v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, ctx->src_fmt.height); @@ -221,23 +220,77 @@ int hantro_postproc_alloc(struct hantro_ctx *ctx) buf_size += hantro_av1_mv_size(pix_mp.width, pix_mp.height); - for (i = 0; i < num_buffers; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; + return buf_size; +} + +static int hantro_postproc_alloc(struct hantro_ctx *ctx, int index) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index]; + unsigned int buf_size = hantro_postproc_buffer_size(ctx); + + if (!buf_size) + return -EINVAL; + + /* + * The buffers on this queue are meant as intermediate + * buffers for the decoder, so no mapping is needed. + */ + priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; + priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, + GFP_KERNEL, priv->attrs); + if (!priv->cpu) + return -ENOMEM; + priv->size = buf_size; + + return 0; +} - /* - * The buffers on this queue are meant as intermediate - * buffers for the decoder, so no mapping is needed. - */ - priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; - priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, - GFP_KERNEL, priv->attrs); - if (!priv->cpu) - return -ENOMEM; - priv->size = buf_size; +int hantro_postproc_init(struct hantro_ctx *ctx) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; + unsigned int num_buffers = vb2_get_num_buffers(cap_queue); + unsigned int i; + int ret; + + for (i = 0; i < num_buffers; i++) { + ret = hantro_postproc_alloc(ctx, i); + if (ret) + return ret; } + return 0; } +dma_addr_t +hantro_postproc_get_dec_buf_addr(struct hantro_ctx *ctx, int index) +{ + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index]; + unsigned int buf_size = hantro_postproc_buffer_size(ctx); + struct hantro_dev *vpu = ctx->dev; + int ret; + + if (priv->size < buf_size && priv->cpu) { + /* buffer is too small, release it */ + dma_free_attrs(vpu->dev, priv->size, priv->cpu, + priv->dma, priv->attrs); + priv->cpu = NULL; + } + + if (!priv->cpu) { + /* buffer not already allocated, try getting a new one */ + ret = hantro_postproc_alloc(ctx, index); + if (ret) + return 0; + } + + if (!priv->cpu) + return 0; + + return priv->dma; +} + static void hantro_postproc_g1_disable(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index db145519f..941fa23c2 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -514,25 +514,14 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, return ret; if (!ctx->is_encoder) { - struct vb2_queue *peer_vq; - /* * In order to support dynamic resolution change, * the decoder admits a resolution change, as long - * as the pixelformat remains. Can't be done if streaming. - */ - if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && - pix_mp->pixelformat != ctx->src_fmt.pixelformat)) - return -EBUSY; - /* - * Since format change on the OUTPUT queue will reset - * the CAPTURE queue, we can't allow doing so - * when the CAPTURE queue has buffers allocated. + * as the pixelformat remains. */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(peer_vq)) + if (vb2_is_streaming(vq) && pix_mp->pixelformat != ctx->src_fmt.pixelformat) { return -EBUSY; + } } else { /* * The encoder doesn't admit a format change if @@ -577,15 +566,8 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, static int hantro_set_fmt_cap(struct hantro_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp) { - struct vb2_queue *vq; int ret; - /* Change not allowed if queue is busy. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - if (ctx->is_encoder) { struct vb2_queue *peer_vq; @@ -936,7 +918,7 @@ static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) } if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) { - ret = hantro_postproc_alloc(ctx); + ret = hantro_postproc_init(ctx); if (ret) goto err_codec_exit; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 5de6b6694..31e9e92e7 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -89,10 +89,10 @@ static int video_mux_link_setup(struct media_entity *entity, /* Propagate the active format to the source */ sd_state = v4l2_subdev_lock_and_get_active_state(sd); - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, - source_pad); - *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state, - vmux->active); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, + source_pad); + *source_mbusformat = *v4l2_subdev_state_get_format(sd_state, + vmux->active); v4l2_subdev_unlock_state(sd_state); } else { if (vmux->active != local->index) @@ -154,11 +154,11 @@ static int video_mux_set_format(struct v4l2_subdev *sd, struct media_pad *pad = &vmux->pads[sdformat->pad]; u16 source_pad = sd->entity.num_pads - 1; - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, sdformat->pad); + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); if (!mbusformat) return -EINVAL; - source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, source_pad); + source_mbusformat = v4l2_subdev_state_get_format(sd_state, source_pad); if (!source_mbusformat) return -EINVAL; @@ -268,8 +268,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, /* Source pad mirrors active sink pad, no limitations on sink pads */ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0) - sdformat->format = *v4l2_subdev_get_pad_format(sd, sd_state, - vmux->active); + sdformat->format = *v4l2_subdev_state_get_format(sd_state, + vmux->active); *mbusformat = sdformat->format; @@ -282,8 +282,8 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } -static int video_mux_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int video_mux_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); struct v4l2_mbus_framefmt *mbusformat; @@ -292,7 +292,7 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, mutex_lock(&vmux->lock); for (i = 0; i < sd->entity.num_pads; i++) { - mbusformat = v4l2_subdev_get_pad_format(sd, sd_state, i); + mbusformat = v4l2_subdev_state_get_format(sd_state, i); *mbusformat = video_mux_format_mbus_default; } @@ -302,7 +302,6 @@ static int video_mux_init_cfg(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { - .init_cfg = video_mux_init_cfg, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = video_mux_set_format, }; @@ -312,6 +311,10 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { .video = &video_mux_subdev_video_ops, }; +static const struct v4l2_subdev_internal_ops video_mux_internal_ops = { + .init_state = video_mux_init_state, +}; + static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asd) @@ -400,6 +403,7 @@ static int video_mux_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vmux); v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops); + vmux->subdev.internal_ops = &video_mux_internal_ops; snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%pOFn", np); vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; vmux->subdev.dev = dev; diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 5b53745fe..f953d5474 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -386,14 +386,6 @@ static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) } } -/** - * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver - * @sd: Pointer to V4L2 subdevice structure - * - * This function prints the current status of Xilinx MIPI CSI-2 - * - * Return: 0 on success - */ static int xcsi2rxss_log_status(struct v4l2_subdev *sd) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); @@ -631,16 +623,6 @@ static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) return IRQ_HANDLED; } -/** - * xcsi2rxss_s_stream - It is used to start/stop the streaming. - * @sd: V4L2 Sub device - * @enable: Flag (True / False) - * - * This function controls the start or stop of streaming for the - * Xilinx MIPI CSI-2 Rx Subsystem. - * - * Return: 0 on success, errors otherwise - */ static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); @@ -671,8 +653,7 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xcsi2rxss->format; default: @@ -680,18 +661,8 @@ __xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, } } -/** - * xcsi2rxss_init_cfg - Initialise the pad format config to default - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * - * This function is used to initialize the pad format with the default - * values. - * - * Return: 0 on success - */ -static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int xcsi2rxss_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); struct v4l2_mbus_framefmt *format; @@ -699,7 +670,7 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, mutex_lock(&xcsi2rxss->lock); for (i = 0; i < XCSI_MEDIA_PADS; i++) { - format = v4l2_subdev_get_try_format(sd, sd_state, i); + format = v4l2_subdev_state_get_format(sd_state, i); *format = xcsi2rxss->default_format; } mutex_unlock(&xcsi2rxss->lock); @@ -707,16 +678,6 @@ static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, return 0; } -/** - * xcsi2rxss_get_format - Get the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to get the pad format information. - * - * Return: 0 on success - */ static int xcsi2rxss_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -732,19 +693,6 @@ static int xcsi2rxss_get_format(struct v4l2_subdev *sd, return 0; } -/** - * xcsi2rxss_set_format - This is used to set the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to set the pad format. Since the pad format is fixed - * in hardware, it can't be modified on run time. So when a format set is - * requested by application, all parameters except the format type is saved - * for the pad and the original pad format is sent back to the application. - * - * Return: 0 on success - */ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -789,14 +737,6 @@ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, return 0; } -/* - * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration - * @sd: pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * Return: -EINVAL or zero on success - */ static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -840,7 +780,6 @@ static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { }; static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { - .init_cfg = xcsi2rxss_init_cfg, .get_fmt = xcsi2rxss_get_format, .set_fmt = xcsi2rxss_set_format, .enum_mbus_code = xcsi2rxss_enum_mbus_code, @@ -853,6 +792,10 @@ static const struct v4l2_subdev_ops xcsi2rxss_ops = { .pad = &xcsi2rxss_pad_ops }; +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = { + .init_state = xcsi2rxss_init_state, +}; + static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) { struct device *dev = xcsi2rxss->dev; @@ -1030,6 +973,7 @@ static int xcsi2rxss_probe(struct platform_device *pdev) /* Initialize V4L2 subdevice and media entity */ subdev = &xcsi2rxss->subdev; v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->internal_ops = &xcsi2rxss_internal_ops; subdev->dev = dev; strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 80353ca44..e05e528ff 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -256,8 +256,7 @@ __xtpg_get_pad_format(struct xtpg_device *xtpg, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, - sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &xtpg->formats[pad]; default: @@ -326,7 +325,7 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, { struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -354,11 +353,11 @@ static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct xtpg_device *xtpg = to_tpg(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); + format = v4l2_subdev_state_get_format(fh->state, 0); *format = xtpg->default_format; if (xtpg->npads == 2) { - format = v4l2_subdev_get_try_format(subdev, fh->state, 1); + format = v4l2_subdev_state_get_format(fh->state, 1); *format = xtpg->default_format; } diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index 5b214bf7f..f1574edd2 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -260,7 +260,7 @@ int xvip_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); + format = v4l2_subdev_state_get_format(sd_state, code->pad); code->code = format->code; @@ -295,7 +295,7 @@ int xvip_enum_frame_size(struct v4l2_subdev *subdev, if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + format = v4l2_subdev_state_get_format(sd_state, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 2afe67ffa..74d69ce22 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -319,6 +319,7 @@ config IR_PWM_TX tristate "PWM IR transmitter" depends on LIRC depends on PWM + depends on HIGH_RES_TIMERS depends on OF help Say Y if you want to use a PWM based IR transmitter. This is diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index 0034f615b..de5bb9a08 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -9,7 +9,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -251,7 +253,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct hix5hd2_ir_priv *priv; struct device_node *node = pdev->dev.of_node; - const struct of_device_id *of_id; const char *map_name; int ret; @@ -259,12 +260,11 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - of_id = of_match_device(hix5hd2_ir_table, dev); - if (!of_id) { + priv->socdata = device_get_match_data(dev); + if (!priv->socdata) { dev_err(dev, "Unable to initialize IR data\n"); return -ENODEV; } - priv->socdata = of_id->data; priv->regmap = syscon_regmap_lookup_by_phandle(node, "hisilicon,power-syscon"); diff --git a/drivers/media/rc/meson-ir-tx.c b/drivers/media/rc/meson-ir-tx.c index 6355b7989..fded2c256 100644 --- a/drivers/media/rc/meson-ir-tx.c +++ b/drivers/media/rc/meson-ir-tx.c @@ -305,7 +305,7 @@ static int meson_irtx_mod_clock_probe(struct meson_irtx *ir, return 0; } -static int __init meson_irtx_probe(struct platform_device *pdev) +static int meson_irtx_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct meson_irtx *ir; @@ -333,20 +333,17 @@ static int __init meson_irtx_probe(struct platform_device *pdev) spin_lock_init(&ir->lock); ret = meson_irtx_mod_clock_probe(ir, &clk_nr); - if (ret) { - dev_err(dev, "modulator clock setup failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "modulator clock setup failed\n"); + meson_irtx_setup(ir, clk_nr); ret = devm_request_irq(dev, irq, meson_irtx_irqhandler, IRQF_TRIGGER_RISING, DRIVER_NAME, ir); - if (ret) { - dev_err(dev, "irq request failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "irq request failed\n"); rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX); if (!rc) @@ -360,25 +357,15 @@ static int __init meson_irtx_probe(struct platform_device *pdev) rc->s_tx_carrier = meson_irtx_set_carrier; rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle; - ret = rc_register_device(rc); + ret = devm_rc_register_device(dev, rc); if (ret < 0) { - dev_err(dev, "rc_dev registration failed\n"); rc_free_device(rc); - return ret; + return dev_err_probe(dev, ret, "rc_dev registration failed\n"); } - platform_set_drvdata(pdev, rc); - return 0; } -static void meson_irtx_remove(struct platform_device *pdev) -{ - struct rc_dev *rc = platform_get_drvdata(pdev); - - rc_unregister_device(rc); -} - static const struct of_device_id meson_irtx_dt_match[] = { { .compatible = "amlogic,meson-g12a-ir-tx", @@ -388,14 +375,13 @@ static const struct of_device_id meson_irtx_dt_match[] = { MODULE_DEVICE_TABLE(of, meson_irtx_dt_match); static struct platform_driver meson_irtx_pd = { - .remove_new = meson_irtx_remove, + .probe = meson_irtx_probe, .driver = { .name = DRIVER_NAME, .of_match_table = meson_irtx_dt_match, }, }; - -module_platform_driver_probe(meson_irtx_pd, meson_irtx_probe); +module_platform_driver(meson_irtx_pd); MODULE_DESCRIPTION("Meson IR TX driver"); MODULE_AUTHOR("Viktor Prutyanov "); diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c index c5f37c03a..fe368aebb 100644 --- a/drivers/media/rc/pwm-ir-tx.c +++ b/drivers/media/rc/pwm-ir-tx.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #define DRIVER_NAME "pwm-ir-tx" @@ -17,8 +19,14 @@ struct pwm_ir { struct pwm_device *pwm; - unsigned int carrier; - unsigned int duty_cycle; + struct hrtimer timer; + struct completion tx_done; + struct pwm_state *state; + u32 carrier; + u32 duty_cycle; + const unsigned int *txbuf; + unsigned int txbuf_len; + unsigned int txbuf_index; }; static const struct of_device_id pwm_ir_of_match[] = { @@ -49,8 +57,8 @@ static int pwm_ir_set_carrier(struct rc_dev *dev, u32 carrier) return 0; } -static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, - unsigned int count) +static int pwm_ir_tx_sleep(struct rc_dev *dev, unsigned int *txbuf, + unsigned int count) { struct pwm_ir *pwm_ir = dev->priv; struct pwm_device *pwm = pwm_ir->pwm; @@ -68,7 +76,7 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, for (i = 0; i < count; i++) { state.enabled = !(i % 2); - pwm_apply_state(pwm, &state); + pwm_apply_might_sleep(pwm, &state); edge = ktime_add_us(edge, txbuf[i]); delta = ktime_us_delta(edge, ktime_get()); @@ -77,11 +85,67 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, } state.enabled = false; - pwm_apply_state(pwm, &state); + pwm_apply_might_sleep(pwm, &state); return count; } +static int pwm_ir_tx_atomic(struct rc_dev *dev, unsigned int *txbuf, + unsigned int count) +{ + struct pwm_ir *pwm_ir = dev->priv; + struct pwm_device *pwm = pwm_ir->pwm; + struct pwm_state state; + + pwm_init_state(pwm, &state); + + state.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier); + pwm_set_relative_duty_cycle(&state, pwm_ir->duty_cycle, 100); + + pwm_ir->txbuf = txbuf; + pwm_ir->txbuf_len = count; + pwm_ir->txbuf_index = 0; + pwm_ir->state = &state; + + hrtimer_start(&pwm_ir->timer, 0, HRTIMER_MODE_REL); + + wait_for_completion(&pwm_ir->tx_done); + + return count; +} + +static enum hrtimer_restart pwm_ir_timer(struct hrtimer *timer) +{ + struct pwm_ir *pwm_ir = container_of(timer, struct pwm_ir, timer); + ktime_t now; + + /* + * If we happen to hit an odd latency spike, loop through the + * pulses until we catch up. + */ + do { + u64 ns; + + pwm_ir->state->enabled = !(pwm_ir->txbuf_index % 2); + pwm_apply_atomic(pwm_ir->pwm, pwm_ir->state); + + if (pwm_ir->txbuf_index >= pwm_ir->txbuf_len) { + complete(&pwm_ir->tx_done); + + return HRTIMER_NORESTART; + } + + ns = US_TO_NS(pwm_ir->txbuf[pwm_ir->txbuf_index]); + hrtimer_add_expires_ns(timer, ns); + + pwm_ir->txbuf_index++; + + now = timer->base->get_time(); + } while (hrtimer_get_expires_tv64(timer) < now); + + return HRTIMER_RESTART; +} + static int pwm_ir_probe(struct platform_device *pdev) { struct pwm_ir *pwm_ir; @@ -103,10 +167,19 @@ static int pwm_ir_probe(struct platform_device *pdev) if (!rcdev) return -ENOMEM; + if (pwm_might_sleep(pwm_ir->pwm)) { + dev_info(&pdev->dev, "TX will not be accurate as PWM device might sleep\n"); + rcdev->tx_ir = pwm_ir_tx_sleep; + } else { + init_completion(&pwm_ir->tx_done); + hrtimer_init(&pwm_ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pwm_ir->timer.function = pwm_ir_timer; + rcdev->tx_ir = pwm_ir_tx_atomic; + } + rcdev->priv = pwm_ir; rcdev->driver_name = DRIVER_NAME; rcdev->device_name = DEVICE_NAME; - rcdev->tx_ir = pwm_ir_tx; rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle; rcdev->s_tx_carrier = pwm_ir_set_carrier; diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig index 459b433e9..5a5379524 100644 --- a/drivers/media/test-drivers/Kconfig +++ b/drivers/media/test-drivers/Kconfig @@ -12,7 +12,6 @@ config VIDEO_VIM2M select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API help This is a virtual test device for the memory-to-memory driver framework. diff --git a/drivers/media/test-drivers/vicodec/Kconfig b/drivers/media/test-drivers/vicodec/Kconfig index a7a828eec..4ea0689c3 100644 --- a/drivers/media/test-drivers/vicodec/Kconfig +++ b/drivers/media/test-drivers/vicodec/Kconfig @@ -5,7 +5,6 @@ config VIDEO_VICODEC select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API help Driver for a Virtual Codec diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 6f0e20df7..e13f5452b 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1240,6 +1240,12 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, struct vicodec_ctx *ctx = file2ctx(file); int ret; + /* + * This ioctl should not be used with a stateless codec that doesn't + * support holding buffers and the associated flush command. + */ + WARN_ON(ctx->is_stateless); + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); if (ret < 0) return ret; @@ -1718,6 +1724,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dst_vq->max_num_buffers = 64; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &vicodec_qops; @@ -2025,7 +2032,7 @@ static const struct v4l2_m2m_ops m2m_ops = { static int register_instance(struct vicodec_dev *dev, struct vicodec_dev_instance *dev_instance, - const char *name, bool is_enc) + const char *name, bool is_enc, bool is_stateless) { struct video_device *vfd; int ret; @@ -2045,10 +2052,11 @@ static int register_instance(struct vicodec_dev *dev, strscpy(vfd->name, name, sizeof(vfd->name)); vfd->device_caps = V4L2_CAP_STREAMING | (multiplanar ? V4L2_CAP_VIDEO_M2M_MPLANE : V4L2_CAP_VIDEO_M2M); - if (is_enc) { + if (is_enc || is_stateless) { v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); - } else { + } + if (!is_enc) { v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); } @@ -2107,17 +2115,17 @@ static int vicodec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); ret = register_instance(dev, &dev->stateful_enc, "stateful-encoder", - true); + true, false); if (ret) goto unreg_dev; ret = register_instance(dev, &dev->stateful_dec, "stateful-decoder", - false); + false, false); if (ret) goto unreg_sf_enc; ret = register_instance(dev, &dev->stateless_dec, "stateless-decoder", - false); + false, true); if (ret) goto unreg_sf_dec; diff --git a/drivers/media/test-drivers/vidtv/vidtv_pes.c b/drivers/media/test-drivers/vidtv/vidtv_pes.c index 782e5e7fb..6a360a0a3 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_pes.c +++ b/drivers/media/test-drivers/vidtv/vidtv_pes.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ #include +#include #include #include diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index aa944270e..2a2d19d23 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -432,7 +432,7 @@ static struct vimc_ent_device *vimc_capture_add(struct vimc_device *vimc, q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &vcapture->lock; q->dev = v4l2_dev->dev; diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index f671251fd..d72ed086e 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -150,18 +150,18 @@ static bool vimc_debayer_src_code_is_valid(u32 code) return false; } -static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_debayer_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; unsigned int i; - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); + mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = sink_fmt_default; mf->code = vdebayer->src_code; } @@ -221,7 +221,7 @@ static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, /* Get the current sink format */ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, 0) : + *v4l2_subdev_state_get_format(sd_state, 0) : vdebayer->sink_fmt; /* Set the right code for the source pad */ @@ -267,8 +267,8 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, sink_fmt = &vdebayer->sink_fmt; src_code = &vdebayer->src_code; } else { - sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - src_code = &v4l2_subdev_get_try_format(sd, sd_state, 1)->code; + sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); + src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; } /* @@ -307,7 +307,6 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { - .init_cfg = vimc_debayer_init_cfg, .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, .get_fmt = vimc_debayer_get_fmt, @@ -395,6 +394,10 @@ static const struct v4l2_subdev_ops vimc_debayer_ops = { .video = &vimc_debayer_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_debayer_internal_ops = { + .init_state = vimc_debayer_init_state, +}; + static unsigned int vimc_debayer_get_val(const u8 *bytes, const unsigned int n_bytes) { @@ -595,6 +598,8 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, if (ret) goto err_free_hdl; + vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; + vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index b671774e2..afe13d6af 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -70,19 +70,19 @@ vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) return r; } -static int vimc_scaler_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_scaler_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf; struct v4l2_rect *r; unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } - r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCALER_SINK); + r = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); *r = crop_rect_default; return 0; @@ -138,7 +138,7 @@ vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&vscaler->sd, sd_state, pad); + return v4l2_subdev_state_get_format(sd_state, pad); else return &vscaler->fmt[pad]; } @@ -149,8 +149,7 @@ vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&vscaler->sd, sd_state, - VIMC_SCALER_SINK); + return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); else return &vscaler->crop_rect; } @@ -293,7 +292,6 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { - .init_cfg = vimc_scaler_init_cfg, .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, .get_fmt = vimc_scaler_get_fmt, @@ -348,6 +346,10 @@ static const struct v4l2_subdev_ops vimc_scaler_ops = { .video = &vimc_scaler_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { + .init_state = vimc_scaler_init_state, +}; + static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { @@ -425,6 +427,8 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, return ERR_PTR(ret); } + vscaler->sd.internal_ops = &vimc_scaler_internal_ops; + vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 41a3dce2d..5e34b1aed 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -41,15 +41,15 @@ static const struct v4l2_mbus_framefmt fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -static int vimc_sensor_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_sensor_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { unsigned int i; for (i = 0; i < sd->entity.num_pads; i++) { struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_get_try_format(sd, sd_state, i); + mf = v4l2_subdev_state_get_format(sd_state, i); *mf = fmt_default; } @@ -100,7 +100,7 @@ static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, container_of(sd, struct vimc_sensor_device, sd); fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : + *v4l2_subdev_state_get_format(sd_state, fmt->pad) : vsensor->mbus_format; return 0; @@ -159,7 +159,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, mf = &vsensor->mbus_format; } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); } /* Set the new format */ @@ -183,7 +183,6 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { - .init_cfg = vimc_sensor_init_cfg, .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, .get_fmt = vimc_sensor_get_fmt, @@ -294,6 +293,10 @@ static const struct v4l2_subdev_ops vimc_sensor_ops = { .video = &vimc_sensor_video_ops, }; +static const struct v4l2_subdev_internal_ops vimc_sensor_internal_ops = { + .init_state = vimc_sensor_init_state, +}; + static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) { struct vimc_sensor_device *vsensor = @@ -429,6 +432,8 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, if (ret) goto err_free_tpg; + vsensor->sd.internal_ops = &vimc_sensor_internal_ops; + vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/test-drivers/visl/Kconfig b/drivers/media/test-drivers/visl/Kconfig index 7508b904f..37be9f267 100644 --- a/drivers/media/test-drivers/visl/Kconfig +++ b/drivers/media/test-drivers/visl/Kconfig @@ -7,7 +7,6 @@ config VIDEO_VISL select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API select VIDEO_V4L2_TPG help diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c index 9970dc739..68dac8962 100644 --- a/drivers/media/test-drivers/visl/visl-core.c +++ b/drivers/media/test-drivers/visl/visl-core.c @@ -211,6 +211,27 @@ const struct visl_ctrls visl_hevc_ctrls = { .num_ctrls = ARRAY_SIZE(visl_hevc_ctrl_descs), }; +static const struct visl_ctrl_desc visl_av1_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_AV1_FRAME, + }, + { + .cfg.id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY, + .cfg.dims = { V4L2_AV1_MAX_TILE_COUNT }, + }, + { + .cfg.id = V4L2_CID_STATELESS_AV1_SEQUENCE, + }, + { + .cfg.id = V4L2_CID_STATELESS_AV1_FILM_GRAIN, + }, +}; + +const struct visl_ctrls visl_av1_ctrls = { + .ctrls = visl_av1_ctrl_descs, + .num_ctrls = ARRAY_SIZE(visl_av1_ctrl_descs), +}; + struct v4l2_ctrl *visl_find_control(struct visl_ctx *ctx, u32 id) { struct v4l2_ctrl_handler *hdl = &ctx->hdl; diff --git a/drivers/media/test-drivers/visl/visl-dec.c b/drivers/media/test-drivers/visl/visl-dec.c index 318d675e5..f21260054 100644 --- a/drivers/media/test-drivers/visl/visl-dec.c +++ b/drivers/media/test-drivers/visl/visl-dec.c @@ -13,12 +13,21 @@ #include "visl-trace-vp9.h" #include "visl-trace-h264.h" #include "visl-trace-hevc.h" +#include "visl-trace-av1.h" #include #include #include #include +#define LAST_BUF_IDX (V4L2_AV1_REF_LAST_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define LAST2_BUF_IDX (V4L2_AV1_REF_LAST2_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define LAST3_BUF_IDX (V4L2_AV1_REF_LAST3_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define GOLDEN_BUF_IDX (V4L2_AV1_REF_GOLDEN_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define BWD_BUF_IDX (V4L2_AV1_REF_BWDREF_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define ALT2_BUF_IDX (V4L2_AV1_REF_ALTREF2_FRAME - V4L2_AV1_REF_LAST_FRAME) +#define ALT_BUF_IDX (V4L2_AV1_REF_ALTREF_FRAME - V4L2_AV1_REF_LAST_FRAME) + static void *plane_vaddr(struct tpg_data *tpg, struct vb2_buffer *buf, u32 p, u32 bpl[TPG_MAX_PLANES], u32 h) { @@ -152,6 +161,55 @@ static void visl_get_ref_frames(struct visl_ctx *ctx, u8 *buf, break; } + + case VISL_CODEC_AV1: { + int idx_last = run->av1.frame->ref_frame_idx[LAST_BUF_IDX]; + int idx_last2 = run->av1.frame->ref_frame_idx[LAST2_BUF_IDX]; + int idx_last3 = run->av1.frame->ref_frame_idx[LAST3_BUF_IDX]; + int idx_golden = run->av1.frame->ref_frame_idx[GOLDEN_BUF_IDX]; + int idx_bwd = run->av1.frame->ref_frame_idx[BWD_BUF_IDX]; + int idx_alt2 = run->av1.frame->ref_frame_idx[ALT2_BUF_IDX]; + int idx_alt = run->av1.frame->ref_frame_idx[ALT_BUF_IDX]; + + struct vb2_buffer *ref_last = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_last]); + struct vb2_buffer *ref_last2 = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_last2]); + struct vb2_buffer *ref_last3 = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_last3]); + struct vb2_buffer *ref_golden = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_golden]); + struct vb2_buffer *ref_bwd = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_bwd]); + struct vb2_buffer *ref_alt2 = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_alt2]); + struct vb2_buffer *ref_alt = + vb2_find_buffer(cap_q, run->av1.frame->reference_frame_ts[idx_alt]); + + scnprintf(buf, buflen, + "ref_last_ts: %llu, vb2_idx: %d\n" + "ref_last2_ts: %llu, vb2_idx: %d\n" + "ref_last3_ts: %llu, vb2_idx: %d\n" + "ref_golden_ts: %llu, vb2_idx: %d\n" + "ref_bwd_ts: %llu, vb2_idx: %d\n" + "ref_alt2_ts: %llu, vb2_idx: %d\n" + "ref_alt_ts: %llu, vb2_idx: %d\n", + run->av1.frame->reference_frame_ts[idx_last], + ref_last ? ref_last->index : -1, + run->av1.frame->reference_frame_ts[idx_last2], + ref_last2 ? ref_last2->index : -1, + run->av1.frame->reference_frame_ts[idx_last3], + ref_last3 ? ref_last3->index : -1, + run->av1.frame->reference_frame_ts[idx_golden], + ref_golden ? ref_golden->index : -1, + run->av1.frame->reference_frame_ts[idx_bwd], + ref_bwd ? ref_bwd->index : -1, + run->av1.frame->reference_frame_ts[idx_alt2], + ref_alt2 ? ref_alt2->index : -1, + run->av1.frame->reference_frame_ts[idx_alt], + ref_alt ? ref_alt->index : -1); + break; + } } } @@ -287,16 +345,23 @@ static void visl_tpg_fill(struct visl_ctx *ctx, struct visl_run *run) frame_dprintk(ctx->dev, run->dst->sequence, "%s\n", buf); len = 0; - for (i = 0; i < out_q->num_buffers; i++) { + for (i = 0; i < vb2_get_num_buffers(out_q); i++) { char entry[] = "index: %u, state: %s, request_fd: %d, "; u32 old_len = len; - char *q_status = visl_get_vb2_state(out_q->bufs[i]->state); + struct vb2_buffer *vb2; + char *q_status; + + vb2 = vb2_get_buffer(out_q, i); + if (!vb2) + continue; + + q_status = visl_get_vb2_state(vb2->state); len += scnprintf(&buf[len], TPG_STR_BUF_SZ - len, entry, i, q_status, - to_vb2_v4l2_buffer(out_q->bufs[i])->request_fd); + to_vb2_v4l2_buffer(vb2)->request_fd); - len += visl_fill_bytesused(to_vb2_v4l2_buffer(out_q->bufs[i]), + len += visl_fill_bytesused(to_vb2_v4l2_buffer(vb2), &buf[len], TPG_STR_BUF_SZ - len); @@ -340,15 +405,22 @@ static void visl_tpg_fill(struct visl_ctx *ctx, struct visl_run *run) frame_dprintk(ctx->dev, run->dst->sequence, "%s\n", buf); len = 0; - for (i = 0; i < cap_q->num_buffers; i++) { + for (i = 0; i < vb2_get_num_buffers(cap_q); i++) { u32 old_len = len; - char *q_status = visl_get_vb2_state(cap_q->bufs[i]->state); + struct vb2_buffer *vb2; + char *q_status; + + vb2 = vb2_get_buffer(cap_q, i); + if (!vb2) + continue; + + q_status = visl_get_vb2_state(vb2->state); len += scnprintf(&buf[len], TPG_STR_BUF_SZ - len, "index: %u, status: %s, timestamp: %llu, is_held: %d", - cap_q->bufs[i]->index, q_status, - cap_q->bufs[i]->timestamp, - to_vb2_v4l2_buffer(cap_q->bufs[i])->is_held); + vb2->index, q_status, + vb2->timestamp, + to_vb2_v4l2_buffer(vb2)->is_held); tpg_gen_text(&ctx->tpg, basep, line++ * line_height, 16, &buf[old_len]); frame_dprintk(ctx->dev, run->dst->sequence, "%s", &buf[old_len]); @@ -410,7 +482,13 @@ static void visl_trace_ctrls(struct visl_ctx *ctx, struct visl_run *run) trace_v4l2_hevc_dpb_entry(&run->hevc.dpram->dpb[i]); trace_v4l2_hevc_pred_weight_table(&run->hevc.spram->pred_weight_table); - break; + break; + case VISL_CODEC_AV1: + trace_v4l2_ctrl_av1_sequence(run->av1.seq); + trace_v4l2_ctrl_av1_frame(run->av1.frame); + trace_v4l2_ctrl_av1_film_grain(run->av1.grain); + trace_v4l2_ctrl_av1_tile_group_entry(run->av1.tge); + break; } } @@ -469,6 +547,12 @@ void visl_device_run(void *priv) run.hevc.sm = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); run.hevc.dpram = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); break; + case VISL_CODEC_AV1: + run.av1.seq = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE); + run.av1.frame = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_FRAME); + run.av1.tge = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY); + run.av1.grain = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN); + break; } frame_dprintk(ctx->dev, run.dst->sequence, diff --git a/drivers/media/test-drivers/visl/visl-dec.h b/drivers/media/test-drivers/visl/visl-dec.h index 4a706a9de..c2c2ef3a8 100644 --- a/drivers/media/test-drivers/visl/visl-dec.h +++ b/drivers/media/test-drivers/visl/visl-dec.h @@ -45,6 +45,13 @@ struct visl_hevc_run { const struct v4l2_ctrl_hevc_decode_params *dpram; }; +struct visl_av1_run { + const struct v4l2_ctrl_av1_sequence *seq; + const struct v4l2_ctrl_av1_frame *frame; + const struct v4l2_ctrl_av1_tile_group_entry *tge; + const struct v4l2_ctrl_av1_film_grain *grain; +}; + struct visl_run { struct vb2_v4l2_buffer *src; struct vb2_v4l2_buffer *dst; @@ -56,6 +63,7 @@ struct visl_run { struct visl_vp9_run vp9; struct visl_h264_run h264; struct visl_hevc_run hevc; + struct visl_av1_run av1; }; }; diff --git a/drivers/media/test-drivers/visl/visl-trace-av1.h b/drivers/media/test-drivers/visl/visl-trace-av1.h new file mode 100644 index 000000000..09f205de5 --- /dev/null +++ b/drivers/media/test-drivers/visl/visl-trace-av1.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(_VISL_TRACE_AV1_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _VISL_TRACE_AV1_H_ + +#include +#include "visl.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM visl_av1_controls + +DECLARE_EVENT_CLASS(v4l2_ctrl_av1_seq_tmpl, + TP_PROTO(const struct v4l2_ctrl_av1_sequence *s), + TP_ARGS(s), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_av1_sequence, s)), + TP_fast_assign(__entry->s = *s;), + TP_printk("\nflags %s\nseq_profile: %u\norder_hint_bits: %u\nbit_depth: %u\n" + "max_frame_width_minus_1: %u\nmax_frame_height_minus_1: %u\n", + __print_flags(__entry->s.flags, "|", + {V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE, "STILL_PICTURE"}, + {V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK, "USE_128X128_SUPERBLOCK"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA, "ENABLE_FILTER_INTRA"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER, "ENABLE_INTRA_EDGE_FILTER"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND, "ENABLE_INTERINTRA_COMPOUND"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND, "ENABLE_MASKED_COMPOUND"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION, "ENABLE_WARPED_MOTION"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER, "ENABLE_DUAL_FILTER"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT, "ENABLE_ORDER_HINT"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP, "ENABLE_JNT_COMP"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS, "ENABLE_REF_FRAME_MVS"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES, "ENABLE_SUPERRES"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF, "ENABLE_CDEF"}, + {V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION, "ENABLE_RESTORATION"}, + {V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME, "MONO_CHROME"}, + {V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE, "COLOR_RANGE"}, + {V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X, "SUBSAMPLING_X"}, + {V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y, "SUBSAMPLING_Y"}, + {V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT, "FILM_GRAIN_PARAMS_PRESENT"}, + {V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q, "SEPARATE_UV_DELTA_Q"}), + __entry->s.seq_profile, + __entry->s.order_hint_bits, + __entry->s.bit_depth, + __entry->s.max_frame_width_minus_1, + __entry->s.max_frame_height_minus_1 + ) +); + +DECLARE_EVENT_CLASS(v4l2_ctrl_av1_tge_tmpl, + TP_PROTO(const struct v4l2_ctrl_av1_tile_group_entry *t), + TP_ARGS(t), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_av1_tile_group_entry, t)), + TP_fast_assign(__entry->t = *t;), + TP_printk("\ntile_offset: %u\n tile_size: %u\n tile_row: %u\ntile_col: %u\n", + __entry->t.tile_offset, + __entry->t.tile_size, + __entry->t.tile_row, + __entry->t.tile_col + ) +); + +DECLARE_EVENT_CLASS(v4l2_ctrl_av1_frame_tmpl, + TP_PROTO(const struct v4l2_ctrl_av1_frame *f), + TP_ARGS(f), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_av1_frame, f)), + TP_fast_assign(__entry->f = *f;), + TP_printk("\ntile_info.flags: %s\ntile_info.context_update_tile_id: %u\n" + "tile_info.tile_cols: %u\ntile_info.tile_rows: %u\n" + "tile_info.mi_col_starts: %s\ntile_info.mi_row_starts: %s\n" + "tile_info.width_in_sbs_minus_1: %s\ntile_info.height_in_sbs_minus_1: %s\n" + "tile_info.tile_size_bytes: %u\nquantization.flags: %s\n" + "quantization.base_q_idx: %u\nquantization.delta_q_y_dc: %d\n" + "quantization.delta_q_u_dc: %d\nquantization.delta_q_u_ac: %d\n" + "quantization.delta_q_v_dc: %d\nquantization.delta_q_v_ac: %d\n" + "quantization.qm_y: %u\nquantization.qm_u: %u\nquantization.qm_v: %u\n" + "quantization.delta_q_res: %u\nsuperres_denom: %u\nsegmentation.flags: %s\n" + "segmentation.last_active_seg_id: %u\nsegmentation.feature_enabled:%s\n" + "loop_filter.flags: %s\nloop_filter.level: %s\nloop_filter.sharpness: %u\n" + "loop_filter.ref_deltas: %s\nloop_filter.mode_deltas: %s\n" + "loop_filter.delta_lf_res: %u\ncdef.damping_minus_3: %u\ncdef.bits: %u\n" + "cdef.y_pri_strength: %s\ncdef.y_sec_strength: %s\n" + "cdef.uv_pri_strength: %s\ncdef.uv_sec_strength:%s\nskip_mode_frame: %s\n" + "primary_ref_frame: %u\nloop_restoration.flags: %s\n" + "loop_restoration.lr_unit_shift: %u\nloop_restoration.lr_uv_shift: %u\n" + "loop_restoration.frame_restoration_type: %s\n" + "loop_restoration.loop_restoration_size: %s\nflags: %s\norder_hint: %u\n" + "upscaled_width: %u\nframe_width_minus_1: %u\nframe_height_minus_1: %u\n" + "render_width_minus_1: %u\nrender_height_minus_1: %u\ncurrent_frame_id: %u\n" + "buffer_removal_time: %s\norder_hints: %s\nreference_frame_ts: %s\n" + "ref_frame_idx: %s\nrefresh_frame_flags: %u\n", + __print_flags(__entry->f.tile_info.flags, "|", + {V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING, "UNIFORM_TILE_SPACING"}), + __entry->f.tile_info.context_update_tile_id, + __entry->f.tile_info.tile_cols, + __entry->f.tile_info.tile_rows, + __print_array(__entry->f.tile_info.mi_col_starts, + ARRAY_SIZE(__entry->f.tile_info.mi_col_starts), + sizeof(__entry->f.tile_info.mi_col_starts[0])), + __print_array(__entry->f.tile_info.mi_row_starts, + ARRAY_SIZE(__entry->f.tile_info.mi_row_starts), + sizeof(__entry->f.tile_info.mi_row_starts[0])), + __print_array(__entry->f.tile_info.width_in_sbs_minus_1, + ARRAY_SIZE(__entry->f.tile_info.width_in_sbs_minus_1), + sizeof(__entry->f.tile_info.width_in_sbs_minus_1[0])), + __print_array(__entry->f.tile_info.height_in_sbs_minus_1, + ARRAY_SIZE(__entry->f.tile_info.height_in_sbs_minus_1), + sizeof(__entry->f.tile_info.height_in_sbs_minus_1[0])), + __entry->f.tile_info.tile_size_bytes, + __print_flags(__entry->f.quantization.flags, "|", + {V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA, "DIFF_UV_DELTA"}, + {V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX, "USING_QMATRIX"}, + {V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT, "DELTA_Q_PRESENT"}), + __entry->f.quantization.base_q_idx, + __entry->f.quantization.delta_q_y_dc, + __entry->f.quantization.delta_q_u_dc, + __entry->f.quantization.delta_q_u_ac, + __entry->f.quantization.delta_q_v_dc, + __entry->f.quantization.delta_q_v_ac, + __entry->f.quantization.qm_y, + __entry->f.quantization.qm_u, + __entry->f.quantization.qm_v, + __entry->f.quantization.delta_q_res, + __entry->f.superres_denom, + __print_flags(__entry->f.segmentation.flags, "|", + {V4L2_AV1_SEGMENTATION_FLAG_ENABLED, "ENABLED"}, + {V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP, "UPDATE_MAP"}, + {V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE, "TEMPORAL_UPDATE"}, + {V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA, "UPDATE_DATA"}, + {V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP, "SEG_ID_PRE_SKIP"}), + __entry->f.segmentation.last_active_seg_id, + __print_array(__entry->f.segmentation.feature_enabled, + ARRAY_SIZE(__entry->f.segmentation.feature_enabled), + sizeof(__entry->f.segmentation.feature_enabled[0])), + __print_flags(__entry->f.loop_filter.flags, "|", + {V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED, "DELTA_ENABLED"}, + {V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE, "DELTA_UPDATE"}, + {V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT, "DELTA_LF_PRESENT"}, + {V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI, "DELTA_LF_MULTI"}), + __print_array(__entry->f.loop_filter.level, + ARRAY_SIZE(__entry->f.loop_filter.level), + sizeof(__entry->f.loop_filter.level[0])), + __entry->f.loop_filter.sharpness, + __print_array(__entry->f.loop_filter.ref_deltas, + ARRAY_SIZE(__entry->f.loop_filter.ref_deltas), + sizeof(__entry->f.loop_filter.ref_deltas[0])), + __print_array(__entry->f.loop_filter.mode_deltas, + ARRAY_SIZE(__entry->f.loop_filter.mode_deltas), + sizeof(__entry->f.loop_filter.mode_deltas[0])), + __entry->f.loop_filter.delta_lf_res, + __entry->f.cdef.damping_minus_3, + __entry->f.cdef.bits, + __print_array(__entry->f.cdef.y_pri_strength, + ARRAY_SIZE(__entry->f.cdef.y_pri_strength), + sizeof(__entry->f.cdef.y_pri_strength[0])), + __print_array(__entry->f.cdef.y_sec_strength, + ARRAY_SIZE(__entry->f.cdef.y_sec_strength), + sizeof(__entry->f.cdef.y_sec_strength[0])), + __print_array(__entry->f.cdef.uv_pri_strength, + ARRAY_SIZE(__entry->f.cdef.uv_pri_strength), + sizeof(__entry->f.cdef.uv_pri_strength[0])), + __print_array(__entry->f.cdef.uv_sec_strength, + ARRAY_SIZE(__entry->f.cdef.uv_sec_strength), + sizeof(__entry->f.cdef.uv_sec_strength[0])), + __print_array(__entry->f.skip_mode_frame, + ARRAY_SIZE(__entry->f.skip_mode_frame), + sizeof(__entry->f.skip_mode_frame[0])), + __entry->f.primary_ref_frame, + __print_flags(__entry->f.loop_restoration.flags, "|", + {V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR, "USES_LR"}, + {V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR, "USES_CHROMA_LR"}), + __entry->f.loop_restoration.lr_unit_shift, + __entry->f.loop_restoration.lr_uv_shift, + __print_array(__entry->f.loop_restoration.frame_restoration_type, + ARRAY_SIZE(__entry->f.loop_restoration.frame_restoration_type), + sizeof(__entry->f.loop_restoration.frame_restoration_type[0])), + __print_array(__entry->f.loop_restoration.loop_restoration_size, + ARRAY_SIZE(__entry->f.loop_restoration.loop_restoration_size), + sizeof(__entry->f.loop_restoration.loop_restoration_size[0])), + __print_flags(__entry->f.flags, "|", + {V4L2_AV1_FRAME_FLAG_SHOW_FRAME, "SHOW_FRAME"}, + {V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME, "SHOWABLE_FRAME"}, + {V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE, "ERROR_RESILIENT_MODE"}, + {V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE, "DISABLE_CDF_UPDATE"}, + {V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS, "ALLOW_SCREEN_CONTENT_TOOLS"}, + {V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV, "FORCE_INTEGER_MV"}, + {V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC, "ALLOW_INTRABC"}, + {V4L2_AV1_FRAME_FLAG_USE_SUPERRES, "USE_SUPERRES"}, + {V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV, "ALLOW_HIGH_PRECISION_MV"}, + {V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE, "IS_MOTION_MODE_SWITCHABLE"}, + {V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS, "USE_REF_FRAME_MVS"}, + {V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF, + "DISABLE_FRAME_END_UPDATE_CDF"}, + {V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION, "ALLOW_WARPED_MOTION"}, + {V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT, "REFERENCE_SELECT"}, + {V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET, "REDUCED_TX_SET"}, + {V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED, "SKIP_MODE_ALLOWED"}, + {V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT, "SKIP_MODE_PRESENT"}, + {V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE, "FRAME_SIZE_OVERRIDE"}, + {V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT, "BUFFER_REMOVAL_TIME_PRESENT"}, + {V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING, "FRAME_REFS_SHORT_SIGNALING"}), + __entry->f.order_hint, + __entry->f.upscaled_width, + __entry->f.frame_width_minus_1, + __entry->f.frame_height_minus_1, + __entry->f.render_width_minus_1, + __entry->f.render_height_minus_1, + __entry->f.current_frame_id, + __print_array(__entry->f.buffer_removal_time, + ARRAY_SIZE(__entry->f.buffer_removal_time), + sizeof(__entry->f.buffer_removal_time[0])), + __print_array(__entry->f.order_hints, + ARRAY_SIZE(__entry->f.order_hints), + sizeof(__entry->f.order_hints[0])), + __print_array(__entry->f.reference_frame_ts, + ARRAY_SIZE(__entry->f.reference_frame_ts), + sizeof(__entry->f.reference_frame_ts[0])), + __print_array(__entry->f.ref_frame_idx, + ARRAY_SIZE(__entry->f.ref_frame_idx), + sizeof(__entry->f.ref_frame_idx[0])), + __entry->f.refresh_frame_flags + ) +); + + +DECLARE_EVENT_CLASS(v4l2_ctrl_av1_film_grain_tmpl, + TP_PROTO(const struct v4l2_ctrl_av1_film_grain *f), + TP_ARGS(f), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_av1_film_grain, f)), + TP_fast_assign(__entry->f = *f;), + TP_printk("\nflags %s\ncr_mult: %u\ngrain_seed: %u\n" + "film_grain_params_ref_idx: %u\nnum_y_points: %u\npoint_y_value: %s\n" + "point_y_scaling: %s\nnum_cb_points: %u\npoint_cb_value: %s\n" + "point_cb_scaling: %s\nnum_cr_points: %u\npoint_cr_value: %s\n" + "point_cr_scaling: %s\ngrain_scaling_minus_8: %u\nar_coeff_lag: %u\n" + "ar_coeffs_y_plus_128: %s\nar_coeffs_cb_plus_128: %s\n" + "ar_coeffs_cr_plus_128: %s\nar_coeff_shift_minus_6: %u\n" + "grain_scale_shift: %u\ncb_mult: %u\ncb_luma_mult: %u\ncr_luma_mult: %u\n" + "cb_offset: %u\ncr_offset: %u\n", + __print_flags(__entry->f.flags, "|", + {V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN, "APPLY_GRAIN"}, + {V4L2_AV1_FILM_GRAIN_FLAG_UPDATE_GRAIN, "UPDATE_GRAIN"}, + {V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA, "CHROMA_SCALING_FROM_LUMA"}, + {V4L2_AV1_FILM_GRAIN_FLAG_OVERLAP, "OVERLAP"}, + {V4L2_AV1_FILM_GRAIN_FLAG_CLIP_TO_RESTRICTED_RANGE, "CLIP_TO_RESTRICTED_RANGE"}), + __entry->f.cr_mult, + __entry->f.grain_seed, + __entry->f.film_grain_params_ref_idx, + __entry->f.num_y_points, + __print_array(__entry->f.point_y_value, + ARRAY_SIZE(__entry->f.point_y_value), + sizeof(__entry->f.point_y_value[0])), + __print_array(__entry->f.point_y_scaling, + ARRAY_SIZE(__entry->f.point_y_scaling), + sizeof(__entry->f.point_y_scaling[0])), + __entry->f.num_cb_points, + __print_array(__entry->f.point_cb_value, + ARRAY_SIZE(__entry->f.point_cb_value), + sizeof(__entry->f.point_cb_value[0])), + __print_array(__entry->f.point_cb_scaling, + ARRAY_SIZE(__entry->f.point_cb_scaling), + sizeof(__entry->f.point_cb_scaling[0])), + __entry->f.num_cr_points, + __print_array(__entry->f.point_cr_value, + ARRAY_SIZE(__entry->f.point_cr_value), + sizeof(__entry->f.point_cr_value[0])), + __print_array(__entry->f.point_cr_scaling, + ARRAY_SIZE(__entry->f.point_cr_scaling), + sizeof(__entry->f.point_cr_scaling[0])), + __entry->f.grain_scaling_minus_8, + __entry->f.ar_coeff_lag, + __print_array(__entry->f.ar_coeffs_y_plus_128, + ARRAY_SIZE(__entry->f.ar_coeffs_y_plus_128), + sizeof(__entry->f.ar_coeffs_y_plus_128[0])), + __print_array(__entry->f.ar_coeffs_cb_plus_128, + ARRAY_SIZE(__entry->f.ar_coeffs_cb_plus_128), + sizeof(__entry->f.ar_coeffs_cb_plus_128[0])), + __print_array(__entry->f.ar_coeffs_cr_plus_128, + ARRAY_SIZE(__entry->f.ar_coeffs_cr_plus_128), + sizeof(__entry->f.ar_coeffs_cr_plus_128[0])), + __entry->f.ar_coeff_shift_minus_6, + __entry->f.grain_scale_shift, + __entry->f.cb_mult, + __entry->f.cb_luma_mult, + __entry->f.cr_luma_mult, + __entry->f.cb_offset, + __entry->f.cr_offset + ) +) + +DEFINE_EVENT(v4l2_ctrl_av1_seq_tmpl, v4l2_ctrl_av1_sequence, + TP_PROTO(const struct v4l2_ctrl_av1_sequence *s), + TP_ARGS(s) +); + +DEFINE_EVENT(v4l2_ctrl_av1_frame_tmpl, v4l2_ctrl_av1_frame, + TP_PROTO(const struct v4l2_ctrl_av1_frame *f), + TP_ARGS(f) +); + +DEFINE_EVENT(v4l2_ctrl_av1_tge_tmpl, v4l2_ctrl_av1_tile_group_entry, + TP_PROTO(const struct v4l2_ctrl_av1_tile_group_entry *t), + TP_ARGS(t) +); + +DEFINE_EVENT(v4l2_ctrl_av1_film_grain_tmpl, v4l2_ctrl_av1_film_grain, + TP_PROTO(const struct v4l2_ctrl_av1_film_grain *f), + TP_ARGS(f) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH ../../drivers/media/test-drivers/visl +#define TRACE_INCLUDE_FILE visl-trace-av1 +#include diff --git a/drivers/media/test-drivers/visl/visl-trace-points.c b/drivers/media/test-drivers/visl/visl-trace-points.c index f7b866534..321ff732c 100644 --- a/drivers/media/test-drivers/visl/visl-trace-points.c +++ b/drivers/media/test-drivers/visl/visl-trace-points.c @@ -8,3 +8,4 @@ #include "visl-trace-vp9.h" #include "visl-trace-h264.h" #include "visl-trace-hevc.h" +#include "visl-trace-av1.h" diff --git a/drivers/media/test-drivers/visl/visl-video.c b/drivers/media/test-drivers/visl/visl-video.c index 9303a3e11..b9a4b44bd 100644 --- a/drivers/media/test-drivers/visl/visl-video.c +++ b/drivers/media/test-drivers/visl/visl-video.c @@ -40,6 +40,9 @@ static void visl_set_current_codec(struct visl_ctx *ctx) case V4L2_PIX_FMT_HEVC_SLICE: ctx->current_codec = VISL_CODEC_HEVC; break; + case V4L2_PIX_FMT_AV1_FRAME: + ctx->current_codec = VISL_CODEC_AV1; + break; default: dprintk(ctx->dev, "Warning: unsupported fourcc: %d\n", fourcc); ctx->current_codec = VISL_CODEC_NONE; @@ -218,6 +221,21 @@ const struct visl_coded_format_desc visl_coded_fmts[] = { .num_decoded_fmts = ARRAY_SIZE(visl_decoded_fmts), .decoded_fmts = visl_decoded_fmts, }, + { + .pixelformat = V4L2_PIX_FMT_AV1_FRAME, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 1, + .min_height = 64, + .max_height = 2304, + .step_height = 1, + }, + .ctrls = &visl_av1_ctrls, + .num_decoded_fmts = ARRAY_SIZE(visl_decoded_fmts), + .decoded_fmts = visl_decoded_fmts, + }, + }; const size_t num_coded_fmts = ARRAY_SIZE(visl_coded_fmts); diff --git a/drivers/media/test-drivers/visl/visl-video.h b/drivers/media/test-drivers/visl/visl-video.h index 27ad70a55..92e274894 100644 --- a/drivers/media/test-drivers/visl/visl-video.h +++ b/drivers/media/test-drivers/visl/visl-video.h @@ -17,6 +17,7 @@ extern const struct visl_ctrls visl_vp8_ctrls; extern const struct visl_ctrls visl_vp9_ctrls; extern const struct visl_ctrls visl_h264_ctrls; extern const struct visl_ctrls visl_hevc_ctrls; +extern const struct visl_ctrls visl_av1_ctrls; int visl_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); diff --git a/drivers/media/test-drivers/visl/visl.h b/drivers/media/test-drivers/visl/visl.h index 31639f2e5..c593b1337 100644 --- a/drivers/media/test-drivers/visl/visl.h +++ b/drivers/media/test-drivers/visl/visl.h @@ -127,6 +127,7 @@ enum visl_codec { VISL_CODEC_VP9, VISL_CODEC_H264, VISL_CODEC_HEVC, + VISL_CODEC_AV1, }; struct visl_blob { diff --git a/drivers/media/test-drivers/vivid/Kconfig b/drivers/media/test-drivers/vivid/Kconfig index 5b08a5ad2..ec2e71d76 100644 --- a/drivers/media/test-drivers/vivid/Kconfig +++ b/drivers/media/test-drivers/vivid/Kconfig @@ -10,7 +10,6 @@ config VIDEO_VIVID select VIDEOBUF2_DMA_CONTIG select VIDEO_V4L2_TPG select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API help Enables a virtual video driver. This driver emulates a webcam, TV, S-Video and HDMI capture hardware, including VBI support for diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 394c9f81e..159c72cbb 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -861,7 +861,7 @@ static const struct media_device_ops vivid_media_ops = { static int vivid_create_queue(struct vivid_dev *dev, struct vb2_queue *q, u32 buf_type, - unsigned int min_buffers_needed, + unsigned int min_queued_buffers, const struct vb2_ops *ops) { if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar) @@ -876,6 +876,20 @@ static int vivid_create_queue(struct vivid_dev *dev, q->type = buf_type; q->io_modes = VB2_MMAP | VB2_DMABUF; q->io_modes |= V4L2_TYPE_IS_OUTPUT(buf_type) ? VB2_WRITE : VB2_READ; + + /* + * The maximum number of buffers is 32768 if PAGE_SHIFT == 12, + * see also MAX_BUFFER_INDEX in videobuf2-core.c. It will be less if + * PAGE_SHIFT > 12, but then max_num_buffers will be clamped by + * videobuf2-core.c to MAX_BUFFER_INDEX. + */ + if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + q->max_num_buffers = 64; + if (buf_type == V4L2_BUF_TYPE_SDR_CAPTURE) + q->max_num_buffers = 1024; + if (buf_type == V4L2_BUF_TYPE_VBI_CAPTURE) + q->max_num_buffers = 32768; + if (allocators[dev->inst] != 1) q->io_modes |= VB2_USERPTR; q->drv_priv = dev; @@ -884,7 +898,7 @@ static int vivid_create_queue(struct vivid_dev *dev, q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = supports_requests[dev->inst] ? 0 : min_buffers_needed; + q->min_queued_buffers = supports_requests[dev->inst] ? 0 : min_queued_buffers; q->lock = &dev->mutex; q->dev = dev->v4l2_dev.dev; q->supports_requests = supports_requests[dev->inst]; diff --git a/drivers/media/test-drivers/vivid/vivid-meta-cap.c b/drivers/media/test-drivers/vivid/vivid-meta-cap.c index 780f96860..0a718d037 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-cap.c @@ -30,9 +30,6 @@ static int meta_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, sizes[0] = size; } - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - *nplanes = 1; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c index 95835b52b..4a569a6e5 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-out.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c @@ -18,6 +18,7 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); unsigned int size = sizeof(struct vivid_meta_out_buf); if (!vivid_is_webcam(dev)) @@ -30,8 +31,8 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, sizes[0] = size; } - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 2) + *nbuffers = 2 - q_num_bufs; *nplanes = 1; return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index c7f6e23df..4b3c6ea0a 100644 --- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c @@ -13,6 +13,7 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); struct v4l2_pix_format *f = &dev->tch_format; unsigned int size = f->sizeimage; @@ -23,8 +24,8 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, sizes[0] = size; } - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 2) + *nbuffers = 2 - q_num_bufs; *nplanes = 1; return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index b65b02eee..3840b3a66 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c @@ -134,9 +134,6 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, sizes[0] = size; - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - *nplanes = 1; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c index cd5647690..434a10676 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c @@ -30,9 +30,6 @@ static int vbi_out_queue_setup(struct vb2_queue *vq, sizes[0] = size; - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - *nplanes = 1; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 3a06df35a..2804975fe 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -117,9 +117,6 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, dev->fmt_cap->data_offset[p]; } - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - *nplanes = buffers; dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 184a6df2c..1653b2988 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -73,12 +73,9 @@ static int vid_out_queue_setup(struct vb2_queue *vq, vfmt->data_offset[p] : size; } - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; - *nplanes = planes; - dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); + dprintk(dev, 1, "%s: count=%u\n", __func__, *nbuffers); for (p = 0; p < planes; p++) dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); return 0; diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 462eb8423..e24e655fb 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -482,12 +482,13 @@ static int airspy_queue_setup(struct vb2_queue *vq, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { struct airspy *s = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ - if (vq->num_buffers + *nbuffers < 8) - *nbuffers = 8 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 8) + *nbuffers = 8 - q_num_bufs; *nplanes = 1; sizes[0] = PAGE_ALIGN(s->buffersize); diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index fe4410a5e..3b75d062e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1218,12 +1218,13 @@ static int queue_setup(struct vb2_queue *vq, { struct cx231xx *dev = vb2_get_drv_priv(vq); unsigned int size = mpeglinesize * mpeglines; + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev->ts1.ts_packet_size = mpeglinesize; dev->ts1.ts_packet_count = mpeglines; - if (vq->num_buffers + *nbuffers < CX231XX_MIN_BUF) - *nbuffers = CX231XX_MIN_BUF - vq->num_buffers; + if (q_num_bufs + *nbuffers < CX231XX_MIN_BUF) + *nbuffers = CX231XX_MIN_BUF - q_num_bufs; if (*nplanes) return sizes[0] < size ? -EINVAL : 0; @@ -1781,7 +1782,7 @@ int cx231xx_417_register(struct cx231xx *dev) q->ops = &cx231xx_video_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &dev->lock; err = vb2_queue_init(q); if (err) diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index e23b8ccd7..8f347bbee 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -714,11 +714,12 @@ static int queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct cx231xx *dev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev->size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - if (vq->num_buffers + *nbuffers < CX231XX_MIN_BUF) - *nbuffers = CX231XX_MIN_BUF - vq->num_buffers; + if (q_num_bufs + *nbuffers < CX231XX_MIN_BUF) + *nbuffers = CX231XX_MIN_BUF - q_num_bufs; if (*nplanes) return sizes[0] < dev->size ? -EINVAL : 0; @@ -1810,7 +1811,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) q->ops = &cx231xx_video_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &dev->lock; ret = vb2_queue_init(q); if (ret) @@ -1870,7 +1871,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) q->ops = &cx231xx_vbi_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_queued_buffers = 1; q->lock = &dev->lock; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c index deba5224c..b5d8c6b75 100644 --- a/drivers/media/usb/dvb-usb/cxusb-analog.c +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -1632,7 +1632,7 @@ static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) cxdev->videoqueue.buf_struct_size = sizeof(struct cxusb_medion_vbuffer); cxdev->videoqueue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - cxdev->videoqueue.min_buffers_needed = 6; + cxdev->videoqueue.min_queued_buffers = 6; cxdev->videoqueue.lock = &cxdev->dev_lock; ret = vb2_queue_init(&cxdev->videoqueue); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 25e0620de..4aef584e2 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1607,7 +1607,8 @@ static int vidioc_g_parm(struct file *file, void *priv, p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; if (dev->is_webcam) { rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, - video, g_frame_interval, &ival); + pad, get_frame_interval, NULL, + &ival); if (!rc) p->parm.capture.timeperframe = ival.interval; } else { @@ -1639,7 +1640,8 @@ static int vidioc_s_parm(struct file *file, void *priv, p->parm.capture.readbuffers = EM28XX_MIN_BUF; p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0, - video, s_frame_interval, &ival); + pad, set_frame_interval, NULL, + &ival); if (!rc) p->parm.capture.timeperframe = ival.interval; return rc; diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 770714c34..e8c8bdb9c 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1257,7 +1257,7 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = video_drvdata(filp); - parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed; + parm->parm.capture.readbuffers = gspca_dev->queue.min_queued_buffers; if (!gspca_dev->sd_desc->get_streamparm) return 0; @@ -1273,7 +1273,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = video_drvdata(filp); - parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed; + parm->parm.capture.readbuffers = gspca_dev->queue.min_queued_buffers; if (!gspca_dev->sd_desc->set_streamparm) { parm->parm.capture.capability = 0; @@ -1517,7 +1517,7 @@ int gspca_dev_probe2(struct usb_interface *intf, q->ops = &gspca_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; + q->min_queued_buffers = 2; q->lock = &gspca_dev->usb_lock; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 3e535be2c..9c0ecd5f0 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -753,12 +753,13 @@ static int hackrf_queue_setup(struct vb2_queue *vq, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { struct hackrf_dev *dev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); dev_dbg(dev->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ - if (vq->num_buffers + *nbuffers < 8) - *nbuffers = 8 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 8) + *nbuffers = 8 - q_num_bufs; *nplanes = 1; sizes[0] = PAGE_ALIGN(dev->buffersize); diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 1e30e0595..702f1c8bd 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -726,9 +726,10 @@ static int usbtv_queue_setup(struct vb2_queue *vq, { struct usbtv *usbtv = vb2_get_drv_priv(vq); unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); - if (vq->num_buffers + *nbuffers < 2) - *nbuffers = 2 - vq->num_buffers; + if (q_num_bufs + *nbuffers < 2) + *nbuffers = 2 - q_num_bufs; if (*nplanes) return sizes[0] < size ? -EINVAL : 0; *nplanes = 1; @@ -962,15 +963,8 @@ ctrl_fail: void usbtv_video_free(struct usbtv *usbtv) { - mutex_lock(&usbtv->vb2q_lock); - mutex_lock(&usbtv->v4l2_lock); - - usbtv_stop(usbtv); vb2_video_unregister_device(&usbtv->vdev); v4l2_device_disconnect(&usbtv->v4l2_dev); - mutex_unlock(&usbtv->v4l2_lock); - mutex_unlock(&usbtv->vb2q_lock); - v4l2_device_put(&usbtv->v4l2_dev); } diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 28dde08ec..7cbf4692b 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1954,7 +1954,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, /* Check if the bandwidth is high enough. */ psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); - if (psize >= bandwidth && psize <= best_psize) { + if (psize >= bandwidth && psize < best_psize) { altsetting = alts->desc.bAlternateSetting; best_psize = psize; best_ep = ep; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 8cfd593d2..3ec323bd5 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -876,9 +876,6 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) if (sd->asc_list.next) { list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list, asc_subdev_entry) { - list_move(&asc->asc_entry, - &asc->notifier->waiting_list); - v4l2_async_unbind_subdev_one(asc->notifier, asc); } } diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 3a4b15a98..273d83de2 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -195,9 +195,9 @@ int v4l2_g_parm_cap(struct video_device *vdev, if (vdev->device_caps & V4L2_CAP_READWRITE) a->parm.capture.readbuffers = 2; - if (v4l2_subdev_has_op(sd, video, g_frame_interval)) + if (v4l2_subdev_has_op(sd, pad, get_frame_interval)) a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - ret = v4l2_subdev_call(sd, video, g_frame_interval, &ival); + ret = v4l2_subdev_call_state_active(sd, pad, get_frame_interval, &ival); if (!ret) a->parm.capture.timeperframe = ival.interval; return ret; @@ -222,9 +222,9 @@ int v4l2_s_parm_cap(struct video_device *vdev, else a->parm.capture.readbuffers = 0; - if (v4l2_subdev_has_op(sd, video, g_frame_interval)) + if (v4l2_subdev_has_op(sd, pad, get_frame_interval)) a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - ret = v4l2_subdev_call(sd, video, s_frame_interval, &ival); + ret = v4l2_subdev_call_state_active(sd, pad, set_frame_interval, &ival); if (!ret) a->parm.capture.timeperframe = ival.interval; return ret; @@ -254,6 +254,9 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index f3bed3785..8c07400bd 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -116,6 +116,9 @@ struct v4l2_format32 { * @flags: additional buffer management attributes (ignored unless the * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and * configured for MMAP streaming I/O). + * @max_num_buffers: if V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS capability flag is set + * this field indicate the maximum possible number of buffers + * for this queue. * @reserved: future extensions */ struct v4l2_create_buffers32 { @@ -125,7 +128,8 @@ struct v4l2_create_buffers32 { struct v4l2_format32 format; __u32 capabilities; __u32 flags; - __u32 reserved[6]; + __u32 max_num_buffers; + __u32 reserved[5]; }; static int get_v4l2_format32(struct v4l2_format *p64, @@ -175,6 +179,9 @@ static int get_v4l2_create32(struct v4l2_create_buffers *p64, return -EFAULT; if (copy_from_user(&p64->flags, &p32->flags, sizeof(p32->flags))) return -EFAULT; + if (copy_from_user(&p64->max_num_buffers, &p32->max_num_buffers, + sizeof(p32->max_num_buffers))) + return -EFAULT; return get_v4l2_format32(&p64->format, &p32->format); } @@ -221,6 +228,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers *p64, offsetof(struct v4l2_create_buffers32, format)) || put_user(p64->capabilities, &p32->capabilities) || put_user(p64->flags, &p32->flags) || + put_user(p64->max_num_buffers, &p32->max_num_buffers) || copy_to_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index f81279492..d13954bd3 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -642,11 +642,13 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); - if (ops->vidioc_g_selection) { + if (ops->vidioc_g_selection && + !test_bit(_IOC_NR(VIDIOC_G_SELECTION), vdev->valid_ioctls)) { __set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); __set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); } - if (ops->vidioc_s_selection) + if (ops->vidioc_s_selection && + !test_bit(_IOC_NR(VIDIOC_S_SELECTION), vdev->valid_ioctls)) __set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 7f181fbbb..89c719214 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1179,7 +1179,9 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev, static const char * const led_props[] = { "led" }; static const struct v4l2_fwnode_int_props props[] = { { "flash-leds", led_props, ARRAY_SIZE(led_props) }, - { "lens-focus", NULL, 0 }, + { "mipi-img-flash-leds", }, + { "lens-focus", }, + { "mipi-img-lens-focus", }, }; unsigned int i; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 9b1de54ce..33076af4d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -483,9 +483,9 @@ static void v4l_print_create_buffers(const void *arg, bool write_only) { const struct v4l2_create_buffers *p = arg; - pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, ", + pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, max num buffers=%u", p->index, p->count, prt_names(p->memory, v4l2_memory_names), - p->capabilities); + p->capabilities, p->max_num_buffers); v4l_print_format(&p->format, write_only); } @@ -2951,7 +2951,7 @@ void v4l_printk_ioctl(const char *prefix, unsigned int cmd) type = "v4l2_int"; break; case 'V': - if (_IOC_NR(cmd) >= V4L2_IOCTLS) { + if (!v4l2_is_known_ioctl(cmd)) { type = "v4l2"; break; } diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 8db9ac9c1..75517134a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -301,9 +301,12 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); - if (!m2m_ctx->out_q_ctx.q.streaming - || !m2m_ctx->cap_q_ctx.q.streaming) { - dprintk("Streaming needs to be on for both queues\n"); + if (!m2m_ctx->out_q_ctx.q.streaming || + (!m2m_ctx->cap_q_ctx.q.streaming && !m2m_ctx->ignore_cap_streaming)) { + if (!m2m_ctx->ignore_cap_streaming) + dprintk("Streaming needs to be on for both queues\n"); + else + dprintk("Streaming needs to be on for the OUTPUT queue\n"); return; } diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index be86b906c..4c6198c48 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -177,7 +177,7 @@ static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + if (!v4l2_subdev_state_get_format(state, pad, stream)) return -EINVAL; return 0; #else @@ -245,29 +245,6 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, sd->ops->pad->enum_frame_size(sd, state, fse); } -static inline int check_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - if (!fi) - return -EINVAL; - - return check_pad(sd, fi->pad); -} - -static int call_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - return check_frame_interval(sd, fi) ? : - sd->ops->video->g_frame_interval(sd, fi); -} - -static int call_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - return check_frame_interval(sd, fi) ? : - sd->ops->video->s_frame_interval(sd, fi); -} - static int call_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_frame_interval_enum *fie) @@ -307,6 +284,33 @@ static int call_set_selection(struct v4l2_subdev *sd, sd->ops->pad->set_selection(sd, state, sel); } +static inline int check_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + if (!fi) + return -EINVAL; + + return check_which(fi->which) ? : check_pad(sd, fi->pad) ? : + check_state(sd, state, fi->which, fi->pad, fi->stream); +} + +static int call_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + return check_frame_interval(sd, state, fi) ? : + sd->ops->pad->get_frame_interval(sd, state, fi); +} + +static int call_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + return check_frame_interval(sd, state, fi) ? : + sd->ops->pad->set_frame_interval(sd, state, fi); +} + static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { @@ -479,6 +483,8 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .enum_frame_interval = call_enum_frame_interval_state, .get_selection = call_get_selection_state, .set_selection = call_set_selection_state, + .get_frame_interval = call_get_frame_interval, + .set_frame_interval = call_set_frame_interval, .get_edid = call_get_edid, .set_edid = call_set_edid, .dv_timings_cap = call_dv_timings_cap, @@ -488,8 +494,6 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { }; static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = { - .g_frame_interval = call_g_frame_interval, - .s_frame_interval = call_s_frame_interval, .s_stream = call_s_stream, }; @@ -531,6 +535,17 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, case VIDIOC_SUBDEV_S_SELECTION: which = ((struct v4l2_subdev_selection *)arg)->which; break; + case VIDIOC_SUBDEV_G_FRAME_INTERVAL: + case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { + struct v4l2_subdev_frame_interval *fi = arg; + + if (!(subdev_fh->client_caps & + V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH)) + fi->which = V4L2_SUBDEV_FORMAT_ACTIVE; + + which = fi->which; + break; + } case VIDIOC_SUBDEV_G_ROUTING: case VIDIOC_SUBDEV_S_ROUTING: which = ((struct v4l2_subdev_routing *)arg)->which; @@ -781,20 +796,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, fi->stream = 0; memset(fi->reserved, 0, sizeof(fi->reserved)); - return v4l2_subdev_call(sd, video, g_frame_interval, arg); + return v4l2_subdev_call(sd, pad, get_frame_interval, state, fi); } case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval *fi = arg; - if (ro_subdev) - return -EPERM; - if (!client_supports_streams) fi->stream = 0; + if (fi->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + memset(fi->reserved, 0, sizeof(fi->reserved)); - return v4l2_subdev_call(sd, video, s_frame_interval, arg); + return v4l2_subdev_call(sd, pad, set_frame_interval, state, fi); } case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { @@ -989,7 +1004,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; /* Filter out unsupported capabilities */ - client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; + client_cap->capabilities &= (V4L2_SUBDEV_CLIENT_CAP_STREAMS | + V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH); subdev_fh->client_caps = client_cap->capabilities; @@ -1448,6 +1464,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, else state->lock = &state->_lock; + state->sd = sd; + /* Drivers that support streams do not need the legacy pad config */ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) { state->pads = kvcalloc(sd->entity.num_pads, @@ -1458,16 +1476,18 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, } } - /* - * There can be no race at this point, but we lock the state anyway to - * satisfy lockdep checks. - */ - v4l2_subdev_lock_state(state); - ret = v4l2_subdev_call(sd, pad, init_cfg, state); - v4l2_subdev_unlock_state(state); + if (sd->internal_ops && sd->internal_ops->init_state) { + /* + * There can be no race at this point, but we lock the state + * anyway to satisfy lockdep checks. + */ + v4l2_subdev_lock_state(state); + ret = sd->internal_ops->init_state(sd, state); + v4l2_subdev_unlock_state(state); - if (ret < 0 && ret != -ENOIOCTLCMD) - goto err; + if (ret) + goto err; + } return state; @@ -1517,7 +1537,8 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) __v4l2_subdev_state_free(sd->active_state); sd->active_state = NULL; - if (list_empty(&sd->async_subdev_endpoint_list)) + /* Uninitialised sub-device, bail out here. */ + if (!sd->async_subdev_endpoint_list.next) return; list_for_each_entry_safe(ase, ase_tmp, &sd->async_subdev_endpoint_list, @@ -1529,6 +1550,144 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd) } EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); +struct v4l2_mbus_framefmt * +__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].format; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format); + +struct v4l2_rect * +__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].crop; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].crop; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop); + +struct v4l2_rect * +__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON_ONCE(!state)) + return NULL; + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].compose; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].compose; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose); + +struct v4l2_fract * +__v4l2_subdev_state_get_interval(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + if (WARN_ON(!state)) + return NULL; + + lockdep_assert_held(state->lock); + + if (state->pads) { + if (stream) + return NULL; + + if (pad >= state->sd->entity.num_pads) + return NULL; + + return &state->pads[pad].interval; + } + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].interval; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_interval); + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int @@ -1585,14 +1744,7 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { struct v4l2_mbus_framefmt *fmt; - if (sd->flags & V4L2_SUBDEV_FL_STREAMS) - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); - else if (format->pad < sd->entity.num_pads && format->stream == 0) - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - else - fmt = NULL; - + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -1602,6 +1754,22 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, } EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt); +int v4l2_subdev_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_fract *interval; + + interval = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream); + if (!interval) + return -EINVAL; + + fi->interval = *interval; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_get_frame_interval); + int v4l2_subdev_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, const struct v4l2_subdev_krouting *routing) @@ -1682,69 +1850,6 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); -struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].fmt; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].crop; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - lockdep_assert_held(state->lock); - - stream_configs = &state->stream_configs; - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].compose; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); - int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, u32 *other_stream) @@ -1789,8 +1894,7 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, if (ret) return NULL; - return v4l2_subdev_state_get_stream_format(state, other_pad, - other_stream); + return v4l2_subdev_state_get_format(state, other_pad, other_stream); } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); -- cgit v1.2.3