summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 12:20:25 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 12:20:25 -0800
commitb7ef56a07672e0d7ebe71c9d9b45f959f0c2f8e8 (patch)
tree47c65da1d9c6514bc74df8b86c29fb87313ca3fe /drivers
parentd061251387903e8502843ac983553f0b2e098ef8 (diff)
parentc824345288d11e269ce41b36c105715bc2286050 (diff)
downloadlinux-next-b7ef56a07672e0d7ebe71c9d9b45f959f0c2f8e8.tar.gz
linux-next-b7ef56a07672e0d7ebe71c9d9b45f959f0c2f8e8.zip
Merge tag 'media/v7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - Add support for GMSL1 and GMSL‑coax modules (PCI mgb4) - Add driver for TI VIP - AV1 – first kernel support (pixel‑format, decoder, transcoder) - Three new camera‑sensor drivers (os05b10, s5k3m5, s5kjn1) - Synopsys CSI‑2 receiver driver - Verisilicon & rkvdec – major fixes and enhancements - IPU6 (and 7) fixes and preparation for metadata - omap3isp: v4l2-compliance updates - Fix DVB streaming, drop wait_prepare/finish (dvb/vb2) * tag 'media/v7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (307 commits) media: uvcvideo: Pass allocation size directly to uvc_alloc_urb_buffer media: uvcvideo: Fix allocation for small frame sizes media: uvcvideo: Return queued buffers on start_streaming() failure media: uvcvideo: Create an ID namespace for streaming output terminals media: rkvdec: Add HEVC support for the VDPU383 variant media: rkvdec: Add HEVC support for the VDPU381 variant media: rkvdec: Add H264 support for the VDPU383 variant media: rkvdec: Add H264 support for the VDPU381 variant media: rkvdec: Disable multicore support media: rkvdec: Enable all clocks without naming them media: rkvdec: Support per-variant interrupt handler media: rkvdec: Add RCB and SRAM support media: rkvdec: Add variant specific coded formats list media: rkvdec: Move hevc functions to common file media: rkvdec: Move h264 functions to common file media: rkvdec: Use structs to represent the HW RPS media: rkvdec: Move cabac tables to their own source file media: rkvdec: Switch to using structs instead of writel media: visl: Add HEVC short and long term RPS sets media: v4l2-ctrls: Add hevc_ext_sps_[ls]t_rps controls ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c49
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c14
-rw-r--r--drivers/media/dvb-core/dmxdev.c46
-rw-r--r--drivers/media/dvb-core/dvb_vb2.c45
-rw-r--r--drivers/media/i2c/Kconfig33
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/adv7180.c55
-rw-r--r--drivers/media/i2c/adv7604.c8
-rw-r--r--drivers/media/i2c/aptina-pll.c2
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c854
-rw-r--r--drivers/media/i2c/ccs/ccs.h17
-rw-r--r--drivers/media/i2c/dw9714.c2
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c4
-rw-r--r--drivers/media/i2c/imx219.c29
-rw-r--r--drivers/media/i2c/mt9m114.c271
-rw-r--r--drivers/media/i2c/og0ve1b.c55
-rw-r--r--drivers/media/i2c/os05b10.c1130
-rw-r--r--drivers/media/i2c/ov01a10.c920
-rw-r--r--drivers/media/i2c/ov2735.c2
-rw-r--r--drivers/media/i2c/ov5647.c878
-rw-r--r--drivers/media/i2c/ov6211.c55
-rw-r--r--drivers/media/i2c/ov9282.c173
-rw-r--r--drivers/media/i2c/s5k3m5.c1377
-rw-r--r--drivers/media/i2c/s5kjn1.c1487
-rw-r--r--drivers/media/i2c/saa6588.c27
-rw-r--r--drivers/media/i2c/tw9903.c1
-rw-r--r--drivers/media/i2c/tw9906.c1
-rw-r--r--drivers/media/mc/mc-device.c30
-rw-r--r--drivers/media/mc/mc-devnode.c5
-rw-r--r--drivers/media/mc/mc-request.c47
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c4
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c1
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c1
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c4
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c2
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.c73
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.h1
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c36
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h4
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c88
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.h6
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.c23
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h2
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.c4
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c12
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c22
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.h16
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs.h6
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_in.c49
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_out.c38
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.c93
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c64
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-tw28.c8
-rw-r--r--drivers/media/platform/amphion/vdec.c78
-rw-r--r--drivers/media/platform/amphion/venc.c20
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.c15
-rw-r--r--drivers/media/platform/amphion/vpu_core.c11
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c3
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c22
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c28
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.h1
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c2
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c194
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c17
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c121
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.h2
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c68
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h12
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h1
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.c17
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h17
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c115
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c3
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c15
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.h3
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c70
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h1
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c31
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c14
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c17
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c2
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c58
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-340.c10
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c18
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c21
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c19
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h1
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c6
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c2
-rw-r--r--drivers/media/platform/qcom/camss/camss.c586
-rw-r--r--drivers/media/platform/qcom/camss/camss.h3
-rw-r--r--drivers/media/platform/qcom/iris/Makefile1
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c7
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h2
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c103
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h3
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c23
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h3
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c6
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c153
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h28
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c24
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h9
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h29
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen1.c47
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c257
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sc7280.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c20
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c16
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c6
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c31
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.c48
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c11
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu3x.c199
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu4x.c369
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c701
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h140
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c188
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_register_defines.h61
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c20
-rw-r--r--drivers/media/platform/qcom/venus/venc.c6
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.c2
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c3
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c5
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c6
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile14
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.c (renamed from drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c)506
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h21
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c258
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h85
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c893
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c511
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h107
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c282
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c179
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h29
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h586
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c469
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c639
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h430
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c538
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c652
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h281
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c232
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c633
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h50
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c24
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c24
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c7
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c10
-rw-r--r--drivers/media/platform/synopsys/Kconfig18
-rw-r--r--drivers/media/platform/synopsys/Makefile2
-rw-r--r--drivers/media/platform/synopsys/dw-mipi-csi2rx.c722
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c90
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h2
-rw-r--r--drivers/media/platform/ti/Kconfig13
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c1
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.c3
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.c1
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.c26
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.c3
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c1
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c178
-rw-r--r--drivers/media/platform/ti/vpe/Makefile2
-rw-r--r--drivers/media/platform/ti/vpe/vip.c3673
-rw-r--r--drivers/media/platform/ti/vpe/vip.h717
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c51
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h6
-rw-r--r--drivers/media/platform/verisilicon/hantro.h2
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c42
-rw-r--r--drivers/media/platform/verisilicon/imx8m_vpu_hw.c8
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c43
-rw-r--r--drivers/media/radio/radio-keene.c2
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c21
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.c7
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.h3
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-hevc.h59
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c5
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c79
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c54
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c14
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c10
-rw-r--r--drivers/media/usb/uvc/uvc_video.c15
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h5
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c45
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c31
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c13
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c3
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c1
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c33
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c5
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c3
-rw-r--r--drivers/staging/media/av7110/av7110_hw.c20
-rw-r--r--drivers/staging/media/av7110/sp8870.c2
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c106
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress.c17
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.c13
-rw-r--r--drivers/staging/media/ipu7/ipu7-mmu.c2
-rw-r--r--drivers/staging/media/ipu7/ipu7.c6
-rw-r--r--drivers/staging/media/tegra-video/csi.c4
-rw-r--r--drivers/staging/media/tegra-video/csi.h4
-rw-r--r--drivers/staging/media/tegra-video/vi.c13
-rw-r--r--drivers/staging/media/tegra-video/vip.c4
-rw-r--r--drivers/staging/media/tegra-video/vip.h4
216 files changed, 21610 insertions, 4465 deletions
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 2df566f409b6..2d1f253b4929 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -605,8 +605,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned i
*/
if (vb2_get_num_buffers(q)) {
bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
- q->cnt_prepare_streaming != q->cnt_unprepare_streaming ||
- q->cnt_wait_prepare != q->cnt_wait_finish;
+ q->cnt_prepare_streaming != q->cnt_unprepare_streaming;
if (unbalanced) {
pr_info("unbalanced counters for queue %p:\n", q);
@@ -617,13 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned i
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;
- q->cnt_wait_finish = 0;
q->cnt_prepare_streaming = 0;
q->cnt_start_streaming = 0;
q->cnt_stop_streaming = 0;
@@ -2037,10 +2031,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
* become ready or for streamoff. Driver's lock is released to
* allow streamoff or qbuf to be called while waiting.
*/
- if (q->ops->wait_prepare)
- call_void_qop(q, wait_prepare, q);
- else if (q->lock)
- mutex_unlock(q->lock);
+ mutex_unlock(q->lock);
/*
* All locks have been released, it is safe to sleep now.
@@ -2050,10 +2041,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
!list_empty(&q->done_list) || !q->streaming ||
q->error);
- if (q->ops->wait_finish)
- call_void_qop(q, wait_finish, q);
- else if (q->lock)
- mutex_lock(q->lock);
+ mutex_lock(q->lock);
q->waiting_in_dqbuf = 0;
/*
@@ -2653,12 +2641,8 @@ int vb2_core_queue_init(struct vb2_queue *q)
if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers))
return -EINVAL;
- /* Either both or none are set */
- if (WARN_ON(!q->ops->wait_prepare ^ !q->ops->wait_finish))
- return -EINVAL;
-
- /* Warn if q->lock is NULL and no custom wait_prepare is provided */
- if (WARN_ON(!q->lock && !q->ops->wait_prepare))
+ /* Warn if q->lock is NULL */
+ if (WARN_ON(!q->lock))
return -EINVAL;
INIT_LIST_HEAD(&q->queued_list);
@@ -3220,17 +3204,10 @@ static int vb2_thread(void *data)
continue;
prequeue--;
} else {
- if (!threadio->stop) {
- if (q->ops->wait_finish)
- call_void_qop(q, wait_finish, q);
- else if (q->lock)
- mutex_lock(q->lock);
+ mutex_lock(q->lock);
+ if (!threadio->stop)
ret = vb2_core_dqbuf(q, &index, NULL, 0);
- if (q->ops->wait_prepare)
- call_void_qop(q, wait_prepare, q);
- else if (q->lock)
- mutex_unlock(q->lock);
- }
+ mutex_unlock(q->lock);
dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret);
if (!ret)
vb = vb2_get_buffer(q, index);
@@ -3245,15 +3222,9 @@ static int vb2_thread(void *data)
if (copy_timestamp)
vb->timestamp = ktime_get_ns();
if (!threadio->stop) {
- if (q->ops->wait_finish)
- call_void_qop(q, wait_finish, q);
- else if (q->lock)
- mutex_lock(q->lock);
+ mutex_lock(q->lock);
ret = vb2_core_qbuf(q, vb, NULL, NULL);
- if (q->ops->wait_prepare)
- call_void_qop(q, wait_prepare, q);
- else if (q->lock)
- mutex_unlock(q->lock);
+ mutex_unlock(q->lock);
}
if (ret || threadio->stop)
break;
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 83862d57b126..4baded4fd3b8 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -1302,20 +1302,6 @@ void vb2_video_unregister_device(struct video_device *vdev)
}
EXPORT_SYMBOL_GPL(vb2_video_unregister_device);
-/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
-
-void vb2_ops_wait_prepare(struct vb2_queue *vq)
-{
- mutex_unlock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
-
-void vb2_ops_wait_finish(struct vb2_queue *vq)
-{
- mutex_lock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
-
/*
* Note that this function is called during validation time and
* thus the req_queue_mutex is held to ensure no request objects
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 8c6f5aafda1d..c946c8ea6e39 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -171,6 +171,7 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
if (dmxdev->may_do_mmap)
dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr",
+ &dmxdev->mutex,
file->f_flags & O_NONBLOCK);
dvbdev->readers--;
}
@@ -397,11 +398,11 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
buffer1, buffer1_len,
- buffer_flags);
+ buffer_flags, true);
if (ret == buffer1_len)
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
buffer2, buffer2_len,
- buffer_flags);
+ buffer_flags, true);
} else {
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
buffer1, buffer1_len);
@@ -452,10 +453,10 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
if (dvb_vb2_is_streaming(ctx)) {
ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len,
- buffer_flags);
+ buffer_flags, false);
if (ret == buffer1_len)
ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len,
- buffer_flags);
+ buffer_flags, false);
} else {
if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
@@ -815,9 +816,21 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
dmxdev->may_do_mmap = 0;
#endif
+ /*
+ * The mutex passed to dvb_vb2_init is unlocked when a buffer
+ * is in a blocking wait. However, dmxdevfilter has two mutexes:
+ * dmxdevfilter->mutex and dmxdev->mutex. So this will not work.
+ * The solution would be to support unlocking two mutexes in vb2,
+ * but since this problem has been here since the beginning and
+ * nobody ever complained, we leave it as-is rather than adding
+ * that second mutex pointer to vb2.
+ *
+ * In the unlikely event that someone complains about this, then
+ * this comment will hopefully help.
+ */
dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
dvb_vb2_init(&dmxdevfilter->vb2_ctx, "demux_filter",
- file->f_flags & O_NONBLOCK);
+ &dmxdevfilter->mutex, file->f_flags & O_NONBLOCK);
dmxdevfilter->type = DMXDEV_TYPE_NONE;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
timer_setup(&dmxdevfilter->timer, dvb_dmxdev_filter_timeout, 0);
@@ -1217,24 +1230,11 @@ static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
- int ret;
if (!dmxdev->may_do_mmap)
return -ENOTTY;
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- ret = dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma);
-
- mutex_unlock(&dmxdevfilter->mutex);
- mutex_unlock(&dmxdev->mutex);
-
- return ret;
+ return dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma);
}
#endif
@@ -1367,7 +1367,6 @@ static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma)
{
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
- int ret;
if (!dmxdev->may_do_mmap)
return -ENOTTY;
@@ -1375,12 +1374,7 @@ static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma)
if (dmxdev->exit)
return -ENODEV;
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- ret = dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma);
- mutex_unlock(&dmxdev->mutex);
- return ret;
+ return dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma);
}
#endif
diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
index 29edaaff7a5c..672b0efdca21 100644
--- a/drivers/media/dvb-core/dvb_vb2.c
+++ b/drivers/media/dvb-core/dvb_vb2.c
@@ -103,31 +103,12 @@ static void _stop_streaming(struct vb2_queue *vq)
spin_unlock_irqrestore(&ctx->slock, flags);
}
-static void _dmxdev_lock(struct vb2_queue *vq)
-{
- struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
-
- mutex_lock(&ctx->mutex);
- dprintk(3, "[%s]\n", ctx->name);
-}
-
-static void _dmxdev_unlock(struct vb2_queue *vq)
-{
- struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
-
- if (mutex_is_locked(&ctx->mutex))
- mutex_unlock(&ctx->mutex);
- dprintk(3, "[%s]\n", ctx->name);
-}
-
static const struct vb2_ops dvb_vb2_qops = {
.queue_setup = _queue_setup,
.buf_prepare = _buffer_prepare,
.buf_queue = _buffer_queue,
.start_streaming = _start_streaming,
.stop_streaming = _stop_streaming,
- .wait_prepare = _dmxdev_unlock,
- .wait_finish = _dmxdev_lock,
};
static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb)
@@ -158,9 +139,10 @@ static const struct vb2_buf_ops dvb_vb2_buf_ops = {
};
/*
- * Videobuf operations
+ * vb2 operations
*/
-int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking)
+int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name,
+ struct mutex *mutex, int nonblocking)
{
struct vb2_queue *q = &ctx->vb_q;
int ret;
@@ -175,14 +157,7 @@ int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking)
q->ops = &dvb_vb2_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->buf_ops = &dvb_vb2_buf_ops;
- ret = vb2_core_queue_init(q);
- if (ret) {
- ctx->state = DVB_VB2_STATE_NONE;
- dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
- return ret;
- }
-
- mutex_init(&ctx->mutex);
+ q->lock = mutex;
spin_lock_init(&ctx->slock);
INIT_LIST_HEAD(&ctx->dvb_q);
@@ -190,6 +165,13 @@ int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking)
ctx->nonblocking = nonblocking;
ctx->state = DVB_VB2_STATE_INIT;
+ ret = vb2_core_queue_init(q);
+ if (ret) {
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+
dprintk(3, "[%s]\n", ctx->name);
return 0;
@@ -249,7 +231,8 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx)
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
const unsigned char *src, int len,
- enum dmx_buffer_flags *buffer_flags)
+ enum dmx_buffer_flags *buffer_flags,
+ bool flush)
{
unsigned long flags = 0;
void *vbuf = NULL;
@@ -306,7 +289,7 @@ int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
}
}
- if (ctx->nonblocking && ctx->buf) {
+ if (flush && ctx->buf) {
vb2_set_plane_payload(&ctx->buf->vb, 0, ll);
vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE);
list_del(&ctx->buf->list);
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4b4db8c4f496..5eb1e0e0a87a 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -305,6 +305,7 @@ config VIDEO_MT9M111
config VIDEO_MT9M114
tristate "onsemi MT9M114 sensor support"
select V4L2_CCI_I2C
+ select VIDEO_APTINA_PLL
help
This is a Video4Linux2 sensor-level driver for the onsemi MT9M114
camera.
@@ -371,8 +372,19 @@ config VIDEO_OG0VE1B
To compile this driver as a module, choose M here: the
module will be called og0ve1b.
+config VIDEO_OS05B10
+ tristate "OmniVision OS05B10 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for Omnivision
+ OS05B10 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called os05b10.
+
config VIDEO_OV01A10
tristate "OmniVision OV01A10 sensor support"
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OV01A10 camera.
@@ -529,6 +541,7 @@ config VIDEO_OV5645
config VIDEO_OV5647
tristate "OmniVision OV5647 sensor support"
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OV5647 camera.
@@ -743,6 +756,16 @@ config VIDEO_S5C73M3
This is a V4L2 sensor driver for Samsung S5C73M3
8 Mpixel camera.
+config VIDEO_S5K3M5
+ tristate "Samsung S5K3M5 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a V4L2 sensor driver for Samsung S5K3M5 13MP raw
+ camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5k3m5.
+
config VIDEO_S5K5BAF
tristate "Samsung S5K5BAF sensor support"
help
@@ -755,6 +778,16 @@ config VIDEO_S5K6A3
This is a V4L2 sensor driver for Samsung S5K6A3 raw
camera sensor.
+config VIDEO_S5KJN1
+ tristate "Samsung S5KJN1 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a V4L2 sensor driver for Samsung S5KJN1 50MP raw
+ camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5kjn1.
+
config VIDEO_VD55G1
tristate "ST VD55G1 sensor support"
select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c5f17602454f..a3a6396df3c4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o
+obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o
obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o
@@ -125,8 +126,10 @@ obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o
obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o
obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
+obj-$(CONFIG_VIDEO_S5K3M5) += s5k3m5.o
obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
+obj-$(CONFIG_VIDEO_S5KJN1) += s5kjn1.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 378f4e6af12c..669b0b3165b1 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -507,6 +507,13 @@ static int adv7180_get_frame_interval(struct v4l2_subdev *sd,
fi->interval.denominator = 25;
}
+ /*
+ * If the de-interlacer is active, the chip produces full video frames
+ * at the field rate.
+ */
+ if (state->field == V4L2_FIELD_NONE)
+ fi->interval.denominator *= 2;
+
return 0;
}
@@ -969,6 +976,32 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd,
}
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7180_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct adv7180_state *state = to_state(sd);
+ int ret;
+
+ ret = adv7180_read(state, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ reg->size = 1;
+
+ return 0;
+}
+
+static int adv7180_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct adv7180_state *state = to_state(sd);
+
+ return adv7180_write(state, reg->reg, reg->val);
+}
+#endif
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.s_std = adv7180_s_std,
.g_std = adv7180_g_std,
@@ -982,6 +1015,10 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.subscribe_event = adv7180_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = adv7180_g_register,
+ .s_register = adv7180_s_register,
+#endif
};
static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
@@ -1066,13 +1103,13 @@ static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
static int adv7182_init(struct adv7180_state *state)
{
- if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+ if (state->csi_client)
adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
- ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
+ state->csi_client->addr << 1);
- if (state->chip_info->flags & ADV7180_FLAG_I2P)
+ if (state->vpp_client)
adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
- ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
+ state->vpp_client->addr << 1);
if (state->chip_info->flags & ADV7180_FLAG_V2) {
/* ADI recommended writes for improved video quality */
@@ -1443,15 +1480,17 @@ static int adv7180_probe(struct i2c_client *client)
state->force_bt656_4 = true;
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
- state->csi_client = i2c_new_dummy_device(client->adapter,
- ADV7180_DEFAULT_CSI_I2C_ADDR);
+ state->csi_client =
+ i2c_new_ancillary_device(client, "csi",
+ ADV7180_DEFAULT_CSI_I2C_ADDR);
if (IS_ERR(state->csi_client))
return PTR_ERR(state->csi_client);
}
if (state->chip_info->flags & ADV7180_FLAG_I2P) {
- state->vpp_client = i2c_new_dummy_device(client->adapter,
- ADV7180_DEFAULT_VPP_I2C_ADDR);
+ state->vpp_client =
+ i2c_new_ancillary_device(client, "vpp",
+ ADV7180_DEFAULT_VPP_I2C_ADDR);
if (IS_ERR(state->vpp_client)) {
ret = PTR_ERR(state->vpp_client);
goto err_unregister_csi_client;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 516553fb17e9..67116a4ef134 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3453,7 +3453,13 @@ static int configure_regmaps(struct adv76xx_state *state)
static void adv76xx_reset(struct adv76xx_state *state)
{
if (state->reset_gpio) {
- /* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */
+ /*
+ * Note: Misinterpretation of reset assertion - do not re-use
+ * this code. The reset pin is using incorrect (for a reset
+ * signal) logical level.
+ *
+ * ADV76XX can be reset by a low reset pulse of minimum 5 ms.
+ */
gpiod_set_value_cansleep(state->reset_gpio, 0);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(state->reset_gpio, 1);
diff --git a/drivers/media/i2c/aptina-pll.c b/drivers/media/i2c/aptina-pll.c
index b1f89bbf9d47..cd2ed4583c97 100644
--- a/drivers/media/i2c/aptina-pll.c
+++ b/drivers/media/i2c/aptina-pll.c
@@ -129,6 +129,8 @@ int aptina_pll_calculate(struct device *dev,
p1_max = min(limits->p1_max, limits->out_clock_max * div /
(pll->ext_clock * pll->m));
+ dev_dbg(dev, "pll: p1 min %u max %u\n", p1_min, p1_max);
+
for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
unsigned int mf_inc = p1 / gcd(div, p1);
unsigned int mf_high;
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 0d7b922fd4c4..aa4dd7e7cf5a 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -13,6 +13,7 @@
* Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com>
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -25,6 +26,7 @@
#include <linux/slab.h>
#include <linux/smiapp.h>
#include <linux/v4l2-mediabus.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -245,6 +247,33 @@ out_err:
return ret;
}
+static u8 ccs_mipi_csi2_data_type(unsigned int bpp)
+{
+ switch (bpp) {
+ case 6:
+ return MIPI_CSI2_DT_RAW6;
+ case 7:
+ return MIPI_CSI2_DT_RAW7;
+ case 8:
+ return MIPI_CSI2_DT_RAW8;
+ case 10:
+ return MIPI_CSI2_DT_RAW10;
+ case 12:
+ return MIPI_CSI2_DT_RAW12;
+ case 14:
+ return MIPI_CSI2_DT_RAW14;
+ case 16:
+ return MIPI_CSI2_DT_RAW16;
+ case 20:
+ return MIPI_CSI2_DT_RAW20;
+ case 24:
+ return MIPI_CSI2_DT_RAW24;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+}
+
static int ccs_read_frame_fmt(struct ccs_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
@@ -483,16 +512,71 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
return ccs_pll_calculate(&client->dev, &lim, pll);
}
+static void
+ccs_get_binning(struct ccs_sensor *sensor, u8 *binning_mode, u8 *binh, u8 *binv)
+{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->binner->sd);
+ const struct v4l2_rect *sink_crop =
+ v4l2_subdev_state_get_crop(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *sink_comp =
+ v4l2_subdev_state_get_compose(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+
+ if (binning_mode)
+ *binning_mode = sink_crop->width == sink_comp->width &&
+ sink_crop->height == sink_comp->height ? 0 : 1;
+
+ *binh = sink_crop->width / sink_comp->width;
+ *binv = sink_crop->height / sink_comp->height;
+}
+
+static void ccs_get_scaling(struct ccs_sensor *sensor, u8 *scaling_mode,
+ u8 *scale_m)
+{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->scaler->sd);
+ const struct v4l2_rect *sink_crop =
+ v4l2_subdev_state_get_crop(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *sink_comp =
+ v4l2_subdev_state_get_compose(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+
+ *scale_m = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) /
+ sink_comp->width;
+
+ if (!scaling_mode)
+ return;
+
+ if (sink_crop->width == sink_comp->width)
+ *scaling_mode = CCS_SCALING_MODE_NO_SCALING;
+ else if (sink_crop->height == sink_comp->height)
+ *scaling_mode = CCS_SCALING_MODE_HORIZONTAL;
+ else
+ *scaling_mode = SMIAPP_SCALING_MODE_BOTH;
+}
+
static int ccs_pll_update(struct ccs_sensor *sensor)
{
struct ccs_pll *pll = &sensor->pll;
+ u8 binh, binv;
+ u8 scale_m;
int rval;
- pll->binning_horizontal = sensor->binning_horizontal;
- pll->binning_vertical = sensor->binning_vertical;
+ ccs_get_binning(sensor, NULL, &binh, &binv);
+
+ if (sensor->scaler)
+ ccs_get_scaling(sensor, NULL, &scale_m);
+ else
+ scale_m = CCS_LIM(sensor, SCALER_N_MIN);
+
+ pll->binning_horizontal = binh;
+ pll->binning_vertical = binv;
pll->link_freq =
sensor->link_freq->qmenu_int[sensor->link_freq->val];
- pll->scale_m = sensor->scale_m;
+ pll->scale_m = scale_m;
pll->bits_per_pixel = sensor->csi_format->compressed;
rval = ccs_pll_try(sensor, pll);
@@ -513,12 +597,13 @@ static int ccs_pll_update(struct ccs_sensor *sensor)
*
*/
-static void __ccs_update_exposure_limits(struct ccs_sensor *sensor)
+static void __ccs_update_exposure_limits(struct ccs_sensor *sensor,
+ const struct v4l2_rect *pa_src)
{
struct v4l2_ctrl *ctrl = sensor->exposure;
int max;
- max = sensor->pa_src.height + sensor->vblank->val -
+ max = pa_src->height + sensor->vblank->val -
CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN);
__v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max);
@@ -621,12 +706,20 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
container_of(ctrl->handler, struct ccs_subdev, ctrl_handler)
->sensor;
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct v4l2_subdev_state *state;
+ const struct v4l2_rect *pa_src = NULL;
int pm_status;
u32 orient = 0;
unsigned int i;
int exposure;
int rval;
+ if (ctrl->id == V4L2_CID_VBLANK || ctrl->id == V4L2_CID_HBLANK) {
+ state = v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ pa_src = v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ }
+
switch (ctrl->id) {
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
@@ -645,7 +738,7 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_VBLANK:
exposure = sensor->exposure->val;
- __ccs_update_exposure_limits(sensor);
+ __ccs_update_exposure_limits(sensor, pa_src);
if (exposure > sensor->exposure->maximum) {
sensor->exposure->val = sensor->exposure->maximum;
@@ -737,12 +830,12 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_VBLANK:
rval = ccs_write(sensor, FRAME_LENGTH_LINES,
- sensor->pa_src.height + ctrl->val);
+ pa_src->height + ctrl->val);
break;
case V4L2_CID_HBLANK:
rval = ccs_write(sensor, LINE_LENGTH_PCK,
- sensor->pa_src.width + ctrl->val);
+ pa_src->width + ctrl->val);
break;
case V4L2_CID_TEST_PATTERN:
@@ -1117,14 +1210,14 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
dev_dbg(&client->dev, "jolly good! %u\n", j);
- sensor->default_mbus_frame_fmts |= 1 << j;
+ sensor->default_mbus_frame_fmts |= BIT_U64(j);
}
}
/* Figure out which BPP values can be used with which formats. */
pll->binning_horizontal = 1;
pll->binning_vertical = 1;
- pll->scale_m = sensor->scale_m;
+ pll->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
sensor->compressed_min_bpp =
@@ -1150,7 +1243,7 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
f->compressed - sensor->compressed_min_bpp];
unsigned int j;
- if (!(sensor->default_mbus_frame_fmts & 1 << i))
+ if (!(sensor->default_mbus_frame_fmts & BIT_U64(i)))
continue;
pll->bits_per_pixel = f->compressed;
@@ -1195,14 +1288,18 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
return 0;
}
-static void ccs_update_blanking(struct ccs_sensor *sensor)
+static void ccs_update_blanking(struct ccs_sensor *sensor,
+ const struct v4l2_rect *pa_src)
{
struct v4l2_ctrl *vblank = sensor->vblank;
struct v4l2_ctrl *hblank = sensor->hblank;
u16 min_fll, max_fll, min_llp, max_llp, min_lbp;
int min, max;
+ u8 binh, binv;
+
+ ccs_get_binning(sensor, NULL, &binh, &binv);
- if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) {
+ if (binv > 1 || binh > 1) {
min_fll = CCS_LIM(sensor, MIN_FRAME_LENGTH_LINES_BIN);
max_fll = CCS_LIM(sensor, MAX_FRAME_LENGTH_LINES_BIN);
min_llp = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN);
@@ -1218,21 +1315,26 @@ static void ccs_update_blanking(struct ccs_sensor *sensor)
min = max_t(int,
CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES),
- min_fll - sensor->pa_src.height);
- max = max_fll - sensor->pa_src.height;
+ min_fll - pa_src->height);
+ max = max_fll - pa_src->height;
__v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min);
- min = max_t(int, min_llp - sensor->pa_src.width, min_lbp);
- max = max_llp - sensor->pa_src.width;
+ min = max_t(int, min_llp - pa_src->width, min_lbp);
+ max = max_llp - pa_src->width;
__v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min);
- __ccs_update_exposure_limits(sensor);
+ __ccs_update_exposure_limits(sensor, pa_src);
}
static int ccs_pll_blanking_update(struct ccs_sensor *sensor)
{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ const struct v4l2_rect *pa_src =
+ v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
int rval;
@@ -1241,15 +1343,15 @@ static int ccs_pll_blanking_update(struct ccs_sensor *sensor)
return rval;
/* Output from pixel array, including blanking */
- ccs_update_blanking(sensor);
+ ccs_update_blanking(sensor, pa_src);
dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val);
dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
sensor->pll.pixel_rate_pixel_array /
- ((sensor->pa_src.width + sensor->hblank->val) *
- (sensor->pa_src.height + sensor->vblank->val) / 100));
+ ((pa_src->width + sensor->hblank->val) *
+ (pa_src->height + sensor->vblank->val) / 100));
return 0;
}
@@ -1704,72 +1806,122 @@ static int ccs_power_off(struct device *dev)
usleep_range(5000, 5000);
regulator_bulk_disable(ARRAY_SIZE(ccs_regulators),
sensor->regulators);
- sensor->streaming = false;
return 0;
}
/* -----------------------------------------------------------------------------
- * Video stream management
+ * V4L2 subdev video operations
*/
-static int ccs_start_streaming(struct ccs_sensor *sensor)
+static int ccs_pm_get_init(struct ccs_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ /*
+ * It can't use pm_runtime_resume_and_get() here, as the driver
+ * relies at the returned value to detect if the device was already
+ * active or not.
+ */
+ rval = pm_runtime_get_sync(&client->dev);
+ if (rval < 0)
+ goto error;
+
+ /* Device was already active, so don't set controls */
+ 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)
+ goto error;
+
+ rval = __v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
+ if (rval)
+ goto error;
+
+ /* Keep PM runtime usage_count incremented on success */
+ return 0;
+
+error:
+ pm_runtime_put(&client->dev);
+ return rval;
+}
+
+static int ccs_enable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct v4l2_subdev_state *pa_state =
+ v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ struct v4l2_subdev_state *src_state =
+ v4l2_subdev_get_locked_active_state(&sensor->src->sd);
+ const struct v4l2_rect *pa_src =
+ v4l2_subdev_state_get_crop(pa_state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *src_src =
+ v4l2_subdev_state_get_crop(src_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int binning_mode;
+ u8 binning_mode, binh, binv;
int rval;
- mutex_lock(&sensor->mutex);
+ if (pad != CCS_PAD_SRC)
+ return -EINVAL;
+
+ if (sensor->streaming) {
+ sensor->streaming |= streams_mask;
+ return 0;
+ }
+
+ rval = ccs_pm_get_init(sensor);
+ if (rval)
+ return rval;
rval = ccs_write(sensor, CSI_DATA_FORMAT,
(sensor->csi_format->width << 8) |
sensor->csi_format->compressed);
if (rval)
- goto out;
+ goto err_pm_put;
/* Binning configuration */
- if (sensor->binning_horizontal == 1 &&
- sensor->binning_vertical == 1) {
- binning_mode = 0;
- } else {
- u8 binning_type =
- (sensor->binning_horizontal << 4)
- | sensor->binning_vertical;
+ ccs_get_binning(sensor, &binning_mode, &binh, &binv);
- rval = ccs_write(sensor, BINNING_TYPE, binning_type);
+ if (binning_mode) {
+ rval = ccs_write(sensor, BINNING_TYPE, (binh << 4) | binv);
if (rval < 0)
- goto out;
-
- binning_mode = 1;
+ goto err_pm_put;
}
rval = ccs_write(sensor, BINNING_MODE, binning_mode);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/* Set up PLL */
rval = ccs_pll_configure(sensor);
if (rval)
- goto out;
+ goto err_pm_put;
/* Analog crop start coordinates */
- rval = ccs_write(sensor, X_ADDR_START, sensor->pa_src.left);
+ rval = ccs_write(sensor, X_ADDR_START, pa_src->left);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, Y_ADDR_START, sensor->pa_src.top);
+ rval = ccs_write(sensor, Y_ADDR_START, pa_src->top);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/* Analog crop end coordinates */
- rval = ccs_write(sensor, X_ADDR_END,
- sensor->pa_src.left + sensor->pa_src.width - 1);
+ rval = ccs_write(sensor, X_ADDR_END, pa_src->left + pa_src->width - 1);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, Y_ADDR_END,
- sensor->pa_src.top + sensor->pa_src.height - 1);
+ rval = ccs_write(sensor, Y_ADDR_END, pa_src->top + pa_src->height - 1);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/*
* Output from pixel array, including blanking, is set using
@@ -1779,46 +1931,57 @@ static int ccs_start_streaming(struct ccs_sensor *sensor)
/* Digital crop */
if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY)
== CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ struct v4l2_subdev_state *scaler_state =
+ v4l2_subdev_get_locked_active_state(&sensor->scaler->sd);
+ const struct v4l2_rect *scaler_sink =
+ v4l2_subdev_state_get_crop(scaler_state,
+ sensor->scaler->sink_pad,
+ CCS_STREAM_PIXEL);
+
rval = ccs_write(sensor, DIGITAL_CROP_X_OFFSET,
- sensor->scaler_sink.left);
+ scaler_sink->left);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_Y_OFFSET,
- sensor->scaler_sink.top);
+ scaler_sink->top);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_WIDTH,
- sensor->scaler_sink.width);
+ scaler_sink->width);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_HEIGHT,
- sensor->scaler_sink.height);
+ scaler_sink->height);
if (rval < 0)
- goto out;
+ goto err_pm_put;
}
/* Scaling */
if (CCS_LIM(sensor, SCALING_CAPABILITY)
!= CCS_SCALING_CAPABILITY_NONE) {
- rval = ccs_write(sensor, SCALING_MODE, sensor->scaling_mode);
+ u8 scaling_mode, scale_m;
+
+ ccs_get_scaling(sensor, &scaling_mode, &scale_m);
+
+ rval = ccs_write(sensor, SCALING_MODE, scaling_mode);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, SCALE_M, sensor->scale_m);
+ rval = ccs_write(sensor, SCALE_M, scale_m);
if (rval < 0)
- goto out;
+ goto err_pm_put;
}
/* Output size from sensor */
- rval = ccs_write(sensor, X_OUTPUT_SIZE, sensor->src_src.width);
+ rval = ccs_write(sensor, X_OUTPUT_SIZE, src_src->width);
if (rval < 0)
- goto out;
- rval = ccs_write(sensor, Y_OUTPUT_SIZE, sensor->src_src.height);
+ goto err_pm_put;
+ rval = ccs_write(sensor, Y_OUTPUT_SIZE, src_src->height);
if (rval < 0)
- goto out;
+ goto err_pm_put;
if (CCS_LIM(sensor, FLASH_MODE_CAPABILITY) &
(CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
@@ -1827,109 +1990,53 @@ static int ccs_start_streaming(struct ccs_sensor *sensor)
sensor->hwcfg.strobe_setup->trigger != 0) {
rval = ccs_setup_flash_strobe(sensor);
if (rval)
- goto out;
+ goto err_pm_put;
}
rval = ccs_call_quirk(sensor, pre_streamon);
if (rval) {
dev_err(&client->dev, "pre_streamon quirks failed\n");
- goto out;
+ goto err_pm_put;
}
rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_STREAMING);
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static int ccs_stop_streaming(struct ccs_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY);
- if (rval)
- goto out;
-
- rval = ccs_call_quirk(sensor, post_streamoff);
- if (rval)
- dev_err(&client->dev, "post_streamoff quirks failed\n");
-
-out:
- mutex_unlock(&sensor->mutex);
- return rval;
-}
+ sensor->streaming |= streams_mask;
-/* -----------------------------------------------------------------------------
- * V4L2 subdev video operations
- */
-
-static int ccs_pm_get_init(struct ccs_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- /*
- * It can't use pm_runtime_resume_and_get() here, as the driver
- * relies at the returned value to detect if the device was already
- * active or not.
- */
- rval = pm_runtime_get_sync(&client->dev);
- if (rval < 0)
- goto error;
-
- /* Device was already active, so don't set controls */
- 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)
- goto error;
+ return 0;
- rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
- if (rval)
- goto error;
+err_pm_put:
+ pm_runtime_put_autosuspend(&client->dev);
- /* Keep PM runtime usage_count incremented on success */
- return 0;
-error:
- pm_runtime_put(&client->dev);
return rval;
}
-static int ccs_set_stream(struct v4l2_subdev *subdev, int enable)
+static int ccs_disable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
int rval;
- if (!enable) {
- ccs_stop_streaming(sensor);
- sensor->streaming = false;
- pm_runtime_put_autosuspend(&client->dev);
+ if (pad != CCS_PAD_SRC)
+ return -EINVAL;
+ sensor->streaming &= ~streams_mask;
+ if (sensor->streaming)
return 0;
- }
- rval = ccs_pm_get_init(sensor);
+ rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY);
if (rval)
return rval;
- sensor->streaming = true;
+ rval = ccs_call_quirk(sensor, post_streamoff);
+ if (rval)
+ dev_err(&client->dev, "post_streamoff quirks failed\n");
- rval = ccs_start_streaming(sensor);
- if (rval < 0) {
- sensor->streaming = false;
- pm_runtime_put_autosuspend(&client->dev);
- }
+ pm_runtime_put_autosuspend(&client->dev);
- return rval;
+ return 0;
}
static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags)
@@ -1955,7 +2062,8 @@ static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags)
}
}
- rval = ccs_pm_get_init(sensor);
+ scoped_guard(mutex, &sensor->mutex)
+ rval = ccs_pm_get_init(sensor);
if (rval)
return rval;
@@ -1979,6 +2087,20 @@ static int ccs_post_streamoff(struct v4l2_subdev *subdev)
return 0;
}
+static const struct ccs_csi_data_format
+*ccs_validate_csi_data_format(struct ccs_sensor *sensor, u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & BIT_U64(i) &&
+ ccs_csi_data_formats[i].code == code)
+ return &ccs_csi_data_formats[i];
+ }
+
+ return sensor->csi_format;
+}
+
static int ccs_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -1987,42 +2109,35 @@ static int ccs_enum_mbus_code(struct v4l2_subdev *subdev,
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
unsigned int i;
int idx = -1;
- int rval = -EINVAL;
-
- mutex_lock(&sensor->mutex);
dev_err(&client->dev, "subdev %s, pad %u, index %u\n",
subdev->name, code->pad, code->index);
if (subdev != &sensor->src->sd || code->pad != CCS_PAD_SRC) {
if (code->index)
- goto out;
+ return -EINVAL;
code->code = sensor->internal_csi_format->code;
- rval = 0;
- goto out;
+
+ return 0;
}
for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i))
+ if (sensor->mbus_frame_fmts & BIT_U64(i))
idx++;
if (idx == code->index) {
code->code = ccs_csi_data_formats[i].code;
dev_err(&client->dev, "found index %u, i %u, code %x\n",
code->index, i, code->code);
- rval = 0;
- break;
+ return 0;
}
}
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
+ return -EINVAL;
}
-static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
+static u32 ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2032,102 +2147,47 @@ static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
return sensor->internal_csi_format->code;
}
-static int __ccs_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
- fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad);
-
- return 0;
-}
-
static int ccs_get_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct ccs_sensor *sensor = to_ccs_sensor(subdev);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = __ccs_get_format(subdev, sd_state, fmt);
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static void ccs_get_crop_compose(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_rect **crops,
- struct v4l2_rect **comps)
-{
- struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- unsigned int i;
+ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
+ fmt->format.code = ccs_get_mbus_code(subdev, fmt->pad);
- if (crops)
- for (i = 0; i < subdev->entity.num_pads; i++)
- crops[i] =
- v4l2_subdev_state_get_crop(sd_state, i);
- if (comps)
- *comps = v4l2_subdev_state_get_compose(sd_state,
- ssd->sink_pad);
+ return 0;
}
/* Changes require propagation only on sink pad. */
static void ccs_propagate(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state, int which,
- int target)
+ struct v4l2_subdev_state *sd_state, int target)
{
- struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ struct v4l2_rect *comp, *crop;
struct v4l2_mbus_framefmt *fmt;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
-
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
switch (target) {
case V4L2_SEL_TGT_CROP:
- comp->width = crops[CCS_PAD_SINK]->width;
- comp->height = crops[CCS_PAD_SINK]->height;
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (ssd == sensor->scaler) {
- sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
- sensor->scaling_mode =
- CCS_SCALING_MODE_NO_SCALING;
- sensor->scaler_sink = *comp;
- } else if (ssd == sensor->binner) {
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
- }
- }
+ crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ comp->width = crop->width;
+ comp->height = crop->height;
fallthrough;
case V4L2_SEL_TGT_COMPOSE:
- *crops[CCS_PAD_SRC] = *comp;
- fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC);
+ crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ *crop = *comp;
+ fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
fmt->width = comp->width;
fmt->height = comp->height;
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src)
- sensor->src_src = *crops[CCS_PAD_SRC];
break;
default:
WARN_ON_ONCE(1);
}
}
-static const struct ccs_csi_data_format
-*ccs_validate_csi_data_format(struct ccs_sensor *sensor, u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i) &&
- ccs_csi_data_formats[i].code == code)
- return &ccs_csi_data_formats[i];
- }
-
- return sensor->csi_format;
-}
-
static int ccs_set_format_source(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -2140,7 +2200,7 @@ static int ccs_set_format_source(struct v4l2_subdev *subdev,
unsigned int i;
int rval;
- rval = __ccs_get_format(subdev, sd_state, fmt);
+ rval = ccs_get_format(subdev, sd_state, fmt);
if (rval)
return rval;
@@ -2187,22 +2247,19 @@ static int ccs_set_format(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *crops[CCS_PADS];
-
- mutex_lock(&sensor->mutex);
+ struct v4l2_rect *crop;
if (fmt->pad == ssd->source_pad) {
int rval;
rval = ccs_set_format_source(subdev, sd_state, fmt);
- mutex_unlock(&sensor->mutex);
-
return rval;
}
/* Sink pad. Width and height are changeable here. */
- fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad);
+ fmt->format.code = ccs_get_mbus_code(subdev, fmt->pad);
+
fmt->format.width &= ~1;
fmt->format.height &= ~1;
fmt->format.field = V4L2_FIELD_NONE;
@@ -2216,15 +2273,14 @@ static int ccs_set_format(struct v4l2_subdev *subdev,
CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE),
CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE));
- ccs_get_crop_compose(subdev, sd_state, crops, NULL);
+ crop = v4l2_subdev_state_get_crop(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
- crops[ssd->sink_pad]->left = 0;
- crops[ssd->sink_pad]->top = 0;
- crops[ssd->sink_pad]->width = fmt->format.width;
- crops[ssd->sink_pad]->height = fmt->format.height;
- ccs_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP);
-
- mutex_unlock(&sensor->mutex);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
@@ -2276,26 +2332,23 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
+ const struct v4l2_rect *sink_crop)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
unsigned int i;
unsigned int binh = 1, binv = 1;
- int best = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width, sel->r.width,
- crops[CCS_PAD_SINK]->height, sel->r.height, sel->flags);
+ int best = scaling_goodness(subdev, sink_crop->width, sel->r.width,
+ sink_crop->height, sel->r.height,
+ sel->flags);
for (i = 0; i < sensor->nbinning_subtypes; i++) {
- int this = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width
- / sensor->binning_subtypes[i].horizontal,
- sel->r.width,
- crops[CCS_PAD_SINK]->height
- / sensor->binning_subtypes[i].vertical,
- sel->r.height, sel->flags);
+ int this = scaling_goodness(subdev,
+ sink_crop->width
+ / sensor->binning_subtypes[i].horizontal,
+ sel->r.width,
+ sink_crop->height
+ / sensor->binning_subtypes[i].vertical,
+ sel->r.height, sel->flags);
if (this > best) {
binh = sensor->binning_subtypes[i].horizontal;
@@ -2303,13 +2356,9 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
best = this;
}
}
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->binning_vertical = binv;
- sensor->binning_horizontal = binh;
- }
- sel->r.width = (crops[CCS_PAD_SINK]->width / binh) & ~1;
- sel->r.height = (crops[CCS_PAD_SINK]->height / binv) & ~1;
+ sel->r.width = (sink_crop->width / binh) & ~1;
+ sel->r.height = (sink_crop->height / binv) & ~1;
}
/*
@@ -2324,8 +2373,7 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
+ const struct v4l2_rect *sink_crop)
{
struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2337,18 +2385,14 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
unsigned int i;
int best = INT_MIN;
- sel->r.width = min_t(unsigned int, sel->r.width,
- crops[CCS_PAD_SINK]->width);
- sel->r.height = min_t(unsigned int, sel->r.height,
- crops[CCS_PAD_SINK]->height);
+ sel->r.width = min_t(unsigned int, sel->r.width, sink_crop->width);
+ sel->r.height = min_t(unsigned int, sel->r.height, sink_crop->height);
- a = crops[CCS_PAD_SINK]->width
- * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width;
- b = crops[CCS_PAD_SINK]->height
- * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height;
- max_m = crops[CCS_PAD_SINK]->width
+ a = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width;
+ b = sink_crop->height * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height;
+ max_m = sink_crop->width
* CCS_LIM(sensor, SCALER_N_MIN)
- / CCS_LIM(sensor, MIN_X_OUTPUT_SIZE);
+ / (CCS_LIM(sensor, MIN_X_OUTPUT_SIZE) ?: 1);
a = clamp(a, CCS_LIM(sensor, SCALER_M_MIN),
CCS_LIM(sensor, SCALER_M_MAX));
@@ -2378,14 +2422,12 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
}
for (i = 0; i < ntry; i++) {
- int this = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width
- / try[i] * CCS_LIM(sensor, SCALER_N_MIN),
- sel->r.width,
- crops[CCS_PAD_SINK]->height,
- sel->r.height,
- sel->flags);
+ int this = scaling_goodness(subdev,
+ sink_crop->width
+ / try[i]
+ * CCS_LIM(sensor, SCALER_N_MIN),
+ sel->r.width, sink_crop->height,
+ sel->r.height, sel->flags);
dev_dbg(&client->dev, "trying factor %u (%u)\n", try[i], i);
@@ -2400,12 +2442,10 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
continue;
this = scaling_goodness(
- subdev, crops[CCS_PAD_SINK]->width
- / try[i]
+ subdev, sink_crop->width / try[i]
* CCS_LIM(sensor, SCALER_N_MIN),
sel->r.width,
- crops[CCS_PAD_SINK]->height
- / try[i]
+ sink_crop->height / try[i]
* CCS_LIM(sensor, SCALER_N_MIN),
sel->r.height,
sel->flags);
@@ -2417,23 +2457,13 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
}
}
- sel->r.width =
- (crops[CCS_PAD_SINK]->width
- / scale_m
- * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
+ sel->r.width = (sink_crop->width / scale_m
+ * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
if (mode == SMIAPP_SCALING_MODE_BOTH)
- sel->r.height =
- (crops[CCS_PAD_SINK]->height
- / scale_m
- * CCS_LIM(sensor, SCALER_N_MIN))
- & ~1;
+ sel->r.height = (sink_crop->height / scale_m
+ * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
else
- sel->r.height = crops[CCS_PAD_SINK]->height;
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->scale_m = scale_m;
- sensor->scaling_mode = mode;
- }
+ sel->r.height = sink_crop->height;
}
/* We're only called on source pads. This function sets scaling. */
static int ccs_set_compose(struct v4l2_subdev *subdev,
@@ -2442,20 +2472,24 @@ static int ccs_set_compose(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ const struct v4l2_rect *sink_crop;
+ struct v4l2_rect *comp;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ sink_crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
sel->r.top = 0;
sel->r.left = 0;
if (ssd == sensor->binner)
- ccs_set_compose_binner(subdev, sd_state, sel, crops, comp);
+ ccs_set_compose_binner(subdev, sd_state, sel, sink_crop);
else
- ccs_set_compose_scaler(subdev, sd_state, sel, crops, comp);
+ ccs_set_compose_scaler(subdev, sd_state, sel, sink_crop);
*comp = sel->r;
- ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_COMPOSE);
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_COMPOSE);
if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
return ccs_pll_blanking_update(sensor);
@@ -2507,9 +2541,13 @@ static int ccs_set_crop(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect src_size = { 0 }, *crops[CCS_PADS], *comp;
+ struct v4l2_rect src_size = { 0 }, *crop;
+ const struct v4l2_rect *comp;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
if (sel->pad == ssd->sink_pad) {
struct v4l2_mbus_framefmt *mfmt =
@@ -2532,13 +2570,10 @@ static int ccs_set_crop(struct v4l2_subdev *subdev,
sel->r.left = min_t(int, sel->r.left, src_size.width - sel->r.width);
sel->r.top = min_t(int, sel->r.top, src_size.height - sel->r.height);
- *crops[sel->pad] = sel->r;
+ *crop = sel->r;
if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK)
- ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP);
- else if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
- ssd == sensor->pixel_array)
- sensor->pa_src = sel->r;
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
@@ -2557,14 +2592,17 @@ static int ccs_get_selection(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ const struct v4l2_rect *crop, *comp;
int ret;
ret = ccs_sel_supported(subdev, sel);
if (ret)
return ret;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
@@ -2584,7 +2622,7 @@ static int ccs_get_selection(struct v4l2_subdev *subdev,
break;
case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- sel->r = *crops[sel->pad];
+ sel->r = *crop;
break;
case V4L2_SEL_TGT_COMPOSE:
sel->r = *comp;
@@ -2605,8 +2643,6 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
if (ret)
return ret;
- mutex_lock(&sensor->mutex);
-
sel->r.left = max(0, sel->r.left & ~1);
sel->r.top = max(0, sel->r.top & ~1);
sel->r.width = CCS_ALIGN_DIM(sel->r.width, sel->flags);
@@ -2628,10 +2664,44 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
ret = -EINVAL;
}
- mutex_unlock(&sensor->mutex);
return ret;
}
+static int ccs_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
+ struct v4l2_mbus_frame_desc *desc)
+{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct v4l2_mbus_frame_desc_entry *entry = desc->entry;
+ struct v4l2_subdev_state *sd_state;
+
+ switch (sensor->hwcfg.csi_signalling_mode) {
+ case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY:
+ case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY:
+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ break;
+ default:
+ /* FIXME: CCP2 support */
+ return -EINVAL;
+ }
+
+ sd_state = v4l2_subdev_lock_and_get_active_state(subdev);
+ if (!sd_state)
+ return -EINVAL;
+
+ entry->pixelcode = sensor->csi_format->code;
+ entry->stream = CCS_STREAM_PIXEL;
+ entry->bus.csi2.dt =
+ sensor->csi_format->width == sensor->csi_format->compressed ?
+ ccs_mipi_csi2_data_type(sensor->csi_format->width) :
+ CCS_DEFAULT_COMPRESSED_DT;
+ entry++;
+ desc->num_entries++;
+
+ v4l2_subdev_unlock_state(sd_state);
+
+ return 0;
+}
+
static int ccs_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2942,6 +3012,8 @@ static void ccs_cleanup(struct ccs_sensor *sensor)
ccs_free_controls(sensor);
}
+static const struct v4l2_subdev_internal_ops ccs_internal_ops;
+
static int ccs_init_subdev(struct ccs_sensor *sensor,
struct ccs_subdev *ssd, const char *name,
unsigned short num_pads, u32 function,
@@ -2954,11 +3026,14 @@ static int ccs_init_subdev(struct ccs_sensor *sensor,
if (!ssd)
return 0;
- if (ssd != sensor->src)
+ if (ssd != sensor->src) {
v4l2_subdev_init(&ssd->sd, &ccs_ops);
+ ssd->sd.internal_ops = &ccs_internal_ops;
+ }
ssd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ssd->sd.entity.function = function;
+ ssd->sd.state_lock = &sensor->mutex;
ssd->sensor = sensor;
ssd->npads = num_pads;
@@ -3004,9 +3079,8 @@ static int ccs_init_state(struct v4l2_subdev *sd,
v4l2_subdev_state_get_format(sd_state, pad);
struct v4l2_rect *crop =
v4l2_subdev_state_get_crop(sd_state, pad);
- bool is_active = !sd->active_state || sd->active_state == sd_state;
- mutex_lock(&sensor->mutex);
+ guard(mutex)(&sensor->mutex);
ccs_get_native_size(ssd, crop);
@@ -3015,28 +3089,21 @@ static int ccs_init_state(struct v4l2_subdev *sd,
fmt->code = sensor->internal_csi_format->code;
fmt->field = V4L2_FIELD_NONE;
- if (ssd == sensor->pixel_array) {
- if (is_active)
- sensor->pa_src = *crop;
-
- mutex_unlock(&sensor->mutex);
+ if (ssd == sensor->pixel_array)
return 0;
- }
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;
- ccs_propagate(sd, sd_state, is_active, V4L2_SEL_TGT_CROP);
-
- mutex_unlock(&sensor->mutex);
+ ccs_propagate(sd, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
static const struct v4l2_subdev_video_ops ccs_video_ops = {
- .s_stream = ccs_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
.pre_streamon = ccs_pre_streamon,
.post_streamoff = ccs_post_streamoff,
};
@@ -3047,6 +3114,9 @@ static const struct v4l2_subdev_pad_ops ccs_pad_ops = {
.set_fmt = ccs_set_format,
.get_selection = ccs_get_selection,
.set_selection = ccs_set_selection,
+ .enable_streams = ccs_enable_streams,
+ .disable_streams = ccs_disable_streams,
+ .get_frame_desc = ccs_get_frame_desc,
};
static const struct v4l2_subdev_sensor_ops ccs_sensor_ops = {
@@ -3064,6 +3134,10 @@ static const struct media_entity_operations ccs_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
+static const struct v4l2_subdev_internal_ops ccs_internal_ops = {
+ .init_state = ccs_init_state,
+};
+
static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = {
.init_state = ccs_init_state,
.registered = ccs_registered,
@@ -3076,11 +3150,9 @@ static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = {
static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
{
- struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN };
- struct fwnode_handle *ep;
- struct fwnode_handle *fwnode = dev_fwnode(dev);
- unsigned int i;
+ struct fwnode_handle *fwnode = dev_fwnode(dev), *ep;
+ struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
int rval;
ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0,
@@ -3107,9 +3179,9 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
break;
case V4L2_MBUS_CSI1:
case V4L2_MBUS_CCP2:
- hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ?
- SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
- SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
+ hwcfg->csi_signalling_mode = bus_cfg.bus.mipi_csi1.strobe ?
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
hwcfg->lanes = 1;
break;
default:
@@ -3118,11 +3190,7 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
goto out_err;
}
- rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
- &hwcfg->ext_clk);
-
- dev_dbg(dev, "clk %u, mode %u\n", hwcfg->ext_clk,
- hwcfg->csi_signalling_mode);
+ dev_dbg(dev, "signalling mode: %u\n", hwcfg->csi_signalling_mode);
if (!bus_cfg.nr_of_link_frequencies) {
dev_warn(dev, "no link frequencies defined\n");
@@ -3130,23 +3198,22 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
goto out_err;
}
- hwcfg->op_sys_clock = devm_kcalloc(
- dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */,
- sizeof(*hwcfg->op_sys_clock), GFP_KERNEL);
+ hwcfg->op_sys_clock =
+ devm_kcalloc(dev,
+ bus_cfg.nr_of_link_frequencies + 1 /* guardian */,
+ sizeof(*hwcfg->op_sys_clock), GFP_KERNEL);
if (!hwcfg->op_sys_clock) {
rval = -ENOMEM;
goto out_err;
}
- for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
+ for (unsigned int i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i];
dev_dbg(dev, "freq %u: %lld\n", i, hwcfg->op_sys_clock[i]);
}
- v4l2_fwnode_endpoint_free(&bus_cfg);
- fwnode_handle_put(ep);
-
- return 0;
+ fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+ &hwcfg->ext_clk);
out_err:
v4l2_fwnode_endpoint_free(&bus_cfg);
@@ -3234,47 +3301,30 @@ static int ccs_probe(struct i2c_client *client)
return rval;
}
- sensor->ext_clk = devm_clk_get(&client->dev, NULL);
- if (PTR_ERR(sensor->ext_clk) == -ENOENT) {
- dev_info(&client->dev, "no clock defined, continuing...\n");
- sensor->ext_clk = NULL;
- } else if (IS_ERR(sensor->ext_clk)) {
- dev_err(&client->dev, "could not get clock (%pe)\n",
- sensor->ext_clk);
- return -EPROBE_DEFER;
- }
-
- if (sensor->ext_clk) {
- if (sensor->hwcfg.ext_clk) {
- unsigned long rate;
+ sensor->ext_clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
+ if (IS_ERR(sensor->ext_clk))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->ext_clk),
+ "could not get clock\n");
- rval = clk_set_rate(sensor->ext_clk,
- sensor->hwcfg.ext_clk);
- if (rval < 0) {
- dev_err(&client->dev,
- "unable to set clock freq to %u\n",
- sensor->hwcfg.ext_clk);
- return rval;
- }
+ if (sensor->hwcfg.ext_clk) {
+ unsigned long rate;
- rate = clk_get_rate(sensor->ext_clk);
- if (rate != sensor->hwcfg.ext_clk) {
- dev_err(&client->dev,
- "can't set clock freq, asked for %u but got %lu\n",
- sensor->hwcfg.ext_clk, rate);
- return -EINVAL;
- }
- } else {
- sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
- dev_dbg(&client->dev, "obtained clock freq %u\n",
- sensor->hwcfg.ext_clk);
+ rval = clk_set_rate(sensor->ext_clk, sensor->hwcfg.ext_clk);
+ if (rval < 0)
+ return dev_err_probe(&client->dev, rval,
+ "unable to set clock freq to %u\n",
+ sensor->hwcfg.ext_clk);
+
+ rate = clk_get_rate(sensor->ext_clk);
+ if (rate != sensor->hwcfg.ext_clk) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "can't set clock freq, asked for %u but got %lu\n",
+ sensor->hwcfg.ext_clk, rate);
}
- } else if (sensor->hwcfg.ext_clk) {
- dev_dbg(&client->dev, "assuming clock freq %u\n",
- sensor->hwcfg.ext_clk);
} else {
- dev_err(&client->dev, "unable to obtain clock freq\n");
- return -EINVAL;
+ sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
+ dev_dbg(&client->dev, "obtained clock freq %u\n",
+ sensor->hwcfg.ext_clk);
}
if (!sensor->hwcfg.ext_clk) {
@@ -3386,8 +3436,6 @@ static int ccs_probe(struct i2c_client *client)
sensor->binning_subtypes[i].vertical);
}
}
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
if (device_create_file(&client->dev, &dev_attr_ident) != 0) {
dev_err(&client->dev, "sysfs ident entry creation failed\n");
@@ -3424,10 +3472,22 @@ static int ccs_probe(struct i2c_client *client)
sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
sensor->ssds_used++;
- sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
-
/* prepare PLL configuration input values */
- sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ switch (sensor->hwcfg.csi_signalling_mode) {
+ case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY:
+ sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_CPHY;
+ break;
+ case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY:
+ case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK:
+ case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE:
+ sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ break;
+ default:
+ dev_err(&client->dev, "unsupported signalling mode %u\n",
+ sensor->hwcfg.csi_signalling_mode);
+ rval = -EINVAL;
+ goto out_cleanup;
+ }
sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
if (CCS_LIM(sensor, CLOCK_CALCULATION) &
CCS_CLOCK_CALCULATION_LANE_SPEED) {
@@ -3519,15 +3579,13 @@ static int ccs_probe(struct i2c_client *client)
goto out_cleanup;
}
- mutex_lock(&sensor->mutex);
- rval = ccs_pll_blanking_update(sensor);
- mutex_unlock(&sensor->mutex);
+ scoped_guard(mutex, &sensor->mutex)
+ rval = ccs_pll_blanking_update(sensor);
if (rval) {
dev_err(&client->dev, "update mode failed\n");
goto out_cleanup;
}
- sensor->streaming = false;
sensor->dev_init_done = true;
sensor->handler_setup_needed = true;
diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h
index 0726c4687f0f..58952c5bf06b 100644
--- a/drivers/media/i2c/ccs/ccs.h
+++ b/drivers/media/i2c/ccs/ccs.h
@@ -48,6 +48,8 @@
#define CCS_COLOUR_COMPONENTS 4
+#define CCS_DEFAULT_COMPRESSED_DT MIPI_CSI2_DT_USER_DEFINED(0)
+
#define SMIAPP_NAME "smiapp"
#define CCS_NAME "ccs"
@@ -177,6 +179,8 @@ struct ccs_csi_data_format {
#define CCS_PAD_SRC 1
#define CCS_PADS 2
+#define CCS_STREAM_PIXEL 0
+
struct ccs_binning_subtype {
u8 horizontal:4;
u8 vertical:4;
@@ -218,27 +222,20 @@ struct ccs_sensor {
void *ccs_limits;
u8 nbinning_subtypes;
struct ccs_binning_subtype binning_subtypes[CCS_LIM_BINNING_SUB_TYPE_MAX_N + 1];
- u32 mbus_frame_fmts;
+ u64 mbus_frame_fmts;
const struct ccs_csi_data_format *csi_format;
const struct ccs_csi_data_format *internal_csi_format;
- struct v4l2_rect pa_src, scaler_sink, src_src;
- u32 default_mbus_frame_fmts;
+ u64 default_mbus_frame_fmts;
int default_pixel_order;
struct ccs_data_container sdata, mdata;
- u8 binning_horizontal;
- u8 binning_vertical;
-
- u8 scale_m;
- u8 scaling_mode;
-
u8 frame_skip;
u16 embedded_start; /* embedded data start line */
u16 embedded_end;
u16 image_start; /* image data start line */
u16 visible_pixel_start; /* start pixel of the visible image */
- bool streaming;
+ u8 streaming;
bool dev_init_done;
bool handler_setup_needed;
u8 compressed_min_bpp;
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 1e7ad355a388..3288de539452 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -149,7 +149,7 @@ static int dw9714_power_up(struct dw9714_device *dw9714_dev)
gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 0);
- usleep_range(1000, 2000);
+ usleep_range(12000, 14000);
return 0;
}
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index 2cb7b718782b..50121c3e5b48 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -835,6 +835,10 @@ static int et8ek8_power_on(struct et8ek8_sensor *sensor)
udelay(10); /* I wish this is a good value */
+ /*
+ * Note: Misinterpretation of reset assertion - do not re-use this code.
+ * The reset pin is using incorrect (for a reset signal) logical level.
+ */
gpiod_set_value(sensor->reset, 1);
msleep(5000 * 1000 / sensor->xclk_freq + 1); /* Wait 5000 cycles */
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index bc55fe2a93b4..fee63bc106d9 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -792,21 +792,6 @@ static int imx219_disable_streams(struct v4l2_subdev *sd,
return ret;
}
-static void imx219_update_pad_format(struct imx219 *imx219,
- const struct imx219_mode *mode,
- struct v4l2_mbus_framefmt *fmt, u32 code)
-{
- /* Bayer order varies with flips */
- fmt->code = imx219_get_format_code(imx219, code);
- fmt->width = mode->width;
- fmt->height = mode->height;
- fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_RAW;
- fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
- fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
- fmt->xfer_func = V4L2_XFER_FUNC_NONE;
-}
-
static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -858,12 +843,24 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
format = v4l2_subdev_state_get_format(state, 0);
prev_line_len = format->width + imx219->hblank->val;
+ /*
+ * Adjust the requested format to match the closest mode. The Bayer
+ * order varies with flips.
+ */
mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes),
width, height,
fmt->format.width, fmt->format.height);
- imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code);
+ fmt->format.code = imx219_get_format_code(imx219, fmt->format.code);
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
*format = fmt->format;
/*
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 51ebbe7ae996..16b0ace15813 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -32,6 +32,8 @@
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
+#include "aptina-pll.h"
+
/* Sysctl registers */
#define MT9M114_CHIP_ID CCI_REG16(0x0000)
#define MT9M114_COMMAND_REGISTER CCI_REG16(0x0080)
@@ -267,9 +269,9 @@
#define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0)
#define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980)
-#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m))
+#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) ((((n) - 1) << 8) | (m))
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982)
-#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) ((p) << 8)
+#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) (((p) - 1) << 8)
#define MT9M114_CAM_PORT_OUTPUT_CONTROL CCI_REG16(0xc984)
#define MT9M114_CAM_PORT_PORT_SELECT_PARALLEL (0 << 0)
#define MT9M114_CAM_PORT_PORT_SELECT_MIPI (1 << 0)
@@ -327,19 +329,22 @@
/*
* The minimum amount of horizontal and vertical blanking is undocumented. The
- * minimum values that have been seen in register lists are 303 and 38, use
+ * minimum values that have been seen in register lists are 303 and 21, use
* them.
*
- * Set the default to achieve 1280x960 at 30fps.
+ * Set the default to achieve full resolution (1296x976 analog crop
+ * rectangle, 1280x960 output size) at 30fps with a 48 MHz pixclock.
*/
#define MT9M114_MIN_HBLANK 303
-#define MT9M114_MIN_VBLANK 38
-#define MT9M114_DEF_HBLANK 323
-#define MT9M114_DEF_VBLANK 39
+#define MT9M114_MIN_VBLANK 21
+#define MT9M114_DEF_HBLANK 308
+#define MT9M114_DEF_VBLANK 21
#define MT9M114_DEF_FRAME_RATE 30
#define MT9M114_MAX_FRAME_RATE 120
+#define MT9M114_DEF_PIXCLOCK 48000000
+
#define MT9M114_PIXEL_ARRAY_WIDTH 1296U
#define MT9M114_PIXEL_ARRAY_HEIGHT 976U
@@ -384,11 +389,7 @@ struct mt9m114 {
struct v4l2_fwnode_endpoint bus_cfg;
bool bypass_pll;
- struct {
- unsigned int m;
- unsigned int n;
- unsigned int p;
- } pll;
+ struct aptina_pll pll;
unsigned int pixrate;
bool streaming;
@@ -758,7 +759,7 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
sensor->pll.n),
&ret);
cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P,
- MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p),
+ MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p1),
&ret);
}
@@ -788,14 +789,6 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
if (ret < 0)
return ret;
- ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE);
- if (ret < 0)
- return ret;
-
- ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND);
- if (ret < 0)
- return ret;
-
return 0;
}
@@ -850,6 +843,18 @@ static int mt9m114_configure_pa(struct mt9m114 *sensor,
return ret;
}
+/*
+ * For source pad formats other then RAW10 the IFP removes a 4 pixel border from
+ * its sink pad format size for demosaicing.
+ */
+static int mt9m114_ifp_get_border(struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format =
+ v4l2_subdev_state_get_format(state, 1);
+
+ return format->code == MEDIA_BUS_FMT_SGRBG10_1X10 ? 0 : 4;
+}
+
static int mt9m114_configure_ifp(struct mt9m114 *sensor,
struct v4l2_subdev_state *state)
{
@@ -857,6 +862,7 @@ static int mt9m114_configure_ifp(struct mt9m114 *sensor,
const struct v4l2_mbus_framefmt *format;
const struct v4l2_rect *crop;
const struct v4l2_rect *compose;
+ unsigned int border;
u64 output_format;
int ret = 0;
@@ -871,15 +877,18 @@ static int mt9m114_configure_ifp(struct mt9m114 *sensor,
return ret;
/*
- * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left
- * and top coordinates to compensate for the lines and columns removed
- * by demosaicing that are taken into account in the crop rectangle but
- * not in the hardware.
+ * Color pipeline (IFP) cropping and scaling. The crop window registers
+ * apply cropping after demosaicing, which itself consumes 4 pixels on
+ * each side of the image. The crop rectangle exposed to userspace
+ * includes that demosaicing border, subtract it from the left and top
+ * coordinates to configure the crop window.
*/
+ border = mt9m114_ifp_get_border(state);
+
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET,
- crop->left - 4, &ret);
+ crop->left - border, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET,
- crop->top - 4, &ret);
+ crop->top - border, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH,
crop->width, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT,
@@ -950,6 +959,10 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor,
if (ret)
return ret;
+ ret = mt9m114_initialize(sensor);
+ if (ret)
+ goto error;
+
ret = mt9m114_configure_ifp(sensor, ifp_state);
if (ret)
goto error;
@@ -1823,6 +1836,41 @@ static int mt9m114_ifp_enum_frameintervals(struct v4l2_subdev *sd,
return 0;
}
+/*
+ * Helper function to update IFP crop, compose rectangles and source format
+ * when the pixel border size changes, which requires resetting these.
+ */
+static void mt9m114_ifp_update_sel_and_src_fmt(struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *src_format, *sink_format;
+ struct v4l2_rect *crop;
+ unsigned int border;
+
+ sink_format = v4l2_subdev_state_get_format(state, 0);
+ src_format = v4l2_subdev_state_get_format(state, 1);
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ border = mt9m114_ifp_get_border(state);
+
+ crop->left = border;
+ crop->top = border;
+ crop->width = sink_format->width - 2 * border;
+ crop->height = sink_format->height - 2 * border;
+ *v4l2_subdev_state_get_compose(state, 0) = *crop;
+
+ src_format->width = crop->width;
+ src_format->height = crop->height;
+
+ if (src_format->code == MEDIA_BUS_FMT_SGRBG10_1X10) {
+ src_format->colorspace = V4L2_COLORSPACE_RAW;
+ src_format->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ src_format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ src_format->colorspace = V4L2_COLORSPACE_SRGB;
+ src_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ src_format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ }
+}
+
static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
@@ -1840,17 +1888,28 @@ static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd,
format->height = clamp(ALIGN(fmt->format.height, 8),
MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT,
MT9M114_PIXEL_ARRAY_HEIGHT);
+
+ /* Propagate changes downstream. */
+ mt9m114_ifp_update_sel_and_src_fmt(state);
} else {
const struct mt9m114_format_info *info;
/* Only the media bus code can be changed on the source pad. */
info = mt9m114_format_info(sensor, 1, fmt->format.code);
- format->code = info->code;
-
- /* If the output format is RAW10, bypass the scaler. */
- if (format->code == MEDIA_BUS_FMT_SGRBG10_1X10)
- *format = *v4l2_subdev_state_get_format(state, 0);
+ /*
+ * If the output format changes from/to RAW10 then the crop
+ * rectangle needs to be adjusted to add / remove the 4 pixel
+ * border used for demosaicing. And these changes then need to
+ * be propagated to the compose rectangle and source format.
+ */
+ if ((format->code == MEDIA_BUS_FMT_SGRBG10_1X10) !=
+ (info->code == MEDIA_BUS_FMT_SGRBG10_1X10)) {
+ format->code = info->code;
+ mt9m114_ifp_update_sel_and_src_fmt(state);
+ } else {
+ format->code = info->code;
+ }
}
fmt->format = *format;
@@ -1864,6 +1923,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd,
{
const struct v4l2_mbus_framefmt *format;
const struct v4l2_rect *crop;
+ unsigned int border;
int ret = 0;
/* Crop and compose are only supported on the sink pad. */
@@ -1878,15 +1938,17 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd,
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
/*
- * The crop default and bounds are equal to the sink
- * format size minus 4 pixels on each side for demosaicing.
+ * Crop defaults and bounds are equal to the sink format size.
+ * For source pad formats other then RAW10 this gets reduced
+ * by 4 pixels on each side for demosaicing.
*/
format = v4l2_subdev_state_get_format(state, 0);
+ border = mt9m114_ifp_get_border(state);
- sel->r.left = 4;
- sel->r.top = 4;
- sel->r.width = format->width - 8;
- sel->r.height = format->height - 8;
+ sel->r.left = border;
+ sel->r.top = border;
+ sel->r.width = format->width - 2 * border;
+ sel->r.height = format->height - 2 * border;
break;
case V4L2_SEL_TGT_COMPOSE:
@@ -1918,9 +1980,10 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
- struct v4l2_mbus_framefmt *format;
+ struct v4l2_mbus_framefmt *format, *src_format;
struct v4l2_rect *crop;
struct v4l2_rect *compose;
+ unsigned int border;
if (sel->target != V4L2_SEL_TGT_CROP &&
sel->target != V4L2_SEL_TGT_COMPOSE)
@@ -1930,27 +1993,37 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd,
if (sel->pad != 0)
return -EINVAL;
- format = v4l2_subdev_state_get_format(state, 0);
crop = v4l2_subdev_state_get_crop(state, 0);
+
+ /* Crop and compose cannot be changed when bypassing the scaler. */
+ src_format = v4l2_subdev_state_get_format(state, 1);
+ if (src_format->code == MEDIA_BUS_FMT_SGRBG10_1X10) {
+ sel->r = *crop;
+ return 0;
+ }
+
+ format = v4l2_subdev_state_get_format(state, 0);
compose = v4l2_subdev_state_get_compose(state, 0);
if (sel->target == V4L2_SEL_TGT_CROP) {
/*
- * Clamp the crop rectangle. Demosaicing removes 4 pixels on
- * each side of the image.
+ * Clamp the crop rectangle. For source pad formats other then
+ * RAW10 demosaicing removes 4 pixels on each side of the image.
*/
- crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), 4,
- format->width - 4 -
+ border = mt9m114_ifp_get_border(state);
+
+ crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), border,
+ format->width - border -
MT9M114_SCALER_CROPPED_INPUT_WIDTH);
- crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), 4,
- format->height - 4 -
+ crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), border,
+ format->height - border -
MT9M114_SCALER_CROPPED_INPUT_HEIGHT);
crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
MT9M114_SCALER_CROPPED_INPUT_WIDTH,
- format->width - 4 - crop->left);
+ format->width - border - crop->left);
crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
MT9M114_SCALER_CROPPED_INPUT_HEIGHT,
- format->height - 4 - crop->top);
+ format->height - border - crop->top);
sel->r = *crop;
@@ -1974,9 +2047,8 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd,
}
/* Propagate the compose rectangle to the source format. */
- format = v4l2_subdev_state_get_format(state, 1);
- format->width = compose->width;
- format->height = compose->height;
+ src_format->width = compose->width;
+ src_format->height = compose->height;
return 0;
}
@@ -2227,6 +2299,13 @@ error_regulator:
static void mt9m114_power_off(struct mt9m114 *sensor)
{
+ unsigned int duration;
+
+ gpiod_set_value(sensor->reset, 1);
+ /* Power off takes 10 clock cycles. Double it to be safe. */
+ duration = DIV_ROUND_UP(2 * 10 * 1000000, clk_get_rate(sensor->clk));
+ fsleep(duration);
+
clk_disable_unprepare(sensor->clk);
regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
}
@@ -2235,19 +2314,8 @@ static int __maybe_unused mt9m114_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct mt9m114 *sensor = ifp_to_mt9m114(sd);
- int ret;
- ret = mt9m114_power_on(sensor);
- if (ret)
- return ret;
-
- ret = mt9m114_initialize(sensor);
- if (ret) {
- mt9m114_power_off(sensor);
- return ret;
- }
-
- return 0;
+ return mt9m114_power_on(sensor);
}
static int __maybe_unused mt9m114_runtime_suspend(struct device *dev)
@@ -2281,14 +2349,39 @@ static int mt9m114_verify_link_frequency(struct mt9m114 *sensor,
return 0;
}
+/*
+ * Based on the docs the PLL is believed to have the following setup:
+ *
+ * +-----+ +-----+ +-----+ +-----+ +-----+
+ * Fin --> | / N | --> | x M | --> | x 2 | --> | / P | --> | / 2 | -->
+ * +-----+ +-----+ +-----+ +-----+ +-----+
+ * fBit fWord fSensor
+ * ext_clock int_clock out_clock pix_clock
+ *
+ * The MT9M114 docs give a max fBit rate of 768 MHz which translates to
+ * an out_clock_max of 384 MHz.
+ */
static int mt9m114_clk_init(struct mt9m114 *sensor)
{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 6000000,
+ .ext_clock_max = 54000000,
+ /* int_clock_* limits are not documented taken from mt9p031.c */
+ .int_clock_min = 2000000,
+ .int_clock_max = 13500000,
+ /* out_clock_min is not documented, taken from mt9p031.c */
+ .out_clock_min = 180000000,
+ .out_clock_max = 384000000,
+ .pix_clock_max = 48000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 192,
+ .p1_min = 8,
+ .p1_max = 8,
+ };
unsigned int pixrate;
-
- /* Hardcode the PLL multiplier and dividers to default settings. */
- sensor->pll.m = 32;
- sensor->pll.n = 1;
- sensor->pll.p = 7;
+ int ret;
/*
* Calculate the pixel rate and link frequency. The CSI-2 bus is clocked
@@ -2308,8 +2401,15 @@ static int mt9m114_clk_init(struct mt9m114 *sensor)
}
/* Check if the PLL configuration fits the configured link frequency. */
- pixrate = clk_get_rate(sensor->clk) * sensor->pll.m
- / ((sensor->pll.n + 1) * (sensor->pll.p + 1));
+ sensor->pll.ext_clock = clk_get_rate(sensor->clk);
+ sensor->pll.pix_clock = MT9M114_DEF_PIXCLOCK;
+
+ ret = aptina_pll_calculate(&sensor->client->dev, &limits, &sensor->pll);
+ if (ret)
+ return ret;
+
+ pixrate = sensor->pll.ext_clock * sensor->pll.m
+ / (sensor->pll.n * sensor->pll.p1);
if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) {
sensor->pixrate = pixrate;
sensor->bypass_pll = false;
@@ -2360,11 +2460,17 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor)
struct fwnode_handle *ep;
int ret;
+ /*
+ * On ACPI systems the fwnode graph can be initialized by a bridge
+ * driver, which may not have probed yet. Wait for this.
+ *
+ * TODO: Return an error once bridge driver code will have moved
+ * to the ACPI core.
+ */
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
- if (!ep) {
- dev_err(&sensor->client->dev, "No endpoint found\n");
- return -EINVAL;
- }
+ if (!ep)
+ return dev_err_probe(&sensor->client->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN;
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
@@ -2434,7 +2540,7 @@ static int mt9m114_probe(struct i2c_client *client)
goto error_ep_free;
}
- sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset)) {
ret = PTR_ERR(sensor->reset);
dev_err_probe(dev, ret, "Failed to get reset GPIO\n");
@@ -2459,8 +2565,8 @@ static int mt9m114_probe(struct i2c_client *client)
/*
* Identify the sensor. The driver supports runtime PM, but needs to
* work when runtime PM is disabled in the kernel. To that end, power
- * the sensor on manually here, and initialize it after identification
- * to reach the same state as if resumed through runtime PM.
+ * the sensor on manually here to reach the same state as if resumed
+ * through runtime PM.
*/
ret = mt9m114_power_on(sensor);
if (ret < 0) {
@@ -2472,10 +2578,6 @@ static int mt9m114_probe(struct i2c_client *client)
if (ret < 0)
goto error_power_off;
- ret = mt9m114_initialize(sensor);
- if (ret < 0)
- goto error_power_off;
-
/*
* Enable runtime PM with autosuspend. As the device has been powered
* manually, mark it as active, and increase the usage count without
@@ -2550,11 +2652,18 @@ static const struct of_device_id mt9m114_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, mt9m114_of_ids);
+static const struct acpi_device_id mt9m114_acpi_ids[] = {
+ { "INT33F0" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_ids);
+
static struct i2c_driver mt9m114_driver = {
.driver = {
.name = "mt9m114",
.pm = &mt9m114_pm_ops,
.of_match_table = mt9m114_of_ids,
+ .acpi_match_table = mt9m114_acpi_ids,
},
.probe = mt9m114_probe,
.remove = mt9m114_remove,
diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
index 262d9df766fe..84a28cdcade1 100644
--- a/drivers/media/i2c/og0ve1b.c
+++ b/drivers/media/i2c/og0ve1b.c
@@ -41,6 +41,10 @@
#define OG0VE1B_ANALOGUE_GAIN_STEP 1
#define OG0VE1B_ANALOGUE_GAIN_DEFAULT 16
+/* Vertical timing size */
+#define OG0VE1B_REG_VTS CCI_REG16(0x380e)
+#define OG0VE1B_VTS_MAX 0xffff
+
/* Test pattern */
#define OG0VE1B_REG_PRE_ISP CCI_REG8(0x5e00)
#define OG0VE1B_TEST_PATTERN_ENABLE BIT(7)
@@ -89,6 +93,8 @@ struct og0ve1b {
struct v4l2_subdev sd;
struct media_pad pad;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
struct v4l2_ctrl_handler ctrl_handler;
/* Saved register value */
@@ -140,8 +146,6 @@ static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = {
{ CCI_REG8(0x380b), 0xe0 },
{ CCI_REG8(0x380c), 0x03 }, /* horizontal timing size */
{ CCI_REG8(0x380d), 0x18 },
- { CCI_REG8(0x380e), 0x02 }, /* vertical timing size */
- { CCI_REG8(0x380f), 0x38 },
{ CCI_REG8(0x3811), 0x04 },
{ CCI_REG8(0x3813), 0x04 },
{ CCI_REG8(0x3814), 0x11 },
@@ -273,8 +277,25 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct og0ve1b *og0ve1b = container_of(ctrl->handler, struct og0ve1b,
ctrl_handler);
+ const struct og0ve1b_mode *mode = &supported_modes[0];
+ s64 exposure_max;
int ret;
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = ctrl->val + mode->height -
+ OG0VE1B_EXPOSURE_MAX_MARGIN;
+ ret = __v4l2_ctrl_modify_range(og0ve1b->exposure,
+ og0ve1b->exposure->minimum,
+ exposure_max,
+ og0ve1b->exposure->step,
+ og0ve1b->exposure->default_value);
+ if (ret)
+ return ret;
+ }
+
/* V4L2 controls are applied, when sensor is powered up for streaming */
if (!pm_runtime_get_if_active(og0ve1b->dev))
return 0;
@@ -288,6 +309,10 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_EXPOSURE,
ctrl->val << 4, NULL);
break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_VTS,
+ ctrl->val + mode->height, NULL);
+ break;
case V4L2_CID_TEST_PATTERN:
ret = og0ve1b_enable_test_pattern(og0ve1b, ctrl->val);
break;
@@ -309,8 +334,8 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
{
struct v4l2_ctrl_handler *ctrl_hdlr = &og0ve1b->ctrl_handler;
const struct og0ve1b_mode *mode = &supported_modes[0];
+ s64 exposure_max, pixel_rate, h_blank, v_blank;
struct v4l2_fwnode_device_properties props;
- s64 exposure_max, pixel_rate, h_blank;
struct v4l2_ctrl *ctrl;
int ret;
@@ -333,24 +358,24 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_VBLANK,
- mode->vts - mode->height,
- mode->vts - mode->height, 1,
- mode->vts - mode->height);
- if (ctrl)
- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v_blank = mode->vts - mode->height;
+ og0ve1b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops,
+ V4L2_CID_VBLANK, v_blank,
+ OG0VE1B_VTS_MAX - mode->height, 1,
+ v_blank);
v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OG0VE1B_ANALOGUE_GAIN_MIN, OG0VE1B_ANALOGUE_GAIN_MAX,
OG0VE1B_ANALOGUE_GAIN_STEP,
OG0VE1B_ANALOGUE_GAIN_DEFAULT);
- exposure_max = (mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN);
- v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops,
- V4L2_CID_EXPOSURE,
- OG0VE1B_EXPOSURE_MIN, exposure_max,
- OG0VE1B_EXPOSURE_STEP,
- OG0VE1B_EXPOSURE_DEFAULT);
+ exposure_max = mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN;
+ og0ve1b->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OG0VE1B_EXPOSURE_MIN,
+ exposure_max,
+ OG0VE1B_EXPOSURE_STEP,
+ OG0VE1B_EXPOSURE_DEFAULT);
v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og0ve1b_ctrl_ops,
V4L2_CID_TEST_PATTERN,
diff --git a/drivers/media/i2c/os05b10.c b/drivers/media/i2c/os05b10.c
new file mode 100644
index 000000000000..e0453c988e4a
--- /dev/null
+++ b/drivers/media/i2c/os05b10.c
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Support for the os05b10
+ *
+ * Copyright (C) 2025 Silicon Signals Pvt. Ltd.
+ *
+ * Inspired from imx219, ov2735 camera drivers.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/units.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+#define OS05B10_XCLK_FREQ (24 * HZ_PER_MHZ)
+
+#define OS05B10_REG_CHIP_ID CCI_REG24(0x300a)
+#define OS05B10_CHIP_ID 0x530641
+
+#define OS05B10_REG_CTRL_MODE CCI_REG8(0x0100)
+#define OS05B10_MODE_STANDBY 0x00
+#define OS05B10_MODE_STREAMING 0x01
+
+#define OS05B10_REG_EXPOSURE CCI_REG24(0x3500)
+#define OS05B10_EXPOSURE_MIN 2
+#define OS05B10_EXPOSURE_STEP 1
+#define OS05B10_EXPOSURE_MARGIN 8
+
+#define OS05B10_REG_ANALOG_GAIN CCI_REG16(0x3508)
+#define OS05B10_ANALOG_GAIN_MIN 0x80
+#define OS05B10_ANALOG_GAIN_MAX 0x7C0
+#define OS05B10_ANALOG_GAIN_STEP 1
+#define OS05B10_ANALOG_GAIN_DEFAULT 0x80
+
+#define OS05B10_REG_HTS CCI_REG16(0x380c)
+
+#define OS05B10_REG_VTS CCI_REG16(0x380e)
+#define OS05B10_VTS_MAX 0x7fff
+
+#define OS05B10_LINK_FREQ_600MHZ (600 * HZ_PER_MHZ)
+
+static const struct v4l2_rect os05b10_native_area = {
+ .top = 0,
+ .left = 0,
+ .width = 2608,
+ .height = 1960,
+};
+
+static const struct v4l2_rect os05b10_active_area = {
+ .top = 8,
+ .left = 8,
+ .width = 2592,
+ .height = 1944,
+};
+
+static const char * const os05b10_supply_name[] = {
+ "avdd", /* Analog supply */
+ "dovdd", /* Digital IO */
+ "dvdd", /* Digital core */
+};
+
+static const struct cci_reg_sequence os05b10_common_regs[] = {
+ { CCI_REG8(0x0301), 0x44 },
+ { CCI_REG8(0x0303), 0x02 },
+ { CCI_REG8(0x0305), 0x32 },
+ { CCI_REG8(0x0306), 0x00 },
+ { CCI_REG8(0x0325), 0x3b },
+ { CCI_REG8(0x3002), 0x21 },
+ { CCI_REG8(0x3016), 0x72 },
+ { CCI_REG8(0x301e), 0xb4 },
+ { CCI_REG8(0x301f), 0xd0 },
+ { CCI_REG8(0x3021), 0x03 },
+ { CCI_REG8(0x3022), 0x01 },
+ { CCI_REG8(0x3107), 0xa1 },
+ { CCI_REG8(0x3108), 0x7d },
+ { CCI_REG8(0x3109), 0xfc },
+ { CCI_REG8(0x3503), 0x88 },
+ { CCI_REG8(0x350a), 0x04 },
+ { CCI_REG8(0x350b), 0x00 },
+ { CCI_REG8(0x350c), 0x00 },
+ { CCI_REG8(0x350d), 0x80 },
+ { CCI_REG8(0x350e), 0x04 },
+ { CCI_REG8(0x350f), 0x00 },
+ { CCI_REG8(0x3510), 0x00 },
+ { CCI_REG8(0x3511), 0x00 },
+ { CCI_REG8(0x3512), 0x20 },
+ { CCI_REG8(0x3600), 0x4d },
+ { CCI_REG8(0x3601), 0x08 },
+ { CCI_REG8(0x3610), 0x87 },
+ { CCI_REG8(0x3611), 0x24 },
+ { CCI_REG8(0x3614), 0x4c },
+ { CCI_REG8(0x3620), 0x0c },
+ { CCI_REG8(0x3632), 0x80 },
+ { CCI_REG8(0x3633), 0x00 },
+ { CCI_REG8(0x3636), 0xcc },
+ { CCI_REG8(0x3637), 0x27 },
+ { CCI_REG8(0x3660), 0x00 },
+ { CCI_REG8(0x3662), 0x10 },
+ { CCI_REG8(0x3665), 0x00 },
+ { CCI_REG8(0x3666), 0x00 },
+ { CCI_REG8(0x366a), 0x14 },
+ { CCI_REG8(0x3670), 0x0b },
+ { CCI_REG8(0x3671), 0x0b },
+ { CCI_REG8(0x3672), 0x0b },
+ { CCI_REG8(0x3673), 0x0b },
+ { CCI_REG8(0x3678), 0x2b },
+ { CCI_REG8(0x367a), 0x11 },
+ { CCI_REG8(0x367b), 0x11 },
+ { CCI_REG8(0x367c), 0x11 },
+ { CCI_REG8(0x367d), 0x11 },
+ { CCI_REG8(0x3681), 0xff },
+ { CCI_REG8(0x3682), 0x86 },
+ { CCI_REG8(0x3683), 0x44 },
+ { CCI_REG8(0x3684), 0x24 },
+ { CCI_REG8(0x3685), 0x00 },
+ { CCI_REG8(0x368a), 0x00 },
+ { CCI_REG8(0x368d), 0x2b },
+ { CCI_REG8(0x368e), 0x2b },
+ { CCI_REG8(0x3690), 0x00 },
+ { CCI_REG8(0x3691), 0x0b },
+ { CCI_REG8(0x3692), 0x0b },
+ { CCI_REG8(0x3693), 0x0b },
+ { CCI_REG8(0x3694), 0x0b },
+ { CCI_REG8(0x369d), 0x68 },
+ { CCI_REG8(0x369e), 0x34 },
+ { CCI_REG8(0x369f), 0x1b },
+ { CCI_REG8(0x36a0), 0x0f },
+ { CCI_REG8(0x36a1), 0x77 },
+ { CCI_REG8(0x36b0), 0x30 },
+ { CCI_REG8(0x36b2), 0x00 },
+ { CCI_REG8(0x36b3), 0x00 },
+ { CCI_REG8(0x36b4), 0x00 },
+ { CCI_REG8(0x36b5), 0x00 },
+ { CCI_REG8(0x36b6), 0x00 },
+ { CCI_REG8(0x36b7), 0x00 },
+ { CCI_REG8(0x36b8), 0x00 },
+ { CCI_REG8(0x36b9), 0x00 },
+ { CCI_REG8(0x36ba), 0x00 },
+ { CCI_REG8(0x36bb), 0x00 },
+ { CCI_REG8(0x36bc), 0x00 },
+ { CCI_REG8(0x36bd), 0x00 },
+ { CCI_REG8(0x36be), 0x00 },
+ { CCI_REG8(0x36bf), 0x00 },
+ { CCI_REG8(0x36c0), 0x01 },
+ { CCI_REG8(0x36c1), 0x00 },
+ { CCI_REG8(0x36c2), 0x00 },
+ { CCI_REG8(0x36c3), 0x00 },
+ { CCI_REG8(0x36c4), 0x00 },
+ { CCI_REG8(0x36c5), 0x00 },
+ { CCI_REG8(0x36c6), 0x00 },
+ { CCI_REG8(0x36c7), 0x00 },
+ { CCI_REG8(0x36c8), 0x00 },
+ { CCI_REG8(0x36c9), 0x00 },
+ { CCI_REG8(0x36ca), 0x0e },
+ { CCI_REG8(0x36cb), 0x0e },
+ { CCI_REG8(0x36cc), 0x0e },
+ { CCI_REG8(0x36cd), 0x0e },
+ { CCI_REG8(0x36ce), 0x0c },
+ { CCI_REG8(0x36cf), 0x0c },
+ { CCI_REG8(0x36d0), 0x0c },
+ { CCI_REG8(0x36d1), 0x0c },
+ { CCI_REG8(0x36d2), 0x00 },
+ { CCI_REG8(0x36d3), 0x08 },
+ { CCI_REG8(0x36d4), 0x10 },
+ { CCI_REG8(0x36d5), 0x10 },
+ { CCI_REG8(0x36d6), 0x00 },
+ { CCI_REG8(0x36d7), 0x08 },
+ { CCI_REG8(0x36d8), 0x10 },
+ { CCI_REG8(0x36d9), 0x10 },
+ { CCI_REG8(0x3701), 0x1d },
+ { CCI_REG8(0x3703), 0x2a },
+ { CCI_REG8(0x3704), 0x05 },
+ { CCI_REG8(0x3709), 0x57 },
+ { CCI_REG8(0x370b), 0x63 },
+ { CCI_REG8(0x3706), 0x28 },
+ { CCI_REG8(0x370a), 0x00 },
+ { CCI_REG8(0x370b), 0x63 },
+ { CCI_REG8(0x370e), 0x0c },
+ { CCI_REG8(0x370f), 0x1c },
+ { CCI_REG8(0x3710), 0x00 },
+ { CCI_REG8(0x3713), 0x00 },
+ { CCI_REG8(0x3714), 0x24 },
+ { CCI_REG8(0x3716), 0x24 },
+ { CCI_REG8(0x371a), 0x1e },
+ { CCI_REG8(0x3724), 0x09 },
+ { CCI_REG8(0x3725), 0xb2 },
+ { CCI_REG8(0x372b), 0x54 },
+ { CCI_REG8(0x3730), 0xe1 },
+ { CCI_REG8(0x3735), 0x80 },
+ { CCI_REG8(0x3739), 0x10 },
+ { CCI_REG8(0x373f), 0xb0 },
+ { CCI_REG8(0x3740), 0x28 },
+ { CCI_REG8(0x3741), 0x21 },
+ { CCI_REG8(0x3742), 0x21 },
+ { CCI_REG8(0x3743), 0x21 },
+ { CCI_REG8(0x3744), 0x63 },
+ { CCI_REG8(0x3745), 0x5a },
+ { CCI_REG8(0x3746), 0x5a },
+ { CCI_REG8(0x3747), 0x5a },
+ { CCI_REG8(0x3748), 0x00 },
+ { CCI_REG8(0x3749), 0x00 },
+ { CCI_REG8(0x374a), 0x00 },
+ { CCI_REG8(0x374b), 0x00 },
+ { CCI_REG8(0x3756), 0x00 },
+ { CCI_REG8(0x3757), 0x0e },
+ { CCI_REG8(0x375d), 0x84 },
+ { CCI_REG8(0x3760), 0x11 },
+ { CCI_REG8(0x3767), 0x08 },
+ { CCI_REG8(0x376f), 0x42 },
+ { CCI_REG8(0x3771), 0x00 },
+ { CCI_REG8(0x3773), 0x01 },
+ { CCI_REG8(0x3774), 0x02 },
+ { CCI_REG8(0x3775), 0x12 },
+ { CCI_REG8(0x3776), 0x02 },
+ { CCI_REG8(0x377b), 0x40 },
+ { CCI_REG8(0x377c), 0x00 },
+ { CCI_REG8(0x377d), 0x0c },
+ { CCI_REG8(0x3782), 0x02 },
+ { CCI_REG8(0x3787), 0x24 },
+ { CCI_REG8(0x378a), 0x01 },
+ { CCI_REG8(0x378d), 0x00 },
+ { CCI_REG8(0x3790), 0x1f },
+ { CCI_REG8(0x3791), 0x58 },
+ { CCI_REG8(0x3795), 0x24 },
+ { CCI_REG8(0x3796), 0x01 },
+ { CCI_REG8(0x3798), 0x40 },
+ { CCI_REG8(0x379c), 0x00 },
+ { CCI_REG8(0x379d), 0x00 },
+ { CCI_REG8(0x379e), 0x00 },
+ { CCI_REG8(0x379f), 0x01 },
+ { CCI_REG8(0x37a1), 0x10 },
+ { CCI_REG8(0x37a6), 0x00 },
+ { CCI_REG8(0x37ab), 0x0e },
+ { CCI_REG8(0x37ac), 0xa0 },
+ { CCI_REG8(0x37be), 0x0a },
+ { CCI_REG8(0x37bf), 0x05 },
+ { CCI_REG8(0x37bb), 0x02 },
+ { CCI_REG8(0x37bf), 0x05 },
+ { CCI_REG8(0x37c2), 0x04 },
+ { CCI_REG8(0x37c4), 0x11 },
+ { CCI_REG8(0x37c5), 0x80 },
+ { CCI_REG8(0x37c6), 0x14 },
+ { CCI_REG8(0x37c7), 0x08 },
+ { CCI_REG8(0x37c8), 0x42 },
+ { CCI_REG8(0x37cd), 0x17 },
+ { CCI_REG8(0x37ce), 0x01 },
+ { CCI_REG8(0x37d8), 0x02 },
+ { CCI_REG8(0x37d9), 0x08 },
+ { CCI_REG8(0x37dc), 0x01 },
+ { CCI_REG8(0x37e0), 0x0c },
+ { CCI_REG8(0x37e1), 0x20 },
+ { CCI_REG8(0x37e2), 0x10 },
+ { CCI_REG8(0x37e3), 0x04 },
+ { CCI_REG8(0x37e4), 0x28 },
+ { CCI_REG8(0x37e5), 0x02 },
+ { CCI_REG8(0x37ef), 0x00 },
+ { CCI_REG8(0x37f4), 0x00 },
+ { CCI_REG8(0x37f5), 0x00 },
+ { CCI_REG8(0x37f6), 0x00 },
+ { CCI_REG8(0x37f7), 0x00 },
+ { CCI_REG8(0x3800), 0x01 },
+ { CCI_REG8(0x3801), 0x30 },
+ { CCI_REG8(0x3802), 0x00 },
+ { CCI_REG8(0x3803), 0x00 },
+ { CCI_REG8(0x3804), 0x0b },
+ { CCI_REG8(0x3805), 0x5f },
+ { CCI_REG8(0x3806), 0x07 },
+ { CCI_REG8(0x3807), 0xa7 },
+ { CCI_REG8(0x3808), 0x0a },
+ { CCI_REG8(0x3809), 0x20 },
+ { CCI_REG8(0x380a), 0x07 },
+ { CCI_REG8(0x380b), 0x98 },
+ { CCI_REG8(0x380c), 0x06 },
+ { CCI_REG8(0x380d), 0xd0 },
+ { CCI_REG8(0x3810), 0x00 },
+ { CCI_REG8(0x3811), 0x08 },
+ { CCI_REG8(0x3812), 0x00 },
+ { CCI_REG8(0x3813), 0x08 },
+ { CCI_REG8(0x3814), 0x01 },
+ { CCI_REG8(0x3815), 0x01 },
+ { CCI_REG8(0x3816), 0x01 },
+ { CCI_REG8(0x3817), 0x01 },
+ { CCI_REG8(0x3818), 0x00 },
+ { CCI_REG8(0x3819), 0x00 },
+ { CCI_REG8(0x381a), 0x00 },
+ { CCI_REG8(0x381b), 0x01 },
+ { CCI_REG8(0x3820), 0x88 },
+ { CCI_REG8(0x3821), 0x00 },
+ { CCI_REG8(0x3822), 0x12 },
+ { CCI_REG8(0x3823), 0x08 },
+ { CCI_REG8(0x3824), 0x00 },
+ { CCI_REG8(0x3825), 0x20 },
+ { CCI_REG8(0x3826), 0x00 },
+ { CCI_REG8(0x3827), 0x08 },
+ { CCI_REG8(0x3829), 0x03 },
+ { CCI_REG8(0x382a), 0x00 },
+ { CCI_REG8(0x382b), 0x00 },
+ { CCI_REG8(0x3832), 0x08 },
+ { CCI_REG8(0x3838), 0x00 },
+ { CCI_REG8(0x3839), 0x00 },
+ { CCI_REG8(0x383a), 0x00 },
+ { CCI_REG8(0x383b), 0x00 },
+ { CCI_REG8(0x383d), 0x01 },
+ { CCI_REG8(0x383e), 0x00 },
+ { CCI_REG8(0x383f), 0x00 },
+ { CCI_REG8(0x3843), 0x00 },
+ { CCI_REG8(0x3880), 0x16 },
+ { CCI_REG8(0x3881), 0x00 },
+ { CCI_REG8(0x3882), 0x08 },
+ { CCI_REG8(0x389a), 0x00 },
+ { CCI_REG8(0x389b), 0x00 },
+ { CCI_REG8(0x38a2), 0x02 },
+ { CCI_REG8(0x38a3), 0x02 },
+ { CCI_REG8(0x38a4), 0x02 },
+ { CCI_REG8(0x38a5), 0x02 },
+ { CCI_REG8(0x38a7), 0x04 },
+ { CCI_REG8(0x38b8), 0x02 },
+ { CCI_REG8(0x3c80), 0x3e },
+ { CCI_REG8(0x3c86), 0x01 },
+ { CCI_REG8(0x3c87), 0x02 },
+ { CCI_REG8(0x389c), 0x00 },
+ { CCI_REG8(0x3ca2), 0x0c },
+ { CCI_REG8(0x3d85), 0x1b },
+ { CCI_REG8(0x3d8c), 0x01 },
+ { CCI_REG8(0x3d8d), 0xe2 },
+ { CCI_REG8(0x3f00), 0xcb },
+ { CCI_REG8(0x3f03), 0x08 },
+ { CCI_REG8(0x3f9e), 0x07 },
+ { CCI_REG8(0x3f9f), 0x04 },
+ { CCI_REG8(0x4000), 0xf3 },
+ { CCI_REG8(0x4002), 0x00 },
+ { CCI_REG8(0x4003), 0x40 },
+ { CCI_REG8(0x4008), 0x02 },
+ { CCI_REG8(0x4009), 0x0d },
+ { CCI_REG8(0x400a), 0x01 },
+ { CCI_REG8(0x400b), 0x00 },
+ { CCI_REG8(0x4040), 0x00 },
+ { CCI_REG8(0x4041), 0x07 },
+ { CCI_REG8(0x4090), 0x14 },
+ { CCI_REG8(0x40b0), 0x01 },
+ { CCI_REG8(0x40b1), 0x01 },
+ { CCI_REG8(0x40b2), 0x30 },
+ { CCI_REG8(0x40b3), 0x04 },
+ { CCI_REG8(0x40b4), 0xe8 },
+ { CCI_REG8(0x40b5), 0x01 },
+ { CCI_REG8(0x40b7), 0x07 },
+ { CCI_REG8(0x40b8), 0xff },
+ { CCI_REG8(0x40b9), 0x00 },
+ { CCI_REG8(0x40ba), 0x00 },
+ { CCI_REG8(0x4300), 0xff },
+ { CCI_REG8(0x4301), 0x00 },
+ { CCI_REG8(0x4302), 0x0f },
+ { CCI_REG8(0x4303), 0x20 },
+ { CCI_REG8(0x4304), 0x20 },
+ { CCI_REG8(0x4305), 0x83 },
+ { CCI_REG8(0x4306), 0x21 },
+ { CCI_REG8(0x430d), 0x00 },
+ { CCI_REG8(0x4505), 0xc4 },
+ { CCI_REG8(0x4506), 0x00 },
+ { CCI_REG8(0x4507), 0x60 },
+ { CCI_REG8(0x4803), 0x00 },
+ { CCI_REG8(0x4809), 0x8e },
+ { CCI_REG8(0x480e), 0x00 },
+ { CCI_REG8(0x4813), 0x00 },
+ { CCI_REG8(0x4814), 0x2a },
+ { CCI_REG8(0x481b), 0x40 },
+ { CCI_REG8(0x481f), 0x30 },
+ { CCI_REG8(0x4825), 0x34 },
+ { CCI_REG8(0x4829), 0x64 },
+ { CCI_REG8(0x4837), 0x12 },
+ { CCI_REG8(0x484b), 0x07 },
+ { CCI_REG8(0x4883), 0x36 },
+ { CCI_REG8(0x4885), 0x03 },
+ { CCI_REG8(0x488b), 0x00 },
+ { CCI_REG8(0x4d06), 0x01 },
+ { CCI_REG8(0x4e00), 0x2a },
+ { CCI_REG8(0x4e0d), 0x00 },
+ { CCI_REG8(0x5000), 0xf9 },
+ { CCI_REG8(0x5001), 0x09 },
+ { CCI_REG8(0x5004), 0x00 },
+ { CCI_REG8(0x5005), 0x0e },
+ { CCI_REG8(0x5036), 0x00 },
+ { CCI_REG8(0x5080), 0x04 },
+ { CCI_REG8(0x5082), 0x00 },
+ { CCI_REG8(0x5180), 0x00 },
+ { CCI_REG8(0x5181), 0x10 },
+ { CCI_REG8(0x5182), 0x01 },
+ { CCI_REG8(0x5183), 0xdf },
+ { CCI_REG8(0x5184), 0x02 },
+ { CCI_REG8(0x5185), 0x6c },
+ { CCI_REG8(0x5189), 0x48 },
+ { CCI_REG8(0x520a), 0x03 },
+ { CCI_REG8(0x520b), 0x0f },
+ { CCI_REG8(0x520c), 0x3f },
+ { CCI_REG8(0x580b), 0x03 },
+ { CCI_REG8(0x580d), 0x00 },
+ { CCI_REG8(0x580f), 0x00 },
+ { CCI_REG8(0x5820), 0x00 },
+ { CCI_REG8(0x5821), 0x00 },
+ { CCI_REG8(0x3222), 0x03 },
+ { CCI_REG8(0x3208), 0x06 },
+ { CCI_REG8(0x3701), 0x1d },
+ { CCI_REG8(0x37ab), 0x01 },
+ { CCI_REG8(0x3790), 0x21 },
+ { CCI_REG8(0x38be), 0x00 },
+ { CCI_REG8(0x3791), 0x5a },
+ { CCI_REG8(0x37bf), 0x1c },
+ { CCI_REG8(0x3610), 0x37 },
+ { CCI_REG8(0x3208), 0x16 },
+ { CCI_REG8(0x3208), 0x07 },
+ { CCI_REG8(0x3701), 0x1d },
+ { CCI_REG8(0x37ab), 0x0e },
+ { CCI_REG8(0x3790), 0x21 },
+ { CCI_REG8(0x38be), 0x00 },
+ { CCI_REG8(0x3791), 0x5a },
+ { CCI_REG8(0x37bf), 0x0a },
+ { CCI_REG8(0x3610), 0x87 },
+ { CCI_REG8(0x3208), 0x17 },
+ { CCI_REG8(0x3208), 0x08 },
+ { CCI_REG8(0x3701), 0x1d },
+ { CCI_REG8(0x37ab), 0x0e },
+ { CCI_REG8(0x3790), 0x21 },
+ { CCI_REG8(0x38be), 0x00 },
+ { CCI_REG8(0x3791), 0x5a },
+ { CCI_REG8(0x37bf), 0x0a },
+ { CCI_REG8(0x3610), 0x87 },
+ { CCI_REG8(0x3208), 0x18 },
+ { CCI_REG8(0x3208), 0x09 },
+ { CCI_REG8(0x3701), 0x1d },
+ { CCI_REG8(0x37ab), 0x0e },
+ { CCI_REG8(0x3790), 0x28 },
+ { CCI_REG8(0x38be), 0x00 },
+ { CCI_REG8(0x3791), 0x63 },
+ { CCI_REG8(0x37bf), 0x0a },
+ { CCI_REG8(0x3610), 0x87 },
+ { CCI_REG8(0x3208), 0x19 },
+};
+
+struct os05b10 {
+ struct device *dev;
+ struct regmap *cci;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct clk *xclk;
+ struct i2c_client *client;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(os05b10_supply_name)];
+
+ /* V4L2 Controls */
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *exposure;
+
+ u32 link_freq_index;
+ u32 data_lanes;
+};
+
+struct os05b10_mode {
+ u32 width;
+ u32 height;
+ u32 vts;
+ u32 hts;
+ u32 exp;
+ u8 bpp;
+};
+
+static const struct os05b10_mode supported_modes_10bit[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .vts = 2006,
+ .hts = 1744,
+ .exp = 1944,
+ .bpp = 10,
+ },
+};
+
+static const s64 link_frequencies[] = {
+ OS05B10_LINK_FREQ_600MHZ,
+};
+
+static const u32 os05b10_mbus_codes[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+};
+
+static inline struct os05b10 *to_os05b10(struct v4l2_subdev *sd)
+{
+ return container_of_const(sd, struct os05b10, sd);
+};
+
+static int os05b10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct os05b10 *os05b10 = container_of_const(ctrl->handler,
+ struct os05b10, handler);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ int vmax, ret;
+
+ state = v4l2_subdev_get_locked_active_state(&os05b10->sd);
+ fmt = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Honour the VBLANK limits when setting exposure. */
+ s64 max = fmt->height + ctrl->val - OS05B10_EXPOSURE_MARGIN;
+
+ ret = __v4l2_ctrl_modify_range(os05b10->exposure,
+ os05b10->exposure->minimum, max,
+ os05b10->exposure->step,
+ os05b10->exposure->default_value);
+ if (ret)
+ return ret;
+ }
+
+ if (pm_runtime_get_if_in_use(os05b10->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ vmax = fmt->height + ctrl->val;
+ ret = cci_write(os05b10->cci, OS05B10_REG_VTS, vmax, NULL);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(os05b10->cci, OS05B10_REG_ANALOG_GAIN,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(os05b10->cci, OS05B10_REG_EXPOSURE,
+ ctrl->val, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(os05b10->dev);
+
+ return ret;
+}
+
+static int os05b10_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(os05b10_mbus_codes))
+ return -EINVAL;
+
+ code->code = os05b10_mbus_codes[code->index];
+
+ return 0;
+}
+
+static int os05b10_set_framing_limits(struct os05b10 *os05b10,
+ const struct os05b10_mode *mode)
+{
+ u32 hblank, vblank, vblank_max, max_exp;
+ int ret;
+
+ hblank = mode->hts - mode->width;
+ ret = __v4l2_ctrl_modify_range(os05b10->hblank, hblank, hblank, 1,
+ hblank);
+ if (ret)
+ return ret;
+
+ vblank = mode->vts - mode->height;
+ vblank_max = OS05B10_VTS_MAX - mode->height;
+ ret = __v4l2_ctrl_modify_range(os05b10->vblank, 0, vblank_max, 1,
+ vblank);
+ if (ret)
+ return ret;
+
+ max_exp = mode->vts - OS05B10_EXPOSURE_MARGIN;
+ return __v4l2_ctrl_modify_range(os05b10->exposure,
+ OS05B10_EXPOSURE_MIN, max_exp,
+ OS05B10_EXPOSURE_STEP, mode->exp);
+}
+
+static int os05b10_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ const struct os05b10_mode *mode = &supported_modes_10bit[0];
+ struct os05b10 *os05b10 = to_os05b10(sd);
+ struct v4l2_mbus_framefmt *format;
+ int ret;
+
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+ format = v4l2_subdev_state_get_format(sd_state, 0);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = os05b10_set_framing_limits(os05b10, mode);
+ if (ret)
+ return ret;
+ }
+
+ *format = fmt->format;
+
+ return 0;
+}
+
+static int os05b10_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:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r = os05b10_native_area;
+ return 0;
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r = os05b10_active_area;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int os05b10_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_10bit))
+ return -EINVAL;
+
+ fse->min_width = supported_modes_10bit[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes_10bit[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int os05b10_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct os05b10 *os05b10 = to_os05b10(sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(os05b10->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Write common registers */
+ ret = cci_multi_reg_write(os05b10->cci, os05b10_common_regs,
+ ARRAY_SIZE(os05b10_common_regs), NULL);
+ if (ret) {
+ dev_err(os05b10->dev, "failed to write common registers\n");
+ goto err_rpm_put;
+ }
+
+ /* Apply customized user controls */
+ ret = __v4l2_ctrl_handler_setup(os05b10->sd.ctrl_handler);
+ if (ret)
+ goto err_rpm_put;
+
+ /* Stream ON */
+ ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE,
+ OS05B10_MODE_STREAMING, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(os05b10->dev);
+
+ return ret;
+}
+
+static int os05b10_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct os05b10 *os05b10 = to_os05b10(sd);
+ int ret;
+
+ ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE,
+ OS05B10_MODE_STANDBY, NULL);
+ if (ret)
+ dev_err(os05b10->dev, "failed to set stream off\n");
+
+ pm_runtime_put(os05b10->dev);
+
+ return 0;
+}
+
+static int os05b10_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *format;
+ const struct os05b10_mode *mode;
+
+ /* Initialize try_fmt */
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ mode = &supported_modes_10bit[0];
+ format->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ /* Update image pad formate */
+ format->width = mode->width;
+ format->height = mode->height;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_RAW;
+ format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ format->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops os05b10_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops os05b10_pad_ops = {
+ .enum_mbus_code = os05b10_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = os05b10_set_pad_format,
+ .get_selection = os05b10_get_selection,
+ .enum_frame_size = os05b10_enum_frame_size,
+ .enable_streams = os05b10_enable_streams,
+ .disable_streams = os05b10_disable_streams,
+};
+
+static const struct v4l2_subdev_internal_ops os05b10_internal_ops = {
+ .init_state = os05b10_init_state,
+};
+
+static const struct v4l2_subdev_ops os05b10_subdev_ops = {
+ .video = &os05b10_video_ops,
+ .pad = &os05b10_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops os05b10_ctrl_ops = {
+ .s_ctrl = os05b10_set_ctrl,
+};
+
+static int os05b10_identify_module(struct os05b10 *os05b10)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(os05b10->cci, OS05B10_REG_CHIP_ID, &val, NULL);
+ if (ret)
+ return dev_err_probe(os05b10->dev, ret,
+ "failed to read chip id %x\n",
+ OS05B10_CHIP_ID);
+
+ if (val != OS05B10_CHIP_ID)
+ return dev_err_probe(os05b10->dev, -ENODEV,
+ "chip id mismatch: %x!=%llx\n",
+ OS05B10_CHIP_ID, val);
+
+ return 0;
+}
+
+static int os05b10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os05b10 *os05b10 = to_os05b10(sd);
+ unsigned long delay_us;
+ int ret;
+
+ /* Enable power rails */
+ ret = regulator_bulk_enable(ARRAY_SIZE(os05b10_supply_name),
+ os05b10->supplies);
+ if (ret) {
+ dev_err(os05b10->dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ /* Enable xclk */
+ ret = clk_prepare_enable(os05b10->xclk);
+ if (ret) {
+ dev_err(os05b10->dev, "failed to enable clock\n");
+ goto err_regulator_off;
+ }
+
+ gpiod_set_value_cansleep(os05b10->reset_gpio, 0);
+
+ /* Delay T1 */
+ fsleep(5 * USEC_PER_MSEC);
+
+ /* Delay T2 (8192 cycles before SCCB/I2C access) */
+ delay_us = DIV_ROUND_UP(8192, OS05B10_XCLK_FREQ / 1000 / 1000);
+ usleep_range(delay_us, delay_us * 2);
+
+ return 0;
+
+err_regulator_off:
+ regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name),
+ os05b10->supplies);
+
+ return ret;
+}
+
+static int os05b10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os05b10 *os05b10 = to_os05b10(sd);
+
+ gpiod_set_value_cansleep(os05b10->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name),
+ os05b10->supplies);
+ clk_disable_unprepare(os05b10->xclk);
+
+ return 0;
+}
+
+static int os05b10_parse_endpoint(struct os05b10 *os05b10)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ unsigned long link_freq_bitmap;
+ struct fwnode_handle *ep;
+ int ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(os05b10->dev), 0, 0, 0);
+ if (!ep) {
+ dev_err(os05b10->dev, "Failed to get next endpoint\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ ret = dev_err_probe(os05b10->dev, -EINVAL,
+ "only 4 data lanes are supported\n");
+ goto error_out;
+ }
+
+ os05b10->data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+ ret = v4l2_link_freq_to_bitmap(os05b10->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_frequencies,
+ ARRAY_SIZE(link_frequencies),
+ &link_freq_bitmap);
+ if (ret) {
+ dev_err(os05b10->dev, "only 600MHz frequency is available\n");
+ goto error_out;
+ }
+
+ os05b10->link_freq_index = __ffs(link_freq_bitmap);
+
+error_out:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static u64 os05b10_pixel_rate(struct os05b10 *os05b10,
+ const struct os05b10_mode *mode)
+{
+ u64 link_freq = link_frequencies[os05b10->link_freq_index];
+ u64 pixel_rate = div_u64(link_freq * 2 * os05b10->data_lanes, mode->bpp);
+
+ dev_dbg(os05b10->dev,
+ "link_freq=%llu bpp=%u lanes=%u pixel_rate=%llu\n",
+ link_freq, mode->bpp, os05b10->data_lanes, pixel_rate);
+
+ return pixel_rate;
+}
+
+static int os05b10_init_controls(struct os05b10 *os05b10)
+{
+ const struct os05b10_mode *mode = &supported_modes_10bit[0];
+ u64 hblank_def, vblank_def, exp_max, pixel_rate;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ int ret;
+
+ ctrl_hdlr = &os05b10->handler;
+ v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+
+ pixel_rate = os05b10_pixel_rate(os05b10, mode);
+ v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, V4L2_CID_PIXEL_RATE,
+ pixel_rate, pixel_rate, 1, pixel_rate);
+
+ os05b10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os05b10_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_frequencies) - 1,
+ os05b10->link_freq_index,
+ link_frequencies);
+
+ if (os05b10->link_freq)
+ os05b10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ hblank_def = mode->hts - mode->width;
+ os05b10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK,
+ hblank_def, hblank_def,
+ 1, hblank_def);
+ if (os05b10->hblank)
+ os05b10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts - mode->height;
+ os05b10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OS05B10_VTS_MAX - mode->height,
+ 1, vblank_def);
+
+ exp_max = mode->vts - OS05B10_EXPOSURE_MARGIN;
+ os05b10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OS05B10_EXPOSURE_MIN,
+ exp_max, OS05B10_EXPOSURE_STEP,
+ mode->exp);
+
+ os05b10->gain = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ OS05B10_ANALOG_GAIN_MIN,
+ OS05B10_ANALOG_GAIN_MAX,
+ OS05B10_ANALOG_GAIN_STEP,
+ OS05B10_ANALOG_GAIN_DEFAULT);
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ dev_err(os05b10->dev, "control init failed (%d)\n", ret);
+ goto error;
+ }
+
+ ret = v4l2_fwnode_device_parse(os05b10->dev, &props);
+ if (ret)
+ goto error;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &os05b10_ctrl_ops,
+ &props);
+ if (ret)
+ goto error;
+
+ os05b10->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int os05b10_probe(struct i2c_client *client)
+{
+ struct os05b10 *os05b10;
+ unsigned int xclk_freq;
+ int ret;
+
+ os05b10 = devm_kzalloc(&client->dev, sizeof(*os05b10), GFP_KERNEL);
+ if (!os05b10)
+ return -ENOMEM;
+
+ os05b10->client = client;
+ os05b10->dev = &client->dev;
+
+ v4l2_i2c_subdev_init(&os05b10->sd, client, &os05b10_subdev_ops);
+
+ os05b10->cci = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(os05b10->cci))
+ return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->cci),
+ "failed to initialize CCI\n");
+
+ os05b10->xclk = devm_v4l2_sensor_clk_get(os05b10->dev, NULL);
+ if (IS_ERR(os05b10->xclk))
+ return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->xclk),
+ "failed to get xclk\n");
+
+ xclk_freq = clk_get_rate(os05b10->xclk);
+ if (xclk_freq != OS05B10_XCLK_FREQ)
+ return dev_err_probe(os05b10->dev, -EINVAL,
+ "xclk frequency not supported: %d Hz\n",
+ xclk_freq);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(os05b10_supply_name); i++)
+ os05b10->supplies[i].supply = os05b10_supply_name[i];
+
+ ret = devm_regulator_bulk_get(os05b10->dev,
+ ARRAY_SIZE(os05b10_supply_name),
+ os05b10->supplies);
+ if (ret)
+ return dev_err_probe(os05b10->dev, ret,
+ "failed to get regulators\n");
+
+ ret = os05b10_parse_endpoint(os05b10);
+ if (ret)
+ return dev_err_probe(os05b10->dev, ret,
+ "failed to parse endpoint configuration\n");
+
+ os05b10->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(os05b10->reset_gpio))
+ return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ ret = os05b10_power_on(os05b10->dev);
+ if (ret)
+ return ret;
+
+ ret = os05b10_identify_module(os05b10);
+ if (ret)
+ goto error_power_off;
+
+ /* This needs the pm runtime to be registered. */
+ ret = os05b10_init_controls(os05b10);
+ if (ret)
+ goto error_power_off;
+
+ /* Initialize subdev */
+ os05b10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ os05b10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ os05b10->sd.internal_ops = &os05b10_internal_ops;
+ os05b10->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&os05b10->sd.entity, 1, &os05b10->pad);
+ if (ret) {
+ dev_err_probe(os05b10->dev, ret,
+ "failed to init entity pads\n");
+ goto error_handler_free;
+ }
+
+ os05b10->sd.state_lock = os05b10->handler.lock;
+ ret = v4l2_subdev_init_finalize(&os05b10->sd);
+ if (ret < 0) {
+ dev_err_probe(os05b10->dev, ret, "subdev init error\n");
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(os05b10->dev);
+ pm_runtime_enable(os05b10->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&os05b10->sd);
+ if (ret < 0) {
+ dev_err_probe(os05b10->dev, ret,
+ "failed to register os05b10 sub-device\n");
+ goto error_subdev_cleanup;
+ }
+
+ pm_runtime_idle(os05b10->dev);
+
+ return 0;
+
+error_subdev_cleanup:
+ v4l2_subdev_cleanup(&os05b10->sd);
+ pm_runtime_disable(os05b10->dev);
+ pm_runtime_set_suspended(os05b10->dev);
+
+error_media_entity:
+ media_entity_cleanup(&os05b10->sd.entity);
+
+error_handler_free:
+ v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler);
+
+error_power_off:
+ os05b10_power_off(os05b10->dev);
+
+ return ret;
+}
+
+static void os05b10_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct os05b10 *os05b10 = to_os05b10(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(&os05b10->sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ os05b10_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(os05b10_pm_ops, os05b10_power_off,
+ os05b10_power_on, NULL);
+
+static const struct of_device_id os05b10_id[] = {
+ { .compatible = "ovti,os05b10" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, os05b10_id);
+
+static struct i2c_driver os05b10_driver = {
+ .driver = {
+ .name = "os05b10",
+ .pm = pm_ptr(&os05b10_pm_ops),
+ .of_match_table = os05b10_id,
+ },
+ .probe = os05b10_probe,
+ .remove = os05b10_remove,
+};
+
+module_i2c_driver(os05b10_driver);
+
+MODULE_DESCRIPTION("OS05B10 Camera Sensor Driver");
+MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>");
+MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c
index 141cb6f75b55..8a29e5b4b6ba 100644
--- a/drivers/media/i2c/ov01a10.c
+++ b/drivers/media/i2c/ov01a10.c
@@ -7,105 +7,98 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#define OV01A10_LINK_FREQ_400MHZ 400000000ULL
-#define OV01A10_SCLK 40000000LL
+#define OV01A10_SCLK 80000000LL
#define OV01A10_DATA_LANES 1
+#define OV01A10_MCLK 19200000
-#define OV01A10_REG_CHIP_ID 0x300a
+#define OV01A10_REG_CHIP_ID CCI_REG24(0x300a)
#define OV01A10_CHIP_ID 0x560141
-#define OV01A10_REG_MODE_SELECT 0x0100
+#define OV01A10_REG_MODE_SELECT CCI_REG8(0x0100)
#define OV01A10_MODE_STANDBY 0x00
#define OV01A10_MODE_STREAMING 0x01
/* pixel array */
-#define OV01A10_PIXEL_ARRAY_WIDTH 1296
-#define OV01A10_PIXEL_ARRAY_HEIGHT 816
-#define OV01A10_ACITVE_WIDTH 1280
-#define OV01A10_ACITVE_HEIGHT 800
+#define OV01A10_NATIVE_WIDTH 1296
+#define OV01A10_NATIVE_HEIGHT 816
+#define OV01A10_DEFAULT_WIDTH 1280
+#define OV01A10_DEFAULT_HEIGHT 800
/* vertical and horizontal timings */
-#define OV01A10_REG_VTS 0x380e
-#define OV01A10_VTS_DEF 0x0380
+#define OV01A10_VTS_DEF 0x0700
#define OV01A10_VTS_MIN 0x0380
#define OV01A10_VTS_MAX 0xffff
#define OV01A10_HTS_DEF 1488
/* exposure controls */
-#define OV01A10_REG_EXPOSURE 0x3501
+#define OV01A10_REG_EXPOSURE CCI_REG16(0x3501)
#define OV01A10_EXPOSURE_MIN 4
#define OV01A10_EXPOSURE_MAX_MARGIN 8
#define OV01A10_EXPOSURE_STEP 1
/* analog gain controls */
-#define OV01A10_REG_ANALOG_GAIN 0x3508
+#define OV01A10_REG_ANALOG_GAIN CCI_REG16(0x3508)
#define OV01A10_ANAL_GAIN_MIN 0x100
-#define OV01A10_ANAL_GAIN_MAX 0xffff
+#define OV01A10_ANAL_GAIN_MAX 0x3fff
#define OV01A10_ANAL_GAIN_STEP 1
/* digital gain controls */
-#define OV01A10_REG_DIGITAL_GAIN_B 0x350a
-#define OV01A10_REG_DIGITAL_GAIN_GB 0x3510
-#define OV01A10_REG_DIGITAL_GAIN_GR 0x3513
-#define OV01A10_REG_DIGITAL_GAIN_R 0x3516
+#define OV01A10_REG_DIGITAL_GAIN_B CCI_REG24(0x350a)
+#define OV01A10_REG_DIGITAL_GAIN_GB CCI_REG24(0x3510)
+#define OV01A10_REG_DIGITAL_GAIN_GR CCI_REG24(0x3513)
+#define OV01A10_REG_DIGITAL_GAIN_R CCI_REG24(0x3516)
#define OV01A10_DGTL_GAIN_MIN 0
-#define OV01A10_DGTL_GAIN_MAX 0x3ffff
+#define OV01A10_DGTL_GAIN_MAX 0x3fff
#define OV01A10_DGTL_GAIN_STEP 1
#define OV01A10_DGTL_GAIN_DEFAULT 1024
-/* test pattern control */
-#define OV01A10_REG_TEST_PATTERN 0x4503
-#define OV01A10_TEST_PATTERN_ENABLE BIT(7)
-#define OV01A10_LINK_FREQ_400MHZ_INDEX 0
+/* timing control */
+#define OV01A10_REG_X_ADDR_START CCI_REG16(0x3800)
+#define OV01A10_REG_Y_ADDR_START CCI_REG16(0x3802)
+#define OV01A10_REG_X_ADDR_END CCI_REG16(0x3804)
+#define OV01A10_REG_Y_ADDR_END CCI_REG16(0x3806)
+#define OV01A10_REG_X_OUTPUT_SIZE CCI_REG16(0x3808)
+#define OV01A10_REG_Y_OUTPUT_SIZE CCI_REG16(0x380a)
+#define OV01A10_REG_HTS CCI_REG16(0x380c) /* in units of 2 pixels */
+#define OV01A10_REG_VTS CCI_REG16(0x380e)
+#define OV01A10_REG_X_WIN CCI_REG16(0x3810)
+#define OV01A10_REG_Y_WIN CCI_REG16(0x3812)
/* flip and mirror control */
-#define OV01A10_REG_FORMAT1 0x3820
+#define OV01A10_REG_FORMAT1 CCI_REG8(0x3820)
#define OV01A10_VFLIP_MASK BIT(4)
#define OV01A10_HFLIP_MASK BIT(3)
-/* window offset */
-#define OV01A10_REG_X_WIN 0x3811
-#define OV01A10_REG_Y_WIN 0x3813
-
-struct ov01a10_reg {
- u16 address;
- u8 val;
-};
-
-struct ov01a10_reg_list {
- u32 num_of_regs;
- const struct ov01a10_reg *regs;
-};
+/* test pattern control */
+#define OV01A10_REG_TEST_PATTERN CCI_REG8(0x4503)
+#define OV01A10_TEST_PATTERN_ENABLE BIT(7)
struct ov01a10_link_freq_config {
- const struct ov01a10_reg_list reg_list;
+ const struct reg_sequence *regs;
+ int regs_len;
};
-struct ov01a10_mode {
- u32 width;
- u32 height;
- u32 hts;
- u32 vts_def;
- u32 vts_min;
- u32 link_freq_index;
-
- const struct ov01a10_reg_list reg_list;
-};
-
-static const struct ov01a10_reg mipi_data_rate_720mbps[] = {
+static const struct reg_sequence mipi_data_rate_720mbps[] = {
{0x0103, 0x01},
{0x0302, 0x00},
{0x0303, 0x06},
{0x0304, 0x01},
- {0x0305, 0xe0},
+ {0x0305, 0xf4},
{0x0306, 0x00},
{0x0308, 0x01},
{0x0309, 0x00},
@@ -116,15 +109,11 @@ static const struct ov01a10_reg mipi_data_rate_720mbps[] = {
{0x0325, 0x68},
};
-static const struct ov01a10_reg sensor_1280x800_setting[] = {
+static const struct reg_sequence ov01a10_global_setting[] = {
{0x3002, 0xa1},
{0x301e, 0xf0},
{0x3022, 0x01},
- {0x3501, 0x03},
- {0x3502, 0x78},
{0x3504, 0x0c},
- {0x3508, 0x01},
- {0x3509, 0x00},
{0x3601, 0xc0},
{0x3603, 0x71},
{0x3610, 0x68},
@@ -168,31 +157,10 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = {
{0x37e4, 0x04},
{0x37e5, 0x03},
{0x37e6, 0x04},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x05},
- {0x3805, 0x0f},
- {0x3806, 0x03},
- {0x3807, 0x2f},
- {0x3808, 0x05},
- {0x3809, 0x00},
- {0x380a, 0x03},
- {0x380b, 0x20},
- {0x380c, 0x02},
- {0x380d, 0xe8},
- {0x380e, 0x03},
- {0x380f, 0x80},
- {0x3810, 0x00},
- {0x3811, 0x08},
- {0x3812, 0x00},
- {0x3813, 0x08},
{0x3814, 0x01},
{0x3815, 0x01},
{0x3816, 0x01},
{0x3817, 0x01},
- {0x3820, 0xa0},
{0x3822, 0x13},
{0x3832, 0x28},
{0x3833, 0x10},
@@ -214,7 +182,6 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = {
{0x4300, 0xff},
{0x4301, 0x00},
{0x4302, 0x0f},
- {0x4503, 0x00},
{0x4601, 0x50},
{0x4800, 0x64},
{0x481f, 0x34},
@@ -233,16 +200,14 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = {
{0x5200, 0x18},
{0x5004, 0x00},
{0x5080, 0x40},
- {0x0305, 0xf4},
{0x0325, 0xc2},
};
static const char * const ov01a10_test_pattern_menu[] = {
"Disabled",
"Color Bar",
- "Top-Bottom Darker Color Bar",
- "Right-Left Darker Color Bar",
- "Color Bar type 4",
+ "Left-Right Darker Color Bar",
+ "Bottom-Top Darker Color Bar",
};
static const s64 link_freq_menu_items[] = {
@@ -250,30 +215,39 @@ static const s64 link_freq_menu_items[] = {
};
static const struct ov01a10_link_freq_config link_freq_configs[] = {
- [OV01A10_LINK_FREQ_400MHZ_INDEX] = {
- .reg_list = {
- .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps),
- .regs = mipi_data_rate_720mbps,
- }
+ {
+ .regs = mipi_data_rate_720mbps,
+ .regs_len = ARRAY_SIZE(mipi_data_rate_720mbps),
},
};
-static const struct ov01a10_mode supported_modes[] = {
- {
- .width = OV01A10_ACITVE_WIDTH,
- .height = OV01A10_ACITVE_HEIGHT,
- .hts = OV01A10_HTS_DEF,
- .vts_def = OV01A10_VTS_DEF,
- .vts_min = OV01A10_VTS_MIN,
- .reg_list = {
- .num_of_regs = ARRAY_SIZE(sensor_1280x800_setting),
- .regs = sensor_1280x800_setting,
- },
- .link_freq_index = OV01A10_LINK_FREQ_400MHZ_INDEX,
- },
+static const struct v4l2_rect ov01a10_default_crop = {
+ .left = (OV01A10_NATIVE_WIDTH - OV01A10_DEFAULT_WIDTH) / 2,
+ .top = (OV01A10_NATIVE_HEIGHT - OV01A10_DEFAULT_HEIGHT) / 2,
+ .width = OV01A10_DEFAULT_WIDTH,
+ .height = OV01A10_DEFAULT_HEIGHT,
+};
+
+static const char * const ov01a10_supply_names[] = {
+ "dovdd", /* Digital I/O power */
+ "avdd", /* Analog power */
+ "dvdd", /* Digital core power */
+};
+
+struct ov01a10_sensor_cfg {
+ const char *model;
+ u32 bus_fmt;
+ int pattern_size;
+ int border_size;
+ u8 format1_base_val;
+ bool invert_hflip_shift;
+ bool invert_vflip_shift;
};
struct ov01a10 {
+ struct device *dev;
+ struct regmap *regmap;
+ const struct ov01a10_sensor_cfg *cfg;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
@@ -284,8 +258,15 @@ struct ov01a10 {
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
- const struct ov01a10_mode *cur_mode;
+ u32 link_freq_index;
+
+ struct clk *clk;
+ struct gpio_desc *reset;
+ struct gpio_desc *powerdown;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov01a10_supply_names)];
};
static inline struct ov01a10 *to_ov01a10(struct v4l2_subdev *subdev)
@@ -293,183 +274,115 @@ static inline struct ov01a10 *to_ov01a10(struct v4l2_subdev *subdev)
return container_of(subdev, struct ov01a10, sd);
}
-static int ov01a10_read_reg(struct ov01a10 *ov01a10, u16 reg, u16 len, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
- struct i2c_msg msgs[2];
- u8 addr_buf[2];
- u8 data_buf[4] = {0};
- int ret = 0;
-
- if (len > sizeof(data_buf))
- return -EINVAL;
-
- put_unaligned_be16(reg, addr_buf);
- msgs[0].addr = client->addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(addr_buf);
- msgs[0].buf = addr_buf;
- msgs[1].addr = client->addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = &data_buf[sizeof(data_buf) - len];
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
-
- if (ret != ARRAY_SIZE(msgs))
- return ret < 0 ? ret : -EIO;
-
- *val = get_unaligned_be32(data_buf);
-
- return 0;
-}
-
-static int ov01a10_write_reg(struct ov01a10 *ov01a10, u16 reg, u16 len, u32 val)
+static struct v4l2_mbus_framefmt *ov01a10_get_active_format(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
- u8 buf[6];
- int ret = 0;
+ struct v4l2_subdev_state *active_state =
+ v4l2_subdev_get_locked_active_state(&ov01a10->sd);
- if (len > 4)
- return -EINVAL;
-
- put_unaligned_be16(reg, buf);
- put_unaligned_be32(val << 8 * (4 - len), buf + 2);
-
- ret = i2c_master_send(client, buf, len + 2);
- if (ret != len + 2)
- return ret < 0 ? ret : -EIO;
-
- return 0;
+ return v4l2_subdev_state_get_format(active_state, 0);
}
-static int ov01a10_write_reg_list(struct ov01a10 *ov01a10,
- const struct ov01a10_reg_list *r_list)
+static struct v4l2_rect *ov01a10_get_active_crop(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
- unsigned int i;
- int ret = 0;
+ struct v4l2_subdev_state *active_state =
+ v4l2_subdev_get_locked_active_state(&ov01a10->sd);
- for (i = 0; i < r_list->num_of_regs; i++) {
- ret = ov01a10_write_reg(ov01a10, r_list->regs[i].address, 1,
- r_list->regs[i].val);
- if (ret) {
- dev_err_ratelimited(&client->dev,
- "write reg 0x%4.4x err = %d\n",
- r_list->regs[i].address, ret);
- return ret;
- }
- }
-
- return 0;
+ return v4l2_subdev_state_get_crop(active_state, 0);
}
static int ov01a10_update_digital_gain(struct ov01a10 *ov01a10, u32 d_gain)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
u32 real = d_gain << 6;
int ret = 0;
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_B, 3, real);
- if (ret) {
- dev_err(&client->dev, "failed to set DIGITAL_GAIN_B\n");
- return ret;
- }
-
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_GB, 3, real);
- if (ret) {
- dev_err(&client->dev, "failed to set DIGITAL_GAIN_GB\n");
- return ret;
- }
-
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_GR, 3, real);
- if (ret) {
- dev_err(&client->dev, "failed to set DIGITAL_GAIN_GR\n");
- return ret;
- }
-
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_R, 3, real);
- if (ret)
- dev_err(&client->dev, "failed to set DIGITAL_GAIN_R\n");
+ cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_B, real, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_GB, real, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_GR, real, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_R, real, &ret);
return ret;
}
static int ov01a10_test_pattern(struct ov01a10 *ov01a10, u32 pattern)
{
- if (!pattern)
- return 0;
+ if (pattern)
+ pattern |= OV01A10_TEST_PATTERN_ENABLE;
- pattern = (pattern - 1) | OV01A10_TEST_PATTERN_ENABLE;
-
- return ov01a10_write_reg(ov01a10, OV01A10_REG_TEST_PATTERN, 1, pattern);
+ return cci_write(ov01a10->regmap, OV01A10_REG_TEST_PATTERN, pattern,
+ NULL);
}
-/* for vflip and hflip, use 0x9 as window offset to keep the bayer */
-static int ov01a10_set_hflip(struct ov01a10 *ov01a10, u32 hflip)
+static void ov01a10_set_format1(struct ov01a10 *ov01a10, int *ret)
{
- int ret;
- u32 val, offset;
+ u8 val = ov01a10->cfg->format1_base_val;
- offset = hflip ? 0x9 : 0x8;
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_X_WIN, 1, offset);
- if (ret)
- return ret;
+ /* hflip register bit is inverted */
+ if (!ov01a10->hflip->val)
+ val |= FIELD_PREP(OV01A10_HFLIP_MASK, 0x1);
- ret = ov01a10_read_reg(ov01a10, OV01A10_REG_FORMAT1, 1, &val);
- if (ret)
- return ret;
+ if (ov01a10->vflip->val)
+ val |= FIELD_PREP(OV01A10_VFLIP_MASK, 0x1);
- val = hflip ? val | FIELD_PREP(OV01A10_HFLIP_MASK, 0x1) :
- val & ~OV01A10_HFLIP_MASK;
-
- return ov01a10_write_reg(ov01a10, OV01A10_REG_FORMAT1, 1, val);
+ cci_write(ov01a10->regmap, OV01A10_REG_FORMAT1, val, ret);
}
-static int ov01a10_set_vflip(struct ov01a10 *ov01a10, u32 vflip)
+static int ov01a10_set_hflip(struct ov01a10 *ov01a10, bool hflip)
{
- int ret;
- u32 val, offset;
+ struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10);
+ const struct ov01a10_sensor_cfg *cfg = ov01a10->cfg;
+ u32 offset;
+ int ret = 0;
- offset = vflip ? 0x9 : 0x8;
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_Y_WIN, 1, offset);
- if (ret)
- return ret;
+ offset = crop->left;
+ if ((hflip ^ cfg->invert_hflip_shift) && cfg->border_size)
+ offset++;
- ret = ov01a10_read_reg(ov01a10, OV01A10_REG_FORMAT1, 1, &val);
- if (ret)
- return ret;
+ cci_write(ov01a10->regmap, OV01A10_REG_X_WIN, offset, &ret);
+ ov01a10_set_format1(ov01a10, &ret);
+
+ return ret;
+}
+
+static int ov01a10_set_vflip(struct ov01a10 *ov01a10, bool vflip)
+{
+ struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10);
+ const struct ov01a10_sensor_cfg *cfg = ov01a10->cfg;
+ u32 offset;
+ int ret = 0;
+
+ offset = crop->top;
+ if ((vflip ^ cfg->invert_vflip_shift) && cfg->border_size)
+ offset++;
- val = vflip ? val | FIELD_PREP(OV01A10_VFLIP_MASK, 0x1) :
- val & ~OV01A10_VFLIP_MASK;
+ cci_write(ov01a10->regmap, OV01A10_REG_Y_WIN, offset, &ret);
+ ov01a10_set_format1(ov01a10, &ret);
- return ov01a10_write_reg(ov01a10, OV01A10_REG_FORMAT1, 1, val);
+ return ret;
}
static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov01a10 *ov01a10 = container_of(ctrl->handler,
struct ov01a10, ctrl_handler);
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
+ struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10);
s64 exposure_max;
int ret = 0;
if (ctrl->id == V4L2_CID_VBLANK) {
- exposure_max = ov01a10->cur_mode->height + ctrl->val -
- OV01A10_EXPOSURE_MAX_MARGIN;
+ exposure_max = fmt->height + ctrl->val -
+ OV01A10_EXPOSURE_MAX_MARGIN;
__v4l2_ctrl_modify_range(ov01a10->exposure,
- ov01a10->exposure->minimum,
- exposure_max, ov01a10->exposure->step,
- exposure_max);
+ OV01A10_EXPOSURE_MIN, exposure_max,
+ OV01A10_EXPOSURE_STEP, exposure_max);
}
- if (!pm_runtime_get_if_in_use(&client->dev))
+ if (!pm_runtime_get_if_in_use(ov01a10->dev))
return 0;
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_ANALOG_GAIN, 2,
- ctrl->val);
+ ret = cci_write(ov01a10->regmap, OV01A10_REG_ANALOG_GAIN,
+ ctrl->val, NULL);
break;
case V4L2_CID_DIGITAL_GAIN:
@@ -477,13 +390,13 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_EXPOSURE:
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_EXPOSURE, 2,
- ctrl->val);
+ ret = cci_write(ov01a10->regmap, OV01A10_REG_EXPOSURE,
+ ctrl->val, NULL);
break;
case V4L2_CID_VBLANK:
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_VTS, 2,
- ov01a10->cur_mode->height + ctrl->val);
+ ret = cci_write(ov01a10->regmap, OV01A10_REG_VTS,
+ fmt->height + ctrl->val, NULL);
break;
case V4L2_CID_TEST_PATTERN:
@@ -503,7 +416,7 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl)
break;
}
- pm_runtime_put(&client->dev);
+ pm_runtime_put(ov01a10->dev);
return ret;
}
@@ -514,16 +427,13 @@ static const struct v4l2_ctrl_ops ov01a10_ctrl_ops = {
static int ov01a10_init_controls(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
struct v4l2_fwnode_device_properties props;
u32 vblank_min, vblank_max, vblank_default;
struct v4l2_ctrl_handler *ctrl_hdlr;
- const struct ov01a10_mode *cur_mode;
s64 exposure_max, h_blank;
int ret = 0;
- int size;
- ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ ret = v4l2_fwnode_device_parse(ov01a10->dev, &props);
if (ret)
return ret;
@@ -532,34 +442,27 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
if (ret)
return ret;
- cur_mode = ov01a10->cur_mode;
- size = ARRAY_SIZE(link_freq_menu_items);
-
ov01a10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
&ov01a10_ctrl_ops,
V4L2_CID_LINK_FREQ,
- size - 1, 0,
+ ov01a10->link_freq_index, 0,
link_freq_menu_items);
- if (ov01a10->link_freq)
- ov01a10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
ov01a10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
V4L2_CID_PIXEL_RATE, 0,
OV01A10_SCLK, 1, OV01A10_SCLK);
- vblank_min = cur_mode->vts_min - cur_mode->height;
- vblank_max = OV01A10_VTS_MAX - cur_mode->height;
- vblank_default = cur_mode->vts_def - cur_mode->height;
+ vblank_min = OV01A10_VTS_MIN - OV01A10_DEFAULT_HEIGHT;
+ vblank_max = OV01A10_VTS_MAX - OV01A10_DEFAULT_HEIGHT;
+ vblank_default = OV01A10_VTS_DEF - OV01A10_DEFAULT_HEIGHT;
ov01a10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
V4L2_CID_VBLANK, vblank_min,
vblank_max, 1, vblank_default);
- h_blank = cur_mode->hts - cur_mode->width;
+ h_blank = OV01A10_HTS_DEF - OV01A10_DEFAULT_WIDTH;
ov01a10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
V4L2_CID_HBLANK, h_blank, h_blank,
1, h_blank);
- if (ov01a10->hblank)
- ov01a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OV01A10_ANAL_GAIN_MIN, OV01A10_ANAL_GAIN_MAX,
@@ -568,7 +471,7 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
OV01A10_DGTL_GAIN_MIN, OV01A10_DGTL_GAIN_MAX,
OV01A10_DGTL_GAIN_STEP, OV01A10_DGTL_GAIN_DEFAULT);
- exposure_max = cur_mode->vts_def - OV01A10_EXPOSURE_MAX_MARGIN;
+ exposure_max = OV01A10_VTS_DEF - OV01A10_EXPOSURE_MAX_MARGIN;
ov01a10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
V4L2_CID_EXPOSURE,
OV01A10_EXPOSURE_MIN,
@@ -581,10 +484,10 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
ARRAY_SIZE(ov01a10_test_pattern_menu) - 1,
0, 0, ov01a10_test_pattern_menu);
- v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_HFLIP,
- 0, 1, 1, 0);
- v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_VFLIP,
- 0, 1, 1, 0);
+ ov01a10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ov01a10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov01a10_ctrl_ops,
&props);
@@ -596,6 +499,9 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10)
goto fail;
}
+ ov01a10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ov01a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
ov01a10->sd.ctrl_handler = ctrl_hdlr;
return 0;
@@ -605,83 +511,104 @@ fail:
return ret;
}
-static void ov01a10_update_pad_format(const struct ov01a10_mode *mode,
- struct v4l2_mbus_framefmt *fmt)
+static void ov01a10_fill_format(struct ov01a10 *ov01a10,
+ struct v4l2_mbus_framefmt *fmt,
+ unsigned int width, unsigned int height)
{
- fmt->width = mode->width;
- fmt->height = mode->height;
- fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->width = width;
+ fmt->height = height;
+ fmt->code = ov01a10->cfg->bus_fmt;
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_RAW;
}
-static int ov01a10_start_streaming(struct ov01a10 *ov01a10)
+static int ov01a10_set_mode(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
- const struct ov01a10_reg_list *reg_list;
- int link_freq_index;
+ struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10);
int ret = 0;
- link_freq_index = ov01a10->cur_mode->link_freq_index;
- reg_list = &link_freq_configs[link_freq_index].reg_list;
- ret = ov01a10_write_reg_list(ov01a10, reg_list);
+ cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_START, 0, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_START, 0, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_END,
+ OV01A10_NATIVE_WIDTH - 1, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_END,
+ OV01A10_NATIVE_HEIGHT - 1, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_X_OUTPUT_SIZE,
+ fmt->width, &ret);
+ cci_write(ov01a10->regmap, OV01A10_REG_Y_OUTPUT_SIZE,
+ fmt->height, &ret);
+ /* HTS register is in units of 2 pixels */
+ cci_write(ov01a10->regmap, OV01A10_REG_HTS,
+ OV01A10_HTS_DEF / 2, &ret);
+ /* OV01A10_REG_VTS is set by vblank control */
+ /* OV01A10_REG_X_WIN is set by hlip control */
+ /* OV01A10_REG_Y_WIN is set by vflip control */
+
+ return ret;
+}
+
+static int ov01a10_start_streaming(struct ov01a10 *ov01a10)
+{
+ const struct ov01a10_link_freq_config *freq_cfg;
+ int ret;
+
+ freq_cfg = &link_freq_configs[ov01a10->link_freq_index];
+ ret = regmap_multi_reg_write(ov01a10->regmap, freq_cfg->regs,
+ freq_cfg->regs_len);
if (ret) {
- dev_err(&client->dev, "failed to set plls\n");
+ dev_err(ov01a10->dev, "failed to set plls\n");
return ret;
}
- reg_list = &ov01a10->cur_mode->reg_list;
- ret = ov01a10_write_reg_list(ov01a10, reg_list);
+ ret = regmap_multi_reg_write(ov01a10->regmap, ov01a10_global_setting,
+ ARRAY_SIZE(ov01a10_global_setting));
if (ret) {
- dev_err(&client->dev, "failed to set mode\n");
+ dev_err(ov01a10->dev, "failed to initialize sensor\n");
return ret;
}
- ret = __v4l2_ctrl_handler_setup(ov01a10->sd.ctrl_handler);
- if (ret)
+ ret = ov01a10_set_mode(ov01a10);
+ if (ret) {
+ dev_err(ov01a10->dev, "failed to set mode\n");
return ret;
+ }
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_MODE_SELECT, 1,
- OV01A10_MODE_STREAMING);
+ ret = __v4l2_ctrl_handler_setup(ov01a10->sd.ctrl_handler);
if (ret)
- dev_err(&client->dev, "failed to start streaming\n");
+ return ret;
- return ret;
+ return cci_write(ov01a10->regmap, OV01A10_REG_MODE_SELECT,
+ OV01A10_MODE_STREAMING, NULL);
}
static void ov01a10_stop_streaming(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
- int ret = 0;
-
- ret = ov01a10_write_reg(ov01a10, OV01A10_REG_MODE_SELECT, 1,
- OV01A10_MODE_STANDBY);
- if (ret)
- dev_err(&client->dev, "failed to stop streaming\n");
+ cci_write(ov01a10->regmap, OV01A10_REG_MODE_SELECT,
+ OV01A10_MODE_STANDBY, NULL);
}
static int ov01a10_set_stream(struct v4l2_subdev *sd, int enable)
{
struct ov01a10 *ov01a10 = to_ov01a10(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
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);
+ ret = pm_runtime_resume_and_get(ov01a10->dev);
if (ret < 0)
goto unlock;
ret = ov01a10_start_streaming(ov01a10);
if (ret) {
- pm_runtime_put(&client->dev);
+ pm_runtime_put(ov01a10->dev);
goto unlock;
}
} else {
ov01a10_stop_streaming(ov01a10);
- pm_runtime_put(&client->dev);
+ pm_runtime_put(ov01a10->dev);
}
unlock:
@@ -690,56 +617,66 @@ unlock:
return ret;
}
+static void ov01a10_update_blank_ctrls(struct ov01a10 *ov01a10,
+ unsigned int width, unsigned int height)
+{
+ s32 hblank, vblank_def;
+
+ vblank_def = OV01A10_VTS_DEF - height;
+ __v4l2_ctrl_modify_range(ov01a10->vblank,
+ OV01A10_VTS_MIN - height,
+ OV01A10_VTS_MAX - height, 1,
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def);
+
+ hblank = OV01A10_HTS_DEF - width;
+ __v4l2_ctrl_modify_range(ov01a10->hblank, hblank, hblank, 1, hblank);
+}
+
static int ov01a10_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
+ struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad);
struct ov01a10 *ov01a10 = to_ov01a10(sd);
- const struct ov01a10_mode *mode;
- struct v4l2_mbus_framefmt *format;
- s32 vblank_def, h_blank;
-
- mode = v4l2_find_nearest_size(supported_modes,
- ARRAY_SIZE(supported_modes), width,
- height, fmt->format.width,
- fmt->format.height);
-
- ov01a10_update_pad_format(mode, &fmt->format);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- ov01a10->cur_mode = mode;
- __v4l2_ctrl_s_ctrl(ov01a10->link_freq, mode->link_freq_index);
- __v4l2_ctrl_s_ctrl_int64(ov01a10->pixel_rate, OV01A10_SCLK);
-
- vblank_def = mode->vts_def - mode->height;
- __v4l2_ctrl_modify_range(ov01a10->vblank,
- mode->vts_min - mode->height,
- OV01A10_VTS_MAX - mode->height, 1,
- vblank_def);
- __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def);
- h_blank = mode->hts - mode->width;
- __v4l2_ctrl_modify_range(ov01a10->hblank, h_blank, h_blank, 1,
- h_blank);
+ const int pattern_size = ov01a10->cfg->pattern_size;
+ const int border_size = ov01a10->cfg->border_size;
+ unsigned int width, height;
+
+ width = clamp_val(ALIGN(fmt->format.width, pattern_size),
+ pattern_size,
+ OV01A10_NATIVE_WIDTH - 2 * border_size);
+ height = clamp_val(ALIGN(fmt->format.height, pattern_size),
+ pattern_size,
+ OV01A10_NATIVE_HEIGHT - 2 * border_size);
+
+ /* Center image for userspace which does not set the crop first */
+ if (width != crop->width || height != crop->height) {
+ crop->left = ALIGN((OV01A10_NATIVE_WIDTH - width) / 2,
+ pattern_size);
+ crop->top = ALIGN((OV01A10_NATIVE_HEIGHT - height) / 2,
+ pattern_size);
+ crop->width = width;
+ crop->height = height;
}
- format = v4l2_subdev_state_get_format(sd_state, fmt->stream);
- *format = fmt->format;
+ ov01a10_fill_format(ov01a10, &fmt->format, width, height);
+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ov01a10_update_blank_ctrls(ov01a10, width, height);
return 0;
}
static int ov01a10_init_state(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state)
+ struct v4l2_subdev_state *sd_state)
{
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- .format = {
- .width = OV01A10_ACITVE_WIDTH,
- .height = OV01A10_ACITVE_HEIGHT,
- },
- };
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
- ov01a10_set_format(sd, state, &fmt);
+ *v4l2_subdev_state_get_crop(sd_state, 0) = ov01a10_default_crop;
+ ov01a10_fill_format(ov01a10, v4l2_subdev_state_get_format(sd_state, 0),
+ OV01A10_DEFAULT_WIDTH, OV01A10_DEFAULT_HEIGHT);
return 0;
}
@@ -748,10 +685,12 @@ static int ov01a10_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ code->code = ov01a10->cfg->bus_fmt;
return 0;
}
@@ -760,14 +699,17 @@ static int ov01a10_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) ||
- fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+ const int pattern_size = ov01a10->cfg->pattern_size;
+ const int border_size = ov01a10->cfg->border_size;
+
+ if (fse->index)
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;
+ fse->min_width = pattern_size;
+ fse->max_width = OV01A10_NATIVE_WIDTH - 2 * border_size;
+ fse->min_height = pattern_size;
+ fse->max_height = OV01A10_NATIVE_HEIGHT - 2 * border_size;
return 0;
}
@@ -776,31 +718,80 @@ static int ov01a10_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+ const int border_size = ov01a10->cfg->border_size;
switch (sel->target) {
- case V4L2_SEL_TGT_NATIVE_SIZE:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.top = 0;
- sel->r.left = 0;
- sel->r.width = OV01A10_PIXEL_ARRAY_WIDTH;
- sel->r.height = OV01A10_PIXEL_ARRAY_HEIGHT;
- return 0;
case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
+ return 0;
case V4L2_SEL_TGT_CROP_DEFAULT:
- sel->r.top = (OV01A10_PIXEL_ARRAY_HEIGHT -
- OV01A10_ACITVE_HEIGHT) / 2;
- sel->r.left = (OV01A10_PIXEL_ARRAY_WIDTH -
- OV01A10_ACITVE_WIDTH) / 2;
- sel->r.width = OV01A10_ACITVE_WIDTH;
- sel->r.height = OV01A10_ACITVE_HEIGHT;
+ sel->r = ov01a10_default_crop;
+ return 0;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ /* Keep a border for hvflip shift to preserve bayer-pattern */
+ sel->r.left = border_size;
+ sel->r.top = border_size;
+ sel->r.width = OV01A10_NATIVE_WIDTH - 2 * border_size;
+ sel->r.height = OV01A10_NATIVE_HEIGHT - 2 * border_size;
+ return 0;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = OV01A10_NATIVE_WIDTH;
+ sel->r.height = OV01A10_NATIVE_HEIGHT;
return 0;
}
return -EINVAL;
}
+static int ov01a10_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+ const int pattern_size = ov01a10->cfg->pattern_size;
+ const int border_size = ov01a10->cfg->border_size;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+ struct v4l2_rect rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /*
+ * Clamp the boundaries of the crop rectangle to the size of the sensor
+ * pixel array. Align to pattern-size to ensure pattern isn't disrupted.
+ */
+ rect.left = clamp_val(ALIGN(sel->r.left, pattern_size), border_size,
+ OV01A10_NATIVE_WIDTH - 2 * border_size);
+ rect.top = clamp_val(ALIGN(sel->r.top, pattern_size), border_size,
+ OV01A10_NATIVE_HEIGHT - 2 * border_size);
+ rect.width = clamp_val(ALIGN(sel->r.width, pattern_size), pattern_size,
+ OV01A10_NATIVE_WIDTH - rect.left - border_size);
+ rect.height = clamp_val(ALIGN(sel->r.height, pattern_size), pattern_size,
+ OV01A10_NATIVE_HEIGHT - rect.top - border_size);
+
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad);
+
+ /* Reset the output size if the crop rectangle size has changed */
+ if (rect.width != crop->width || rect.height != crop->height) {
+ format = v4l2_subdev_state_get_format(sd_state, sel->pad);
+ format->width = rect.width;
+ format->height = rect.height;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ov01a10_update_blank_ctrls(ov01a10, rect.width,
+ rect.height);
+ }
+
+ *crop = rect;
+ sel->r = rect;
+
+ return 0;
+}
+
static const struct v4l2_subdev_core_ops ov01a10_core_ops = {
.log_status = v4l2_ctrl_subdev_log_status,
};
@@ -813,6 +804,7 @@ static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = {
.set_fmt = ov01a10_set_format,
.get_fmt = v4l2_subdev_get_fmt,
.get_selection = ov01a10_get_selection,
+ .set_selection = ov01a10_set_selection,
.enum_mbus_code = ov01a10_enum_mbus_code,
.enum_frame_size = ov01a10_enum_frame_size,
};
@@ -831,18 +823,103 @@ static const struct media_entity_operations ov01a10_subdev_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
+static int ov01a10_get_pm_resources(struct ov01a10 *ov01a10)
+{
+ unsigned long freq;
+ int i, ret;
+
+ ov01a10->clk = devm_v4l2_sensor_clk_get(ov01a10->dev, NULL);
+ if (IS_ERR(ov01a10->clk))
+ return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->clk),
+ "getting clock\n");
+
+ freq = clk_get_rate(ov01a10->clk);
+ if (freq != OV01A10_MCLK)
+ return dev_err_probe(ov01a10->dev, -EINVAL,
+ "external clock %lu is not supported",
+ freq);
+
+ ov01a10->reset = devm_gpiod_get_optional(ov01a10->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov01a10->reset))
+ return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->reset),
+ "getting reset gpio\n");
+
+ ov01a10->powerdown = devm_gpiod_get_optional(ov01a10->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov01a10->powerdown))
+ return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->powerdown),
+ "getting powerdown gpio\n");
+
+ for (i = 0; i < ARRAY_SIZE(ov01a10_supply_names); i++)
+ ov01a10->supplies[i].supply = ov01a10_supply_names[i];
+
+ ret = devm_regulator_bulk_get(ov01a10->dev,
+ ARRAY_SIZE(ov01a10_supply_names),
+ ov01a10->supplies);
+ if (ret)
+ return dev_err_probe(ov01a10->dev, ret, "getting regulators\n");
+
+ return 0;
+}
+
+static int ov01a10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+ int ret;
+
+ ret = clk_prepare_enable(ov01a10->clk);
+ if (ret) {
+ dev_err(dev, "Error enabling clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov01a10_supply_names),
+ ov01a10->supplies);
+ if (ret) {
+ dev_err(dev, "Error enabling regulators: %d\n", ret);
+ clk_disable_unprepare(ov01a10->clk);
+ return ret;
+ }
+
+ if (ov01a10->reset || ov01a10->powerdown) {
+ /* Assert reset/powerdown for at least 2ms on back to back off-on */
+ fsleep(2000);
+ gpiod_set_value_cansleep(ov01a10->powerdown, 0);
+ gpiod_set_value_cansleep(ov01a10->reset, 0);
+ fsleep(20000);
+ }
+
+ return 0;
+}
+
+static int ov01a10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov01a10 *ov01a10 = to_ov01a10(sd);
+
+ gpiod_set_value_cansleep(ov01a10->reset, 1);
+ gpiod_set_value_cansleep(ov01a10->powerdown, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ov01a10_supply_names),
+ ov01a10->supplies);
+
+ clk_disable_unprepare(ov01a10->clk);
+ return 0;
+}
+
static int ov01a10_identify_module(struct ov01a10 *ov01a10)
{
- struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd);
int ret;
- u32 val;
+ u64 val;
- ret = ov01a10_read_reg(ov01a10, OV01A10_REG_CHIP_ID, 3, &val);
+ ret = cci_read(ov01a10->regmap, OV01A10_REG_CHIP_ID, &val, NULL);
if (ret)
return ret;
if (val != OV01A10_CHIP_ID) {
- dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+ dev_err(ov01a10->dev, "chip id mismatch: %x!=%llx\n",
OV01A10_CHIP_ID, val);
return -EIO;
}
@@ -850,43 +927,114 @@ static int ov01a10_identify_module(struct ov01a10 *ov01a10)
return 0;
}
+static int ov01a10_check_hwcfg(struct ov01a10 *ov01a10)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ struct fwnode_handle *ep, *fwnode = dev_fwnode(ov01a10->dev);
+ unsigned long link_freq_bitmap;
+ int ret;
+
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver,
+ * wait for this.
+ */
+ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
+ if (!ep)
+ return dev_err_probe(ov01a10->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return dev_err_probe(ov01a10->dev, ret, "parsing endpoint\n");
+
+ ret = v4l2_link_freq_to_bitmap(ov01a10->dev,
+ bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_freq_menu_items,
+ ARRAY_SIZE(link_freq_menu_items),
+ &link_freq_bitmap);
+ if (ret)
+ goto check_hwcfg_error;
+
+ /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */
+ ov01a10->link_freq_index = ffs(link_freq_bitmap) - 1;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV01A10_DATA_LANES) {
+ ret = dev_err_probe(ov01a10->dev, -EINVAL,
+ "number of CSI2 data lanes %u is not supported\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ goto check_hwcfg_error;
+ }
+
+check_hwcfg_error:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ return ret;
+}
+
static void ov01a10_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);
- pm_runtime_set_suspended(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ ov01a10_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
}
static int ov01a10_probe(struct i2c_client *client)
{
- struct device *dev = &client->dev;
+ const struct ov01a10_sensor_cfg *cfg;
struct ov01a10 *ov01a10;
- int ret = 0;
+ int ret;
+
+ cfg = device_get_match_data(&client->dev);
+ if (!cfg)
+ return -EINVAL;
- ov01a10 = devm_kzalloc(dev, sizeof(*ov01a10), GFP_KERNEL);
+ ov01a10 = devm_kzalloc(&client->dev, sizeof(*ov01a10), GFP_KERNEL);
if (!ov01a10)
return -ENOMEM;
+ ov01a10->dev = &client->dev;
+ ov01a10->cfg = cfg;
+
+ ov01a10->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(ov01a10->regmap))
+ return PTR_ERR(ov01a10->regmap);
+
v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops);
+ /* Override driver->name with actual sensor model */
+ v4l2_i2c_subdev_set_name(&ov01a10->sd, client, cfg->model, NULL);
ov01a10->sd.internal_ops = &ov01a10_internal_ops;
- ret = ov01a10_identify_module(ov01a10);
+ ret = ov01a10_check_hwcfg(ov01a10);
+ if (ret)
+ return ret;
+
+ ret = ov01a10_get_pm_resources(ov01a10);
+ if (ret)
+ return ret;
+
+ ret = ov01a10_power_on(&client->dev);
if (ret)
- return dev_err_probe(dev, ret,
- "failed to find sensor\n");
+ return ret;
- ov01a10->cur_mode = &supported_modes[0];
+ ret = ov01a10_identify_module(ov01a10);
+ if (ret)
+ goto err_power_off;
ret = ov01a10_init_controls(ov01a10);
- if (ret) {
- dev_err(dev, "failed to init controls: %d\n", ret);
- return ret;
- }
+ if (ret)
+ goto err_power_off;
ov01a10->sd.state_lock = ov01a10->ctrl_handler.lock;
ov01a10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -895,36 +1043,31 @@ static int ov01a10_probe(struct i2c_client *client)
ov01a10->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&ov01a10->sd.entity, 1, &ov01a10->pad);
- if (ret) {
- dev_err(dev, "Failed to init entity pads: %d\n", ret);
+ if (ret)
goto err_handler_free;
- }
ret = v4l2_subdev_init_finalize(&ov01a10->sd);
- if (ret) {
- dev_err(dev, "Failed to allocate subdev state: %d\n", ret);
+ if (ret)
goto err_media_entity_cleanup;
- }
/*
* Device is already turned on by i2c-core with ACPI domain PM.
* Enable runtime PM and turn off the device.
*/
pm_runtime_set_active(&client->dev);
- pm_runtime_enable(dev);
- pm_runtime_idle(dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_idle(&client->dev);
ret = v4l2_async_register_subdev_sensor(&ov01a10->sd);
- if (ret < 0) {
- dev_err(dev, "Failed to register subdev: %d\n", ret);
+ if (ret)
goto err_pm_disable;
- }
return 0;
err_pm_disable:
- pm_runtime_disable(dev);
+ pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
+ v4l2_subdev_cleanup(&ov01a10->sd);
err_media_entity_cleanup:
media_entity_cleanup(&ov01a10->sd.entity);
@@ -932,12 +1075,44 @@ err_media_entity_cleanup:
err_handler_free:
v4l2_ctrl_handler_free(ov01a10->sd.ctrl_handler);
+err_power_off:
+ ov01a10_power_off(&client->dev);
+
return ret;
}
+static DEFINE_RUNTIME_DEV_PM_OPS(ov01a10_pm_ops, ov01a10_power_off,
+ ov01a10_power_on, NULL);
+
#ifdef CONFIG_ACPI
+/*
+ * The native ov01a10 bayer-pattern is GBRG, but there was a driver bug enabling
+ * hflip/mirroring by default resulting in BGGR. Because of this bug Intel's
+ * proprietary IPU6 userspace stack expects BGGR. So we report BGGR to not break
+ * userspace and fix things up by shifting the crop window-x coordinate by 1
+ * when hflip is *disabled*.
+ */
+static const struct ov01a10_sensor_cfg ov01a10_cfg = {
+ .model = "ov01a10",
+ .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pattern_size = 2, /* 2x2 */
+ .border_size = 2,
+ .format1_base_val = 0xa0,
+ .invert_hflip_shift = true,
+ .invert_vflip_shift = false,
+};
+
+static const struct ov01a10_sensor_cfg ov01a1b_cfg = {
+ .model = "ov01a1b",
+ .bus_fmt = MEDIA_BUS_FMT_Y10_1X10,
+ .pattern_size = 2, /* Keep coordinates aligned to a multiple of 2 */
+ .border_size = 0,
+ .format1_base_val = 0xa0,
+};
+
static const struct acpi_device_id ov01a10_acpi_ids[] = {
- { "OVTI01A0" },
+ { "OVTI01A0", (uintptr_t)&ov01a10_cfg },
+ { "OVTI01AB", (uintptr_t)&ov01a1b_cfg },
{ }
};
@@ -947,6 +1122,7 @@ MODULE_DEVICE_TABLE(acpi, ov01a10_acpi_ids);
static struct i2c_driver ov01a10_i2c_driver = {
.driver = {
.name = "ov01a10",
+ .pm = pm_sleep_ptr(&ov01a10_pm_ops),
.acpi_match_table = ACPI_PTR(ov01a10_acpi_ids),
},
.probe = ov01a10_probe,
diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c
index b96600204141..dcb1add1fd9f 100644
--- a/drivers/media/i2c/ov2735.c
+++ b/drivers/media/i2c/ov2735.c
@@ -993,7 +993,7 @@ static int ov2735_probe(struct i2c_client *client)
"failed to parse endpoint configuration\n");
ov2735->reset_gpio = devm_gpiod_get_optional(ov2735->dev,
- "reset", GPIOD_OUT_LOW);
+ "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ov2735->reset_gpio))
return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->reset_gpio),
"failed to get reset GPIO\n");
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index e193fef4fced..6a46ef7233ac 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -20,8 +20,11 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -41,25 +44,26 @@
#define MIPI_CTRL00_BUS_IDLE BIT(2)
#define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0)
-#define OV5647_SW_STANDBY 0x0100
-#define OV5647_SW_RESET 0x0103
-#define OV5647_REG_CHIPID_H 0x300a
-#define OV5647_REG_CHIPID_L 0x300b
-#define OV5640_REG_PAD_OUT 0x300d
-#define OV5647_REG_EXP_HI 0x3500
-#define OV5647_REG_EXP_MID 0x3501
-#define OV5647_REG_EXP_LO 0x3502
-#define OV5647_REG_AEC_AGC 0x3503
-#define OV5647_REG_GAIN_HI 0x350a
-#define OV5647_REG_GAIN_LO 0x350b
-#define OV5647_REG_VTS_HI 0x380e
-#define OV5647_REG_VTS_LO 0x380f
-#define OV5647_REG_FRAME_OFF_NUMBER 0x4202
-#define OV5647_REG_MIPI_CTRL00 0x4800
-#define OV5647_REG_MIPI_CTRL14 0x4814
-#define OV5647_REG_AWB 0x5001
-#define OV5647_REG_ISPCTRL3D 0x503d
-
+#define OV5647_SW_STANDBY CCI_REG8(0x0100)
+#define OV5647_SW_RESET CCI_REG8(0x0103)
+#define OV5647_REG_CHIPID CCI_REG16(0x300a)
+#define OV5640_REG_PAD_OUT CCI_REG8(0x300d)
+#define OV5647_REG_EXPOSURE CCI_REG24(0x3500)
+#define OV5647_REG_AEC_AGC CCI_REG8(0x3503)
+#define OV5647_REG_GAIN CCI_REG16(0x350a)
+#define OV5647_REG_HTS CCI_REG16(0x380c)
+#define OV5647_REG_VTS CCI_REG16(0x380e)
+#define OV5647_REG_TIMING_TC_V CCI_REG8(0x3820)
+#define OV5647_REG_TIMING_TC_H CCI_REG8(0x3821)
+#define OV5647_REG_FRAME_OFF_NUMBER CCI_REG8(0x4202)
+#define OV5647_REG_MIPI_CTRL00 CCI_REG8(0x4800)
+#define OV5647_REG_MIPI_CTRL14 CCI_REG8(0x4814)
+#define OV5647_REG_MIPI_CTRL14_CHANNEL_MASK GENMASK(7, 6)
+#define OV5647_REG_MIPI_CTRL14_CHANNEL_SHIFT 6
+#define OV5647_REG_AWB CCI_REG8(0x5001)
+#define OV5647_REG_ISPCTRL3D CCI_REG8(0x503d)
+
+#define OV5647_CHIP_ID 0x5647
#define REG_TERM 0xfffe
#define VAL_TERM 0xfe
#define REG_DLY 0xffff
@@ -69,39 +73,54 @@
#define OV5647_NATIVE_HEIGHT 1956U
#define OV5647_PIXEL_ARRAY_LEFT 16U
-#define OV5647_PIXEL_ARRAY_TOP 16U
+#define OV5647_PIXEL_ARRAY_TOP 6U
#define OV5647_PIXEL_ARRAY_WIDTH 2592U
#define OV5647_PIXEL_ARRAY_HEIGHT 1944U
-#define OV5647_VBLANK_MIN 4
+#define OV5647_VBLANK_MIN 24
#define OV5647_VTS_MAX 32767
+#define OV5647_HTS_MAX 0x1fff
+
#define OV5647_EXPOSURE_MIN 4
#define OV5647_EXPOSURE_STEP 1
#define OV5647_EXPOSURE_DEFAULT 1000
#define OV5647_EXPOSURE_MAX 65535
-struct regval_list {
- u16 addr;
- u8 data;
+/* regulator supplies */
+static const char * const ov5647_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+#define OV5647_NUM_SUPPLIES ARRAY_SIZE(ov5647_supply_names)
+
+#define FREQ_INDEX_FULL 0
+#define FREQ_INDEX_VGA 1
+static const s64 ov5647_link_freqs[] = {
+ [FREQ_INDEX_FULL] = 218750000,
+ [FREQ_INDEX_VGA] = 145833300,
};
struct ov5647_mode {
struct v4l2_mbus_framefmt format;
struct v4l2_rect crop;
u64 pixel_rate;
+ unsigned int link_freq_index;
int hts;
int vts;
- const struct regval_list *reg_list;
+ const struct reg_sequence *reg_list;
unsigned int num_regs;
};
struct ov5647 {
struct v4l2_subdev sd;
+ struct regmap *regmap;
struct media_pad pad;
- struct mutex lock;
struct clk *xclk;
struct gpio_desc *pwdn;
+ struct regulator_bulk_data supplies[OV5647_NUM_SUPPLIES];
bool clock_ncont;
struct v4l2_ctrl_handler ctrls;
const struct ov5647_mode *mode;
@@ -109,6 +128,9 @@ struct ov5647 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *link_freq;
};
static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd)
@@ -130,34 +152,28 @@ static const u8 ov5647_test_pattern_val[] = {
0x81, /* Random Data */
};
-static const struct regval_list sensor_oe_disable_regs[] = {
+static const struct reg_sequence sensor_oe_disable_regs[] = {
{0x3000, 0x00},
{0x3001, 0x00},
{0x3002, 0x00},
};
-static const struct regval_list sensor_oe_enable_regs[] = {
+static const struct reg_sequence sensor_oe_enable_regs[] = {
{0x3000, 0x0f},
{0x3001, 0xff},
{0x3002, 0xe4},
};
-static struct regval_list ov5647_2592x1944_10bpp[] = {
+static const struct reg_sequence ov5647_common_regs[] = {
{0x0100, 0x00},
{0x0103, 0x01},
{0x3034, 0x1a},
{0x3035, 0x21},
- {0x3036, 0x69},
{0x303c, 0x11},
{0x3106, 0xf5},
- {0x3821, 0x06},
- {0x3820, 0x00},
{0x3827, 0xec},
{0x370c, 0x03},
- {0x3612, 0x5b},
- {0x3618, 0x04},
{0x5000, 0x06},
- {0x5002, 0x41},
{0x5003, 0x08},
{0x5a00, 0x08},
{0x3000, 0x00},
@@ -172,26 +188,6 @@ static struct regval_list ov5647_2592x1944_10bpp[] = {
{0x3a19, 0xf8},
{0x3c01, 0x80},
{0x3b07, 0x0c},
- {0x380c, 0x0b},
- {0x380d, 0x1c},
- {0x3814, 0x11},
- {0x3815, 0x11},
- {0x3708, 0x64},
- {0x3709, 0x12},
- {0x3808, 0x0a},
- {0x3809, 0x20},
- {0x380a, 0x07},
- {0x380b, 0x98},
- {0x3800, 0x00},
- {0x3801, 0x00},
- {0x3802, 0x00},
- {0x3803, 0x00},
- {0x3804, 0x0a},
- {0x3805, 0x3f},
- {0x3806, 0x07},
- {0x3807, 0xa3},
- {0x3811, 0x10},
- {0x3813, 0x06},
{0x3630, 0x2e},
{0x3632, 0xe2},
{0x3633, 0x23},
@@ -211,11 +207,6 @@ static struct regval_list ov5647_2592x1944_10bpp[] = {
{0x3f06, 0x10},
{0x3f01, 0x0a},
{0x3a08, 0x01},
- {0x3a09, 0x28},
- {0x3a0a, 0x00},
- {0x3a0b, 0xf6},
- {0x3a0d, 0x08},
- {0x3a0e, 0x06},
{0x3a0f, 0x58},
{0x3a10, 0x50},
{0x3a1b, 0x58},
@@ -223,54 +214,57 @@ static struct regval_list ov5647_2592x1944_10bpp[] = {
{0x3a11, 0x60},
{0x3a1f, 0x28},
{0x4001, 0x02},
- {0x4004, 0x04},
{0x4000, 0x09},
+ {0x3503, 0x03},
+};
+
+static const struct reg_sequence ov5647_2592x1944_10bpp[] = {
+ {0x3036, 0x69},
+ {0x3821, 0x02},
+ {0x3820, 0x00},
+ {0x3612, 0x5b},
+ {0x3618, 0x04},
+ {0x5002, 0x41},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+ {0x3709, 0x12},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xa3},
+ {0x3808, 0x0a},
+ {0x3809, 0x20},
+ {0x380a, 0x07},
+ {0x380b, 0x98},
+ {0x3811, 0x10},
+ {0x3813, 0x06},
+ {0x3a09, 0x28},
+ {0x3a0a, 0x00},
+ {0x3a0b, 0xf6},
+ {0x3a0d, 0x08},
+ {0x3a0e, 0x06},
+ {0x4004, 0x04},
{0x4837, 0x19},
{0x4800, 0x24},
- {0x3503, 0x03},
{0x0100, 0x01},
};
-static struct regval_list ov5647_1080p30_10bpp[] = {
- {0x0100, 0x00},
- {0x0103, 0x01},
- {0x3034, 0x1a},
- {0x3035, 0x21},
- {0x3036, 0x62},
- {0x303c, 0x11},
- {0x3106, 0xf5},
- {0x3821, 0x06},
+static const struct reg_sequence ov5647_1080p30_10bpp[] = {
+ {0x3036, 0x69},
+ {0x3821, 0x02},
{0x3820, 0x00},
- {0x3827, 0xec},
- {0x370c, 0x03},
{0x3612, 0x5b},
{0x3618, 0x04},
- {0x5000, 0x06},
{0x5002, 0x41},
- {0x5003, 0x08},
- {0x5a00, 0x08},
- {0x3000, 0x00},
- {0x3001, 0x00},
- {0x3002, 0x00},
- {0x3016, 0x08},
- {0x3017, 0xe0},
- {0x3018, 0x44},
- {0x301c, 0xf8},
- {0x301d, 0xf0},
- {0x3a18, 0x00},
- {0x3a19, 0xf8},
- {0x3c01, 0x80},
- {0x3b07, 0x0c},
- {0x380c, 0x09},
- {0x380d, 0x70},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3708, 0x64},
{0x3709, 0x12},
- {0x3808, 0x07},
- {0x3809, 0x80},
- {0x380a, 0x04},
- {0x380b, 0x38},
{0x3800, 0x01},
{0x3801, 0x5c},
{0x3802, 0x01},
@@ -279,75 +273,30 @@ static struct regval_list ov5647_1080p30_10bpp[] = {
{0x3805, 0xe3},
{0x3806, 0x05},
{0x3807, 0xf1},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
{0x3811, 0x04},
{0x3813, 0x02},
- {0x3630, 0x2e},
- {0x3632, 0xe2},
- {0x3633, 0x23},
- {0x3634, 0x44},
- {0x3636, 0x06},
- {0x3620, 0x64},
- {0x3621, 0xe0},
- {0x3600, 0x37},
- {0x3704, 0xa0},
- {0x3703, 0x5a},
- {0x3715, 0x78},
- {0x3717, 0x01},
- {0x3731, 0x02},
- {0x370b, 0x60},
- {0x3705, 0x1a},
- {0x3f05, 0x02},
- {0x3f06, 0x10},
- {0x3f01, 0x0a},
- {0x3a08, 0x01},
{0x3a09, 0x4b},
{0x3a0a, 0x01},
{0x3a0b, 0x13},
{0x3a0d, 0x04},
{0x3a0e, 0x03},
- {0x3a0f, 0x58},
- {0x3a10, 0x50},
- {0x3a1b, 0x58},
- {0x3a1e, 0x50},
- {0x3a11, 0x60},
- {0x3a1f, 0x28},
- {0x4001, 0x02},
{0x4004, 0x04},
- {0x4000, 0x09},
{0x4837, 0x19},
{0x4800, 0x34},
- {0x3503, 0x03},
{0x0100, 0x01},
};
-static struct regval_list ov5647_2x2binned_10bpp[] = {
- {0x0100, 0x00},
- {0x0103, 0x01},
- {0x3034, 0x1a},
- {0x3035, 0x21},
- {0x3036, 0x62},
- {0x303c, 0x11},
- {0x3106, 0xf5},
- {0x3827, 0xec},
- {0x370c, 0x03},
+static const struct reg_sequence ov5647_2x2binned_10bpp[] = {
+ {0x3036, 0x69},
+ {0x3821, 0x03},
+ {0x3820, 0x41},
{0x3612, 0x59},
{0x3618, 0x00},
- {0x5000, 0x06},
{0x5002, 0x41},
- {0x5003, 0x08},
- {0x5a00, 0x08},
- {0x3000, 0x00},
- {0x3001, 0x00},
- {0x3002, 0x00},
- {0x3016, 0x08},
- {0x3017, 0xe0},
- {0x3018, 0x44},
- {0x301c, 0xf8},
- {0x301d, 0xf0},
- {0x3a18, 0x00},
- {0x3a19, 0xf8},
- {0x3c01, 0x80},
- {0x3b07, 0x0c},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
@@ -360,50 +309,18 @@ static struct regval_list ov5647_2x2binned_10bpp[] = {
{0x3809, 0x10},
{0x380a, 0x03},
{0x380b, 0xcc},
- {0x380c, 0x07},
- {0x380d, 0x68},
{0x3811, 0x0c},
{0x3813, 0x06},
{0x3814, 0x31},
{0x3815, 0x31},
- {0x3630, 0x2e},
- {0x3632, 0xe2},
- {0x3633, 0x23},
- {0x3634, 0x44},
- {0x3636, 0x06},
- {0x3620, 0x64},
- {0x3621, 0xe0},
- {0x3600, 0x37},
- {0x3704, 0xa0},
- {0x3703, 0x5a},
- {0x3715, 0x78},
- {0x3717, 0x01},
- {0x3731, 0x02},
- {0x370b, 0x60},
- {0x3705, 0x1a},
- {0x3f05, 0x02},
- {0x3f06, 0x10},
- {0x3f01, 0x0a},
- {0x3a08, 0x01},
{0x3a09, 0x28},
{0x3a0a, 0x00},
{0x3a0b, 0xf6},
{0x3a0d, 0x08},
{0x3a0e, 0x06},
- {0x3a0f, 0x58},
- {0x3a10, 0x50},
- {0x3a1b, 0x58},
- {0x3a1e, 0x50},
- {0x3a11, 0x60},
- {0x3a1f, 0x28},
- {0x4001, 0x02},
{0x4004, 0x04},
- {0x4000, 0x09},
{0x4837, 0x16},
{0x4800, 0x24},
- {0x3503, 0x03},
- {0x3820, 0x41},
- {0x3821, 0x07},
{0x350a, 0x00},
{0x350b, 0x10},
{0x3500, 0x00},
@@ -413,38 +330,16 @@ static struct regval_list ov5647_2x2binned_10bpp[] = {
{0x0100, 0x01},
};
-static struct regval_list ov5647_640x480_10bpp[] = {
- {0x0100, 0x00},
- {0x0103, 0x01},
- {0x3035, 0x11},
+static const struct reg_sequence ov5647_640x480_10bpp[] = {
{0x3036, 0x46},
- {0x303c, 0x11},
- {0x3821, 0x07},
+ {0x3821, 0x03},
{0x3820, 0x41},
- {0x370c, 0x03},
{0x3612, 0x59},
{0x3618, 0x00},
- {0x5000, 0x06},
- {0x5003, 0x08},
- {0x5a00, 0x08},
- {0x3000, 0xff},
- {0x3001, 0xff},
- {0x3002, 0xff},
- {0x301d, 0xf0},
- {0x3a18, 0x00},
- {0x3a19, 0xf8},
- {0x3c01, 0x80},
- {0x3b07, 0x0c},
- {0x380c, 0x07},
- {0x380d, 0x3c},
{0x3814, 0x35},
{0x3815, 0x35},
{0x3708, 0x64},
{0x3709, 0x52},
- {0x3808, 0x02},
- {0x3809, 0x80},
- {0x380a, 0x01},
- {0x380b, 0xe0},
{0x3800, 0x00},
{0x3801, 0x10},
{0x3802, 0x00},
@@ -453,53 +348,17 @@ static struct regval_list ov5647_640x480_10bpp[] = {
{0x3805, 0x2f},
{0x3806, 0x07},
{0x3807, 0x9f},
- {0x3630, 0x2e},
- {0x3632, 0xe2},
- {0x3633, 0x23},
- {0x3634, 0x44},
- {0x3620, 0x64},
- {0x3621, 0xe0},
- {0x3600, 0x37},
- {0x3704, 0xa0},
- {0x3703, 0x5a},
- {0x3715, 0x78},
- {0x3717, 0x01},
- {0x3731, 0x02},
- {0x370b, 0x60},
- {0x3705, 0x1a},
- {0x3f05, 0x02},
- {0x3f06, 0x10},
- {0x3f01, 0x0a},
- {0x3a08, 0x01},
+ {0x3808, 0x02},
+ {0x3809, 0x80},
+ {0x380a, 0x01},
+ {0x380b, 0xe0},
{0x3a09, 0x2e},
{0x3a0a, 0x00},
{0x3a0b, 0xfb},
{0x3a0d, 0x02},
{0x3a0e, 0x01},
- {0x3a0f, 0x58},
- {0x3a10, 0x50},
- {0x3a1b, 0x58},
- {0x3a1e, 0x50},
- {0x3a11, 0x60},
- {0x3a1f, 0x28},
- {0x4001, 0x02},
{0x4004, 0x02},
- {0x4000, 0x09},
- {0x3000, 0x00},
- {0x3001, 0x00},
- {0x3002, 0x00},
- {0x3017, 0xe0},
- {0x301c, 0xfc},
- {0x3636, 0x06},
- {0x3016, 0x08},
- {0x3827, 0xec},
- {0x3018, 0x44},
- {0x3035, 0x21},
- {0x3106, 0xf5},
- {0x3034, 0x1a},
- {0x301c, 0xf8},
{0x4800, 0x34},
- {0x3503, 0x03},
{0x0100, 0x01},
};
@@ -508,7 +367,7 @@ static const struct ov5647_mode ov5647_modes[] = {
{
.format = {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
+ .colorspace = V4L2_COLORSPACE_RAW,
.field = V4L2_FIELD_NONE,
.width = 2592,
.height = 1944
@@ -520,6 +379,7 @@ static const struct ov5647_mode ov5647_modes[] = {
.height = 1944
},
.pixel_rate = 87500000,
+ .link_freq_index = FREQ_INDEX_FULL,
.hts = 2844,
.vts = 0x7b0,
.reg_list = ov5647_2592x1944_10bpp,
@@ -529,7 +389,7 @@ static const struct ov5647_mode ov5647_modes[] = {
{
.format = {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
+ .colorspace = V4L2_COLORSPACE_RAW,
.field = V4L2_FIELD_NONE,
.width = 1920,
.height = 1080
@@ -540,7 +400,8 @@ static const struct ov5647_mode ov5647_modes[] = {
.width = 1928,
.height = 1080,
},
- .pixel_rate = 81666700,
+ .pixel_rate = 87500000,
+ .link_freq_index = FREQ_INDEX_FULL,
.hts = 2416,
.vts = 0x450,
.reg_list = ov5647_1080p30_10bpp,
@@ -550,7 +411,7 @@ static const struct ov5647_mode ov5647_modes[] = {
{
.format = {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
+ .colorspace = V4L2_COLORSPACE_RAW,
.field = V4L2_FIELD_NONE,
.width = 1296,
.height = 972
@@ -561,7 +422,8 @@ static const struct ov5647_mode ov5647_modes[] = {
.width = 2592,
.height = 1944,
},
- .pixel_rate = 81666700,
+ .pixel_rate = 87500000,
+ .link_freq_index = FREQ_INDEX_FULL,
.hts = 1896,
.vts = 0x59b,
.reg_list = ov5647_2x2binned_10bpp,
@@ -571,7 +433,7 @@ static const struct ov5647_mode ov5647_modes[] = {
{
.format = {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
+ .colorspace = V4L2_COLORSPACE_RAW,
.field = V4L2_FIELD_NONE,
.width = 640,
.height = 480
@@ -582,7 +444,8 @@ static const struct ov5647_mode ov5647_modes[] = {
.width = 2560,
.height = 1920,
},
- .pixel_rate = 55000000,
+ .pixel_rate = 58333000,
+ .link_freq_index = FREQ_INDEX_VGA,
.hts = 1852,
.vts = 0x1f8,
.reg_list = ov5647_640x480_10bpp,
@@ -594,109 +457,36 @@ static const struct ov5647_mode ov5647_modes[] = {
#define OV5647_DEFAULT_MODE (&ov5647_modes[3])
#define OV5647_DEFAULT_FORMAT (ov5647_modes[3].format)
-static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
-{
- unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- ret = i2c_master_send(client, data, 4);
- if (ret < 0) {
- dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
- __func__, reg);
- return ret;
- }
-
- return 0;
-}
-
-static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
-{
- unsigned char data[3] = { reg >> 8, reg & 0xff, val};
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- ret = i2c_master_send(client, data, 3);
- if (ret < 0) {
- dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
- __func__, reg);
- return ret;
- }
-
- return 0;
-}
-
-static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 buf[2] = { reg >> 8, reg & 0xff };
- struct i2c_msg msg[2];
- int ret;
-
- 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 = buf;
- msg[1].len = 1;
-
- ret = i2c_transfer(client->adapter, msg, 2);
- if (ret != 2) {
- dev_err(&client->dev, "%s: i2c read error, reg: %x = %d\n",
- __func__, reg, ret);
- return ret >= 0 ? -EINVAL : ret;
- }
-
- *val = buf[0];
-
- return 0;
-}
-
-static int ov5647_write_array(struct v4l2_subdev *sd,
- const struct regval_list *regs, int array_size)
-{
- int i, ret;
-
- for (i = 0; i < array_size; i++) {
- ret = ov5647_write(sd, regs[i].addr, regs[i].data);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
{
- u8 channel_id;
- int ret;
-
- ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id);
- if (ret < 0)
- return ret;
-
- channel_id &= ~(3 << 6);
+ struct ov5647 *sensor = to_sensor(sd);
- return ov5647_write(sd, OV5647_REG_MIPI_CTRL14,
- channel_id | (channel << 6));
+ return cci_update_bits(sensor->regmap, OV5647_REG_MIPI_CTRL14,
+ OV5647_REG_MIPI_CTRL14_CHANNEL_MASK,
+ channel << OV5647_REG_MIPI_CTRL14_CHANNEL_SHIFT,
+ NULL);
}
static int ov5647_set_mode(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5647 *sensor = to_sensor(sd);
- u8 resetval, rdval;
+ u64 resetval, rdval;
int ret;
- ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+ ret = cci_read(sensor->regmap, OV5647_SW_STANDBY, &rdval, NULL);
if (ret < 0)
return ret;
- ret = ov5647_write_array(sd, sensor->mode->reg_list,
- sensor->mode->num_regs);
+ ret = regmap_multi_reg_write(sensor->regmap, ov5647_common_regs,
+ ARRAY_SIZE(ov5647_common_regs));
+ if (ret < 0) {
+ dev_err(&client->dev, "write sensor common regs error\n");
+ return ret;
+ }
+
+ ret = regmap_multi_reg_write(sensor->regmap, sensor->mode->reg_list,
+ sensor->mode->num_regs);
if (ret < 0) {
dev_err(&client->dev, "write sensor default regs error\n");
return ret;
@@ -706,13 +496,13 @@ static int ov5647_set_mode(struct v4l2_subdev *sd)
if (ret < 0)
return ret;
- ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
+ ret = cci_read(sensor->regmap, OV5647_SW_STANDBY, &resetval, NULL);
if (ret < 0)
return ret;
if (!(resetval & 0x01)) {
dev_err(&client->dev, "Device was in SW standby");
- ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
+ ret = cci_write(sensor->regmap, OV5647_SW_STANDBY, 0x01, NULL);
if (ret < 0)
return ret;
}
@@ -720,54 +510,71 @@ static int ov5647_set_mode(struct v4l2_subdev *sd)
return 0;
}
-static int ov5647_stream_on(struct v4l2_subdev *sd)
+static int ov5647_stream_stop(struct ov5647 *sensor)
+{
+ int ret = 0;
+
+ cci_write(sensor->regmap, OV5647_REG_MIPI_CTRL00,
+ MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE |
+ MIPI_CTRL00_CLOCK_LANE_DISABLE, &ret);
+ cci_write(sensor->regmap, OV5647_REG_FRAME_OFF_NUMBER, 0x0f, &ret);
+ cci_write(sensor->regmap, OV5640_REG_PAD_OUT, 0x01, &ret);
+
+ return ret;
+}
+
+static int ov5647_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5647 *sensor = to_sensor(sd);
u8 val = MIPI_CTRL00_BUS_IDLE;
int ret;
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ return ret;
+
ret = ov5647_set_mode(sd);
if (ret) {
dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret);
- return ret;
+ goto done;
}
/* Apply customized values from user when stream starts. */
ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler);
if (ret)
- return ret;
+ goto done;
if (sensor->clock_ncont)
val |= MIPI_CTRL00_CLOCK_LANE_GATE |
MIPI_CTRL00_LINE_SYNC_ENABLE;
- ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val);
- if (ret < 0)
- return ret;
+ cci_write(sensor->regmap, OV5647_REG_MIPI_CTRL00, val, &ret);
+ cci_write(sensor->regmap, OV5647_REG_FRAME_OFF_NUMBER, 0x00, &ret);
+ cci_write(sensor->regmap, OV5640_REG_PAD_OUT, 0x00, &ret);
- ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00);
- if (ret < 0)
- return ret;
+done:
+ if (ret)
+ pm_runtime_put(&client->dev);
- return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00);
+ return ret;
}
-static int ov5647_stream_off(struct v4l2_subdev *sd)
+static int ov5647_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_sensor(sd);
int ret;
- ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00,
- MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE |
- MIPI_CTRL00_CLOCK_LANE_DISABLE);
- if (ret < 0)
- return ret;
+ ret = ov5647_stream_stop(sensor);
- ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f);
- if (ret < 0)
- return ret;
+ pm_runtime_put(&client->dev);
- return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01);
+ return ret;
}
static int ov5647_power_on(struct device *dev)
@@ -777,26 +584,35 @@ static int ov5647_power_on(struct device *dev)
dev_dbg(dev, "OV5647 power on\n");
- if (sensor->pwdn) {
- gpiod_set_value_cansleep(sensor->pwdn, 0);
- msleep(PWDN_ACTIVE_DELAY_MS);
+ ret = regulator_bulk_enable(OV5647_NUM_SUPPLIES, sensor->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
}
+ ret = gpiod_set_value_cansleep(sensor->pwdn, 0);
+ if (ret < 0) {
+ dev_err(dev, "pwdn gpio set value failed: %d\n", ret);
+ goto error_reg_disable;
+ }
+
+ msleep(PWDN_ACTIVE_DELAY_MS);
+
ret = clk_prepare_enable(sensor->xclk);
if (ret < 0) {
dev_err(dev, "clk prepare enable failed\n");
goto error_pwdn;
}
- ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs,
- ARRAY_SIZE(sensor_oe_enable_regs));
+ ret = regmap_multi_reg_write(sensor->regmap, sensor_oe_enable_regs,
+ ARRAY_SIZE(sensor_oe_enable_regs));
if (ret < 0) {
dev_err(dev, "write sensor_oe_enable_regs error\n");
goto error_clk_disable;
}
/* Stream off to coax lanes into LP-11 state. */
- ret = ov5647_stream_off(&sensor->sd);
+ ret = ov5647_stream_stop(sensor);
if (ret < 0) {
dev_err(dev, "camera not available, check power\n");
goto error_clk_disable;
@@ -808,6 +624,8 @@ error_clk_disable:
clk_disable_unprepare(sensor->xclk);
error_pwdn:
gpiod_set_value_cansleep(sensor->pwdn, 1);
+error_reg_disable:
+ regulator_bulk_disable(OV5647_NUM_SUPPLIES, sensor->supplies);
return ret;
}
@@ -815,28 +633,23 @@ error_pwdn:
static int ov5647_power_off(struct device *dev)
{
struct ov5647 *sensor = dev_get_drvdata(dev);
- u8 rdval;
int ret;
dev_dbg(dev, "OV5647 power off\n");
- ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs,
- ARRAY_SIZE(sensor_oe_disable_regs));
+ ret = regmap_multi_reg_write(sensor->regmap, sensor_oe_disable_regs,
+ ARRAY_SIZE(sensor_oe_disable_regs));
if (ret < 0)
dev_dbg(dev, "disable oe failed\n");
/* Enter software standby */
- ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval);
- if (ret < 0)
- dev_dbg(dev, "software standby failed\n");
-
- rdval &= ~0x01;
- ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval);
+ ret = cci_update_bits(sensor->regmap, OV5647_SW_STANDBY, 0x01, 0x00, NULL);
if (ret < 0)
dev_dbg(dev, "software standby failed\n");
clk_disable_unprepare(sensor->xclk);
gpiod_set_value_cansleep(sensor->pwdn, 1);
+ regulator_bulk_disable(OV5647_NUM_SUPPLIES, sensor->supplies);
return 0;
}
@@ -845,10 +658,11 @@ static int ov5647_power_off(struct device *dev)
static int ov5647_sensor_get_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
+ struct ov5647 *sensor = to_sensor(sd);
int ret;
- u8 val;
+ u64 val;
- ret = ov5647_read(sd, reg->reg & 0xff, &val);
+ ret = cci_read(sensor->regmap, reg->reg & 0xff, &val, NULL);
if (ret < 0)
return ret;
@@ -861,7 +675,9 @@ static int ov5647_sensor_get_register(struct v4l2_subdev *sd,
static int ov5647_sensor_set_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ struct ov5647 *sensor = to_sensor(sd);
+
+ return cci_write(sensor->regmap, reg->reg & 0xff, reg->val & 0xff, NULL);
}
#endif
@@ -890,49 +706,30 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647,
return NULL;
}
-static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
+static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+/*
+ * This function returns the mbus code for the current settings of the HFLIP
+ * and VFLIP controls.
+ */
+static u32 ov5647_get_mbus_code(struct v4l2_subdev *sd)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5647 *sensor = to_sensor(sd);
- int ret;
-
- mutex_lock(&sensor->lock);
-
- if (enable) {
- ret = pm_runtime_resume_and_get(&client->dev);
- if (ret < 0)
- goto error_unlock;
-
- ret = ov5647_stream_on(sd);
- if (ret < 0) {
- dev_err(&client->dev, "stream start failed: %d\n", ret);
- goto error_pm;
- }
- } else {
- ret = ov5647_stream_off(sd);
- if (ret < 0) {
- dev_err(&client->dev, "stream stop failed: %d\n", ret);
- goto error_pm;
- }
- pm_runtime_put(&client->dev);
- }
-
- mutex_unlock(&sensor->lock);
-
- return 0;
-
-error_pm:
- pm_runtime_put(&client->dev);
-error_unlock:
- mutex_unlock(&sensor->lock);
+ /* The control values are only 0 or 1. */
+ int index = sensor->hflip->val | (sensor->vflip->val << 1);
+
+ static const u32 codes[4] = {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10
+ };
- return ret;
+ return codes[index];
}
-static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
- .s_stream = ov5647_s_stream,
-};
-
static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -940,7 +737,7 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ code->code = ov5647_get_mbus_code(sd);
return 0;
}
@@ -951,7 +748,7 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
{
const struct v4l2_mbus_framefmt *fmt;
- if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10 ||
+ if (fse->code != ov5647_get_mbus_code(sd) ||
fse->index >= ARRAY_SIZE(ov5647_modes))
return -EINVAL;
@@ -972,7 +769,6 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd,
const struct v4l2_mbus_framefmt *sensor_format;
struct ov5647 *sensor = to_sensor(sd);
- mutex_lock(&sensor->lock);
switch (format->which) {
case V4L2_SUBDEV_FORMAT_TRY:
sensor_format = v4l2_subdev_state_get_format(sd_state,
@@ -984,7 +780,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd,
}
*fmt = *sensor_format;
- mutex_unlock(&sensor->lock);
+ /* The code we pass back must reflect the current h/vflips. */
+ fmt->code = ov5647_get_mbus_code(sd);
return 0;
}
@@ -1002,7 +799,6 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
fmt->width, fmt->height);
/* Update the sensor mode and apply at it at streamon time. */
- mutex_lock(&sensor->lock);
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
*v4l2_subdev_state_get_format(sd_state, format->pad) = mode->format;
} else {
@@ -1014,7 +810,8 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
mode->pixel_rate, 1, mode->pixel_rate);
hblank = mode->hts - mode->format.width;
- __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1,
+ __v4l2_ctrl_modify_range(sensor->hblank, hblank,
+ OV5647_HTS_MAX - mode->format.width, 1,
hblank);
vblank = mode->vts - mode->format.height;
@@ -1029,9 +826,12 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
sensor->exposure->minimum,
exposure_max, sensor->exposure->step,
exposure_def);
+
+ __v4l2_ctrl_s_ctrl(sensor->link_freq, mode->link_freq_index);
}
*fmt = mode->format;
- mutex_unlock(&sensor->lock);
+ /* The code we pass back must reflect the current h/vflips. */
+ fmt->code = ov5647_get_mbus_code(sd);
return 0;
}
@@ -1044,10 +844,8 @@ static int ov5647_get_selection(struct v4l2_subdev *sd,
case V4L2_SEL_TGT_CROP: {
struct ov5647 *sensor = to_sensor(sd);
- mutex_lock(&sensor->lock);
sel->r = *__ov5647_get_pad_crop(sensor, sd_state, sel->pad,
sel->which);
- mutex_unlock(&sensor->lock);
return 0;
}
@@ -1079,6 +877,8 @@ static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
.set_fmt = ov5647_set_pad_fmt,
.get_fmt = ov5647_get_pad_fmt,
.get_selection = ov5647_get_selection,
+ .enable_streams = ov5647_enable_streams,
+ .disable_streams = ov5647_disable_streams,
};
static const struct v4l2_subdev_ops ov5647_subdev_ops = {
@@ -1089,33 +889,27 @@ static const struct v4l2_subdev_ops ov5647_subdev_ops = {
static int ov5647_detect(struct v4l2_subdev *sd)
{
+ struct ov5647 *sensor = to_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 read;
+ u64 read;
int ret;
- ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
+ ret = cci_write(sensor->regmap, OV5647_SW_RESET, 0x01, NULL);
if (ret < 0)
return ret;
- ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read);
+ ret = cci_read(sensor->regmap, OV5647_REG_CHIPID, &read, NULL);
if (ret < 0)
- return ret;
-
- if (read != 0x56) {
- dev_err(&client->dev, "ID High expected 0x56 got %x", read);
- return -ENODEV;
- }
-
- ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read);
- if (ret < 0)
- return ret;
+ return dev_err_probe(&client->dev, ret,
+ "failed to read chip id %x\n",
+ OV5647_REG_CHIPID);
- if (read != 0x47) {
- dev_err(&client->dev, "ID Low expected 0x47 got %x", read);
+ if (read != OV5647_CHIP_ID) {
+ dev_err(&client->dev, "Chip ID expected 0x5647 got 0x%llx", read);
return -ENODEV;
}
- return ov5647_write(sd, OV5647_SW_RESET, 0x00);
+ return cci_write(sensor->regmap, OV5647_SW_RESET, 0x00, NULL);
}
static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
@@ -1138,74 +932,6 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
.open = ov5647_open,
};
-static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
-{
- return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
-}
-
-static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
-{
- int ret;
- u8 reg;
-
- /* Non-zero turns on AGC by clearing bit 1.*/
- ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
- if (ret)
- return ret;
-
- return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1)
- : reg | BIT(1));
-}
-
-static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
-{
- int ret;
- u8 reg;
-
- /*
- * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
- * clearing bit 0.
- */
- ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
- if (ret)
- return ret;
-
- return ov5647_write(sd, OV5647_REG_AEC_AGC,
- val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0)
- : reg & ~BIT(0));
-}
-
-static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
-{
- int ret;
-
- /* 10 bits of gain, 2 in the high register. */
- ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
- if (ret)
- return ret;
-
- return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
-}
-
-static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
-{
- int ret;
-
- /*
- * Sensor has 20 bits, but the bottom 4 bits are fractions of a line
- * which we leave as zero (and don't receive in "val").
- */
- ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
- if (ret)
- return ret;
-
- ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
- if (ret)
- return ret;
-
- return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
-}
-
static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5647 *sensor = container_of(ctrl->handler,
@@ -1214,9 +940,6 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
-
- /* v4l2_ctrl_lock() locks our own mutex */
-
if (ctrl->id == V4L2_CID_VBLANK) {
int exposure_max, exposure_def;
@@ -1239,33 +962,55 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = ov5647_s_auto_white_balance(sd, ctrl->val);
+ ret = cci_write(sensor->regmap, OV5647_REG_AWB,
+ ctrl->val ? 1 : 0, NULL);
break;
case V4L2_CID_AUTOGAIN:
- ret = ov5647_s_autogain(sd, ctrl->val);
+ /* Non-zero turns on AGC by clearing bit 1.*/
+ return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1),
+ ctrl->val ? 0 : BIT(1), NULL);
break;
case V4L2_CID_EXPOSURE_AUTO:
- ret = ov5647_s_exposure_auto(sd, ctrl->val);
+ /*
+ * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
+ * clearing bit 0.
+ */
+ return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0),
+ ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL);
break;
case V4L2_CID_ANALOGUE_GAIN:
- ret = ov5647_s_analogue_gain(sd, ctrl->val);
+ /* 10 bits of gain, 2 in the high register. */
+ return cci_write(sensor->regmap, OV5647_REG_GAIN,
+ ctrl->val & 0x3ff, NULL);
break;
case V4L2_CID_EXPOSURE:
- ret = ov5647_s_exposure(sd, ctrl->val);
+ /*
+ * Sensor has 20 bits, but the bottom 4 bits are fractions of a line
+ * which we leave as zero (and don't receive in "val").
+ */
+ ret = cci_write(sensor->regmap, OV5647_REG_EXPOSURE,
+ ctrl->val << 4, NULL);
break;
case V4L2_CID_VBLANK:
- ret = ov5647_write16(sd, OV5647_REG_VTS_HI,
- sensor->mode->format.height + ctrl->val);
+ ret = cci_write(sensor->regmap, OV5647_REG_VTS,
+ sensor->mode->format.height + ctrl->val, NULL);
+ break;
+ case V4L2_CID_HBLANK:
+ ret = cci_write(sensor->regmap, OV5647_REG_HTS,
+ sensor->mode->format.width + ctrl->val, &ret);
break;
case V4L2_CID_TEST_PATTERN:
- ret = ov5647_write(sd, OV5647_REG_ISPCTRL3D,
- ov5647_test_pattern_val[ctrl->val]);
+ ret = cci_write(sensor->regmap, OV5647_REG_ISPCTRL3D,
+ ov5647_test_pattern_val[ctrl->val], NULL);
break;
-
- /* Read-only, but we adjust it based on mode. */
- case V4L2_CID_PIXEL_RATE:
- case V4L2_CID_HBLANK:
- /* Read-only, but we adjust it based on mode. */
+ case V4L2_CID_HFLIP:
+ /* There's an in-built hflip in the sensor, so account for that here. */
+ ret = cci_update_bits(sensor->regmap, OV5647_REG_TIMING_TC_H, BIT(1),
+ ctrl->val ? 0 : BIT(1), NULL);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = cci_update_bits(sensor->regmap, OV5647_REG_TIMING_TC_V, BIT(1),
+ ctrl->val ? BIT(1) : 0, NULL);
break;
default:
@@ -1284,12 +1029,24 @@ static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
.s_ctrl = ov5647_s_ctrl,
};
+static int ov5647_configure_regulators(struct device *dev,
+ struct ov5647 *sensor)
+{
+ for (unsigned int i = 0; i < OV5647_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = ov5647_supply_names[i];
+
+ return devm_regulator_bulk_get(dev, OV5647_NUM_SUPPLIES,
+ sensor->supplies);
+}
+
static int ov5647_init_controls(struct ov5647 *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ struct v4l2_fwnode_device_properties props;
int hblank, exposure_max, exposure_def;
+ struct device *dev = &client->dev;
- v4l2_ctrl_handler_init(&sensor->ctrls, 9);
+ v4l2_ctrl_handler_init(&sensor->ctrls, 14);
v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
@@ -1314,16 +1071,17 @@ static int ov5647_init_controls(struct ov5647 *sensor)
V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32);
/* By default, PIXEL_RATE is read only, but it does change per mode */
- sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, NULL,
V4L2_CID_PIXEL_RATE,
sensor->mode->pixel_rate,
sensor->mode->pixel_rate, 1,
sensor->mode->pixel_rate);
- /* By default, HBLANK is read only, but it does change per mode. */
hblank = sensor->mode->hts - sensor->mode->format.width;
sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
- V4L2_CID_HBLANK, hblank, hblank, 1,
+ V4L2_CID_HBLANK, hblank,
+ OV5647_HTS_MAX -
+ sensor->mode->format.width, 1,
hblank);
sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
@@ -1338,11 +1096,28 @@ static int ov5647_init_controls(struct ov5647 *sensor)
ARRAY_SIZE(ov5647_test_pattern_menu) - 1,
0, 0, ov5647_test_pattern_menu);
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ sensor->link_freq =
+ v4l2_ctrl_new_int_menu(&sensor->ctrls, NULL, V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(ov5647_link_freqs) - 1,
+ sensor->mode->link_freq_index,
+ ov5647_link_freqs);
+ if (sensor->link_freq)
+ sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_fwnode_device_parse(dev, &props);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &ov5647_ctrl_ops,
+ &props);
+
if (sensor->ctrls.error)
goto handler_free;
- sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->sd.ctrl_handler = &sensor->ctrls;
return 0;
@@ -1416,25 +1191,34 @@ static int ov5647_probe(struct i2c_client *client)
return -EINVAL;
}
- mutex_init(&sensor->lock);
+ ret = ov5647_configure_regulators(dev, sensor);
+ if (ret)
+ dev_err_probe(dev, ret, "Failed to get power regulators\n");
sensor->mode = OV5647_DEFAULT_MODE;
- ret = ov5647_init_controls(sensor);
- if (ret)
- goto mutex_destroy;
-
sd = &sensor->sd;
v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
sd->internal_ops = &ov5647_subdev_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ ret = ov5647_init_controls(sensor);
+ if (ret)
+ return ret;
+
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
if (ret < 0)
goto ctrl_handler_free;
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap)) {
+ ret = dev_err_probe(dev, PTR_ERR(sensor->regmap),
+ "Failed to init CCI\n");
+ goto entity_cleanup;
+ }
+
ret = ov5647_power_on(dev);
if (ret)
goto entity_cleanup;
@@ -1443,27 +1227,35 @@ static int ov5647_probe(struct i2c_client *client)
if (ret < 0)
goto power_off;
- ret = v4l2_async_register_subdev(sd);
- if (ret < 0)
+ sd->state_lock = sensor->ctrls.lock;
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to init subdev\n");
goto power_off;
+ }
/* Enable runtime PM and turn off the device */
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev_sensor(sd);
+ if (ret < 0)
+ goto v4l2_subdev_cleanup;
+
pm_runtime_idle(dev);
dev_dbg(dev, "OmniVision OV5647 camera driver probed\n");
return 0;
+v4l2_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
power_off:
ov5647_power_off(dev);
entity_cleanup:
media_entity_cleanup(&sd->entity);
ctrl_handler_free:
v4l2_ctrl_handler_free(&sensor->ctrls);
-mutex_destroy:
- mutex_destroy(&sensor->lock);
return ret;
}
@@ -1474,11 +1266,11 @@ static void ov5647_remove(struct i2c_client *client)
struct ov5647 *sensor = to_sensor(sd);
v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sensor->sd.entity);
v4l2_ctrl_handler_free(&sensor->ctrls);
v4l2_device_unregister_subdev(sd);
pm_runtime_disable(&client->dev);
- mutex_destroy(&sensor->lock);
}
static const struct dev_pm_ops ov5647_pm_ops = {
diff --git a/drivers/media/i2c/ov6211.c b/drivers/media/i2c/ov6211.c
index e3ac5ecf27d1..034d5d57d67e 100644
--- a/drivers/media/i2c/ov6211.c
+++ b/drivers/media/i2c/ov6211.c
@@ -41,6 +41,10 @@
#define OV6211_ANALOGUE_GAIN_STEP 1
#define OV6211_ANALOGUE_GAIN_DEFAULT 160
+/* Vertical timing size */
+#define OV6211_REG_VTS CCI_REG16(0x380e)
+#define OV6211_VTS_MAX 0xffff
+
/* Test pattern */
#define OV6211_REG_PRE_ISP CCI_REG8(0x5e00)
#define OV6211_TEST_PATTERN_ENABLE BIT(7)
@@ -89,6 +93,8 @@ struct ov6211 {
struct v4l2_subdev sd;
struct media_pad pad;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
struct v4l2_ctrl_handler ctrl_handler;
/* Saved register values */
@@ -167,8 +173,6 @@ static const struct cci_reg_sequence ov6211_400x400_120fps_mode[] = {
{ CCI_REG8(0x380b), 0x90 },
{ CCI_REG8(0x380c), 0x05 }, /* horizontal timing size */
{ CCI_REG8(0x380d), 0xf2 },
- { CCI_REG8(0x380e), 0x01 }, /* vertical timing size */
- { CCI_REG8(0x380f), 0xb6 },
{ CCI_REG8(0x3810), 0x00 },
{ CCI_REG8(0x3811), 0x04 },
{ CCI_REG8(0x3812), 0x00 },
@@ -251,8 +255,25 @@ static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov6211 *ov6211 = container_of(ctrl->handler, struct ov6211,
ctrl_handler);
+ const struct ov6211_mode *mode = &supported_modes[0];
+ s64 exposure_max;
int ret;
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = ctrl->val + mode->height -
+ OV6211_EXPOSURE_MAX_MARGIN;
+ ret = __v4l2_ctrl_modify_range(ov6211->exposure,
+ ov6211->exposure->minimum,
+ exposure_max,
+ ov6211->exposure->step,
+ ov6211->exposure->default_value);
+ if (ret)
+ return ret;
+ }
+
/* V4L2 controls are applied, when sensor is powered up for streaming */
if (!pm_runtime_get_if_active(ov6211->dev))
return 0;
@@ -266,6 +287,10 @@ static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl)
ret = cci_write(ov6211->regmap, OV6211_REG_EXPOSURE,
ctrl->val << 4, NULL);
break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(ov6211->regmap, OV6211_REG_VTS,
+ ctrl->val + mode->height, NULL);
+ break;
case V4L2_CID_TEST_PATTERN:
ret = ov6211_set_test_pattern(ov6211, ctrl->val);
break;
@@ -287,8 +312,8 @@ static int ov6211_init_controls(struct ov6211 *ov6211)
{
struct v4l2_ctrl_handler *ctrl_hdlr = &ov6211->ctrl_handler;
const struct ov6211_mode *mode = &supported_modes[0];
+ s64 exposure_max, pixel_rate, h_blank, v_blank;
struct v4l2_fwnode_device_properties props;
- s64 exposure_max, pixel_rate, h_blank;
struct v4l2_ctrl *ctrl;
int ret;
@@ -311,24 +336,24 @@ static int ov6211_init_controls(struct ov6211 *ov6211)
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_VBLANK,
- mode->vts - mode->height,
- mode->vts - mode->height, 1,
- mode->vts - mode->height);
- if (ctrl)
- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v_blank = mode->vts - mode->height;
+ ov6211->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops,
+ V4L2_CID_VBLANK, v_blank,
+ OV6211_VTS_MAX - mode->height, 1,
+ v_blank);
v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OV6211_ANALOGUE_GAIN_MIN, OV6211_ANALOGUE_GAIN_MAX,
OV6211_ANALOGUE_GAIN_STEP,
OV6211_ANALOGUE_GAIN_DEFAULT);
- exposure_max = (mode->vts - OV6211_EXPOSURE_MAX_MARGIN);
- v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops,
- V4L2_CID_EXPOSURE,
- OV6211_EXPOSURE_MIN, exposure_max,
- OV6211_EXPOSURE_STEP,
- OV6211_EXPOSURE_DEFAULT);
+ exposure_max = mode->vts - OV6211_EXPOSURE_MAX_MARGIN;
+ ov6211->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV6211_EXPOSURE_MIN,
+ exposure_max,
+ OV6211_EXPOSURE_STEP,
+ OV6211_EXPOSURE_DEFAULT);
v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov6211_ctrl_ops,
V4L2_CID_TEST_PATTERN,
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 3e24d88f603c..ded9b2044ff8 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/math.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -37,6 +38,29 @@
#define OV9282_REG_ID 0x300a
#define OV9282_ID 0x9281
+/* Output enable registers */
+#define OV9282_REG_OUTPUT_ENABLE4 0x3004
+#define OV9282_OUTPUT_ENABLE4_GPIO2 BIT(1)
+#define OV9282_OUTPUT_ENABLE4_D9 BIT(0)
+
+#define OV9282_REG_OUTPUT_ENABLE5 0x3005
+#define OV9282_OUTPUT_ENABLE5_D8 BIT(7)
+#define OV9282_OUTPUT_ENABLE5_D7 BIT(6)
+#define OV9282_OUTPUT_ENABLE5_D6 BIT(5)
+#define OV9282_OUTPUT_ENABLE5_D5 BIT(4)
+#define OV9282_OUTPUT_ENABLE5_D4 BIT(3)
+#define OV9282_OUTPUT_ENABLE5_D3 BIT(2)
+#define OV9282_OUTPUT_ENABLE5_D2 BIT(1)
+#define OV9282_OUTPUT_ENABLE5_D1 BIT(0)
+
+#define OV9282_REG_OUTPUT_ENABLE6 0x3006
+#define OV9282_OUTPUT_ENABLE6_D0 BIT(7)
+#define OV9282_OUTPUT_ENABLE6_PCLK BIT(6)
+#define OV9282_OUTPUT_ENABLE6_HREF BIT(5)
+#define OV9282_OUTPUT_ENABLE6_STROBE BIT(3)
+#define OV9282_OUTPUT_ENABLE6_ILPWM BIT(2)
+#define OV9282_OUTPUT_ENABLE6_VSYNC BIT(1)
+
/* Exposure control */
#define OV9282_REG_EXPOSURE 0x3500
#define OV9282_EXPOSURE_MIN 1
@@ -74,6 +98,10 @@
#define OV9282_REG_MIPI_CTRL00 0x4800
#define OV9282_GATED_CLOCK BIT(5)
+/* Flash/Strobe control registers */
+#define OV9282_REG_STROBE_FRAME_SPAN 0x3925
+#define OV9282_STROBE_FRAME_SPAN_DEFAULT 0x0000001a
+
/* Input clock rate */
#define OV9282_INCLK_RATE 24000000
@@ -101,6 +129,8 @@
#define OV9282_REG_MIN 0x00
#define OV9282_REG_MAX 0xfffff
+#define OV9282_STROBE_SPAN_FACTOR 192
+
static const char * const ov9282_supply_names[] = {
"avdd", /* Analog power */
"dovdd", /* Digital I/O power */
@@ -169,6 +199,7 @@ struct ov9282_mode {
* @exp_ctrl: Pointer to exposure control
* @again_ctrl: Pointer to analog gain control
* @pixel_rate: Pointer to pixel rate control
+ * @flash_duration: Pointer to flash duration control
* @vblank: Vertical blanking in lines
* @noncontinuous_clock: Selection of CSI2 noncontinuous clock mode
* @cur_mode: Pointer to current selected sensor mode
@@ -191,6 +222,7 @@ struct ov9282 {
struct v4l2_ctrl *again_ctrl;
};
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *flash_duration;
u32 vblank;
bool noncontinuous_clock;
const struct ov9282_mode *cur_mode;
@@ -213,9 +245,9 @@ static const struct ov9282_reg common_regs[] = {
{0x0302, 0x32},
{0x030e, 0x02},
{0x3001, 0x00},
- {0x3004, 0x00},
- {0x3005, 0x00},
- {0x3006, 0x04},
+ {OV9282_REG_OUTPUT_ENABLE4, 0x00},
+ {OV9282_REG_OUTPUT_ENABLE5, 0x00},
+ {OV9282_REG_OUTPUT_ENABLE6, OV9282_OUTPUT_ENABLE6_ILPWM},
{0x3011, 0x0a},
{0x3013, 0x18},
{0x301c, 0xf0},
@@ -582,6 +614,15 @@ static int ov9282_update_controls(struct ov9282 *ov9282,
mode->vblank_max, 1, mode->vblank);
}
+static u32 ov9282_exposure_to_us(struct ov9282 *ov9282, u32 exposure)
+{
+ /* calculate exposure time in µs */
+ u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val;
+ u32 trow_us = frame_width / (ov9282->pixel_rate->val / 1000000UL);
+
+ return exposure * trow_us;
+}
+
/**
* ov9282_update_exp_gain() - Set updated exposure and gain
* @ov9282: pointer to ov9282 device
@@ -593,9 +634,10 @@ static int ov9282_update_controls(struct ov9282 *ov9282,
static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain)
{
int ret;
+ u32 exposure_us = ov9282_exposure_to_us(ov9282, exposure);
- dev_dbg(ov9282->dev, "Set exp %u, analog gain %u",
- exposure, gain);
+ dev_dbg(ov9282->dev, "Set exp %u (~%u us), analog gain %u",
+ exposure, exposure_us, gain);
ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1);
if (ret)
@@ -606,6 +648,12 @@ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain)
goto error_release_group_hold;
ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain);
+ if (ret)
+ goto error_release_group_hold;
+
+ ret = __v4l2_ctrl_modify_range(ov9282->flash_duration,
+ 0, exposure_us, 1,
+ OV9282_STROBE_FRAME_SPAN_DEFAULT);
error_release_group_hold:
ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0);
@@ -647,6 +695,75 @@ static int ov9282_set_ctrl_vflip(struct ov9282 *ov9282, int value)
current_val);
}
+static int ov9282_set_ctrl_flash_strobe_oe(struct ov9282 *ov9282, bool enable)
+{
+ u32 current_val;
+ int ret;
+
+ ret = ov9282_read_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, &current_val);
+ if (ret)
+ return ret;
+
+ if (enable)
+ current_val |= OV9282_OUTPUT_ENABLE6_STROBE;
+ else
+ current_val &= ~OV9282_OUTPUT_ENABLE6_STROBE;
+
+ return ov9282_write_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, current_val);
+}
+
+static u32 ov9282_us_to_flash_duration(struct ov9282 *ov9282, u32 value)
+{
+ /*
+ * Calculate "strobe_frame_span" increments from a given value (µs).
+ * This is quite tricky as "The step width of shift and span is
+ * programmable under system clock domain.", but it's not documented
+ * how to program this step width (at least in the datasheet available
+ * to the author at time of writing).
+ * The formula below is interpolated from different modes/framerates
+ * and should work quite well for most settings.
+ */
+ u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val;
+
+ return value * OV9282_STROBE_SPAN_FACTOR / frame_width;
+}
+
+static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value)
+{
+ /*
+ * Calculate back to microseconds from "strobe_frame_span" increments.
+ * As the calculation in ov9282_us_to_flash_duration uses an integer
+ * divison round up here.
+ */
+ u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val;
+
+ return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR);
+}
+
+static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value)
+{
+ u32 val = ov9282_us_to_flash_duration(ov9282, value);
+ int ret;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN, 1,
+ (val >> 24) & 0xff);
+ if (ret)
+ return ret;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 1, 1,
+ (val >> 16) & 0xff);
+ if (ret)
+ return ret;
+
+ ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 2, 1,
+ (val >> 8) & 0xff);
+ if (ret)
+ return ret;
+
+ return ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 3, 1,
+ val & 0xff);
+}
+
/**
* ov9282_set_ctrl() - Set subdevice control
* @ctrl: pointer to v4l2_ctrl structure
@@ -713,6 +830,12 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_HTS, 2,
(ctrl->val + ov9282->cur_mode->width) >> 1);
break;
+ case V4L2_CID_FLASH_STROBE_OE:
+ ret = ov9282_set_ctrl_flash_strobe_oe(ov9282, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_DURATION:
+ ret = ov9282_set_ctrl_flash_duration(ov9282, ctrl->val);
+ break;
default:
dev_err(ov9282->dev, "Invalid control %d", ctrl->id);
ret = -EINVAL;
@@ -723,9 +846,36 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
return ret;
}
+static int ov9282_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9282 *ov9282 =
+ container_of_const(ctrl->handler, struct ov9282, ctrl_handler);
+
+ if (ctrl->id == V4L2_CID_FLASH_DURATION) {
+ u32 us = ctrl->val;
+ u32 fd = ov9282_us_to_flash_duration(ov9282, us);
+
+ /* get nearest strobe_duration value */
+ u32 us0 = ov9282_flash_duration_to_us(ov9282, fd);
+ u32 us1 = ov9282_flash_duration_to_us(ov9282, fd + 1);
+
+ if (abs(us1 - us) < abs(us - us0))
+ ctrl->val = us1;
+ else
+ ctrl->val = us0;
+
+ if (us != ctrl->val)
+ dev_dbg(ov9282->dev, "using next valid strobe_duration %u instead of %u\n",
+ ctrl->val, us);
+ }
+
+ return 0;
+}
+
/* V4l2 subdevice control ops*/
static const struct v4l2_ctrl_ops ov9282_ctrl_ops = {
.s_ctrl = ov9282_set_ctrl,
+ .try_ctrl = ov9282_try_ctrl,
};
/**
@@ -1299,10 +1449,11 @@ static int ov9282_init_controls(struct ov9282 *ov9282)
const struct ov9282_mode *mode = ov9282->cur_mode;
struct v4l2_fwnode_device_properties props;
u32 hblank_min;
+ u32 exposure_us;
u32 lpfr;
int ret;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
if (ret)
return ret;
@@ -1367,6 +1518,16 @@ static int ov9282_init_controls(struct ov9282 *ov9282)
OV9282_TIMING_HTS_MAX - mode->width,
1, hblank_min);
+ /* Flash/Strobe controls */
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_OE, 0, 1, 1, 0);
+
+ exposure_us = ov9282_exposure_to_us(ov9282, OV9282_EXPOSURE_DEFAULT);
+ ov9282->flash_duration =
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops,
+ V4L2_CID_FLASH_DURATION, 0, exposure_us, 1,
+ OV9282_STROBE_FRAME_SPAN_DEFAULT);
+
ret = v4l2_fwnode_device_parse(ov9282->dev, &props);
if (!ret) {
/* Failure sets ctrl_hdlr->error, which we check afterwards anyway */
diff --git a/drivers/media/i2c/s5k3m5.c b/drivers/media/i2c/s5k3m5.c
new file mode 100644
index 000000000000..c591b580d2e7
--- /dev/null
+++ b/drivers/media/i2c/s5k3m5.c
@@ -0,0 +1,1377 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Linaro Ltd
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define S5K3M5_LINK_FREQ_602P5MHZ (602500ULL * HZ_PER_KHZ)
+#define S5K3M5_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
+#define S5K3M5_DATA_LANES 4
+
+/* Register map is similar to MIPI CCS compliant camera sensors */
+#define S5K3M5_REG_CHIP_ID CCI_REG16(0x0000)
+#define S5K3M5_CHIP_ID 0x30d5
+
+/* Both streaming and flipping settings are controlled at the same time */
+#define S5K3M5_REG_CTRL_MODE CCI_REG16(0x0100)
+#define S5K3M5_MODE_STREAMING BIT(8)
+#define S5K3M5_VFLIP BIT(1)
+#define S5K3M5_HFLIP BIT(0)
+
+#define S5K3M5_REG_EXPOSURE CCI_REG16(0x0202)
+#define S5K3M5_EXPOSURE_MIN 8
+#define S5K3M5_EXPOSURE_STEP 1
+#define S5K3M5_EXPOSURE_MARGIN 4
+
+#define S5K3M5_REG_AGAIN CCI_REG16(0x0204)
+#define S5K3M5_AGAIN_MIN 1
+#define S5K3M5_AGAIN_MAX 16
+#define S5K3M5_AGAIN_STEP 1
+#define S5K3M5_AGAIN_DEFAULT 1
+#define S5K3M5_AGAIN_SHIFT 5
+
+#define S5K3M5_REG_VTS CCI_REG16(0x0340)
+#define S5K3M5_VTS_MAX 0xffff
+
+#define S5K3M5_REG_HTS CCI_REG16(0x0342)
+#define S5K3M5_REG_X_ADDR_START CCI_REG16(0x0344)
+#define S5K3M5_REG_Y_ADDR_START CCI_REG16(0x0346)
+#define S5K3M5_REG_X_ADDR_END CCI_REG16(0x0348)
+#define S5K3M5_REG_Y_ADDR_END CCI_REG16(0x034a)
+#define S5K3M5_REG_X_OUTPUT_SIZE CCI_REG16(0x034c)
+#define S5K3M5_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e)
+
+#define S5K3M5_REG_TEST_PATTERN CCI_REG16(0x0600)
+
+#define to_s5k3m5(_sd) container_of(_sd, struct s5k3m5, sd)
+
+static const s64 s5k3m5_link_freq_menu[] = {
+ S5K3M5_LINK_FREQ_602P5MHZ,
+};
+
+/* List of supported formats to cover horizontal and vertical flip controls */
+static const u32 s5k3m5_mbus_formats[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
+};
+
+struct s5k3m5_reg_list {
+ const struct cci_reg_sequence *regs;
+ unsigned int num_regs;
+};
+
+struct s5k3m5_mode {
+ u32 width; /* Frame width in pixels */
+ u32 height; /* Frame height in pixels */
+ u32 hts; /* Horizontal timing size */
+ u32 vts; /* Default vertical timing size */
+ u32 exposure; /* Default exposure value */
+
+ const struct s5k3m5_reg_list reg_list; /* Sensor register setting */
+};
+
+static const char * const s5k3m5_test_pattern_menu[] = {
+ "Disabled",
+ "Solid colour",
+ "Colour bars",
+ "Fade to grey colour bars",
+ "PN9",
+};
+
+static const char * const s5k3m5_supply_names[] = {
+ "afvdd", /* Autofocus power */
+ "vdda", /* Analog power */
+ "vddd", /* Digital core power */
+ "vddio", /* Digital I/O power */
+};
+
+#define S5K3M5_NUM_SUPPLIES ARRAY_SIZE(s5k3m5_supply_names)
+
+struct s5k3m5 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[S5K3M5_NUM_SUPPLIES];
+
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hflip;
+
+ const struct s5k3m5_mode *mode;
+};
+
+static const struct cci_reg_sequence burst_array_setting[] = {
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0549 },
+ { CCI_REG16(0x6f12), 0x0448 },
+ { CCI_REG16(0x6f12), 0x054a },
+ { CCI_REG16(0x6f12), 0xc1f8 },
+ { CCI_REG16(0x6f12), 0xc804 },
+ { CCI_REG16(0x6f12), 0x101a },
+ { CCI_REG16(0x6f12), 0xa1f8 },
+ { CCI_REG16(0x6f12), 0xcc04 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x1bb9 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x4210 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x2e50 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x7000 },
+ { CCI_REG16(0x6f12), 0x10b5 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x4ff9 },
+ { CCI_REG16(0x6f12), 0x9949 },
+ { CCI_REG16(0x6f12), 0x0120 },
+ { CCI_REG16(0x6f12), 0x0880 },
+ { CCI_REG16(0x6f12), 0x10bd },
+ { CCI_REG16(0x6f12), 0x2de9 },
+ { CCI_REG16(0x6f12), 0xf041 },
+ { CCI_REG16(0x6f12), 0x974c },
+ { CCI_REG16(0x6f12), 0x954f },
+ { CCI_REG16(0x6f12), 0x0026 },
+ { CCI_REG16(0x6f12), 0xb4f8 },
+ { CCI_REG16(0x6f12), 0x6a52 },
+ { CCI_REG16(0x6f12), 0x3888 },
+ { CCI_REG16(0x6f12), 0x08b1 },
+ { CCI_REG16(0x6f12), 0xa4f8 },
+ { CCI_REG16(0x6f12), 0x6a62 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x43f9 },
+ { CCI_REG16(0x6f12), 0x3e80 },
+ { CCI_REG16(0x6f12), 0xa4f8 },
+ { CCI_REG16(0x6f12), 0x6a52 },
+ { CCI_REG16(0x6f12), 0xbde8 },
+ { CCI_REG16(0x6f12), 0xf081 },
+ { CCI_REG16(0x6f12), 0x2de9 },
+ { CCI_REG16(0x6f12), 0xf041 },
+ { CCI_REG16(0x6f12), 0x0746 },
+ { CCI_REG16(0x6f12), 0x8c48 },
+ { CCI_REG16(0x6f12), 0x0e46 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0x4068 },
+ { CCI_REG16(0x6f12), 0x84b2 },
+ { CCI_REG16(0x6f12), 0x050c },
+ { CCI_REG16(0x6f12), 0x2146 },
+ { CCI_REG16(0x6f12), 0x2846 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x36f9 },
+ { CCI_REG16(0x6f12), 0x3146 },
+ { CCI_REG16(0x6f12), 0x3846 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x37f9 },
+ { CCI_REG16(0x6f12), 0x874f },
+ { CCI_REG16(0x6f12), 0x4df2 },
+ { CCI_REG16(0x6f12), 0x0c26 },
+ { CCI_REG16(0x6f12), 0x4ff4 },
+ { CCI_REG16(0x6f12), 0x8061 },
+ { CCI_REG16(0x6f12), 0x3a78 },
+ { CCI_REG16(0x6f12), 0x3046 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x29f9 },
+ { CCI_REG16(0x6f12), 0x7878 },
+ { CCI_REG16(0x6f12), 0xb8b3 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0x8021 },
+ { CCI_REG16(0x6f12), 0x3046 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x22f9 },
+ { CCI_REG16(0x6f12), 0x8048 },
+ { CCI_REG16(0x6f12), 0x0088 },
+ { CCI_REG16(0x6f12), 0x804b },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x5c02 },
+ { CCI_REG16(0x6f12), 0x7e48 },
+ { CCI_REG16(0x6f12), 0x001d },
+ { CCI_REG16(0x6f12), 0x0088 },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x5e02 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0x5c02 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0x5e12 },
+ { CCI_REG16(0x6f12), 0x4218 },
+ { CCI_REG16(0x6f12), 0x02d0 },
+ { CCI_REG16(0x6f12), 0x8002 },
+ { CCI_REG16(0x6f12), 0xb0fb },
+ { CCI_REG16(0x6f12), 0xf2f2 },
+ { CCI_REG16(0x6f12), 0x91b2 },
+ { CCI_REG16(0x6f12), 0x784a },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x6012 },
+ { CCI_REG16(0x6f12), 0xb2f8 },
+ { CCI_REG16(0x6f12), 0x1602 },
+ { CCI_REG16(0x6f12), 0xb2f8 },
+ { CCI_REG16(0x6f12), 0x1422 },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x9805 },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x9a25 },
+ { CCI_REG16(0x6f12), 0x8018 },
+ { CCI_REG16(0x6f12), 0x04d0 },
+ { CCI_REG16(0x6f12), 0x9202 },
+ { CCI_REG16(0x6f12), 0xb2fb },
+ { CCI_REG16(0x6f12), 0xf0f0 },
+ { CCI_REG16(0x6f12), 0xa3f8 },
+ { CCI_REG16(0x6f12), 0x9c05 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0x9c05 },
+ { CCI_REG16(0x6f12), 0x0a18 },
+ { CCI_REG16(0x6f12), 0x01fb },
+ { CCI_REG16(0x6f12), 0x1020 },
+ { CCI_REG16(0x6f12), 0x40f3 },
+ { CCI_REG16(0x6f12), 0x9510 },
+ { CCI_REG16(0x6f12), 0x1028 },
+ { CCI_REG16(0x6f12), 0x06dc },
+ { CCI_REG16(0x6f12), 0x0028 },
+ { CCI_REG16(0x6f12), 0x05da },
+ { CCI_REG16(0x6f12), 0x0020 },
+ { CCI_REG16(0x6f12), 0x03e0 },
+ { CCI_REG16(0x6f12), 0xffe7 },
+ { CCI_REG16(0x6f12), 0x0122 },
+ { CCI_REG16(0x6f12), 0xc5e7 },
+ { CCI_REG16(0x6f12), 0x1020 },
+ { CCI_REG16(0x6f12), 0x6849 },
+ { CCI_REG16(0x6f12), 0x0880 },
+ { CCI_REG16(0x6f12), 0x2146 },
+ { CCI_REG16(0x6f12), 0x2846 },
+ { CCI_REG16(0x6f12), 0xbde8 },
+ { CCI_REG16(0x6f12), 0xf041 },
+ { CCI_REG16(0x6f12), 0x0122 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0xe2b8 },
+ { CCI_REG16(0x6f12), 0xf0b5 },
+ { CCI_REG16(0x6f12), 0x644c },
+ { CCI_REG16(0x6f12), 0xdde9 },
+ { CCI_REG16(0x6f12), 0x0565 },
+ { CCI_REG16(0x6f12), 0x08b1 },
+ { CCI_REG16(0x6f12), 0x2788 },
+ { CCI_REG16(0x6f12), 0x0760 },
+ { CCI_REG16(0x6f12), 0x09b1 },
+ { CCI_REG16(0x6f12), 0x6088 },
+ { CCI_REG16(0x6f12), 0x0860 },
+ { CCI_REG16(0x6f12), 0x12b1 },
+ { CCI_REG16(0x6f12), 0xa088 },
+ { CCI_REG16(0x6f12), 0x401c },
+ { CCI_REG16(0x6f12), 0x1060 },
+ { CCI_REG16(0x6f12), 0x0bb1 },
+ { CCI_REG16(0x6f12), 0xe088 },
+ { CCI_REG16(0x6f12), 0x1860 },
+ { CCI_REG16(0x6f12), 0x0eb1 },
+ { CCI_REG16(0x6f12), 0xa07b },
+ { CCI_REG16(0x6f12), 0x3060 },
+ { CCI_REG16(0x6f12), 0x002d },
+ { CCI_REG16(0x6f12), 0x01d0 },
+ { CCI_REG16(0x6f12), 0xe07b },
+ { CCI_REG16(0x6f12), 0x2860 },
+ { CCI_REG16(0x6f12), 0xf0bd },
+ { CCI_REG16(0x6f12), 0x70b5 },
+ { CCI_REG16(0x6f12), 0x0646 },
+ { CCI_REG16(0x6f12), 0x5048 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0x8068 },
+ { CCI_REG16(0x6f12), 0x84b2 },
+ { CCI_REG16(0x6f12), 0x050c },
+ { CCI_REG16(0x6f12), 0x2146 },
+ { CCI_REG16(0x6f12), 0x2846 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0xbef8 },
+ { CCI_REG16(0x6f12), 0x3046 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0xc5f8 },
+ { CCI_REG16(0x6f12), 0x5248 },
+ { CCI_REG16(0x6f12), 0x0368 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0x7401 },
+ { CCI_REG16(0x6f12), 0x010a },
+ { CCI_REG16(0x6f12), 0x5048 },
+ { CCI_REG16(0x6f12), 0x4268 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x5010 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x7511 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x5210 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0x7811 },
+ { CCI_REG16(0x6f12), 0x090a },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x5810 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x7911 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x5a10 },
+ { CCI_REG16(0x6f12), 0x33f8 },
+ { CCI_REG16(0x6f12), 0xf01f },
+ { CCI_REG16(0x6f12), 0x0068 },
+ { CCI_REG16(0x6f12), 0x090a },
+ { CCI_REG16(0x6f12), 0x00f8 },
+ { CCI_REG16(0x6f12), 0xce1f },
+ { CCI_REG16(0x6f12), 0x5978 },
+ { CCI_REG16(0x6f12), 0x8170 },
+ { CCI_REG16(0x6f12), 0x5988 },
+ { CCI_REG16(0x6f12), 0x090a },
+ { CCI_REG16(0x6f12), 0x0171 },
+ { CCI_REG16(0x6f12), 0xd978 },
+ { CCI_REG16(0x6f12), 0x8171 },
+ { CCI_REG16(0x6f12), 0x988c },
+ { CCI_REG16(0x6f12), 0x000a },
+ { CCI_REG16(0x6f12), 0x9074 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x2500 },
+ { CCI_REG16(0x6f12), 0x1075 },
+ { CCI_REG16(0x6f12), 0xd88c },
+ { CCI_REG16(0x6f12), 0x000a },
+ { CCI_REG16(0x6f12), 0x9075 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x2700 },
+ { CCI_REG16(0x6f12), 0x1076 },
+ { CCI_REG16(0x6f12), 0xb3f8 },
+ { CCI_REG16(0x6f12), 0xb000 },
+ { CCI_REG16(0x6f12), 0x000a },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x7e00 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0xb100 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x8000 },
+ { CCI_REG16(0x6f12), 0x2f48 },
+ { CCI_REG16(0x6f12), 0x90f8 },
+ { CCI_REG16(0x6f12), 0xb313 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x8210 },
+ { CCI_REG16(0x6f12), 0x90f8 },
+ { CCI_REG16(0x6f12), 0xb103 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x8400 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0xb400 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x8600 },
+ { CCI_REG16(0x6f12), 0x0020 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x8800 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x6211 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x9610 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0112 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0x9e10 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0212 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xa010 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xa200 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xa400 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0512 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xa610 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0612 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xa810 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0712 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xaa10 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xac00 },
+ { CCI_REG16(0x6f12), 0x5a20 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xad00 },
+ { CCI_REG16(0x6f12), 0x93f8 },
+ { CCI_REG16(0x6f12), 0x0902 },
+ { CCI_REG16(0x6f12), 0x82f8 },
+ { CCI_REG16(0x6f12), 0xae00 },
+ { CCI_REG16(0x6f12), 0x2146 },
+ { CCI_REG16(0x6f12), 0x2846 },
+ { CCI_REG16(0x6f12), 0xbde8 },
+ { CCI_REG16(0x6f12), 0x7040 },
+ { CCI_REG16(0x6f12), 0x0122 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x47b8 },
+ { CCI_REG16(0x6f12), 0x10b5 },
+ { CCI_REG16(0x6f12), 0x0f4c },
+ { CCI_REG16(0x6f12), 0x0020 },
+ { CCI_REG16(0x6f12), 0x2080 },
+ { CCI_REG16(0x6f12), 0xaff2 },
+ { CCI_REG16(0x6f12), 0x3321 },
+ { CCI_REG16(0x6f12), 0x1748 },
+ { CCI_REG16(0x6f12), 0x0161 },
+ { CCI_REG16(0x6f12), 0xaff2 },
+ { CCI_REG16(0x6f12), 0x2d21 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0x8161 },
+ { CCI_REG16(0x6f12), 0xaff2 },
+ { CCI_REG16(0x6f12), 0x1121 },
+ { CCI_REG16(0x6f12), 0x1448 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x45f8 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0xaff2 },
+ { CCI_REG16(0x6f12), 0x2911 },
+ { CCI_REG16(0x6f12), 0x6060 },
+ { CCI_REG16(0x6f12), 0x1148 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x3ef8 },
+ { CCI_REG16(0x6f12), 0x0022 },
+ { CCI_REG16(0x6f12), 0xaff2 },
+ { CCI_REG16(0x6f12), 0x6b11 },
+ { CCI_REG16(0x6f12), 0xa060 },
+ { CCI_REG16(0x6f12), 0x0f48 },
+ { CCI_REG16(0x6f12), 0x00f0 },
+ { CCI_REG16(0x6f12), 0x37f8 },
+ { CCI_REG16(0x6f12), 0xe060 },
+ { CCI_REG16(0x6f12), 0x10bd },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x4200 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x2e50 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x41d0 },
+ { CCI_REG16(0x6f12), 0x4000 },
+ { CCI_REG16(0x6f12), 0x9404 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x38e0 },
+ { CCI_REG16(0x6f12), 0x4000 },
+ { CCI_REG16(0x6f12), 0xd000 },
+ { CCI_REG16(0x6f12), 0x4000 },
+ { CCI_REG16(0x6f12), 0xa410 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x2c66 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x0890 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x3620 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x0840 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x6f12), 0x020d },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x67cd },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x3ae1 },
+ { CCI_REG16(0x6f12), 0x45f6 },
+ { CCI_REG16(0x6f12), 0x250c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x45f6 },
+ { CCI_REG16(0x6f12), 0xf31c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x4af2 },
+ { CCI_REG16(0x6f12), 0xd74c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x40f2 },
+ { CCI_REG16(0x6f12), 0x0d2c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x010c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x46f2 },
+ { CCI_REG16(0x6f12), 0xcd7c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x4af6 },
+ { CCI_REG16(0x6f12), 0xe75c },
+ { CCI_REG16(0x6f12), 0xc0f2 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x6f12), 0x6047 },
+ { CCI_REG16(0x6f12), 0x30d5 },
+ { CCI_REG16(0x6f12), 0x00fc },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x000e },
+};
+
+static const struct cci_reg_sequence init_array_setting[] = {
+ { CCI_REG16(0x602a), 0x41d0 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x1662 },
+ { CCI_REG16(0x6f12), 0x1e00 },
+ { CCI_REG16(0x602a), 0x1c9a },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0ff2 },
+ { CCI_REG16(0x6f12), 0x0020 },
+ { CCI_REG16(0x602a), 0x0ef6 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x23b2 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x602a), 0x0fe4 },
+ { CCI_REG16(0x6f12), 0x0107 },
+ { CCI_REG16(0x6f12), 0x07d0 },
+ { CCI_REG16(0x602a), 0x12f8 },
+ { CCI_REG16(0x6f12), 0x3d09 },
+ { CCI_REG16(0x602a), 0x0e18 },
+ { CCI_REG16(0x6f12), 0x0040 },
+ { CCI_REG16(0x602a), 0x1066 },
+ { CCI_REG16(0x6f12), 0x000c },
+ { CCI_REG16(0x602a), 0x13de },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x12f2 },
+ { CCI_REG16(0x6f12), 0x0f0f },
+ { CCI_REG16(0x602a), 0x13dc },
+ { CCI_REG16(0x6f12), 0x806f },
+ { CCI_REG16(0xf46e), 0x00c3 },
+ { CCI_REG16(0xf46c), 0xbfa0 },
+ { CCI_REG16(0xf44a), 0x0007 },
+ { CCI_REG16(0xf456), 0x000a },
+ { CCI_REG16(0x6028), 0x2000 },
+ { CCI_REG16(0x602a), 0x12f6 },
+ { CCI_REG16(0x6f12), 0x7008 },
+ { CCI_REG16(0x0bc6), 0x0000 },
+ { CCI_REG16(0x0b36), 0x0001 },
+};
+
+static const struct cci_reg_sequence s5k3m5_4208x3120_30fps_mode[] = {
+ { CCI_REG16(0x6214), 0x7971 },
+ { CCI_REG16(0x6218), 0x7150 },
+ { S5K3M5_REG_X_ADDR_START, 0x0000 },
+ { S5K3M5_REG_Y_ADDR_START, 0x0008 },
+ { S5K3M5_REG_X_ADDR_END, 0x1077 },
+ { S5K3M5_REG_Y_ADDR_END, 0x0c37 },
+ { S5K3M5_REG_X_OUTPUT_SIZE, 0x1070 },
+ { S5K3M5_REG_Y_OUTPUT_SIZE, 0x0c30 },
+ { S5K3M5_REG_VTS, 0x0cf2 },
+ { S5K3M5_REG_HTS, 0x12f0 },
+
+ { CCI_REG16(0x0900), 0x0011 },
+ { CCI_REG16(0x0380), 0x0001 },
+ { CCI_REG16(0x0382), 0x0001 },
+ { CCI_REG16(0x0384), 0x0001 },
+ { CCI_REG16(0x0386), 0x0001 },
+ { CCI_REG16(0x0402), 0x1010 },
+ { CCI_REG16(0x0404), 0x1000 },
+ { CCI_REG16(0x0350), 0x0008 },
+ { CCI_REG16(0x0352), 0x0000 },
+
+ /* Clock settings */
+ { CCI_REG16(0x0136), 0x1800 },
+ { CCI_REG16(0x013e), 0x0000 },
+ { CCI_REG16(0x0300), 0x0008 },
+ { CCI_REG16(0x0302), 0x0001 },
+ { CCI_REG16(0x0304), 0x0006 },
+ { CCI_REG16(0x0306), 0x00f1 },
+ { CCI_REG16(0x0308), 0x0008 },
+ { CCI_REG16(0x030a), 0x0001 },
+ { CCI_REG16(0x030c), 0x0000 },
+ { CCI_REG16(0x030e), 0x0003 },
+ { CCI_REG16(0x0310), 0x005a },
+ { CCI_REG16(0x0312), 0x0000 },
+
+ { CCI_REG16(0x0b06), 0x0101 },
+ { CCI_REG16(0x6028), 0x2000 },
+ { CCI_REG16(0x602a), 0x1ff6 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x021e), 0x0000 },
+ { CCI_REG16(0x0202), 0x0100 },
+ { CCI_REG16(0x0204), 0x0020 },
+ { CCI_REG16(0x0d00), 0x0100 },
+ { CCI_REG16(0x0d02), 0x0001 },
+ { CCI_REG16(0x0114), 0x0301 },
+ { CCI_REG16(0x0d06), 0x0208 },
+ { CCI_REG16(0x0d08), 0x0300 },
+ { CCI_REG16(0x6028), 0x2000 },
+ { CCI_REG16(0x602a), 0x0f10 },
+ { CCI_REG16(0x6f12), 0x0003 },
+ { CCI_REG16(0x602a), 0x0f12 },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x2bc0 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x0b30), 0x0000 },
+ { CCI_REG16(0x0b32), 0x0000 },
+ { CCI_REG16(0x0b34), 0x0001 },
+ { CCI_REG16(0x0804), 0x0200 },
+ { CCI_REG16(0x0810), 0x0020 },
+};
+
+static const struct cci_reg_sequence s5k3m5_2104x1184_60fps_mode[] = {
+ { CCI_REG16(0x6214), 0x7971 },
+ { CCI_REG16(0x6218), 0x7150 },
+ { S5K3M5_REG_X_ADDR_START, 0x0000 },
+ { S5K3M5_REG_Y_ADDR_START, 0x0180 },
+ { S5K3M5_REG_X_ADDR_END, 0x1077 },
+ { S5K3M5_REG_Y_ADDR_END, 0x0abf },
+ { S5K3M5_REG_X_OUTPUT_SIZE, 0x0838 },
+ { S5K3M5_REG_Y_OUTPUT_SIZE, 0x04a0 },
+ { S5K3M5_REG_VTS, 0x0658 },
+ { S5K3M5_REG_HTS, 0x1340 },
+
+ { CCI_REG16(0x0900), 0x0112 },
+ { CCI_REG16(0x0380), 0x0001 },
+ { CCI_REG16(0x0382), 0x0001 },
+ { CCI_REG16(0x0384), 0x0001 },
+ { CCI_REG16(0x0386), 0x0003 },
+ { CCI_REG16(0x0402), 0x1010 },
+ { CCI_REG16(0x0404), 0x2000 },
+ { CCI_REG16(0x0350), 0x0004 },
+ { CCI_REG16(0x0352), 0x0000 },
+
+ /* Clock settings */
+ { CCI_REG16(0x0136), 0x1800 },
+ { CCI_REG16(0x013e), 0x0000 },
+ { CCI_REG16(0x0300), 0x0008 },
+ { CCI_REG16(0x0302), 0x0001 },
+ { CCI_REG16(0x0304), 0x0006 },
+ { CCI_REG16(0x0306), 0x00f1 },
+ { CCI_REG16(0x0308), 0x0008 },
+ { CCI_REG16(0x030a), 0x0001 },
+ { CCI_REG16(0x030c), 0x0000 },
+ { CCI_REG16(0x030e), 0x0003 },
+ { CCI_REG16(0x0310), 0x0063 },
+ { CCI_REG16(0x0312), 0x0001 },
+
+ { CCI_REG16(0x0b06), 0x0101 },
+ { CCI_REG16(0x6028), 0x2000 },
+ { CCI_REG16(0x602a), 0x1ff6 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x021e), 0x0000 },
+ { CCI_REG16(0x0202), 0x0100 },
+ { CCI_REG16(0x0204), 0x0020 },
+ { CCI_REG16(0x0d00), 0x0101 },
+ { CCI_REG16(0x0d02), 0x0101 },
+ { CCI_REG16(0x0114), 0x0301 },
+ { CCI_REG16(0x0d06), 0x0208 },
+ { CCI_REG16(0x0d08), 0x0300 },
+ { CCI_REG16(0x6028), 0x2000 },
+ { CCI_REG16(0x602a), 0x0f10 },
+ { CCI_REG16(0x6f12), 0x0003 },
+ { CCI_REG16(0x602a), 0x0f12 },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x2bc0 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x0b30), 0x0000 },
+ { CCI_REG16(0x0b32), 0x0000 },
+ { CCI_REG16(0x0b34), 0x0001 },
+ { CCI_REG16(0x0804), 0x0200 },
+ { CCI_REG16(0x0810), 0x0020 },
+};
+
+static const struct s5k3m5_mode s5k3m5_supported_modes[] = {
+ {
+ .width = 4208,
+ .height = 3120,
+ .hts = 4848,
+ .vts = 3314,
+ .exposure = 256,
+ .reg_list = {
+ .regs = s5k3m5_4208x3120_30fps_mode,
+ .num_regs = ARRAY_SIZE(s5k3m5_4208x3120_30fps_mode),
+ },
+ },
+ {
+ .width = 2104,
+ .height = 1184,
+ .hts = 4928,
+ .vts = 1624,
+ .exposure = 256,
+ .reg_list = {
+ .regs = s5k3m5_2104x1184_60fps_mode,
+ .num_regs = ARRAY_SIZE(s5k3m5_2104x1184_60fps_mode),
+ },
+ },
+};
+
+static int s5k3m5_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5k3m5 *s5k3m5 = container_of(ctrl->handler, struct s5k3m5,
+ ctrl_handler);
+ const struct s5k3m5_mode *mode = s5k3m5->mode;
+ s64 exposure_max;
+ int ret;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ /* The orientation settings are applied along with streaming */
+ return 0;
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = mode->height + ctrl->val - S5K3M5_EXPOSURE_MARGIN;
+ __v4l2_ctrl_modify_range(s5k3m5->exposure,
+ s5k3m5->exposure->minimum,
+ exposure_max,
+ s5k3m5->exposure->step,
+ s5k3m5->exposure->default_value);
+ break;
+ }
+
+ /* V4L2 controls are applied, when sensor is powered up for streaming */
+ if (!pm_runtime_get_if_active(s5k3m5->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(s5k3m5->regmap, S5K3M5_REG_AGAIN,
+ ctrl->val << S5K3M5_AGAIN_SHIFT, NULL);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(s5k3m5->regmap, S5K3M5_REG_EXPOSURE,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(s5k3m5->regmap, S5K3M5_REG_VTS,
+ ctrl->val + mode->height, NULL);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = cci_write(s5k3m5->regmap, S5K3M5_REG_TEST_PATTERN,
+ ctrl->val, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(s5k3m5->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5k3m5_ctrl_ops = {
+ .s_ctrl = s5k3m5_set_ctrl,
+};
+
+static inline u64 s5k3m5_freq_to_pixel_rate(const u64 freq)
+{
+ return div_u64(freq * 2 * S5K3M5_DATA_LANES, 10);
+}
+
+static int s5k3m5_init_controls(struct s5k3m5 *s5k3m5)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr = &s5k3m5->ctrl_handler;
+ const struct s5k3m5_mode *mode = s5k3m5->mode;
+ s64 pixel_rate, hblank, vblank, exposure_max;
+ struct v4l2_fwnode_device_properties props;
+ int ret;
+
+ v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+
+ s5k3m5->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(s5k3m5_link_freq_menu) - 1,
+ 0, s5k3m5_link_freq_menu);
+ if (s5k3m5->link_freq)
+ s5k3m5->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = s5k3m5_freq_to_pixel_rate(s5k3m5_link_freq_menu[0]);
+ s5k3m5->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ hblank = mode->hts - mode->width;
+ s5k3m5->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_HBLANK, hblank,
+ hblank, 1, hblank);
+ if (s5k3m5->hblank)
+ s5k3m5->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank = mode->vts - mode->height;
+ s5k3m5->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_VBLANK, vblank,
+ S5K3M5_VTS_MAX - mode->height, 1,
+ vblank);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ S5K3M5_AGAIN_MIN, S5K3M5_AGAIN_MAX,
+ S5K3M5_AGAIN_STEP, S5K3M5_AGAIN_DEFAULT);
+
+ exposure_max = mode->vts - S5K3M5_EXPOSURE_MARGIN;
+ s5k3m5->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ S5K3M5_EXPOSURE_MIN,
+ exposure_max,
+ S5K3M5_EXPOSURE_STEP,
+ mode->exposure);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(s5k3m5_test_pattern_menu) - 1,
+ 0, 0, s5k3m5_test_pattern_menu);
+
+ s5k3m5->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (s5k3m5->hflip)
+ s5k3m5->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ s5k3m5->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (s5k3m5->vflip)
+ s5k3m5->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ ret = v4l2_fwnode_device_parse(s5k3m5->dev, &props);
+ if (ret)
+ goto error_free_hdlr;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &s5k3m5_ctrl_ops,
+ &props);
+ if (ret)
+ goto error_free_hdlr;
+
+ s5k3m5->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_free_hdlr:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int s5k3m5_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+ const struct s5k3m5_reg_list *reg_list = &s5k3m5->mode->reg_list;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(s5k3m5->dev);
+ if (ret)
+ return ret;
+
+ /* Page pointer */
+ cci_write(s5k3m5->regmap, CCI_REG16(0x6028), 0x4000, &ret);
+
+ /* Set version */
+ cci_write(s5k3m5->regmap, CCI_REG16(0x0000), 0x0006, &ret);
+ cci_write(s5k3m5->regmap, CCI_REG16(0x0000), S5K3M5_CHIP_ID, &ret);
+ cci_write(s5k3m5->regmap, CCI_REG16(0x6214), 0x7971, &ret);
+ cci_write(s5k3m5->regmap, CCI_REG16(0x6218), 0x7150, &ret);
+ if (ret)
+ goto error;
+
+ usleep_range(4 * USEC_PER_MSEC, 5 * USEC_PER_MSEC);
+
+ cci_write(s5k3m5->regmap, CCI_REG16(0x0a02), 0x7800, &ret);
+ cci_write(s5k3m5->regmap, CCI_REG16(0x6028), 0x2000, &ret);
+ cci_write(s5k3m5->regmap, CCI_REG16(0x602a), 0x3eac, &ret);
+
+ /* Sensor init settings */
+ cci_multi_reg_write(s5k3m5->regmap, burst_array_setting,
+ ARRAY_SIZE(burst_array_setting), &ret);
+ cci_multi_reg_write(s5k3m5->regmap, init_array_setting,
+ ARRAY_SIZE(init_array_setting), &ret);
+ cci_multi_reg_write(s5k3m5->regmap, reg_list->regs,
+ reg_list->num_regs, &ret);
+ if (ret)
+ goto error;
+
+ ret = __v4l2_ctrl_handler_setup(s5k3m5->sd.ctrl_handler);
+
+ cci_write(s5k3m5->regmap, S5K3M5_REG_CTRL_MODE,
+ S5K3M5_MODE_STREAMING |
+ (s5k3m5->vflip->val ? S5K3M5_VFLIP : 0) |
+ (s5k3m5->hflip->val ? S5K3M5_HFLIP : 0), &ret);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ dev_err(s5k3m5->dev, "failed to start streaming: %d\n", ret);
+ pm_runtime_put_autosuspend(s5k3m5->dev);
+
+ return ret;
+}
+
+static int s5k3m5_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+ int ret;
+
+ ret = cci_write(s5k3m5->regmap, S5K3M5_REG_CTRL_MODE, 0, NULL);
+ if (ret)
+ dev_err(s5k3m5->dev, "failed to stop streaming: %d\n", ret);
+
+ pm_runtime_put_autosuspend(s5k3m5->dev);
+
+ return ret;
+}
+
+static u32 s5k3m5_get_format_code(struct s5k3m5 *s5k3m5)
+{
+ unsigned int i;
+
+ i = (s5k3m5->vflip->val ? 2 : 0) | (s5k3m5->hflip->val ? 1 : 0);
+
+ return s5k3m5_mbus_formats[i];
+}
+
+static void s5k3m5_update_pad_format(struct s5k3m5 *s5k3m5,
+ const struct s5k3m5_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->code = s5k3m5_get_format_code(s5k3m5);
+ 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_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int s5k3m5_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+ s64 hblank, vblank, exposure_max;
+ const struct s5k3m5_mode *mode;
+
+ mode = v4l2_find_nearest_size(s5k3m5_supported_modes,
+ ARRAY_SIZE(s5k3m5_supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ s5k3m5_update_pad_format(s5k3m5, mode, &fmt->format);
+
+ /* Format code could be updated with respect to flip controls */
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY || s5k3m5->mode == mode)
+ goto set_format;
+
+ /* Update limits and set FPS and exposure to default values */
+ hblank = mode->hts - mode->width;
+ __v4l2_ctrl_modify_range(s5k3m5->hblank, hblank, hblank, 1, hblank);
+
+ vblank = mode->vts - mode->height;
+ __v4l2_ctrl_modify_range(s5k3m5->vblank, vblank,
+ S5K3M5_VTS_MAX - mode->height, 1, vblank);
+ __v4l2_ctrl_s_ctrl(s5k3m5->vblank, vblank);
+
+ exposure_max = mode->vts - S5K3M5_EXPOSURE_MARGIN;
+ __v4l2_ctrl_modify_range(s5k3m5->exposure, S5K3M5_EXPOSURE_MIN,
+ exposure_max, S5K3M5_EXPOSURE_STEP,
+ mode->exposure);
+ __v4l2_ctrl_s_ctrl(s5k3m5->exposure, mode->exposure);
+
+ if (s5k3m5->sd.ctrl_handler->error)
+ return s5k3m5->sd.ctrl_handler->error;
+
+ s5k3m5->mode = mode;
+
+set_format:
+ *v4l2_subdev_state_get_format(state, 0) = fmt->format;
+
+ return 0;
+}
+
+static int s5k3m5_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+
+ /* Media bus code index is constant, but code formats are not */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = s5k3m5_get_format_code(s5k3m5);
+
+ return 0;
+}
+
+static int s5k3m5_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+
+ if (fse->index >= ARRAY_SIZE(s5k3m5_supported_modes))
+ return -EINVAL;
+
+ if (fse->code != s5k3m5_get_format_code(s5k3m5))
+ return -EINVAL;
+
+ fse->min_width = s5k3m5_supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = s5k3m5_supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int s5k3m5_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = s5k3m5->mode->width;
+ sel->r.height = s5k3m5->mode->width;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s5k3m5_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ /* Media bus code depends on current flip controls */
+ .width = s5k3m5->mode->width,
+ .height = s5k3m5->mode->height,
+ },
+ };
+
+ s5k3m5_set_pad_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops s5k3m5_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops s5k3m5_pad_ops = {
+ .set_fmt = s5k3m5_set_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .get_selection = s5k3m5_get_selection,
+ .enum_mbus_code = s5k3m5_enum_mbus_code,
+ .enum_frame_size = s5k3m5_enum_frame_size,
+ .enable_streams = s5k3m5_enable_streams,
+ .disable_streams = s5k3m5_disable_streams,
+};
+
+static const struct v4l2_subdev_ops s5k3m5_subdev_ops = {
+ .video = &s5k3m5_video_ops,
+ .pad = &s5k3m5_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops s5k3m5_internal_ops = {
+ .init_state = s5k3m5_init_state,
+};
+
+static const struct media_entity_operations s5k3m5_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int s5k3m5_identify_sensor(struct s5k3m5 *s5k3m5)
+{
+ u64 val;
+ int ret;
+
+ ret = cci_read(s5k3m5->regmap, S5K3M5_REG_CHIP_ID, &val, NULL);
+ if (ret) {
+ dev_err(s5k3m5->dev, "failed to read chip id: %d\n", ret);
+ return ret;
+ }
+
+ if (val != S5K3M5_CHIP_ID) {
+ dev_err(s5k3m5->dev, "chip id mismatch: %x!=%llx\n",
+ S5K3M5_CHIP_ID, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int s5k3m5_check_hwcfg(struct s5k3m5 *s5k3m5)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(s5k3m5->dev), *ep;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus = {
+ .mipi_csi2 = {
+ .num_data_lanes = S5K3M5_DATA_LANES,
+ },
+ },
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ unsigned long freq_bitmap;
+ int ret;
+
+ if (!fwnode)
+ return -ENODEV;
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != S5K3M5_DATA_LANES) {
+ dev_err(s5k3m5->dev, "Invalid number of data lanes: %u\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto endpoint_free;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(s5k3m5->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ s5k3m5_link_freq_menu,
+ ARRAY_SIZE(s5k3m5_link_freq_menu),
+ &freq_bitmap);
+
+endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int s5k3m5_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(s5k3m5->mclk);
+ if (ret)
+ goto disable_regulators;
+
+ gpiod_set_value_cansleep(s5k3m5->reset_gpio, 0);
+ usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC);
+
+ return 0;
+
+disable_regulators:
+ regulator_bulk_disable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies);
+
+ return ret;
+}
+
+static int s5k3m5_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+
+ gpiod_set_value_cansleep(s5k3m5->reset_gpio, 1);
+
+ clk_disable_unprepare(s5k3m5->mclk);
+
+ regulator_bulk_disable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies);
+
+ return 0;
+}
+
+static int s5k3m5_probe(struct i2c_client *client)
+{
+ struct s5k3m5 *s5k3m5;
+ unsigned long freq;
+ unsigned int i;
+ int ret;
+
+ s5k3m5 = devm_kzalloc(&client->dev, sizeof(*s5k3m5), GFP_KERNEL);
+ if (!s5k3m5)
+ return -ENOMEM;
+
+ s5k3m5->dev = &client->dev;
+ v4l2_i2c_subdev_init(&s5k3m5->sd, client, &s5k3m5_subdev_ops);
+
+ s5k3m5->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(s5k3m5->regmap))
+ return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->regmap),
+ "failed to init CCI\n");
+
+ s5k3m5->mclk = devm_v4l2_sensor_clk_get(s5k3m5->dev, NULL);
+ if (IS_ERR(s5k3m5->mclk))
+ return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->mclk),
+ "failed to get MCLK clock\n");
+
+ freq = clk_get_rate(s5k3m5->mclk);
+ if (freq != S5K3M5_MCLK_FREQ_24MHZ)
+ return dev_err_probe(s5k3m5->dev, -EINVAL,
+ "MCLK clock frequency %lu is not supported\n",
+ freq);
+
+ ret = s5k3m5_check_hwcfg(s5k3m5);
+ if (ret)
+ return dev_err_probe(s5k3m5->dev, ret,
+ "failed to check HW configuration\n");
+
+ s5k3m5->reset_gpio = devm_gpiod_get_optional(s5k3m5->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(s5k3m5->reset_gpio))
+ return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->reset_gpio),
+ "cannot get reset GPIO\n");
+
+ for (i = 0; i < S5K3M5_NUM_SUPPLIES; i++)
+ s5k3m5->supplies[i].supply = s5k3m5_supply_names[i];
+
+ ret = devm_regulator_bulk_get(s5k3m5->dev, S5K3M5_NUM_SUPPLIES,
+ s5k3m5->supplies);
+ if (ret)
+ return dev_err_probe(s5k3m5->dev, ret,
+ "failed to get supply regulators\n");
+
+ /* The sensor must be powered on to read the CHIP_ID register */
+ ret = s5k3m5_power_on(s5k3m5->dev);
+ if (ret)
+ return ret;
+
+ ret = s5k3m5_identify_sensor(s5k3m5);
+ if (ret) {
+ dev_err_probe(s5k3m5->dev, ret, "failed to find sensor\n");
+ goto power_off;
+ }
+
+ s5k3m5->mode = &s5k3m5_supported_modes[0];
+ ret = s5k3m5_init_controls(s5k3m5);
+ if (ret) {
+ dev_err_probe(s5k3m5->dev, ret, "failed to init controls\n");
+ goto power_off;
+ }
+
+ s5k3m5->sd.state_lock = s5k3m5->ctrl_handler.lock;
+ s5k3m5->sd.internal_ops = &s5k3m5_internal_ops;
+ s5k3m5->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ s5k3m5->sd.entity.ops = &s5k3m5_subdev_entity_ops;
+ s5k3m5->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ s5k3m5->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&s5k3m5->sd.entity, 1, &s5k3m5->pad);
+ if (ret) {
+ dev_err_probe(s5k3m5->dev, ret,
+ "failed to init media entity pads\n");
+ goto v4l2_ctrl_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&s5k3m5->sd);
+ if (ret < 0) {
+ dev_err_probe(s5k3m5->dev, ret,
+ "failed to init media entity pads\n");
+ goto media_entity_cleanup;
+ }
+
+ pm_runtime_set_active(s5k3m5->dev);
+ pm_runtime_enable(s5k3m5->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&s5k3m5->sd);
+ if (ret < 0) {
+ dev_err_probe(s5k3m5->dev, ret,
+ "failed to register V4L2 subdev\n");
+ goto subdev_cleanup;
+ }
+
+ pm_runtime_set_autosuspend_delay(s5k3m5->dev, 1000);
+ pm_runtime_use_autosuspend(s5k3m5->dev);
+ pm_runtime_idle(s5k3m5->dev);
+
+ return 0;
+
+subdev_cleanup:
+ v4l2_subdev_cleanup(&s5k3m5->sd);
+ pm_runtime_disable(s5k3m5->dev);
+ pm_runtime_set_suspended(s5k3m5->dev);
+
+media_entity_cleanup:
+ media_entity_cleanup(&s5k3m5->sd.entity);
+
+v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(s5k3m5->sd.ctrl_handler);
+
+power_off:
+ s5k3m5_power_off(s5k3m5->dev);
+
+ return ret;
+}
+
+static void s5k3m5_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k3m5 *s5k3m5 = to_s5k3m5(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ pm_runtime_disable(s5k3m5->dev);
+
+ if (!pm_runtime_status_suspended(s5k3m5->dev)) {
+ s5k3m5_power_off(s5k3m5->dev);
+ pm_runtime_set_suspended(s5k3m5->dev);
+ }
+}
+
+static const struct dev_pm_ops s5k3m5_pm_ops = {
+ SET_RUNTIME_PM_OPS(s5k3m5_power_off, s5k3m5_power_on, NULL)
+};
+
+static const struct of_device_id s5k3m5_of_match[] = {
+ { .compatible = "samsung,s5k3m5" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k3m5_of_match);
+
+static struct i2c_driver s5k3m5_i2c_driver = {
+ .driver = {
+ .name = "s5k3m5",
+ .pm = &s5k3m5_pm_ops,
+ .of_match_table = s5k3m5_of_match,
+ },
+ .probe = s5k3m5_probe,
+ .remove = s5k3m5_remove,
+};
+
+module_i2c_driver(s5k3m5_i2c_driver);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>");
+MODULE_DESCRIPTION("Samsung S5K3M5 image sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/s5kjn1.c b/drivers/media/i2c/s5kjn1.c
new file mode 100644
index 000000000000..a707cb740556
--- /dev/null
+++ b/drivers/media/i2c/s5kjn1.c
@@ -0,0 +1,1487 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Linaro Ltd
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define S5KJN1_LINK_FREQ_700MHZ (700ULL * HZ_PER_MHZ)
+#define S5KJN1_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
+#define S5KJN1_DATA_LANES 4
+
+/* Register map is similar to MIPI CCS compliant camera sensors */
+#define S5KJN1_REG_CHIP_ID CCI_REG16(0x0000)
+#define S5KJN1_CHIP_ID 0x38e1
+
+#define S5KJN1_REG_CTRL_MODE CCI_REG8(0x0100)
+#define S5KJN1_MODE_STREAMING BIT(0)
+
+#define S5KJN1_REG_ORIENTATION CCI_REG8(0x0101)
+#define S5KJN1_VFLIP BIT(1)
+#define S5KJN1_HFLIP BIT(0)
+
+#define S5KJN1_REG_EXPOSURE CCI_REG16(0x0202)
+#define S5KJN1_EXPOSURE_MIN 8
+#define S5KJN1_EXPOSURE_STEP 1
+
+#define S5KJN1_REG_AGAIN CCI_REG16(0x0204)
+#define S5KJN1_AGAIN_MIN 1
+#define S5KJN1_AGAIN_MAX 64
+#define S5KJN1_AGAIN_STEP 1
+#define S5KJN1_AGAIN_DEFAULT 6
+#define S5KJN1_AGAIN_SHIFT 5
+
+#define S5KJN1_REG_VTS CCI_REG16(0x0340)
+#define S5KJN1_VTS_MAX 0xffff
+
+#define S5KJN1_REG_HTS CCI_REG16(0x0342)
+#define S5KJN1_REG_X_ADDR_START CCI_REG16(0x0344)
+#define S5KJN1_REG_Y_ADDR_START CCI_REG16(0x0346)
+#define S5KJN1_REG_X_ADDR_END CCI_REG16(0x0348)
+#define S5KJN1_REG_Y_ADDR_END CCI_REG16(0x034a)
+#define S5KJN1_REG_X_OUTPUT_SIZE CCI_REG16(0x034c)
+#define S5KJN1_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e)
+
+#define S5KJN1_REG_TEST_PATTERN CCI_REG16(0x0600)
+
+#define to_s5kjn1(_sd) container_of(_sd, struct s5kjn1, sd)
+
+static const s64 s5kjn1_link_freq_menu[] = {
+ S5KJN1_LINK_FREQ_700MHZ,
+};
+
+/* List of supported formats to cover horizontal and vertical flip controls */
+static const u32 s5kjn1_mbus_formats[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
+};
+
+struct s5kjn1_reg_list {
+ const struct cci_reg_sequence *regs;
+ unsigned int num_regs;
+};
+
+struct s5kjn1_mode {
+ u32 width; /* Frame width in pixels */
+ u32 height; /* Frame height in pixels */
+ u32 hts; /* Horizontal timing size */
+ u32 vts; /* Default vertical timing size */
+ u32 exposure; /* Default exposure value */
+ u32 exposure_margin; /* Exposure margin */
+
+ const struct s5kjn1_reg_list reg_list; /* Sensor register setting */
+};
+
+static const char * const s5kjn1_test_pattern_menu[] = {
+ "Disabled",
+ "Solid color",
+ "Color bars",
+ "Fade to grey color bars",
+ "PN9",
+};
+
+struct s5kjn1 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator *afvdd; /* Autofocus actuator power */
+ struct regulator *vdda; /* Analog power */
+ struct regulator *vddd; /* Digital core power */
+ struct regulator *vddio; /* Digital I/O power */
+
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hflip;
+
+ const struct s5kjn1_mode *mode;
+};
+
+static const struct cci_reg_sequence init_array_setting[] = {
+ { CCI_REG16(0x6028), 0x2400 },
+ { CCI_REG16(0x602a), 0x1354 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x6f12), 0x7017 },
+ { CCI_REG16(0x602a), 0x13b2 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1236 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a0a },
+ { CCI_REG16(0x6f12), 0x4c0a },
+ { CCI_REG16(0x602a), 0x2210 },
+ { CCI_REG16(0x6f12), 0x3401 },
+ { CCI_REG16(0x602a), 0x2176 },
+ { CCI_REG16(0x6f12), 0x6400 },
+ { CCI_REG16(0x602a), 0x222e },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x602a), 0x06b6 },
+ { CCI_REG16(0x6f12), 0x0a00 },
+ { CCI_REG16(0x602a), 0x06bc },
+ { CCI_REG16(0x6f12), 0x1001 },
+ { CCI_REG16(0x602a), 0x2140 },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x602a), 0x1a0e },
+ { CCI_REG16(0x6f12), 0x9600 },
+ { CCI_REG16(0x6028), 0x4000 },
+ { CCI_REG16(0xf44e), 0x0011 },
+ { CCI_REG16(0xf44c), 0x0b0b },
+ { CCI_REG16(0xf44a), 0x0006 },
+ { CCI_REG16(0x0118), 0x0002 },
+ { CCI_REG16(0x011a), 0x0001 },
+};
+
+static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode[] = {
+ { CCI_REG16(0x6028), 0x2400 },
+ { CCI_REG16(0x602a), 0x1a28 },
+ { CCI_REG16(0x6f12), 0x4c00 },
+ { CCI_REG16(0x602a), 0x065a },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x139e },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x139c },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x13a0 },
+ { CCI_REG16(0x6f12), 0x0a00 },
+ { CCI_REG16(0x6f12), 0x0120 },
+ { CCI_REG16(0x602a), 0x2072 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a64 },
+ { CCI_REG16(0x6f12), 0x0301 },
+ { CCI_REG16(0x6f12), 0xff00 },
+ { CCI_REG16(0x602a), 0x19e6 },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x1a30 },
+ { CCI_REG16(0x6f12), 0x3401 },
+ { CCI_REG16(0x602a), 0x19fc },
+ { CCI_REG16(0x6f12), 0x0b00 },
+ { CCI_REG16(0x602a), 0x19f4 },
+ { CCI_REG16(0x6f12), 0x0606 },
+ { CCI_REG16(0x602a), 0x19f8 },
+ { CCI_REG16(0x6f12), 0x1010 },
+ { CCI_REG16(0x602a), 0x1b26 },
+ { CCI_REG16(0x6f12), 0x6f80 },
+ { CCI_REG16(0x6f12), 0xa060 },
+ { CCI_REG16(0x602a), 0x1a3c },
+ { CCI_REG16(0x6f12), 0x6207 },
+ { CCI_REG16(0x602a), 0x1a48 },
+ { CCI_REG16(0x6f12), 0x6207 },
+ { CCI_REG16(0x602a), 0x1444 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x602a), 0x144c },
+ { CCI_REG16(0x6f12), 0x3f00 },
+ { CCI_REG16(0x6f12), 0x3f00 },
+ { CCI_REG16(0x602a), 0x7f6c },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x6f12), 0x2f00 },
+ { CCI_REG16(0x6f12), 0xfa00 },
+ { CCI_REG16(0x6f12), 0x2400 },
+ { CCI_REG16(0x6f12), 0xe500 },
+ { CCI_REG16(0x602a), 0x0650 },
+ { CCI_REG16(0x6f12), 0x0600 },
+ { CCI_REG16(0x602a), 0x0654 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a46 },
+ { CCI_REG16(0x6f12), 0x8a00 },
+ { CCI_REG16(0x602a), 0x1a52 },
+ { CCI_REG16(0x6f12), 0xbf00 },
+ { CCI_REG16(0x602a), 0x0674 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x602a), 0x0668 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x602a), 0x0684 },
+ { CCI_REG16(0x6f12), 0x4001 },
+ { CCI_REG16(0x602a), 0x0688 },
+ { CCI_REG16(0x6f12), 0x4001 },
+ { CCI_REG16(0x602a), 0x147c },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x1480 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x19f6 },
+ { CCI_REG16(0x6f12), 0x0904 },
+ { CCI_REG16(0x602a), 0x0812 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a02 },
+ { CCI_REG16(0x6f12), 0x1800 },
+ { CCI_REG16(0x602a), 0x2148 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x2042 },
+ { CCI_REG16(0x6f12), 0x1a00 },
+ { CCI_REG16(0x602a), 0x0874 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x09c0 },
+ { CCI_REG16(0x6f12), 0x2008 },
+ { CCI_REG16(0x602a), 0x09c4 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x602a), 0x19fe },
+ { CCI_REG16(0x6f12), 0x0e1c },
+ { CCI_REG16(0x602a), 0x4d92 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x84c8 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x4d94 },
+ { CCI_REG16(0x6f12), 0x0005 },
+ { CCI_REG16(0x6f12), 0x000a },
+ { CCI_REG16(0x6f12), 0x0010 },
+ { CCI_REG16(0x6f12), 0x0810 },
+ { CCI_REG16(0x6f12), 0x000a },
+ { CCI_REG16(0x6f12), 0x0040 },
+ { CCI_REG16(0x6f12), 0x0810 },
+ { CCI_REG16(0x6f12), 0x0810 },
+ { CCI_REG16(0x6f12), 0x8002 },
+ { CCI_REG16(0x6f12), 0xfd03 },
+ { CCI_REG16(0x6f12), 0x0010 },
+ { CCI_REG16(0x6f12), 0x1510 },
+ { CCI_REG16(0x602a), 0x3570 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x3574 },
+ { CCI_REG16(0x6f12), 0x1201 },
+ { CCI_REG16(0x602a), 0x21e4 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x602a), 0x21ec },
+ { CCI_REG16(0x6f12), 0x1f04 },
+ { CCI_REG16(0x602a), 0x2080 },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x6f12), 0xff00 },
+ { CCI_REG16(0x6f12), 0x7f01 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x6f12), 0x8001 },
+ { CCI_REG16(0x6f12), 0xd244 },
+ { CCI_REG16(0x6f12), 0xd244 },
+ { CCI_REG16(0x6f12), 0x14f4 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x20ba },
+ { CCI_REG16(0x6f12), 0x141c },
+ { CCI_REG16(0x6f12), 0x111c },
+ { CCI_REG16(0x6f12), 0x54f4 },
+ { CCI_REG16(0x602a), 0x120e },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x212e },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x13ae },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x602a), 0x0718 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x602a), 0x0710 },
+ { CCI_REG16(0x6f12), 0x0002 },
+ { CCI_REG16(0x6f12), 0x0804 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x1b5c },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0786 },
+ { CCI_REG16(0x6f12), 0x7701 },
+ { CCI_REG16(0x602a), 0x2022 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x602a), 0x1360 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x1376 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x6f12), 0x6038 },
+ { CCI_REG16(0x6f12), 0x7038 },
+ { CCI_REG16(0x6f12), 0x8038 },
+ { CCI_REG16(0x602a), 0x1386 },
+ { CCI_REG16(0x6f12), 0x0b00 },
+ { CCI_REG16(0x602a), 0x06fa },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x4a94 },
+ { CCI_REG16(0x6f12), 0x0900 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0300 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0300 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0900 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0a76 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0aee },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0b66 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0bde },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0be8 },
+ { CCI_REG16(0x6f12), 0x3000 },
+ { CCI_REG16(0x6f12), 0x3000 },
+ { CCI_REG16(0x602a), 0x0c56 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0c60 },
+ { CCI_REG16(0x6f12), 0x3000 },
+ { CCI_REG16(0x6f12), 0x3000 },
+ { CCI_REG16(0x602a), 0x0cb6 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x0cf2 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x602a), 0x0cf0 },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x602a), 0x11b8 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x11f6 },
+ { CCI_REG16(0x6f12), 0x0020 },
+ { CCI_REG16(0x602a), 0x4a74 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0xd8ff },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0xd8ff },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x218e },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x2268 },
+ { CCI_REG16(0x6f12), 0xf279 },
+ { CCI_REG16(0x602a), 0x5006 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x500e },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x4e70 },
+ { CCI_REG16(0x6f12), 0x2062 },
+ { CCI_REG16(0x6f12), 0x5501 },
+ { CCI_REG16(0x602a), 0x06dc },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6028), 0x4000 },
+ { CCI_REG16(0xf46a), 0xae80 },
+ { S5KJN1_REG_X_ADDR_START, 0x0000 },
+ { S5KJN1_REG_Y_ADDR_START, 0x0000 },
+ { S5KJN1_REG_X_ADDR_END, 0x1fff },
+ { S5KJN1_REG_Y_ADDR_END, 0x181f },
+ { S5KJN1_REG_X_OUTPUT_SIZE, 0x0ff0 },
+ { S5KJN1_REG_Y_OUTPUT_SIZE, 0x0c00 },
+ { CCI_REG16(0x0350), 0x0008 },
+ { CCI_REG16(0x0352), 0x0008 },
+ { CCI_REG16(0x0900), 0x0122 },
+ { CCI_REG16(0x0380), 0x0002 },
+ { CCI_REG16(0x0382), 0x0002 },
+ { CCI_REG16(0x0384), 0x0002 },
+ { CCI_REG16(0x0386), 0x0002 },
+ { CCI_REG16(0x0110), 0x1002 },
+ { CCI_REG16(0x0114), 0x0301 },
+ { CCI_REG16(0x0116), 0x3000 },
+
+ /* Clock settings */
+ { CCI_REG16(0x0136), 0x1800 },
+ { CCI_REG16(0x013e), 0x0000 },
+ { CCI_REG16(0x0300), 0x0006 },
+ { CCI_REG16(0x0302), 0x0001 },
+ { CCI_REG16(0x0304), 0x0004 },
+ { CCI_REG16(0x0306), 0x008c },
+ { CCI_REG16(0x0308), 0x0008 },
+ { CCI_REG16(0x030a), 0x0001 },
+ { CCI_REG16(0x030c), 0x0000 },
+ { CCI_REG16(0x030e), 0x0004 },
+ { CCI_REG16(0x0310), 0x0092 },
+ { CCI_REG16(0x0312), 0x0000 },
+
+ { CCI_REG16(0x080e), 0x0000 },
+ { S5KJN1_REG_VTS, 0x10c0 },
+ { S5KJN1_REG_HTS, 0x1100 },
+ { CCI_REG16(0x0702), 0x0000 },
+ { S5KJN1_REG_EXPOSURE, 0x0100 },
+ { CCI_REG16(0x0200), 0x0100 },
+ { CCI_REG16(0x0d00), 0x0101 },
+ { CCI_REG16(0x0d02), 0x0101 },
+ { CCI_REG16(0x0d04), 0x0102 },
+ { CCI_REG16(0x6226), 0x0000 },
+ { CCI_REG16(0x0816), 0x1c00 },
+};
+
+static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_mode[] = {
+ { CCI_REG16(0x6028), 0x2400 },
+ { CCI_REG16(0x602a), 0x1a28 },
+ { CCI_REG16(0x6f12), 0x4c00 },
+ { CCI_REG16(0x602a), 0x065a },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x139e },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x602a), 0x139c },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x13a0 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0120 },
+ { CCI_REG16(0x602a), 0x2072 },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x602a), 0x1a64 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x19e6 },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x1a30 },
+ { CCI_REG16(0x6f12), 0x3403 },
+ { CCI_REG16(0x602a), 0x19fc },
+ { CCI_REG16(0x6f12), 0x0700 },
+ { CCI_REG16(0x602a), 0x19f4 },
+ { CCI_REG16(0x6f12), 0x0707 },
+ { CCI_REG16(0x602a), 0x19f8 },
+ { CCI_REG16(0x6f12), 0x0b0b },
+ { CCI_REG16(0x602a), 0x1b26 },
+ { CCI_REG16(0x6f12), 0x6f80 },
+ { CCI_REG16(0x6f12), 0xa060 },
+ { CCI_REG16(0x602a), 0x1a3c },
+ { CCI_REG16(0x6f12), 0x8207 },
+ { CCI_REG16(0x602a), 0x1a48 },
+ { CCI_REG16(0x6f12), 0x8207 },
+ { CCI_REG16(0x602a), 0x1444 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x6f12), 0x2000 },
+ { CCI_REG16(0x602a), 0x144c },
+ { CCI_REG16(0x6f12), 0x3f00 },
+ { CCI_REG16(0x6f12), 0x3f00 },
+ { CCI_REG16(0x602a), 0x7f6c },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x6f12), 0x2f00 },
+ { CCI_REG16(0x6f12), 0xfa00 },
+ { CCI_REG16(0x6f12), 0x2400 },
+ { CCI_REG16(0x6f12), 0xe500 },
+ { CCI_REG16(0x602a), 0x0650 },
+ { CCI_REG16(0x6f12), 0x0600 },
+ { CCI_REG16(0x602a), 0x0654 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a46 },
+ { CCI_REG16(0x6f12), 0x8500 },
+ { CCI_REG16(0x602a), 0x1a52 },
+ { CCI_REG16(0x6f12), 0x9800 },
+ { CCI_REG16(0x602a), 0x0674 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x602a), 0x0668 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x602a), 0x0684 },
+ { CCI_REG16(0x6f12), 0x4001 },
+ { CCI_REG16(0x602a), 0x0688 },
+ { CCI_REG16(0x6f12), 0x4001 },
+ { CCI_REG16(0x602a), 0x147c },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x602a), 0x1480 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x602a), 0x19f6 },
+ { CCI_REG16(0x6f12), 0x0404 },
+ { CCI_REG16(0x602a), 0x0812 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1a02 },
+ { CCI_REG16(0x6f12), 0x1800 },
+ { CCI_REG16(0x602a), 0x2148 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x2042 },
+ { CCI_REG16(0x6f12), 0x1a00 },
+ { CCI_REG16(0x602a), 0x0874 },
+ { CCI_REG16(0x6f12), 0x0106 },
+ { CCI_REG16(0x602a), 0x09c0 },
+ { CCI_REG16(0x6f12), 0x4000 },
+ { CCI_REG16(0x602a), 0x09c4 },
+ { CCI_REG16(0x6f12), 0x4000 },
+ { CCI_REG16(0x602a), 0x19fe },
+ { CCI_REG16(0x6f12), 0x0c1c },
+ { CCI_REG16(0x602a), 0x4d92 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x84c8 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x4d94 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x3570 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x3574 },
+ { CCI_REG16(0x6f12), 0x7306 },
+ { CCI_REG16(0x602a), 0x21e4 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x602a), 0x21ec },
+ { CCI_REG16(0x6f12), 0x6902 },
+ { CCI_REG16(0x602a), 0x2080 },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x6f12), 0xff00 },
+ { CCI_REG16(0x6f12), 0x0002 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x6f12), 0x0002 },
+ { CCI_REG16(0x6f12), 0xd244 },
+ { CCI_REG16(0x6f12), 0xd244 },
+ { CCI_REG16(0x6f12), 0x14f4 },
+ { CCI_REG16(0x6f12), 0x101c },
+ { CCI_REG16(0x6f12), 0x0d1c },
+ { CCI_REG16(0x6f12), 0x54f4 },
+ { CCI_REG16(0x602a), 0x20ba },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x120e },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x212e },
+ { CCI_REG16(0x6f12), 0x0200 },
+ { CCI_REG16(0x602a), 0x13ae },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x0718 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0710 },
+ { CCI_REG16(0x6f12), 0x0010 },
+ { CCI_REG16(0x6f12), 0x0201 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x602a), 0x1b5c },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0786 },
+ { CCI_REG16(0x6f12), 0x1401 },
+ { CCI_REG16(0x602a), 0x2022 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x6f12), 0x0500 },
+ { CCI_REG16(0x602a), 0x1360 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x1376 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x6038 },
+ { CCI_REG16(0x6f12), 0x7038 },
+ { CCI_REG16(0x6f12), 0x8038 },
+ { CCI_REG16(0x602a), 0x1386 },
+ { CCI_REG16(0x6f12), 0x0b00 },
+ { CCI_REG16(0x602a), 0x06fa },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x4a94 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0400 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x6f12), 0x0800 },
+ { CCI_REG16(0x602a), 0x0a76 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0aee },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0b66 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0bde },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0be8 },
+ { CCI_REG16(0x6f12), 0x5000 },
+ { CCI_REG16(0x6f12), 0x5000 },
+ { CCI_REG16(0x602a), 0x0c56 },
+ { CCI_REG16(0x6f12), 0x1000 },
+ { CCI_REG16(0x602a), 0x0c60 },
+ { CCI_REG16(0x6f12), 0x5000 },
+ { CCI_REG16(0x6f12), 0x5000 },
+ { CCI_REG16(0x602a), 0x0cb6 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x0cf2 },
+ { CCI_REG16(0x6f12), 0x0001 },
+ { CCI_REG16(0x602a), 0x0cf0 },
+ { CCI_REG16(0x6f12), 0x0101 },
+ { CCI_REG16(0x602a), 0x11b8 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x11f6 },
+ { CCI_REG16(0x6f12), 0x0010 },
+ { CCI_REG16(0x602a), 0x4a74 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x218e },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x2268 },
+ { CCI_REG16(0x6f12), 0xf279 },
+ { CCI_REG16(0x602a), 0x5006 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x602a), 0x500e },
+ { CCI_REG16(0x6f12), 0x0100 },
+ { CCI_REG16(0x602a), 0x4e70 },
+ { CCI_REG16(0x6f12), 0x2062 },
+ { CCI_REG16(0x6f12), 0x5501 },
+ { CCI_REG16(0x602a), 0x06dc },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6f12), 0x0000 },
+ { CCI_REG16(0x6028), 0x4000 },
+ { CCI_REG16(0xf46a), 0xae80 },
+ { S5KJN1_REG_X_ADDR_START, 0x0000 },
+ { S5KJN1_REG_Y_ADDR_START, 0x0000 },
+ { S5KJN1_REG_X_ADDR_END, 0x1fff },
+ { S5KJN1_REG_Y_ADDR_END, 0x181f },
+ { S5KJN1_REG_X_OUTPUT_SIZE, 0x1fe0 },
+ { S5KJN1_REG_Y_OUTPUT_SIZE, 0x1800 },
+ { CCI_REG16(0x0350), 0x0010 },
+ { CCI_REG16(0x0352), 0x0010 },
+ { CCI_REG16(0x0900), 0x0111 },
+ { CCI_REG16(0x0380), 0x0001 },
+ { CCI_REG16(0x0382), 0x0001 },
+ { CCI_REG16(0x0384), 0x0001 },
+ { CCI_REG16(0x0386), 0x0001 },
+ { CCI_REG16(0x0110), 0x1002 },
+ { CCI_REG16(0x0114), 0x0300 },
+ { CCI_REG16(0x0116), 0x3000 },
+
+ /* Clock settings */
+ { CCI_REG16(0x0136), 0x1800 },
+ { CCI_REG16(0x013e), 0x0000 },
+ { CCI_REG16(0x0300), 0x0006 },
+ { CCI_REG16(0x0302), 0x0001 },
+ { CCI_REG16(0x0304), 0x0004 },
+ { CCI_REG16(0x0306), 0x008c },
+ { CCI_REG16(0x0308), 0x0008 },
+ { CCI_REG16(0x030a), 0x0001 },
+ { CCI_REG16(0x030c), 0x0000 },
+ { CCI_REG16(0x030e), 0x0004 },
+ { CCI_REG16(0x0310), 0x0074 },
+ { CCI_REG16(0x0312), 0x0000 },
+
+ { CCI_REG16(0x080e), 0x0000 },
+ { S5KJN1_REG_VTS, 0x1900 },
+ { S5KJN1_REG_HTS, 0x21f0 },
+ { CCI_REG16(0x0702), 0x0000 },
+ { S5KJN1_REG_EXPOSURE, 0x0100 },
+ { CCI_REG16(0x0200), 0x0100 },
+ { CCI_REG16(0x0d00), 0x0100 },
+ { CCI_REG16(0x0d02), 0x0001 },
+ { CCI_REG16(0x0d04), 0x0002 },
+ { CCI_REG16(0x6226), 0x0000 },
+};
+
+static const struct s5kjn1_mode s5kjn1_supported_modes[] = {
+ {
+ .width = 4080,
+ .height = 3072,
+ .hts = 4352,
+ .vts = 4288,
+ .exposure = 3840,
+ .exposure_margin = 22,
+ .reg_list = {
+ .regs = s5kjn1_4080x3072_30fps_mode,
+ .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_mode),
+ },
+ },
+ {
+ .width = 8160,
+ .height = 6144,
+ .hts = 8688,
+ .vts = 6400,
+ .exposure = 6144,
+ .exposure_margin = 44,
+ .reg_list = {
+ .regs = s5kjn1_8160x6144_10fps_mode,
+ .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_mode),
+ },
+ },
+};
+
+static int s5kjn1_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5kjn1 *s5kjn1 = container_of(ctrl->handler, struct s5kjn1,
+ ctrl_handler);
+ const struct s5kjn1_mode *mode = s5kjn1->mode;
+ s64 exposure_max;
+ int ret;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = mode->height + ctrl->val - mode->exposure_margin;
+ __v4l2_ctrl_modify_range(s5kjn1->exposure,
+ s5kjn1->exposure->minimum,
+ exposure_max,
+ s5kjn1->exposure->step,
+ s5kjn1->exposure->default_value);
+ break;
+ }
+
+ /* V4L2 controls are applied, when sensor is powered up for streaming */
+ if (!pm_runtime_get_if_active(s5kjn1->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_AGAIN,
+ ctrl->val << S5KJN1_AGAIN_SHIFT, NULL);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_EXPOSURE,
+ ctrl->val, NULL);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_VTS,
+ ctrl->val + mode->height, NULL);
+ break;
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_ORIENTATION,
+ (s5kjn1->vflip->val ? S5KJN1_VFLIP : 0) |
+ (s5kjn1->hflip->val ? S5KJN1_HFLIP : 0), NULL);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_TEST_PATTERN,
+ ctrl->val, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(s5kjn1->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5kjn1_ctrl_ops = {
+ .s_ctrl = s5kjn1_set_ctrl,
+};
+
+static inline u64 s5kjn1_freq_to_pixel_rate(const u64 freq)
+{
+ return div_u64(freq * 2 * S5KJN1_DATA_LANES, 10);
+}
+
+static int s5kjn1_init_controls(struct s5kjn1 *s5kjn1)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr = &s5kjn1->ctrl_handler;
+ const struct s5kjn1_mode *mode = s5kjn1->mode;
+ s64 pixel_rate, hblank, vblank, exposure_max;
+ struct v4l2_fwnode_device_properties props;
+ int ret;
+
+ v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+
+ s5kjn1->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(s5kjn1_link_freq_menu) - 1,
+ 0, s5kjn1_link_freq_menu);
+ if (s5kjn1->link_freq)
+ s5kjn1->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = s5kjn1_freq_to_pixel_rate(s5kjn1_link_freq_menu[0]);
+ s5kjn1->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ hblank = mode->hts - mode->width;
+ s5kjn1->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_HBLANK, hblank,
+ hblank, 1, hblank);
+ if (s5kjn1->hblank)
+ s5kjn1->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank = mode->vts - mode->height;
+ s5kjn1->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_VBLANK, vblank,
+ S5KJN1_VTS_MAX - mode->height, 1,
+ vblank);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ S5KJN1_AGAIN_MIN, S5KJN1_AGAIN_MAX,
+ S5KJN1_AGAIN_STEP, S5KJN1_AGAIN_DEFAULT);
+
+ exposure_max = mode->vts - mode->exposure_margin;
+ s5kjn1->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ S5KJN1_EXPOSURE_MIN,
+ exposure_max,
+ S5KJN1_EXPOSURE_STEP,
+ mode->exposure);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(s5kjn1_test_pattern_menu) - 1,
+ 0, 0, s5kjn1_test_pattern_menu);
+
+ s5kjn1->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (s5kjn1->hflip)
+ s5kjn1->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ s5kjn1->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (s5kjn1->vflip)
+ s5kjn1->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ ret = v4l2_fwnode_device_parse(s5kjn1->dev, &props);
+ if (ret)
+ goto error_free_hdlr;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &s5kjn1_ctrl_ops,
+ &props);
+ if (ret)
+ goto error_free_hdlr;
+
+ s5kjn1->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_free_hdlr:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int s5kjn1_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+ const struct s5kjn1_reg_list *reg_list = &s5kjn1->mode->reg_list;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(s5kjn1->dev);
+ if (ret)
+ return ret;
+
+ /* Page pointer */
+ cci_write(s5kjn1->regmap, CCI_REG16(0x6028), 0x4000, &ret);
+
+ /* Set version */
+ cci_write(s5kjn1->regmap, CCI_REG16(0x0000), 0x0003, &ret);
+ cci_write(s5kjn1->regmap, CCI_REG16(0x0000), S5KJN1_CHIP_ID, &ret);
+ cci_write(s5kjn1->regmap, CCI_REG16(0x001e), 0x0007, &ret);
+ cci_write(s5kjn1->regmap, CCI_REG16(0x6028), 0x4000, &ret);
+ cci_write(s5kjn1->regmap, CCI_REG16(0x6010), 0x0001, &ret);
+ if (ret)
+ goto error;
+
+ usleep_range(5 * USEC_PER_MSEC, 6 * USEC_PER_MSEC);
+
+ cci_write(s5kjn1->regmap, CCI_REG16(0x6226), 0x0001, &ret);
+ if (ret)
+ goto error;
+
+ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
+
+ /* Sensor init settings */
+ cci_multi_reg_write(s5kjn1->regmap, init_array_setting,
+ ARRAY_SIZE(init_array_setting), &ret);
+ cci_multi_reg_write(s5kjn1->regmap, reg_list->regs,
+ reg_list->num_regs, &ret);
+ if (ret)
+ goto error;
+
+ ret = __v4l2_ctrl_handler_setup(s5kjn1->sd.ctrl_handler);
+
+ cci_write(s5kjn1->regmap, S5KJN1_REG_CTRL_MODE,
+ S5KJN1_MODE_STREAMING, &ret);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ dev_err(s5kjn1->dev, "failed to start streaming: %d\n", ret);
+ pm_runtime_put_autosuspend(s5kjn1->dev);
+
+ return ret;
+}
+
+static int s5kjn1_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+ int ret;
+
+ ret = cci_write(s5kjn1->regmap, S5KJN1_REG_CTRL_MODE, 0x0, NULL);
+ if (ret)
+ dev_err(s5kjn1->dev, "failed to stop streaming: %d\n", ret);
+
+ pm_runtime_put_autosuspend(s5kjn1->dev);
+
+ return ret;
+}
+
+static u32 s5kjn1_get_format_code(struct s5kjn1 *s5kjn1)
+{
+ unsigned int i;
+
+ i = (s5kjn1->vflip->val ? 2 : 0) | (s5kjn1->hflip->val ? 1 : 0);
+
+ return s5kjn1_mbus_formats[i];
+}
+
+static void s5kjn1_update_pad_format(struct s5kjn1 *s5kjn1,
+ const struct s5kjn1_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->code = s5kjn1_get_format_code(s5kjn1);
+ 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_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int s5kjn1_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+ s64 hblank, vblank, exposure_max;
+ const struct s5kjn1_mode *mode;
+
+ mode = v4l2_find_nearest_size(s5kjn1_supported_modes,
+ ARRAY_SIZE(s5kjn1_supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ s5kjn1_update_pad_format(s5kjn1, mode, &fmt->format);
+
+ /* Format code could be updated with respect to flip controls */
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY || s5kjn1->mode == mode)
+ goto set_format;
+
+ /* Update limits and set FPS and exposure to default values */
+ hblank = mode->hts - mode->width;
+ __v4l2_ctrl_modify_range(s5kjn1->hblank, hblank, hblank, 1, hblank);
+
+ vblank = mode->vts - mode->height;
+ __v4l2_ctrl_modify_range(s5kjn1->vblank, vblank,
+ S5KJN1_VTS_MAX - mode->height, 1, vblank);
+ __v4l2_ctrl_s_ctrl(s5kjn1->vblank, vblank);
+
+ exposure_max = mode->vts - mode->exposure_margin;
+ __v4l2_ctrl_modify_range(s5kjn1->exposure, S5KJN1_EXPOSURE_MIN,
+ exposure_max, S5KJN1_EXPOSURE_STEP,
+ mode->exposure);
+ __v4l2_ctrl_s_ctrl(s5kjn1->exposure, mode->exposure);
+
+ if (s5kjn1->sd.ctrl_handler->error)
+ return s5kjn1->sd.ctrl_handler->error;
+
+ s5kjn1->mode = mode;
+
+set_format:
+ *v4l2_subdev_state_get_format(state, 0) = fmt->format;
+
+ return 0;
+}
+
+static int s5kjn1_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+
+ /* Media bus code index is constant, but code formats are not */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = s5kjn1_get_format_code(s5kjn1);
+
+ return 0;
+}
+
+static int s5kjn1_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+
+ if (fse->index >= ARRAY_SIZE(s5kjn1_supported_modes))
+ return -EINVAL;
+
+ if (fse->code != s5kjn1_get_format_code(s5kjn1))
+ return -EINVAL;
+
+ fse->min_width = s5kjn1_supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = s5kjn1_supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int s5kjn1_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = s5kjn1->mode->width;
+ sel->r.height = s5kjn1->mode->width;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s5kjn1_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ /* Media bus code depends on current flip controls */
+ .width = s5kjn1->mode->width,
+ .height = s5kjn1->mode->height,
+ },
+ };
+
+ s5kjn1_set_pad_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops s5kjn1_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops s5kjn1_pad_ops = {
+ .set_fmt = s5kjn1_set_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .get_selection = s5kjn1_get_selection,
+ .enum_mbus_code = s5kjn1_enum_mbus_code,
+ .enum_frame_size = s5kjn1_enum_frame_size,
+ .enable_streams = s5kjn1_enable_streams,
+ .disable_streams = s5kjn1_disable_streams,
+};
+
+static const struct v4l2_subdev_ops s5kjn1_subdev_ops = {
+ .video = &s5kjn1_video_ops,
+ .pad = &s5kjn1_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops s5kjn1_internal_ops = {
+ .init_state = s5kjn1_init_state,
+};
+
+static const struct media_entity_operations s5kjn1_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int s5kjn1_identify_sensor(struct s5kjn1 *s5kjn1)
+{
+ u64 val;
+ int ret;
+
+ ret = cci_read(s5kjn1->regmap, S5KJN1_REG_CHIP_ID, &val, NULL);
+ if (ret) {
+ dev_err(s5kjn1->dev, "failed to read chip id: %d\n", ret);
+ return ret;
+ }
+
+ if (val != S5KJN1_CHIP_ID) {
+ dev_err(s5kjn1->dev, "chip id mismatch: %x!=%llx\n",
+ S5KJN1_CHIP_ID, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int s5kjn1_check_hwcfg(struct s5kjn1 *s5kjn1)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(s5kjn1->dev), *ep;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus = {
+ .mipi_csi2 = {
+ .num_data_lanes = S5KJN1_DATA_LANES,
+ },
+ },
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ unsigned long freq_bitmap;
+ int ret;
+
+ if (!fwnode)
+ return -ENODEV;
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != S5KJN1_DATA_LANES) {
+ dev_err(s5kjn1->dev, "Invalid number of data lanes: %u\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto endpoint_free;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(s5kjn1->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ s5kjn1_link_freq_menu,
+ ARRAY_SIZE(s5kjn1_link_freq_menu),
+ &freq_bitmap);
+
+endpoint_free:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int s5kjn1_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+ int ret;
+
+ if (s5kjn1->vddd) {
+ ret = regulator_enable(s5kjn1->vddd);
+ if (ret)
+ return ret;
+
+ usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
+ }
+
+ if (s5kjn1->vdda) {
+ ret = regulator_enable(s5kjn1->vdda);
+ if (ret)
+ goto disable_vddd;
+ }
+
+ if (s5kjn1->vddio) {
+ ret = regulator_enable(s5kjn1->vddio);
+ if (ret)
+ goto disable_vdda;
+ }
+
+ if (s5kjn1->afvdd) {
+ ret = regulator_enable(s5kjn1->afvdd);
+ if (ret)
+ goto disable_vddio;
+ }
+
+ ret = clk_prepare_enable(s5kjn1->mclk);
+ if (ret)
+ goto disable_regulators;
+
+ gpiod_set_value_cansleep(s5kjn1->reset_gpio, 0);
+ usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC);
+
+ return 0;
+
+disable_regulators:
+ if (s5kjn1->afvdd)
+ regulator_disable(s5kjn1->afvdd);
+
+disable_vddio:
+ if (s5kjn1->vddio)
+ regulator_disable(s5kjn1->vddio);
+
+disable_vdda:
+ if (s5kjn1->vdda)
+ regulator_disable(s5kjn1->vdda);
+
+disable_vddd:
+ if (s5kjn1->vddd) {
+ usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
+ regulator_disable(s5kjn1->vddd);
+ }
+
+ return ret;
+}
+
+static int s5kjn1_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+
+ gpiod_set_value_cansleep(s5kjn1->reset_gpio, 1);
+
+ clk_disable_unprepare(s5kjn1->mclk);
+
+ if (s5kjn1->afvdd)
+ regulator_disable(s5kjn1->afvdd);
+
+ if (s5kjn1->vddio)
+ regulator_disable(s5kjn1->vddio);
+
+ if (s5kjn1->vdda)
+ regulator_disable(s5kjn1->vdda);
+
+ if (s5kjn1->vddd) {
+ usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
+ regulator_disable(s5kjn1->vddd);
+ }
+
+ return 0;
+}
+
+static int s5kjn1_probe(struct i2c_client *client)
+{
+ struct s5kjn1 *s5kjn1;
+ unsigned long freq;
+ int ret;
+
+ s5kjn1 = devm_kzalloc(&client->dev, sizeof(*s5kjn1), GFP_KERNEL);
+ if (!s5kjn1)
+ return -ENOMEM;
+
+ s5kjn1->dev = &client->dev;
+ v4l2_i2c_subdev_init(&s5kjn1->sd, client, &s5kjn1_subdev_ops);
+
+ s5kjn1->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(s5kjn1->regmap))
+ return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->regmap),
+ "failed to init CCI\n");
+
+ s5kjn1->mclk = devm_v4l2_sensor_clk_get(s5kjn1->dev, NULL);
+ if (IS_ERR(s5kjn1->mclk))
+ return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->mclk),
+ "failed to get MCLK clock\n");
+
+ freq = clk_get_rate(s5kjn1->mclk);
+ if (freq != S5KJN1_MCLK_FREQ_24MHZ)
+ return dev_err_probe(s5kjn1->dev, -EINVAL,
+ "MCLK clock frequency %lu is not supported\n",
+ freq);
+
+ ret = s5kjn1_check_hwcfg(s5kjn1);
+ if (ret)
+ return dev_err_probe(s5kjn1->dev, ret,
+ "failed to check HW configuration\n");
+
+ s5kjn1->reset_gpio = devm_gpiod_get_optional(s5kjn1->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(s5kjn1->reset_gpio))
+ return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->reset_gpio),
+ "cannot get reset GPIO\n");
+
+ s5kjn1->afvdd = devm_regulator_get_optional(s5kjn1->dev, "afvdd");
+ if (IS_ERR(s5kjn1->afvdd)) {
+ ret = PTR_ERR(s5kjn1->afvdd);
+ if (ret != -ENODEV) {
+ return dev_err_probe(s5kjn1->dev, ret,
+ "Failed to get 'afvdd' regulator\n");
+ }
+
+ s5kjn1->afvdd = NULL;
+ }
+
+ s5kjn1->vdda = devm_regulator_get_optional(s5kjn1->dev, "vdda");
+ if (IS_ERR(s5kjn1->vdda)) {
+ ret = PTR_ERR(s5kjn1->vdda);
+ if (ret != -ENODEV) {
+ return dev_err_probe(s5kjn1->dev, ret,
+ "Failed to get 'vdda' regulator\n");
+ }
+
+ s5kjn1->vdda = NULL;
+ }
+
+ s5kjn1->vddd = devm_regulator_get_optional(s5kjn1->dev, "vddd");
+ if (IS_ERR(s5kjn1->vddd)) {
+ ret = PTR_ERR(s5kjn1->vddd);
+ if (ret != -ENODEV) {
+ return dev_err_probe(s5kjn1->dev, ret,
+ "Failed to get 'vddd' regulator\n");
+ }
+
+ s5kjn1->vddd = NULL;
+ }
+
+ s5kjn1->vddio = devm_regulator_get_optional(s5kjn1->dev, "vddio");
+ if (IS_ERR(s5kjn1->vddio)) {
+ ret = PTR_ERR(s5kjn1->vddio);
+ if (ret != -ENODEV) {
+ return dev_err_probe(s5kjn1->dev, ret,
+ "Failed to get 'vddio' regulator\n");
+ }
+
+ s5kjn1->vddio = NULL;
+ }
+
+ /* The sensor must be powered on to read the CHIP_ID register */
+ ret = s5kjn1_power_on(s5kjn1->dev);
+ if (ret)
+ return ret;
+
+ ret = s5kjn1_identify_sensor(s5kjn1);
+ if (ret) {
+ dev_err_probe(s5kjn1->dev, ret, "failed to find sensor\n");
+ goto power_off;
+ }
+
+ s5kjn1->mode = &s5kjn1_supported_modes[0];
+ ret = s5kjn1_init_controls(s5kjn1);
+ if (ret) {
+ dev_err_probe(s5kjn1->dev, ret, "failed to init controls\n");
+ goto power_off;
+ }
+
+ s5kjn1->sd.state_lock = s5kjn1->ctrl_handler.lock;
+ s5kjn1->sd.internal_ops = &s5kjn1_internal_ops;
+ s5kjn1->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ s5kjn1->sd.entity.ops = &s5kjn1_subdev_entity_ops;
+ s5kjn1->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ s5kjn1->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&s5kjn1->sd.entity, 1, &s5kjn1->pad);
+ if (ret) {
+ dev_err_probe(s5kjn1->dev, ret,
+ "failed to init media entity pads\n");
+ goto v4l2_ctrl_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&s5kjn1->sd);
+ if (ret < 0) {
+ dev_err_probe(s5kjn1->dev, ret,
+ "failed to init media entity pads\n");
+ goto media_entity_cleanup;
+ }
+
+ pm_runtime_set_active(s5kjn1->dev);
+ pm_runtime_enable(s5kjn1->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&s5kjn1->sd);
+ if (ret < 0) {
+ dev_err_probe(s5kjn1->dev, ret,
+ "failed to register V4L2 subdev\n");
+ goto subdev_cleanup;
+ }
+
+ pm_runtime_set_autosuspend_delay(s5kjn1->dev, 1000);
+ pm_runtime_use_autosuspend(s5kjn1->dev);
+ pm_runtime_idle(s5kjn1->dev);
+
+ return 0;
+
+subdev_cleanup:
+ v4l2_subdev_cleanup(&s5kjn1->sd);
+ pm_runtime_disable(s5kjn1->dev);
+ pm_runtime_set_suspended(s5kjn1->dev);
+
+media_entity_cleanup:
+ media_entity_cleanup(&s5kjn1->sd.entity);
+
+v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(s5kjn1->sd.ctrl_handler);
+
+power_off:
+ s5kjn1_power_off(s5kjn1->dev);
+
+ return ret;
+}
+
+static void s5kjn1_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5kjn1 *s5kjn1 = to_s5kjn1(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ pm_runtime_disable(s5kjn1->dev);
+
+ if (!pm_runtime_status_suspended(s5kjn1->dev)) {
+ s5kjn1_power_off(s5kjn1->dev);
+ pm_runtime_set_suspended(s5kjn1->dev);
+ }
+}
+
+static const struct dev_pm_ops s5kjn1_pm_ops = {
+ SET_RUNTIME_PM_OPS(s5kjn1_power_off, s5kjn1_power_on, NULL)
+};
+
+static const struct of_device_id s5kjn1_of_match[] = {
+ { .compatible = "samsung,s5kjn1" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5kjn1_of_match);
+
+static struct i2c_driver s5kjn1_i2c_driver = {
+ .driver = {
+ .name = "s5kjn1",
+ .pm = &s5kjn1_pm_ops,
+ .of_match_table = s5kjn1_of_match,
+ },
+ .probe = s5kjn1_probe,
+ .remove = s5kjn1_remove,
+};
+
+module_i2c_driver(s5kjn1_i2c_driver);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>");
+MODULE_DESCRIPTION("Samsung S5KJN1 image sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index fb09e4560d8a..90ae4121a68a 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -49,8 +49,6 @@ MODULE_LICENSE("GPL");
/* ---------------------------------------------------------------------- */
#define UNSET (-1U)
-#define PREFIX "saa6588: "
-#define dprintk if (debug) printk
struct saa6588 {
struct v4l2_subdev sd;
@@ -144,14 +142,14 @@ static bool block_from_buf(struct saa6588 *s, unsigned char *buf)
if (s->rd_index == s->wr_index) {
if (debug > 2)
- dprintk(PREFIX "Read: buffer empty.\n");
+ v4l2_info(&s->sd, "Read: buffer empty.\n");
return false;
}
if (debug > 2) {
- dprintk(PREFIX "Read: ");
+ v4l2_info(&s->sd, "Read: ");
for (i = s->rd_index; i < s->rd_index + 3; i++)
- dprintk("0x%02x ", s->buffer[i]);
+ v4l2_info(&s->sd, "0x%02x ", s->buffer[i]);
}
memcpy(buf, &s->buffer[s->rd_index], 3);
@@ -162,7 +160,7 @@ static bool block_from_buf(struct saa6588 *s, unsigned char *buf)
s->block_count--;
if (debug > 2)
- dprintk("%d blocks total.\n", s->block_count);
+ v4l2_info(&s->sd, "%d blocks total.\n", s->block_count);
return true;
}
@@ -222,11 +220,11 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf)
unsigned int i;
if (debug > 3)
- dprintk(PREFIX "New block: ");
+ v4l2_info(&s->sd, "New block: ");
for (i = 0; i < 3; ++i) {
if (debug > 3)
- dprintk("0x%02x ", blockbuf[i]);
+ v4l2_info(&s->sd, "0x%02x ", blockbuf[i]);
s->buffer[s->wr_index] = blockbuf[i];
s->wr_index++;
}
@@ -242,7 +240,7 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf)
s->block_count++;
if (debug > 3)
- dprintk("%d blocks total.\n", s->block_count);
+ v4l2_info(&s->sd, "%d blocks total.\n", s->block_count);
}
static void saa6588_i2c_poll(struct saa6588 *s)
@@ -257,7 +255,7 @@ static void saa6588_i2c_poll(struct saa6588 *s)
SAA6588 returns garbage otherwise. */
if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) {
if (debug > 1)
- dprintk(PREFIX "read error!\n");
+ v4l2_info(&s->sd, "read error!\n");
return;
}
@@ -267,7 +265,7 @@ static void saa6588_i2c_poll(struct saa6588 *s)
blocknum = tmpbuf[0] >> 5;
if (blocknum == s->last_blocknum) {
if (debug > 3)
- dprintk("Saw block %d again.\n", blocknum);
+ v4l2_info(&s->sd, "Saw block %d again.\n", blocknum);
return;
}
@@ -370,12 +368,13 @@ static void saa6588_configure(struct saa6588 *s)
break;
}
- dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n",
- buf[0], buf[1], buf[2]);
+ if (debug)
+ v4l2_info(&s->sd, "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n",
+ buf[0], buf[1], buf[2]);
rc = i2c_master_send(client, buf, 3);
if (rc != 3)
- printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc);
+ v4l2_info(&s->sd, "i2c i/o error: rc == %d (should be 3)\n", rc);
}
/* ---------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c
index b996a05e56f2..c3eafd5d5dc8 100644
--- a/drivers/media/i2c/tw9903.c
+++ b/drivers/media/i2c/tw9903.c
@@ -228,6 +228,7 @@ static int tw9903_probe(struct i2c_client *client)
if (write_regs(sd, initial_registers) < 0) {
v4l2_err(client, "error initializing TW9903\n");
+ v4l2_ctrl_handler_free(hdl);
return -EINVAL;
}
diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c
index 6220f4fddbab..0ab43fe42d7f 100644
--- a/drivers/media/i2c/tw9906.c
+++ b/drivers/media/i2c/tw9906.c
@@ -196,6 +196,7 @@ static int tw9906_probe(struct i2c_client *client)
if (write_regs(sd, initial_registers) < 0) {
v4l2_err(client, "error initializing TW9906\n");
+ v4l2_ctrl_handler_free(hdl);
return -EINVAL;
}
diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index c0dd4ae57227..5a4465b290f0 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -679,6 +679,23 @@ void media_device_unregister_entity(struct media_entity *entity)
}
EXPORT_SYMBOL_GPL(media_device_unregister_entity);
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Log the state of media requests. Very useful for debugging.
+ */
+static int media_device_requests(struct seq_file *file, void *priv)
+{
+ struct media_device *dev = dev_get_drvdata(file->private);
+
+ seq_printf(file, "number of requests: %d\n",
+ atomic_read(&dev->num_requests));
+ seq_printf(file, "number of request objects: %d\n",
+ atomic_read(&dev->num_request_objects));
+
+ return 0;
+}
+#endif
+
void media_device_init(struct media_device *mdev)
{
INIT_LIST_HEAD(&mdev->entities);
@@ -697,6 +714,9 @@ void media_device_init(struct media_device *mdev)
media_set_bus_info(mdev->bus_info, sizeof(mdev->bus_info),
mdev->dev);
+ atomic_set(&mdev->num_requests, 0);
+ atomic_set(&mdev->num_request_objects, 0);
+
dev_dbg(mdev->dev, "Media device initialized\n");
}
EXPORT_SYMBOL_GPL(media_device_init);
@@ -748,6 +768,15 @@ int __must_check __media_device_register(struct media_device *mdev,
dev_dbg(mdev->dev, "Media device registered\n");
+#ifdef CONFIG_DEBUG_FS
+ if (!media_debugfs_root)
+ media_debugfs_root = debugfs_create_dir("media", NULL);
+ mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev),
+ media_debugfs_root);
+ debugfs_create_devm_seqfile(&devnode->dev, "requests",
+ mdev->media_dir, media_device_requests);
+#endif
+
return 0;
}
EXPORT_SYMBOL_GPL(__media_device_register);
@@ -824,6 +853,7 @@ void media_device_unregister(struct media_device *mdev)
dev_dbg(mdev->dev, "Media device unregistered\n");
+ debugfs_remove_recursive(mdev->media_dir);
device_remove_file(&mdev->devnode->dev, &dev_attr_model);
media_devnode_unregister(mdev->devnode);
/* devnode free is handled in media_devnode_*() */
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 6daa7aa99442..51260a7d6921 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -45,6 +45,9 @@ static dev_t media_dev_t;
static DEFINE_MUTEX(media_devnode_lock);
static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
+/* debugfs */
+struct dentry *media_debugfs_root;
+
/* Called when the last user of the media device exits. */
static void media_devnode_release(struct device *cd)
{
@@ -231,6 +234,7 @@ int __must_check media_devnode_register(struct media_device *mdev,
if (devnode->parent)
devnode->dev.parent = devnode->parent;
dev_set_name(&devnode->dev, "media%d", devnode->minor);
+ dev_set_drvdata(&devnode->dev, mdev);
device_initialize(&devnode->dev);
/* Part 2: Initialize the character device */
@@ -309,6 +313,7 @@ static int __init media_devnode_init(void)
static void __exit media_devnode_exit(void)
{
+ debugfs_remove_recursive(media_debugfs_root);
bus_unregister(&media_bus_type);
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
}
diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c
index 3cca9a0c7c97..8ad10c72f9db 100644
--- a/drivers/media/mc/mc-request.c
+++ b/drivers/media/mc/mc-request.c
@@ -54,6 +54,7 @@ static void media_request_clean(struct media_request *req)
req->access_count = 0;
WARN_ON(req->num_incomplete_objects);
req->num_incomplete_objects = 0;
+ req->manual_completion = false;
wake_up_interruptible_all(&req->poll_wait);
}
@@ -74,6 +75,7 @@ static void media_request_release(struct kref *kref)
mdev->ops->req_free(req);
else
kfree(req);
+ atomic_dec(&mdev->num_requests);
}
void media_request_put(struct media_request *req)
@@ -298,6 +300,7 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
req->mdev = mdev;
req->state = MEDIA_REQUEST_STATE_IDLE;
req->num_incomplete_objects = 0;
+ req->manual_completion = false;
kref_init(&req->kref);
INIT_LIST_HEAD(&req->objects);
spin_lock_init(&req->lock);
@@ -317,6 +320,7 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
atomic_inc_return(&mdev->request_id), fd_prepare_fd(fdf));
+ atomic_inc(&mdev->num_requests);
dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
*alloc_fd = fd_publish(fdf);
@@ -337,10 +341,12 @@ static void media_request_object_release(struct kref *kref)
struct media_request_object *obj =
container_of(kref, struct media_request_object, kref);
struct media_request *req = obj->req;
+ struct media_device *mdev = obj->mdev;
if (WARN_ON(req))
media_request_object_unbind(obj);
obj->ops->release(obj);
+ atomic_dec(&mdev->num_request_objects);
}
struct media_request_object *
@@ -405,6 +411,7 @@ int media_request_object_bind(struct media_request *req,
obj->req = req;
obj->ops = ops;
obj->priv = priv;
+ obj->mdev = req->mdev;
if (is_buffer)
list_add_tail(&obj->list, &req->objects);
@@ -412,6 +419,7 @@ int media_request_object_bind(struct media_request *req,
list_add(&obj->list, &req->objects);
req->num_incomplete_objects++;
ret = 0;
+ atomic_inc(&obj->mdev->num_request_objects);
unlock:
spin_unlock_irqrestore(&req->lock, flags);
@@ -449,7 +457,7 @@ void media_request_object_unbind(struct media_request_object *obj)
req->num_incomplete_objects--;
if (req->state == MEDIA_REQUEST_STATE_QUEUED &&
- !req->num_incomplete_objects) {
+ !req->num_incomplete_objects && !req->manual_completion) {
req->state = MEDIA_REQUEST_STATE_COMPLETE;
completed = true;
wake_up_interruptible_all(&req->poll_wait);
@@ -478,7 +486,7 @@ void media_request_object_complete(struct media_request_object *obj)
WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
goto unlock;
- if (!--req->num_incomplete_objects) {
+ if (!--req->num_incomplete_objects && !req->manual_completion) {
req->state = MEDIA_REQUEST_STATE_COMPLETE;
wake_up_interruptible_all(&req->poll_wait);
completed = true;
@@ -489,3 +497,38 @@ unlock:
media_request_put(req);
}
EXPORT_SYMBOL_GPL(media_request_object_complete);
+
+void media_request_manual_complete(struct media_request *req)
+{
+ bool completed = false;
+ unsigned long flags;
+
+ if (WARN_ON_ONCE(!req))
+ return;
+
+ spin_lock_irqsave(&req->lock, flags);
+
+ if (WARN_ON_ONCE(!req->manual_completion))
+ goto unlock;
+
+ if (WARN_ON_ONCE(req->state != MEDIA_REQUEST_STATE_QUEUED))
+ goto unlock;
+
+ req->manual_completion = false;
+ /*
+ * It is expected that all other objects in this request are
+ * completed when this function is called. WARN if that is
+ * not the case.
+ */
+ if (!WARN_ON(req->num_incomplete_objects)) {
+ req->state = MEDIA_REQUEST_STATE_COMPLETE;
+ wake_up_interruptible_all(&req->poll_wait);
+ completed = true;
+ }
+
+unlock:
+ spin_unlock_irqrestore(&req->lock, flags);
+ if (completed)
+ media_request_put(req);
+}
+EXPORT_SYMBOL_GPL(media_request_manual_complete);
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index 25dc8d4dc5b7..717fc6c9ef21 100644
--- a/drivers/media/pci/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -392,8 +392,10 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
chip->period_size, chip->num_periods, 1);
- if (ret < 0)
+ if (ret < 0) {
+ cx23885_alsa_dma_unmap(chip);
goto error;
+ }
/* Loop back to start of program */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index a42f0c03a7ca..f463365163b7 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -535,6 +535,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream,
chip->period_size, chip->num_periods, 1);
if (ret < 0) {
pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n");
+ cx25821_alsa_dma_unmap(chip);
goto error;
}
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index 6627fa9166d3..a7336be44474 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -908,6 +908,7 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
if (!dev->lmmio) {
CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n");
+ release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0));
cx25821_iounmap(dev);
return -ENOMEM;
}
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 29fb1311e443..4e574d8390b4 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -483,8 +483,10 @@ static int snd_cx88_hw_params(struct snd_pcm_substream *substream,
ret = cx88_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
chip->period_size, chip->num_periods, 1);
- if (ret < 0)
+ if (ret < 0) {
+ cx88_alsa_dma_unmap(chip);
goto error;
+ }
/* Loop back to start of program */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index 43a2a16a3c2a..7e539a0c6c92 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -88,7 +88,7 @@ s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
if (!csi2)
return -EINVAL;
- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ src_pad = media_pad_remote_pad_unique(&csi2->asd.sd.entity.pads[CSI2_PAD_SINK]);
if (IS_ERR(src_pad)) {
dev_err(&csi2->isys->adev->auxdev.dev,
"can't get source pad of %s (%pe)\n",
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
index aa2cf7287477..fabaed63df0c 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
@@ -3,6 +3,7 @@
* Copyright (C) 2013--2024 Intel Corporation
*/
#include <linux/atomic.h>
+#include <linux/cleanup.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/list.h>
@@ -131,9 +132,6 @@ void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
list_add_tail(&ib->head, &aq->incoming);
spin_unlock_irqrestore(&aq->lock, flags);
- if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE)
- vb2_buffer_done(vb, state);
-
if (first) {
dev_dbg(dev,
"queue buf list %p flags %lx, s %d, %d bufs\n",
@@ -201,6 +199,8 @@ static int buffer_list_get(struct ipu6_isys_stream *stream,
unsigned long flags;
unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING;
+ lockdep_assert_held(&stream->mutex);
+
bl->nbufs = 0;
INIT_LIST_HEAD(&bl->head);
@@ -287,16 +287,15 @@ ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
/* Start streaming for real. The buffer list must be available. */
static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
- struct ipu6_isys_buffer_list *bl, bool error)
+ struct ipu6_isys_buffer_list *bl)
{
struct ipu6_isys_stream *stream = av->stream;
struct device *dev = &stream->isys->adev->auxdev.dev;
struct ipu6_isys_buffer_list __bl;
int ret;
- mutex_lock(&stream->isys->stream_mutex);
+ guard(mutex)(&stream->isys->stream_mutex);
ret = ipu6_isys_video_set_streaming(av, 1, bl);
- mutex_unlock(&stream->isys->stream_mutex);
if (ret)
goto out_requeue;
@@ -334,10 +333,7 @@ static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
out_requeue:
if (bl && bl->nbufs)
ipu6_isys_buffer_list_queue(bl,
- IPU6_ISYS_BUFFER_LIST_FL_INCOMING |
- (error ?
- IPU6_ISYS_BUFFER_LIST_FL_SET_STATE :
- 0), error ? VB2_BUF_STATE_ERROR :
+ IPU6_ISYS_BUFFER_LIST_FL_INCOMING,
VB2_BUF_STATE_QUEUED);
flush_firmware_streamon_fail(stream);
@@ -353,8 +349,6 @@ static void buf_queue(struct vb2_buffer *vb)
vb2_buffer_to_ipu6_isys_video_buffer(vvb);
struct ipu6_isys_buffer *ib = &ivb->ib;
struct device *dev = &av->isys->adev->auxdev.dev;
- struct media_pipeline *media_pipe =
- media_entity_pipeline(&av->vdev.entity);
struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
struct ipu6_isys_stream *stream = av->stream;
struct ipu6_isys_buffer_list bl;
@@ -372,8 +366,8 @@ static void buf_queue(struct vb2_buffer *vb)
list_add(&ib->head, &aq->incoming);
spin_unlock_irqrestore(&aq->lock, flags);
- if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
- dev_dbg(dev, "media pipeline is not ready for %s\n",
+ if (!vb2_start_streaming_called(vb->vb2_queue)) {
+ dev_dbg(dev, "start_streaming hasn't been called yet on %s\n",
av->vdev.name);
return;
}
@@ -406,13 +400,6 @@ static void buf_queue(struct vb2_buffer *vb)
ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl);
ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
- if (!stream->streaming) {
- ret = ipu6_isys_stream_start(av, &bl, true);
- if (ret)
- dev_err(dev, "stream start failed.\n");
- goto out;
- }
-
/*
* We must queue the buffers in the buffer list to the
* appropriate video buffer queues BEFORE passing them to the
@@ -433,14 +420,13 @@ out:
static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
{
- struct v4l2_mbus_framefmt format;
+ struct v4l2_mbus_framefmt format, *__format;
struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
struct device *dev = &av->isys->adev->auxdev.dev;
struct media_pad *remote_pad =
media_pad_remote_pad_first(av->vdev.entity.pads);
struct v4l2_subdev *sd;
u32 r_stream, code;
- int ret;
if (!remote_pad)
return -ENOTCONN;
@@ -448,13 +434,20 @@ static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
sd = media_entity_to_v4l2_subdev(remote_pad->entity);
r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
- ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
- &format);
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_lock_and_get_active_state(sd);
- if (ret) {
+ __format = v4l2_subdev_state_get_format(state, remote_pad->index,
+ r_stream);
+ if (__format)
+ format = *__format;
+
+ v4l2_subdev_unlock_state(state);
+
+ if (!__format) {
dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
sd->entity.name, remote_pad->index, r_stream);
- return ret;
+ return -EPIPE;
}
if (format.width != ipu6_isys_get_frame_width(av) ||
@@ -545,14 +538,28 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
struct ipu6_isys_buffer_list __bl, *bl = NULL;
struct ipu6_isys_stream *stream;
- struct media_entity *source_entity = NULL;
+ struct media_pad *source_pad, *remote_pad;
int nr_queues, ret;
dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
av->vdev.name, ipu6_isys_get_frame_width(av),
ipu6_isys_get_frame_height(av), pfmt->css_pixelformat);
- ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
+ remote_pad = media_pad_remote_pad_unique(&av->pad);
+ if (IS_ERR(remote_pad)) {
+ dev_dbg(dev, "failed to get remote pad\n");
+ ret = PTR_ERR(remote_pad);
+ goto out_return_buffers;
+ }
+
+ source_pad = media_pad_remote_pad_unique(&remote_pad->entity->pads[0]);
+ if (IS_ERR(source_pad)) {
+ dev_dbg(dev, "No external source entity\n");
+ ret = PTR_ERR(source_pad);
+ goto out_return_buffers;
+ }
+
+ ret = ipu6_isys_setup_video(av, remote_pad, source_pad, &nr_queues);
if (ret < 0) {
dev_dbg(dev, "failed to setup video\n");
goto out_return_buffers;
@@ -573,7 +580,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
stream = av->stream;
mutex_lock(&stream->mutex);
if (!stream->nr_streaming) {
- ret = ipu6_isys_video_prepare_stream(av, source_entity,
+ ret = ipu6_isys_video_prepare_stream(av, source_pad->entity,
nr_queues);
if (ret)
goto out_fw_close;
@@ -584,7 +591,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
stream->nr_queues);
list_add(&aq->node, &stream->queues);
- ipu6_isys_configure_stream_watermark(av, true);
+ ipu6_isys_configure_stream_watermark(av, source_pad->entity);
ipu6_isys_update_stream_watermark(av, true);
if (stream->nr_streaming != stream->nr_queues)
@@ -597,7 +604,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
goto out;
}
- ret = ipu6_isys_stream_start(av, bl, false);
+ ret = ipu6_isys_stream_start(av, bl);
if (ret)
goto out_stream_start;
@@ -637,10 +644,10 @@ static void stop_streaming(struct vb2_queue *q)
mutex_lock(&av->isys->stream_mutex);
if (stream->nr_streaming == stream->nr_queues && stream->streaming)
ipu6_isys_video_set_streaming(av, 0, NULL);
+ list_del(&aq->node);
mutex_unlock(&av->isys->stream_mutex);
stream->nr_streaming--;
- list_del(&aq->node);
stream->streaming = 0;
mutex_unlock(&stream->mutex);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
index 844dfda15ab6..dec1fed44dd2 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
@@ -39,7 +39,6 @@ struct ipu6_isys_video_buffer {
#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0)
#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1)
-#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2)
struct ipu6_isys_buffer_list {
struct list_head head;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
index 869e7d4ba572..dbd6f76a066d 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
@@ -265,42 +265,6 @@ static int subdev_set_routing(struct v4l2_subdev *sd,
return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
}
-int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
- struct v4l2_mbus_framefmt *format)
-{
- struct v4l2_mbus_framefmt *fmt;
- struct v4l2_subdev_state *state;
-
- if (!sd || !format)
- return -EINVAL;
-
- state = v4l2_subdev_lock_and_get_active_state(sd);
- fmt = v4l2_subdev_state_get_format(state, pad, stream);
- if (fmt)
- *format = *fmt;
- v4l2_subdev_unlock_state(state);
-
- return fmt ? 0 : -EINVAL;
-}
-
-int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
- struct v4l2_rect *crop)
-{
- struct v4l2_subdev_state *state;
- struct v4l2_rect *rect;
-
- if (!sd || !crop)
- return -EINVAL;
-
- state = v4l2_subdev_lock_and_get_active_state(sd);
- rect = v4l2_subdev_state_get_crop(state, pad, stream);
- if (rect)
- *crop = *rect;
- v4l2_subdev_unlock_state(state);
-
- return rect ? 0 : -EINVAL;
-}
-
u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
{
struct v4l2_subdev_state *state;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
index 268dfa01e903..35069099c364 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
@@ -38,10 +38,6 @@ int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum
*code);
u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
-int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
- struct v4l2_mbus_framefmt *format);
-int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
- struct v4l2_rect *crop);
int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
enum v4l2_subdev_format_whence which,
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
index dec8f5ffcfa5..3ac48d2076da 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
@@ -455,6 +455,7 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
{
struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd);
struct ipu6_fw_isys_input_pin_info_abi *input_pin;
struct ipu6_fw_isys_output_pin_info_abi *output_pin;
struct ipu6_isys_stream *stream = av->stream;
@@ -464,26 +465,13 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
struct v4l2_rect v4l2_crop;
struct ipu6_isys *isys = av->isys;
- struct device *dev = &isys->adev->auxdev.dev;
int input_pins = cfg->nof_input_pins++;
int output_pins;
u32 src_stream;
- int ret;
src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
- ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
- &fmt);
- if (ret < 0) {
- dev_err(dev, "can't get stream format (%d)\n", ret);
- return ret;
- }
-
- ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
- &v4l2_crop);
- if (ret < 0) {
- dev_err(dev, "can't get stream crop (%d)\n", ret);
- return ret;
- }
+ fmt = *v4l2_subdev_state_get_format(state, src_pad->index, src_stream);
+ v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream);
input_pin = &cfg->input_pins[input_pins];
input_pin->input_res.width = fmt.width;
@@ -745,17 +733,16 @@ int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
stream->stream_source = stream->asd->source;
csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
csi2->receiver_errors = 0;
- stream->source_entity = source_entity;
dev_dbg(&av->isys->adev->auxdev.dev,
"prepare stream: external entity %s\n",
- stream->source_entity->name);
+ source_entity->name);
return 0;
}
void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
- bool state)
+ struct media_entity *source)
{
struct ipu6_isys *isys = av->isys;
struct ipu6_isys_csi2 *csi2 = NULL;
@@ -769,10 +756,7 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
u64 pixel_rate = 0;
int ret;
- if (!state)
- return;
-
- esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
+ esd = media_entity_to_v4l2_subdev(source);
av->watermark.width = ipu6_isys_get_frame_width(av);
av->watermark.height = ipu6_isys_get_frame_height(av);
@@ -788,13 +772,16 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
link_freq = ipu6_isys_csi2_get_link_freq(csi2);
if (link_freq > 0) {
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_lock_and_get_active_state(&csi2->asd.sd);
+
lanes = csi2->nlanes;
- ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
- av->source_stream, &format);
- if (!ret) {
- bpp = ipu6_isys_mbus_code_to_bpp(format.code);
- pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
- }
+ format = *v4l2_subdev_state_get_format(state, 0,
+ av->source_stream);
+ bpp = ipu6_isys_mbus_code_to_bpp(format.code);
+ pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
+
+ v4l2_subdev_unlock_state(state);
}
av->watermark.pixel_rate = pixel_rate;
@@ -804,7 +791,7 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
iwake_watermark->force_iwake_disable = true;
mutex_unlock(&iwake_watermark->mutex);
dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
- av->stream->source_entity->name);
+ source->name);
}
}
@@ -1011,9 +998,6 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
dev_dbg(dev, "set stream: %d\n", state);
- if (WARN(!stream->source_entity, "No source entity for stream\n"))
- return -ENODEV;
-
sd = &stream->asd->sd;
r_pad = media_pad_remote_pad_first(&av->pad);
r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
@@ -1036,11 +1020,10 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
sd->name, r_pad->index, stream_mask);
ret = v4l2_subdev_disable_streams(sd, r_pad->index,
stream_mask);
- if (ret) {
+ if (ret)
dev_err(dev, "stream off %s failed with %d\n", sd->name,
ret);
- return ret;
- }
+
close_streaming_firmware(av);
} else {
ret = start_stream_firmware(av, bl);
@@ -1066,6 +1049,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
out_media_entity_stop_streaming_firmware:
stop_streaming_firmware(av);
+ close_streaming_firmware(av);
return ret;
}
@@ -1179,7 +1163,8 @@ void ipu6_isys_fw_close(struct ipu6_isys *isys)
}
int ipu6_isys_setup_video(struct ipu6_isys_video *av,
- struct media_entity **source_entity, int *nr_queues)
+ struct media_pad *remote_pad,
+ struct media_pad *source_pad, int *nr_queues)
{
const struct ipu6_isys_pixelformat *pfmt =
ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
@@ -1188,30 +1173,13 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av,
struct v4l2_subdev_route *route = NULL;
struct v4l2_subdev_route *r;
struct v4l2_subdev_state *state;
- struct ipu6_isys_subdev *asd;
- struct v4l2_subdev *remote_sd;
- struct media_pipeline *pipeline;
- struct media_pad *source_pad, *remote_pad;
+ struct v4l2_subdev *remote_sd =
+ media_entity_to_v4l2_subdev(remote_pad->entity);
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(remote_sd);
int ret = -EINVAL;
*nr_queues = 0;
- remote_pad = media_pad_remote_pad_unique(&av->pad);
- if (IS_ERR(remote_pad)) {
- dev_dbg(dev, "failed to get remote pad\n");
- return PTR_ERR(remote_pad);
- }
-
- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- asd = to_ipu6_isys_subdev(remote_sd);
- source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
- if (!source_pad) {
- dev_dbg(dev, "No external source entity\n");
- return -ENODEV;
- }
-
- *source_entity = source_pad->entity;
-
/* Find the root */
state = v4l2_subdev_lock_and_get_active_state(remote_sd);
for_each_active_route(&state->routing, r) {
@@ -1231,7 +1199,7 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av,
ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
to_ipu6_isys_csi2(asd),
- *source_entity, &entry);
+ source_pad->entity, &entry);
if (ret == -ENOIOCTLCMD) {
av->vc = 0;
av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code);
@@ -1247,11 +1215,7 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av,
return ret;
}
- pipeline = media_entity_pipeline(&av->vdev.entity);
- if (!pipeline)
- ret = video_device_pipeline_alloc_start(&av->vdev);
- else
- ret = video_device_pipeline_start(&av->vdev, pipeline);
+ ret = video_device_pipeline_alloc_start(&av->vdev);
if (ret < 0) {
dev_dbg(dev, "media pipeline start failed\n");
return ret;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
index 1dd36f2a077e..2ff53315d7b9 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
@@ -43,7 +43,6 @@ struct sequence_info {
*/
struct ipu6_isys_stream {
struct mutex mutex;
- struct media_entity *source_entity;
atomic_t sequence;
unsigned int seq_index;
struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF];
@@ -113,7 +112,8 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
int ipu6_isys_fw_open(struct ipu6_isys *isys);
void ipu6_isys_fw_close(struct ipu6_isys *isys);
int ipu6_isys_setup_video(struct ipu6_isys_video *av,
- struct media_entity **source_entity, int *nr_queues);
+ struct media_pad *remote_pad,
+ struct media_pad *source_pad, int *nr_queues);
int ipu6_isys_video_init(struct ipu6_isys_video *av);
void ipu6_isys_video_cleanup(struct ipu6_isys_video *av);
void ipu6_isys_put_stream(struct ipu6_isys_stream *stream);
@@ -123,7 +123,7 @@ struct ipu6_isys_stream *
ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc);
void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
- bool state);
+ struct media_entity *source);
void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
u32 ipu6_isys_get_format(struct ipu6_isys_video *av);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c
index fc0ec0a4b8f5..c9cdeb7054d7 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c
@@ -269,7 +269,7 @@ fail:
return ret;
}
-void isys_setup_hw(struct ipu6_isys *isys)
+static void isys_setup_hw(struct ipu6_isys *isys)
{
void __iomem *base = isys->pdata->base;
const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
@@ -341,7 +341,7 @@ static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
}
}
-irqreturn_t isys_isr(struct ipu6_bus_device *adev)
+static irqreturn_t isys_isr(struct ipu6_bus_device *adev)
{
struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
void __iomem *base = isys->pdata->base;
@@ -857,9 +857,6 @@ static int isys_runtime_pm_resume(struct device *dev)
unsigned long flags;
int ret;
- if (!isys)
- return 0;
-
ret = ipu6_mmu_hw_init(adev->mmu);
if (ret)
return ret;
@@ -884,13 +881,9 @@ static int isys_runtime_pm_resume(struct device *dev)
static int isys_runtime_pm_suspend(struct device *dev)
{
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
- struct ipu6_isys *isys;
+ struct ipu6_isys *isys = dev_get_drvdata(dev);
unsigned long flags;
- isys = dev_get_drvdata(dev);
- if (!isys)
- return 0;
-
spin_lock_irqsave(&isys->power_lock, flags);
isys->power = 0;
spin_unlock_irqrestore(&isys->power_lock, flags);
@@ -1070,10 +1063,6 @@ static int isys_probe(struct auxiliary_device *auxdev,
if (!isys->csi2)
return -ENOMEM;
- ret = ipu6_mmu_hw_init(adev->mmu);
- if (ret)
- return ret;
-
/* initial sensor type */
isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
@@ -1125,8 +1114,6 @@ static int isys_probe(struct auxiliary_device *auxdev,
if (ret)
goto free_fw_msg_bufs;
- ipu6_mmu_hw_cleanup(adev->mmu);
-
return 0;
free_fw_msg_bufs:
@@ -1148,8 +1135,6 @@ release_firmware:
mutex_destroy(&isys->mutex);
mutex_destroy(&isys->stream_mutex);
- ipu6_mmu_hw_cleanup(adev->mmu);
-
return ret;
}
@@ -1373,7 +1358,7 @@ MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
-MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
+MODULE_AUTHOR("Hongju Wang");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel IPU6 input system driver");
MODULE_IMPORT_NS("INTEL_IPU6");
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
index 0e2c8b71edfc..7fb8cb820912 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
@@ -181,8 +181,6 @@ void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
-void isys_setup_hw(struct ipu6_isys *isys);
-irqreturn_t isys_isr(struct ipu6_bus_device *adev);
void update_watermark_setting(struct ipu6_isys *isys);
int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
index 6d1c0b90169d..85cc6d5b4dd1 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
@@ -102,7 +102,7 @@ static void page_table_dump(struct ipu6_mmu_info *mmu_info)
if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval)
continue;
- l2_phys = TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx];)
+ l2_phys = TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]);
dev_dbg(mmu_info->dev,
"l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pap\n",
l1_idx, iova, iova + ISP_PAGE_SIZE, &l2_phys);
@@ -248,7 +248,7 @@ static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info)
dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
- for (i = 0; i < ISP_L1PT_PTES; i++)
+ for (i = 0; i < ISP_L2PT_PTES; i++)
pt[i] = mmu_info->dummy_page_pteval;
return pt;
diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
index 1f4f20b9c94d..24238f8311a6 100644
--- a/drivers/media/pci/intel/ipu6/ipu6.c
+++ b/drivers/media/pci/intel/ipu6/ipu6.c
@@ -630,21 +630,21 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret) {
dev_err_probe(&isp->pdev->dev, ret,
"Failed to set MMU hardware\n");
- goto out_ipu6_bus_del_devices;
+ goto out_ipu6_rpm_put;
}
ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw,
&isp->psys->fw_sgt);
if (ret) {
dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n");
- goto out_ipu6_bus_del_devices;
+ goto out_ipu6_rpm_put;
}
ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data);
if (ret) {
dev_err_probe(&isp->pdev->dev, ret,
"failed to create pkg dir\n");
- goto out_ipu6_bus_del_devices;
+ goto out_ipu6_rpm_put;
}
ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr,
@@ -652,7 +652,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
IRQF_SHARED, IPU6_NAME, isp);
if (ret) {
dev_err_probe(dev, ret, "Requesting irq failed\n");
- goto out_ipu6_bus_del_devices;
+ goto out_ipu6_rpm_put;
}
ret = ipu6_buttress_authenticate(isp);
@@ -683,6 +683,8 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_free_irq:
devm_free_irq(dev, pdev->irq, isp);
+out_ipu6_rpm_put:
+ pm_runtime_put_sync(&isp->psys->auxdev.dev);
out_ipu6_bus_del_devices:
if (isp->psys) {
ipu6_cpd_free_pkg_dir(isp->psys);
@@ -841,6 +843,6 @@ MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
-MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
+MODULE_AUTHOR("Hongju Wang");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel IPU6 PCI driver");
diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c
index 3ce6b717ca32..a7cb8dc50b53 100644
--- a/drivers/media/pci/mgb4/mgb4_core.c
+++ b/drivers/media/pci/mgb4/mgb4_core.c
@@ -381,6 +381,18 @@ static int get_serial_number(struct mgb4_dev *mgbdev)
return 0;
}
+static const char *module_type_str(struct mgb4_dev *mgbdev)
+{
+ if (MGB4_IS_FPDL3(mgbdev))
+ return "FPDL3";
+ else if (MGB4_IS_GMSL3(mgbdev))
+ return "GMSL3";
+ else if (MGB4_IS_GMSL1(mgbdev))
+ return "GMSL1";
+ else
+ return "UNKNOWN";
+}
+
static int get_module_version(struct mgb4_dev *mgbdev)
{
struct device *dev = &mgbdev->pdev->dev;
@@ -402,19 +414,21 @@ static int get_module_version(struct mgb4_dev *mgbdev)
}
mgbdev->module_version = ~((u32)version) & 0xff;
- if (!(MGB4_IS_FPDL3(mgbdev) || MGB4_IS_GMSL(mgbdev))) {
+ if (!(MGB4_IS_FPDL3(mgbdev) ||
+ MGB4_IS_GMSL3(mgbdev) ||
+ MGB4_IS_GMSL1(mgbdev))) {
dev_err(dev, "unknown module type\n");
return -EINVAL;
}
fw_version = mgb4_read_reg(&mgbdev->video, 0xC4) >> 24;
if ((MGB4_IS_FPDL3(mgbdev) && fw_version != 1) ||
- (MGB4_IS_GMSL(mgbdev) && fw_version != 2)) {
+ (MGB4_IS_GMSL3(mgbdev) && fw_version != 2) ||
+ (MGB4_IS_GMSL1(mgbdev) && fw_version != 3)) {
dev_err(dev, "module/firmware type mismatch\n");
return -EINVAL;
}
- dev_info(dev, "%s module detected\n",
- MGB4_IS_FPDL3(mgbdev) ? "FPDL3" : "GMSL");
+ dev_info(dev, "%s module detected\n", module_type_str(mgbdev));
return 0;
}
diff --git a/drivers/media/pci/mgb4/mgb4_core.h b/drivers/media/pci/mgb4/mgb4_core.h
index cc24068400a2..bfae75f30c7c 100644
--- a/drivers/media/pci/mgb4/mgb4_core.h
+++ b/drivers/media/pci/mgb4/mgb4_core.h
@@ -18,14 +18,20 @@
#define MGB4_VIN_DEVICES 2
#define MGB4_VOUT_DEVICES 2
-#define MGB4_IS_GMSL(mgbdev) \
- ((((mgbdev)->module_version >> 4) >= 2) && \
- (((mgbdev)->module_version >> 4) <= 4))
+#define MGB4_IS_GMSL1(mgbdev) \
+ (((mgbdev)->module_version >> 4) == 6)
+#define MGB4_IS_GMSL3(mgbdev) \
+ (((((mgbdev)->module_version >> 4) >= 2) && \
+ (((mgbdev)->module_version >> 4) <= 4)) || \
+ (((mgbdev)->module_version >> 4) == 8))
+#define MGB4_IS_GMSL3C(mgbdev) \
+ (((mgbdev)->module_version >> 4) == 8)
#define MGB4_IS_FPDL3(mgbdev) \
(((mgbdev)->module_version >> 4) == 1)
#define MGB4_HAS_VOUT(mgbdev) \
- ((((mgbdev)->module_version >> 4) >= 1) && \
- (((mgbdev)->module_version >> 4) <= 3))
+ (((((mgbdev)->module_version >> 4) >= 1) && \
+ (((mgbdev)->module_version >> 4) <= 3)) || \
+ ((((mgbdev)->module_version >> 4) == 6)))
struct mgb4_dma_channel {
struct dma_chan *chan;
diff --git a/drivers/media/pci/mgb4/mgb4_sysfs.h b/drivers/media/pci/mgb4/mgb4_sysfs.h
index 017d82c0624e..fa7fd232c2c2 100644
--- a/drivers/media/pci/mgb4/mgb4_sysfs.h
+++ b/drivers/media/pci/mgb4/mgb4_sysfs.h
@@ -11,8 +11,10 @@
extern struct attribute *mgb4_pci_attrs[];
extern struct attribute *mgb4_fpdl3_in_attrs[];
-extern struct attribute *mgb4_gmsl_in_attrs[];
+extern struct attribute *mgb4_gmsl3_in_attrs[];
+extern struct attribute *mgb4_gmsl1_in_attrs[];
extern struct attribute *mgb4_fpdl3_out_attrs[];
-extern struct attribute *mgb4_gmsl_out_attrs[];
+extern struct attribute *mgb4_gmsl3_out_attrs[];
+extern struct attribute *mgb4_gmsl1_out_attrs[];
#endif
diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_in.c b/drivers/media/pci/mgb4/mgb4_sysfs_in.c
index 9626fa59e3d3..4cd4addcd0a5 100644
--- a/drivers/media/pci/mgb4/mgb4_sysfs_in.c
+++ b/drivers/media/pci/mgb4/mgb4_sysfs_in.c
@@ -36,10 +36,13 @@ static ssize_t oldi_lane_width_show(struct device *dev,
u32 config;
int ret;
- i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
- i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
- i2c_single_val = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
- i2c_dual_val = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
+ if (MGB4_IS_GMSL1(mgbdev))
+ return sprintf(buf, "0\n");
+
+ i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49;
+ i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03;
+ i2c_single_val = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02;
+ i2c_dual_val = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00;
mutex_lock(&mgbdev->i2c_lock);
ret = mgb4_i2c_read_byte(&vindev->deser, i2c_reg);
@@ -79,21 +82,24 @@ static ssize_t oldi_lane_width_store(struct device *dev,
if (ret)
return ret;
+ if (MGB4_IS_GMSL1(mgbdev))
+ return val ? -EINVAL : count;
+
switch (val) {
case 0: /* single */
fpga_data = 0;
- i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
+ i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02;
break;
case 1: /* dual */
fpga_data = 1U << 9;
- i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
+ i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00;
break;
default:
return -EINVAL;
}
- i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
- i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
+ i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49;
+ i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03;
mutex_lock(&mgbdev->i2c_lock);
ret = mgb4_i2c_mask_byte(&vindev->deser, i2c_reg, i2c_mask, i2c_data);
@@ -102,7 +108,7 @@ static ssize_t oldi_lane_width_store(struct device *dev,
return -EIO;
mgb4_mask_reg(&mgbdev->video, vindev->config->regs.config, 1U << 9,
fpga_data);
- if (MGB4_IS_GMSL(mgbdev)) {
+ if (MGB4_IS_GMSL3(mgbdev)) {
/* reset input link */
mutex_lock(&mgbdev->i2c_lock);
ret = mgb4_i2c_mask_byte(&vindev->deser, 0x10, 1U << 5, 1U << 5);
@@ -745,7 +751,7 @@ struct attribute *mgb4_fpdl3_in_attrs[] = {
NULL
};
-struct attribute *mgb4_gmsl_in_attrs[] = {
+struct attribute *mgb4_gmsl3_in_attrs[] = {
&dev_attr_input_id.attr,
&dev_attr_link_status.attr,
&dev_attr_stream_status.attr,
@@ -770,3 +776,26 @@ struct attribute *mgb4_gmsl_in_attrs[] = {
&dev_attr_gmsl_fec.attr,
NULL
};
+
+struct attribute *mgb4_gmsl1_in_attrs[] = {
+ &dev_attr_input_id.attr,
+ &dev_attr_link_status.attr,
+ &dev_attr_stream_status.attr,
+ &dev_attr_video_width.attr,
+ &dev_attr_video_height.attr,
+ &dev_attr_hsync_status.attr,
+ &dev_attr_vsync_status.attr,
+ &dev_attr_oldi_lane_width.attr,
+ &dev_attr_color_mapping.attr,
+ &dev_attr_hsync_gap_length.attr,
+ &dev_attr_vsync_gap_length.attr,
+ &dev_attr_pclk_frequency.attr,
+ &dev_attr_hsync_width.attr,
+ &dev_attr_vsync_width.attr,
+ &dev_attr_hback_porch.attr,
+ &dev_attr_hfront_porch.attr,
+ &dev_attr_vback_porch.attr,
+ &dev_attr_vfront_porch.attr,
+ &dev_attr_frequency_range.attr,
+ NULL
+};
diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c
index 573aa61c69d4..5769f3ca6c2f 100644
--- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c
+++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c
@@ -665,6 +665,7 @@ static ssize_t pclk_frequency_store(struct device *dev,
{
struct video_device *vdev = to_video_device(dev);
struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
+ struct mgb4_dev *mgbdev = voutdev->mgbdev;
unsigned long val;
int ret;
unsigned int dp;
@@ -679,14 +680,16 @@ static ssize_t pclk_frequency_store(struct device *dev,
return -EBUSY;
}
- dp = (val > 50000) ? 1 : 0;
+ dp = (MGB4_IS_FPDL3(mgbdev) && val > 50000) ? 1 : 0;
voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, val >> dp) << dp;
-
- mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config,
- 0x10, dp << 4);
- mutex_lock(&voutdev->mgbdev->i2c_lock);
- ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, ((~dp) & 1) << 6);
- mutex_unlock(&voutdev->mgbdev->i2c_lock);
+ mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0x10,
+ dp << 4);
+ if (MGB4_IS_FPDL3(mgbdev)) {
+ mutex_lock(&mgbdev->i2c_lock);
+ ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6,
+ ((~dp) & 1) << 6);
+ mutex_unlock(&mgbdev->i2c_lock);
+ }
mutex_unlock(voutdev->vdev.lock);
@@ -731,7 +734,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = {
NULL
};
-struct attribute *mgb4_gmsl_out_attrs[] = {
+struct attribute *mgb4_gmsl3_out_attrs[] = {
&dev_attr_output_id.attr,
&dev_attr_video_source.attr,
&dev_attr_display_width.attr,
@@ -739,3 +742,22 @@ struct attribute *mgb4_gmsl_out_attrs[] = {
&dev_attr_frame_rate.attr,
NULL
};
+
+struct attribute *mgb4_gmsl1_out_attrs[] = {
+ &dev_attr_output_id.attr,
+ &dev_attr_video_source.attr,
+ &dev_attr_display_width.attr,
+ &dev_attr_display_height.attr,
+ &dev_attr_frame_rate.attr,
+ &dev_attr_hsync_polarity.attr,
+ &dev_attr_vsync_polarity.attr,
+ &dev_attr_de_polarity.attr,
+ &dev_attr_pclk_frequency.attr,
+ &dev_attr_hsync_width.attr,
+ &dev_attr_vsync_width.attr,
+ &dev_attr_hback_porch.attr,
+ &dev_attr_hfront_porch.attr,
+ &dev_attr_vback_porch.attr,
+ &dev_attr_vfront_porch.attr,
+ NULL
+};
diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c
index 4b38076486ff..e782db79686f 100644
--- a/drivers/media/pci/mgb4/mgb4_vin.c
+++ b/drivers/media/pci/mgb4/mgb4_vin.c
@@ -32,7 +32,8 @@
#include "mgb4_vin.h"
ATTRIBUTE_GROUPS(mgb4_fpdl3_in);
-ATTRIBUTE_GROUPS(mgb4_gmsl_in);
+ATTRIBUTE_GROUPS(mgb4_gmsl3_in);
+ATTRIBUTE_GROUPS(mgb4_gmsl1_in);
static const struct mgb4_vin_config vin_cfg[] = {
{0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}},
@@ -44,23 +45,43 @@ static const struct i2c_board_info fpdl3_deser_info[] = {
{I2C_BOARD_INFO("deserializer2", 0x36)},
};
-static const struct i2c_board_info gmsl_deser_info[] = {
+static const struct i2c_board_info gmsl3_deser_info[] = {
{I2C_BOARD_INFO("deserializer1", 0x4C)},
{I2C_BOARD_INFO("deserializer2", 0x2A)},
};
+static const struct i2c_board_info gmsl3c_deser_info[] = {
+ {I2C_BOARD_INFO("deserializer1", 0x6A)},
+ {I2C_BOARD_INFO("deserializer2", 0x6C)},
+};
+
+static const struct i2c_board_info gmsl1_deser_info[] = {
+ {I2C_BOARD_INFO("deserializer1", 0x2C)},
+ {I2C_BOARD_INFO("deserializer2", 0x6C)},
+};
+
static const struct mgb4_i2c_kv fpdl3_i2c[] = {
{0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8},
{0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00}
};
-static const struct mgb4_i2c_kv gmsl_i2c[] = {
+static const struct mgb4_i2c_kv gmsl3_i2c[] = {
{0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0},
{0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40},
{0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07},
{0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40}
};
+static const struct mgb4_i2c_kv gmsl3c_i2c[] = {
+ {0x01, 0x03, 0x02}, {0x300, 0x0C, 0x08}, {0x03, 0xC0, 0x00},
+ {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x05}, {0x05, 0xC0, 0x40},
+ {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x00},
+ {0x308, 0x01, 0x00}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40}
+};
+
+static const struct mgb4_i2c_kv gmsl1_i2c[] = {
+};
+
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
@@ -796,22 +817,36 @@ static irqreturn_t err_handler(int irq, void *ctx)
static int deser_init(struct mgb4_vin_dev *vindev, int id)
{
- int rv, addr_size;
- size_t values_count;
- const struct mgb4_i2c_kv *values;
- const struct i2c_board_info *info;
+ int rv, addr_size = 0;
+ size_t count = 0;
+ const struct mgb4_i2c_kv *values = NULL;
+ const struct i2c_board_info *info = NULL;
struct device *dev = &vindev->mgbdev->pdev->dev;
- if (MGB4_IS_GMSL(vindev->mgbdev)) {
- info = &gmsl_deser_info[id];
- addr_size = 16;
- values = gmsl_i2c;
- values_count = ARRAY_SIZE(gmsl_i2c);
- } else {
+ if (MGB4_IS_GMSL3(vindev->mgbdev)) {
+ if (MGB4_IS_GMSL3C(vindev->mgbdev)) {
+ info = &gmsl3c_deser_info[id];
+ addr_size = 16;
+ values = gmsl3c_i2c;
+ count = ARRAY_SIZE(gmsl3c_i2c);
+ } else {
+ info = &gmsl3_deser_info[id];
+ addr_size = 16;
+ values = gmsl3_i2c;
+ count = ARRAY_SIZE(gmsl3_i2c);
+ }
+ } else if (MGB4_IS_FPDL3(vindev->mgbdev)) {
info = &fpdl3_deser_info[id];
addr_size = 8;
values = fpdl3_i2c;
- values_count = ARRAY_SIZE(fpdl3_i2c);
+ count = ARRAY_SIZE(fpdl3_i2c);
+ } else if (MGB4_IS_GMSL1(vindev->mgbdev)) {
+ info = &gmsl1_deser_info[id];
+ addr_size = 8;
+ values = gmsl1_i2c;
+ count = ARRAY_SIZE(gmsl1_i2c);
+ } else {
+ return -EINVAL;
}
rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info,
@@ -820,7 +855,7 @@ static int deser_init(struct mgb4_vin_dev *vindev, int id)
dev_err(dev, "failed to create deserializer\n");
return rv;
}
- rv = mgb4_i2c_configure(&vindev->deser, values, values_count);
+ rv = mgb4_i2c_configure(&vindev->deser, values, count);
if (rv < 0) {
dev_err(dev, "failed to configure deserializer\n");
goto err_i2c_dev;
@@ -838,11 +873,12 @@ static void fpga_init(struct mgb4_vin_dev *vindev)
{
struct mgb4_regs *video = &vindev->mgbdev->video;
const struct mgb4_vin_regs *regs = &vindev->config->regs;
+ int dp = MGB4_IS_GMSL1(vindev->mgbdev) ? 0 : 1;
mgb4_write_reg(video, regs->config, 0x00000001);
mgb4_write_reg(video, regs->sync, 0x03E80002);
mgb4_write_reg(video, regs->padding, 0x00000000);
- mgb4_write_reg(video, regs->config, 1U << 9);
+ mgb4_write_reg(video, regs->config, dp << 9);
}
static void create_debugfs(struct mgb4_vin_dev *vindev)
@@ -890,10 +926,21 @@ static void create_debugfs(struct mgb4_vin_dev *vindev)
#endif
}
+static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev)
+{
+ if (MGB4_IS_FPDL3(mgbdev))
+ return mgb4_fpdl3_in_groups;
+ else if (MGB4_IS_GMSL3(mgbdev))
+ return mgb4_gmsl3_in_groups;
+ else if (MGB4_IS_GMSL1(mgbdev))
+ return mgb4_gmsl1_in_groups;
+ else
+ return NULL;
+}
+
struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
{
int rv;
- const struct attribute_group **groups;
struct mgb4_vin_dev *vindev;
struct pci_dev *pdev = mgbdev->pdev;
struct device *dev = &pdev->dev;
@@ -914,14 +961,13 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
INIT_WORK(&vindev->dma_work, dma_transfer);
INIT_WORK(&vindev->err_work, signal_change);
- /* IRQ callback */
+ /* IRQ callbacks */
vin_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->vin_irq);
rv = request_irq(vin_irq, vin_handler, 0, "mgb4-vin", vindev);
if (rv) {
dev_err(dev, "failed to register vin irq handler\n");
goto err_alloc;
}
- /* Error IRQ callback */
err_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->err_irq);
rv = request_irq(err_irq, err_handler, 0, "mgb4-err", vindev);
if (rv) {
@@ -986,9 +1032,7 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
}
/* Module sysfs attributes */
- groups = MGB4_IS_GMSL(mgbdev)
- ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;
- rv = device_add_groups(&vindev->vdev.dev, groups);
+ rv = device_add_groups(&vindev->vdev.dev, module_groups(mgbdev));
if (rv) {
dev_err(dev, "failed to create sysfs attributes\n");
goto err_video_dev;
@@ -1014,7 +1058,6 @@ err_alloc:
void mgb4_vin_free(struct mgb4_vin_dev *vindev)
{
- const struct attribute_group **groups;
int vin_irq = xdma_get_user_irq(vindev->mgbdev->xdev,
vindev->config->vin_irq);
int err_irq = xdma_get_user_irq(vindev->mgbdev->xdev,
@@ -1025,9 +1068,7 @@ void mgb4_vin_free(struct mgb4_vin_dev *vindev)
free_irq(vin_irq, vindev);
free_irq(err_irq, vindev);
- groups = MGB4_IS_GMSL(vindev->mgbdev)
- ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;
- device_remove_groups(&vindev->vdev.dev, groups);
+ device_remove_groups(&vindev->vdev.dev, module_groups(vindev->mgbdev));
mgb4_i2c_free(&vindev->deser);
video_unregister_device(&vindev->vdev);
diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c
index fd93fbbaf755..44e9565d4d06 100644
--- a/drivers/media/pci/mgb4/mgb4_vout.c
+++ b/drivers/media/pci/mgb4/mgb4_vout.c
@@ -25,7 +25,8 @@
#include "mgb4_vout.h"
ATTRIBUTE_GROUPS(mgb4_fpdl3_out);
-ATTRIBUTE_GROUPS(mgb4_gmsl_out);
+ATTRIBUTE_GROUPS(mgb4_gmsl3_out);
+ATTRIBUTE_GROUPS(mgb4_gmsl1_out);
static const struct mgb4_vout_config vout_cfg[] = {
{0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7C, 0xE0}},
@@ -37,10 +38,18 @@ static const struct i2c_board_info fpdl3_ser_info[] = {
{I2C_BOARD_INFO("serializer2", 0x16)},
};
+static const struct i2c_board_info gmsl1_ser_info[] = {
+ {I2C_BOARD_INFO("serializer1", 0x24)},
+ {I2C_BOARD_INFO("serializer2", 0x22)},
+};
+
static const struct mgb4_i2c_kv fpdl3_i2c[] = {
{0x05, 0xFF, 0x04}, {0x06, 0xFF, 0x01}, {0xC2, 0xFF, 0x80}
};
+static const struct mgb4_i2c_kv gmsl1_i2c[] = {
+};
+
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
@@ -634,12 +643,24 @@ static irqreturn_t handler(int irq, void *ctx)
static int ser_init(struct mgb4_vout_dev *voutdev, int id)
{
- int rv;
- const struct i2c_board_info *info = &fpdl3_ser_info[id];
struct mgb4_i2c_client *ser = &voutdev->ser;
struct device *dev = &voutdev->mgbdev->pdev->dev;
+ const struct i2c_board_info *info = NULL;
+ const struct mgb4_i2c_kv *values = NULL;
+ size_t count = 0;
+ int rv;
+
+ if (MGB4_IS_FPDL3(voutdev->mgbdev)) {
+ info = &fpdl3_ser_info[id];
+ values = fpdl3_i2c;
+ count = ARRAY_SIZE(fpdl3_i2c);
+ } else if (MGB4_IS_GMSL1(voutdev->mgbdev)) {
+ info = &gmsl1_ser_info[id];
+ values = gmsl1_i2c;
+ count = ARRAY_SIZE(gmsl1_i2c);
+ }
- if (MGB4_IS_GMSL(voutdev->mgbdev))
+ if (!info)
return 0;
rv = mgb4_i2c_init(ser, voutdev->mgbdev->i2c_adap, info, 8);
@@ -647,7 +668,7 @@ static int ser_init(struct mgb4_vout_dev *voutdev, int id)
dev_err(dev, "failed to create serializer\n");
return rv;
}
- rv = mgb4_i2c_configure(ser, fpdl3_i2c, ARRAY_SIZE(fpdl3_i2c));
+ rv = mgb4_i2c_configure(ser, values, count);
if (rv < 0) {
dev_err(dev, "failed to configure serializer\n");
goto err_i2c_dev;
@@ -665,18 +686,19 @@ static void fpga_init(struct mgb4_vout_dev *voutdev)
{
struct mgb4_regs *video = &voutdev->mgbdev->video;
const struct mgb4_vout_regs *regs = &voutdev->config->regs;
+ int dp = MGB4_IS_GMSL1(voutdev->mgbdev) ? 0 : 1;
+ u32 source = (voutdev->config->id + MGB4_VIN_DEVICES) << 2;
- mgb4_write_reg(video, regs->config, 0x00000011);
+ mgb4_write_reg(video, regs->config, 0x00000001);
mgb4_write_reg(video, regs->resolution, (1280 << 16) | 640);
mgb4_write_reg(video, regs->hsync, 0x00283232);
mgb4_write_reg(video, regs->vsync, 0x40141F1E);
mgb4_write_reg(video, regs->frame_limit, MGB4_HW_FREQ / 60);
mgb4_write_reg(video, regs->padding, 0x00000000);
- voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> 1) << 1;
+ voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> dp) << dp;
- mgb4_write_reg(video, regs->config,
- (voutdev->config->id + MGB4_VIN_DEVICES) << 2 | 1 << 4);
+ mgb4_write_reg(video, regs->config, source | dp << 4);
}
static void create_debugfs(struct mgb4_vout_dev *voutdev)
@@ -720,10 +742,21 @@ static void create_debugfs(struct mgb4_vout_dev *voutdev)
#endif
}
+static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev)
+{
+ if (MGB4_IS_FPDL3(mgbdev))
+ return mgb4_fpdl3_out_groups;
+ else if (MGB4_IS_GMSL3(mgbdev))
+ return mgb4_gmsl3_out_groups;
+ else if (MGB4_IS_GMSL1(mgbdev))
+ return mgb4_gmsl1_out_groups;
+ else
+ return NULL;
+}
+
struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id)
{
int rv, irq;
- const struct attribute_group **groups;
struct mgb4_vout_dev *voutdev;
struct pci_dev *pdev = mgbdev->pdev;
struct device *dev = &pdev->dev;
@@ -804,9 +837,7 @@ struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id)
}
/* Module sysfs attributes */
- groups = MGB4_IS_GMSL(mgbdev)
- ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups;
- rv = device_add_groups(&voutdev->vdev.dev, groups);
+ rv = device_add_groups(&voutdev->vdev.dev, module_groups(mgbdev));
if (rv) {
dev_err(dev, "failed to create sysfs attributes\n");
goto err_video_dev;
@@ -830,15 +861,10 @@ err_alloc:
void mgb4_vout_free(struct mgb4_vout_dev *voutdev)
{
- const struct attribute_group **groups;
int irq = xdma_get_user_irq(voutdev->mgbdev->xdev, voutdev->config->irq);
free_irq(irq, voutdev);
-
- groups = MGB4_IS_GMSL(voutdev->mgbdev)
- ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups;
- device_remove_groups(&voutdev->vdev.dev, groups);
-
+ device_remove_groups(&voutdev->vdev.dev, module_groups(voutdev->mgbdev));
mgb4_i2c_free(&voutdev->ser);
video_unregister_device(&voutdev->vdev);
v4l2_device_unregister(&voutdev->v4l2dev);
diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c
index 1b7c22a9bc94..8f53946c6792 100644
--- a/drivers/media/pci/solo6x10/solo6x10-tw28.c
+++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c
@@ -166,7 +166,7 @@ static const u8 tbl_tw2865_pal_template[] = {
0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
};
-#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
+#define is_tw286x(__solo, __id) (!((__solo)->tw2815 & (1U << (__id))))
static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
u8 tw_off)
@@ -686,6 +686,9 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
chip_num = ch / 4;
ch %= 4;
+ if (chip_num >= TW_NUM_CHIP)
+ return -EINVAL;
+
if (val > 255 || val < 0)
return -ERANGE;
@@ -758,6 +761,9 @@ int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
chip_num = ch / 4;
ch %= 4;
+ if (chip_num >= TW_NUM_CHIP)
+ return -EINVAL;
+
switch (ctrl) {
case V4L2_CID_SHARPNESS:
/* Only 286x has sharpness */
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index c0d2aabb9e0e..adf53b49020e 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -9,7 +9,6 @@
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/vmalloc.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -17,7 +16,6 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/videobuf2-vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_core.h"
@@ -724,6 +722,7 @@ static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd
switch (cmd->cmd) {
case V4L2_DEC_CMD_START:
vdec_cmd_start(inst);
+ vb2_clear_last_buffer_dequeued(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx));
break;
case V4L2_DEC_CMD_STOP:
vdec_cmd_stop(inst);
@@ -949,7 +948,7 @@ static void vdec_stop_done(struct vpu_inst *inst)
vpu_inst_unlock(inst);
}
-static bool vdec_check_source_change(struct vpu_inst *inst)
+static bool vdec_check_source_change(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr)
{
struct vdec_t *vdec = inst->priv;
const struct vpu_format *sibling;
@@ -961,26 +960,35 @@ static bool vdec_check_source_change(struct vpu_inst *inst)
return false;
sibling = vpu_helper_find_sibling(inst, inst->cap_format.type, inst->cap_format.pixfmt);
- if (sibling && vdec->codec_info.pixfmt == sibling->pixfmt)
- vdec->codec_info.pixfmt = inst->cap_format.pixfmt;
+ if (sibling && hdr->pixfmt == sibling->pixfmt)
+ hdr->pixfmt = inst->cap_format.pixfmt;
if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx)))
return true;
- if (inst->cap_format.pixfmt != vdec->codec_info.pixfmt)
+ if (inst->cap_format.pixfmt != hdr->pixfmt)
return true;
- if (inst->cap_format.width != vdec->codec_info.decoded_width)
+ if (inst->cap_format.width != hdr->decoded_width)
return true;
- if (inst->cap_format.height != vdec->codec_info.decoded_height)
+ if (inst->cap_format.height != hdr->decoded_height)
return true;
if (vpu_get_num_buffers(inst, inst->cap_format.type) < inst->min_buffer_cap)
return true;
- if (inst->crop.left != vdec->codec_info.offset_x)
+ if (inst->crop.left != hdr->offset_x)
return true;
- if (inst->crop.top != vdec->codec_info.offset_y)
+ if (inst->crop.top != hdr->offset_y)
return true;
- if (inst->crop.width != vdec->codec_info.width)
+ if (inst->crop.width != hdr->width)
return true;
- if (inst->crop.height != vdec->codec_info.height)
+ if (inst->crop.height != hdr->height)
+ return true;
+ if (!hdr->progressive)
+ return true;
+
+ if (vdec->seq_hdr_found &&
+ (hdr->color_primaries != vdec->codec_info.color_primaries ||
+ hdr->transfer_chars != vdec->codec_info.transfer_chars ||
+ hdr->matrix_coeffs != vdec->codec_info.matrix_coeffs ||
+ hdr->full_range != vdec->codec_info.full_range))
return true;
return false;
@@ -1332,20 +1340,25 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info
struct vdec_t *vdec = inst->priv;
vpu_inst_lock(inst);
- memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info));
- vpu_trace(inst->dev, "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d\n",
+ vpu_trace(inst->dev,
+ "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d, colorspace: %d, %d, %d, %d\n",
inst->id,
- vdec->codec_info.decoded_width,
- vdec->codec_info.decoded_height,
- vdec->codec_info.offset_x,
- vdec->codec_info.offset_y,
- vdec->codec_info.width,
- vdec->codec_info.height,
+ hdr->decoded_width,
+ hdr->decoded_height,
+ hdr->offset_x,
+ hdr->offset_y,
+ hdr->width,
+ hdr->height,
hdr->num_ref_frms,
- hdr->num_dpb_frms);
+ hdr->num_dpb_frms,
+ hdr->color_primaries,
+ hdr->transfer_chars,
+ hdr->matrix_coeffs,
+ hdr->full_range);
inst->min_buffer_cap = hdr->num_ref_frms + hdr->num_dpb_frms;
- vdec->is_source_changed = vdec_check_source_change(inst);
+ vdec->is_source_changed = vdec_check_source_change(inst, hdr);
+ memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info));
vdec_init_fmt(inst);
vdec_init_crop(inst);
vdec_init_mbi(inst);
@@ -1374,7 +1387,12 @@ static void vdec_event_resolution_change(struct vpu_inst *inst)
{
struct vdec_t *vdec = inst->priv;
- vpu_trace(inst->dev, "[%d]\n", inst->id);
+ vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n",
+ inst->id,
+ vdec->params.frame_count,
+ vdec->decoded_frame_count,
+ vdec->display_frame_count,
+ vdec->sequence);
vpu_inst_lock(inst);
vdec->seq_tag = vdec->codec_info.tag;
vdec_clear_fs(&vdec->mbi);
@@ -1642,9 +1660,9 @@ static void vdec_cleanup(struct vpu_inst *inst)
vdec->slots = NULL;
vdec->slot_count = 0;
}
- vfree(vdec);
+ kfree(vdec);
inst->priv = NULL;
- vfree(inst);
+ kfree(inst);
}
static void vdec_init_params(struct vdec_t *vdec)
@@ -1909,13 +1927,13 @@ static int vdec_open(struct file *file)
struct vdec_t *vdec;
int ret;
- inst = vzalloc(sizeof(*inst));
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst)
return -ENOMEM;
- vdec = vzalloc(sizeof(*vdec));
+ vdec = kzalloc(sizeof(*vdec), GFP_KERNEL);
if (!vdec) {
- vfree(inst);
+ kfree(inst);
return -ENOMEM;
}
@@ -1923,8 +1941,8 @@ static int vdec_open(struct file *file)
sizeof(*vdec->slots),
GFP_KERNEL | __GFP_ZERO);
if (!vdec->slots) {
- vfree(vdec);
- vfree(inst);
+ kfree(vdec);
+ kfree(inst);
return -ENOMEM;
}
vdec->slot_count = VDEC_SLOT_CNT_DFT;
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index aced76401b69..9e5cbc2b0d3f 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -13,14 +13,12 @@
#include <linux/videodev2.h>
#include <linux/ktime.h>
#include <linux/rational.h>
-#include <linux/vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/videobuf2-vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_core.h"
@@ -844,7 +842,7 @@ static int venc_get_encoded_frames(struct vpu_inst *inst)
v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx)))
break;
list_del_init(&frame->list);
- vfree(frame);
+ kfree(frame);
}
return 0;
@@ -860,7 +858,7 @@ static int venc_frame_encoded(struct vpu_inst *inst, void *arg)
if (!info)
return -EINVAL;
venc = inst->priv;
- frame = vzalloc(sizeof(*frame));
+ frame = kzalloc(sizeof(*frame), GFP_KERNEL);
if (!frame)
return -ENOMEM;
@@ -912,9 +910,9 @@ static void venc_cleanup(struct vpu_inst *inst)
return;
venc = inst->priv;
- vfree(venc);
+ kfree(venc);
inst->priv = NULL;
- vfree(inst);
+ kfree(inst);
}
static int venc_start_session(struct vpu_inst *inst, u32 type)
@@ -1067,7 +1065,7 @@ static void venc_cleanup_frames(struct venc_t *venc)
list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
list_del_init(&frame->list);
- vfree(frame);
+ kfree(frame);
}
}
@@ -1151,7 +1149,7 @@ static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb)
return ret;
list_del_init(&frame->list);
- vfree(frame);
+ kfree(frame);
return 0;
}
@@ -1309,13 +1307,13 @@ static int venc_open(struct file *file)
struct venc_t *venc;
int ret;
- inst = vzalloc(sizeof(*inst));
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst)
return -ENOMEM;
- venc = vzalloc(sizeof(*venc));
+ venc = kzalloc(sizeof(*venc), GFP_KERNEL);
if (!venc) {
- vfree(inst);
+ kfree(inst);
return -ENOMEM;
}
diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c
index 5695f5c1cb3e..ab69412e0aa7 100644
--- a/drivers/media/platform/amphion/vpu_cmds.c
+++ b/drivers/media/platform/amphion/vpu_cmds.c
@@ -13,7 +13,6 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
-#include <linux/vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_cmds.h"
@@ -84,13 +83,13 @@ static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data
int i;
int ret;
- cmd = vzalloc(sizeof(*cmd));
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return NULL;
- cmd->pkt = vzalloc(sizeof(*cmd->pkt));
+ cmd->pkt = kzalloc(sizeof(*cmd->pkt), GFP_KERNEL);
if (!cmd->pkt) {
- vfree(cmd);
+ kfree(cmd);
return NULL;
}
@@ -98,8 +97,8 @@ static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data
ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data);
if (ret) {
dev_err(inst->dev, "iface pack cmd %s fail\n", vpu_id_name(id));
- vfree(cmd->pkt);
- vfree(cmd);
+ kfree(cmd->pkt);
+ kfree(cmd);
return NULL;
}
for (i = 0; i < ARRAY_SIZE(vpu_cmd_requests); i++) {
@@ -118,8 +117,8 @@ static void vpu_free_cmd(struct vpu_cmd_t *cmd)
return;
if (cmd->last_response_cmd)
atomic_long_set(cmd->last_response_cmd, cmd->key);
- vfree(cmd->pkt);
- vfree(cmd);
+ kfree(cmd->pkt);
+ kfree(cmd);
}
static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd)
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index 168f0514851e..85cc4a14f8ed 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -17,7 +17,6 @@
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/firmware.h>
-#include <linux/vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_core.h"
@@ -265,7 +264,7 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core)
INIT_WORK(&core->msg_work, vpu_msg_run_work);
INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work);
buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
- core->msg_buffer = vzalloc(buffer_size);
+ core->msg_buffer = kzalloc(buffer_size, GFP_KERNEL);
if (!core->msg_buffer) {
dev_err(core->dev, "failed allocate buffer for fifo\n");
ret = -ENOMEM;
@@ -282,10 +281,8 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core)
return 0;
error:
- if (core->msg_buffer) {
- vfree(core->msg_buffer);
- core->msg_buffer = NULL;
- }
+ kfree(core->msg_buffer);
+ core->msg_buffer = NULL;
if (core->workqueue) {
destroy_workqueue(core->workqueue);
core->workqueue = NULL;
@@ -308,7 +305,7 @@ static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
vpu_core_put_vpu(core);
core->vpu = NULL;
- vfree(core->msg_buffer);
+ kfree(core->msg_buffer);
core->msg_buffer = NULL;
if (core->workqueue) {
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index 47dff9a35bb4..64fc88d89ccc 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -102,7 +102,6 @@ static int vpu_notify_eos(struct vpu_inst *inst)
int vpu_notify_source_change(struct vpu_inst *inst)
{
static const struct v4l2_event ev = {
- .id = 0,
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION
};
@@ -670,7 +669,6 @@ 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_queued_buffers = 1;
src_vq->dev = inst->vpu->dev;
src_vq->lock = &inst->lock;
ret = vb2_queue_init(src_vq);
@@ -687,7 +685,6 @@ 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_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 b83e43245277..41cb96f60110 100644
--- a/drivers/media/platform/aspeed/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -26,6 +26,7 @@
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/ktime.h>
+#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <media/v4l2-ctrls.h>
@@ -310,6 +311,7 @@ struct aspeed_video {
void __iomem *base;
struct clk *eclk;
struct clk *vclk;
+ struct reset_control *reset;
struct device *dev;
struct v4l2_ctrl_handler ctrl_handler;
@@ -720,6 +722,13 @@ static void aspeed_video_on(struct aspeed_video *video)
set_bit(VIDEO_CLOCKS_ON, &video->flags);
}
+static void aspeed_video_reset(struct aspeed_video *v)
+{
+ reset_control_assert(v->reset);
+ usleep_range(100, 150);
+ reset_control_deassert(v->reset);
+}
+
static void aspeed_video_bufs_done(struct aspeed_video *video,
enum vb2_buffer_state state)
{
@@ -742,7 +751,9 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay)
video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
- aspeed_video_off(video);
+ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
+ aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff);
+ aspeed_video_reset(video);
aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
schedule_delayed_work(&video->res_work, delay);
@@ -1984,8 +1995,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q)
* Need to force stop any DMA and try and get HW into a good
* state for future calls to start streaming again.
*/
- aspeed_video_off(video);
- aspeed_video_on(video);
+ aspeed_video_reset(video);
aspeed_video_init_regs(video);
@@ -2230,6 +2240,12 @@ static int aspeed_video_init(struct aspeed_video *video)
}
dev_info(video->dev, "irq %d\n", irq);
+ video->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(video->reset)) {
+ dev_err(dev, "Unable to get reset\n");
+ return PTR_ERR(video->reset);
+ }
+
video->eclk = devm_clk_get(dev, "eclk");
if (IS_ERR(video->eclk)) {
dev_err(dev, "Unable to get ECLK\n");
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
index f03ad9c0de22..53a0ac068c2e 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
@@ -27,6 +27,11 @@ const char *state_to_str(enum vpu_instance_state state)
}
}
+int wave5_kfifo_alloc(struct vpu_instance *inst)
+{
+ return kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
+}
+
void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp)
{
int i;
@@ -49,7 +54,7 @@ void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp)
v4l2_fh_del(&inst->v4l2_fh, filp);
v4l2_fh_exit(&inst->v4l2_fh);
}
- list_del_init(&inst->list);
+ kfifo_free(&inst->irq_status);
ida_free(&inst->dev->inst_ida, inst->id);
kfree(inst->codec_info);
kfree(inst);
@@ -61,8 +66,29 @@ int wave5_vpu_release_device(struct file *filp,
{
struct vpu_instance *inst = file_to_vpu_inst(filp);
int ret = 0;
+ unsigned long flags;
v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
+ /*
+ * To prevent Null reference exception, the existing irq handler were
+ * separated to two modules.
+ * One is to queue interrupt reason into the irq handler,
+ * the other is irq_thread to call the wave5_vpu_dec_finish_decode
+ * to get decoded frame.
+ * The list of instances should be protected between all flow of the
+ * decoding process, but to protect the list in the irq_handler, spin lock
+ * should be used, and mutex should be used in the irq_thread because spin lock
+ * is not able to be used because mutex is already being used
+ * in the wave5_vpu_dec_finish_decode.
+ * So the spin lock and mutex were used to protect the list in the release function.
+ */
+ ret = mutex_lock_interruptible(&inst->dev->irq_lock);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&inst->dev->irq_spinlock, flags);
+ list_del_init(&inst->list);
+ spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags);
+ mutex_unlock(&inst->dev->irq_lock);
if (inst->state != VPU_INST_STATE_NONE) {
u32 fail_res;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
index 976a402e426f..d61fdbda359d 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
@@ -33,4 +33,5 @@ void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp,
unsigned int width,
unsigned int height,
const struct v4l2_frmsize_stepwise *frmsize);
+int wave5_kfifo_alloc(struct vpu_instance *inst);
#endif
diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
index d94cf84c3ee5..687ce6ccf3ae 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
@@ -102,7 +102,7 @@ static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_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);
+ dev_dbg(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);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index e3038c18ca36..8917542b993c 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -136,6 +136,18 @@ valid_state_switch:
return 0;
}
+static int set_instance_state(struct vpu_instance *inst, enum vpu_instance_state state)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+ ret = switch_state(inst, state);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
+ return ret;
+}
+
static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
{
int ret;
@@ -227,7 +239,7 @@ static int start_decode(struct vpu_instance *inst, u32 *fail_res)
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);
+ set_instance_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);
@@ -268,6 +280,7 @@ static void send_eos_event(struct vpu_instance *inst)
v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
inst->eos = false;
+ inst->sent_eos = true;
}
static int handle_dynamic_resolution_change(struct vpu_instance *inst)
@@ -347,13 +360,12 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
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__);
+ dev_dbg(inst->dev->dev, "%s: could not get output info.", __func__);
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
return;
}
@@ -442,18 +454,17 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *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__);
- pm_runtime_put_autosuspend(inst->dev->dev);
- v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ if (inst->sent_eos &&
+ v4l2_m2m_get_curr_priv(inst->v4l2_m2m_dev)) {
+ struct queue_status_info q_status;
+
+ 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)
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
+
+ inst->queuing_fail = false;
}
static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
@@ -1142,11 +1153,30 @@ static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t b
return 0;
}
+static struct vpu_src_buffer *inst_src_buf_remove(struct vpu_instance *inst)
+{
+ struct vpu_src_buffer *b;
+ int ret;
+
+ ret = mutex_lock_interruptible(&inst->feed_lock);
+ if (ret)
+ return NULL;
+
+ if (list_empty(&inst->avail_src_bufs)) {
+ mutex_unlock(&inst->feed_lock);
+ return NULL;
+ }
+ b = list_first_entry(&inst->avail_src_bufs, struct vpu_src_buffer, list);
+ list_del_init(&b->list);
+ mutex_unlock(&inst->feed_lock);
+ return b;
+}
+
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;
+ struct vpu_src_buffer *vpu_buf;
+ int ret = 0;
if (m2m_ctx->last_src_buf) {
struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
@@ -1157,9 +1187,8 @@ static int fill_ringbuffer(struct vpu_instance *inst)
}
}
- 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);
+ while ((vpu_buf = inst_src_buf_remove(inst)) != NULL) {
+ struct vb2_v4l2_buffer *vbuf = &vpu_buf->v4l2_m2m_buf.vb;
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);
@@ -1219,9 +1248,12 @@ static int fill_ringbuffer(struct vpu_instance *inst)
dev_dbg(inst->dev->dev, "last src buffer written to the ring buffer\n");
break;
}
+
+ inst->queuing_num++;
+ break;
}
- return 0;
+ return ret;
}
static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb)
@@ -1230,10 +1262,16 @@ static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb)
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);
+ int ret;
vpu_buf->consumed = false;
vbuf->sequence = inst->queued_src_buf_num++;
-
+ ret = mutex_lock_interruptible(&inst->feed_lock);
+ if (ret)
+ return;
+ INIT_LIST_HEAD(&vpu_buf->list);
+ list_add_tail(&vpu_buf->list, &inst->avail_src_bufs);
+ mutex_unlock(&inst->feed_lock);
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
}
@@ -1243,6 +1281,7 @@ static void wave5_vpu_dec_buf_queue_dst(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;
+ pm_runtime_resume_and_get(inst->dev->dev);
vbuf->sequence = inst->queued_dst_buf_num++;
if (inst->state == VPU_INST_STATE_PIC_RUN) {
@@ -1275,6 +1314,7 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
} else {
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
}
+ pm_runtime_put_autosuspend(inst->dev->dev);
}
static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb)
@@ -1286,10 +1326,13 @@ static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb)
__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)
+ 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)
+ if (inst->empty_queue)
+ inst->empty_queue = false;
+ } 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)
@@ -1383,6 +1426,12 @@ static int streamoff_output(struct vb2_queue *q)
dma_addr_t new_rd_ptr;
struct dec_output_info dec_info;
unsigned int i;
+ struct vpu_src_buffer *vpu_buf;
+
+ inst->retry = false;
+ inst->queuing_num = 0;
+ while ((vpu_buf = inst_src_buf_remove(inst)) != NULL)
+ ;
for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) {
ret = wave5_vpu_dec_set_disp_flag(inst, i);
@@ -1468,21 +1517,21 @@ 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);
pm_runtime_resume_and_get(inst->dev->dev);
-
+ inst->empty_queue = true;
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)
+ if ((inst->state == VPU_INST_STATE_STOP ||
+ inst->state == VPU_INST_STATE_INIT_SEQ ||
+ q_status.instance_queue_count == 0) &&
+ q_status.report_queue_count == 0)
break;
if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
@@ -1496,6 +1545,8 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
else
streamoff_capture(q);
+ inst->empty_queue = false;
+ inst->sent_eos = false;
pm_runtime_put_autosuspend(inst->dev->dev);
}
@@ -1577,10 +1628,18 @@ static void wave5_vpu_dec_device_run(void *priv)
dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
pm_runtime_resume_and_get(inst->dev->dev);
- ret = fill_ringbuffer(inst);
- if (ret) {
- dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
- goto finish_job_and_return;
+ if (!inst->retry) {
+ ret = fill_ringbuffer(inst);
+ if (ret < 0) {
+ dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
+ goto finish_job_and_return;
+ } else if (!inst->eos &&
+ inst->queuing_num == 0 &&
+ inst->state == VPU_INST_STATE_PIC_RUN) {
+ dev_dbg(inst->dev->dev, "%s: no bitstream for feeding, so skip ", __func__);
+ inst->empty_queue = true;
+ goto finish_job_and_return;
+ }
}
switch (inst->state) {
@@ -1605,7 +1664,7 @@ static void wave5_vpu_dec_device_run(void *priv)
}
spin_unlock_irqrestore(&inst->state_spinlock, flags);
} else {
- switch_state(inst, VPU_INST_STATE_INIT_SEQ);
+ set_instance_state(inst, VPU_INST_STATE_INIT_SEQ);
}
break;
@@ -1616,8 +1675,7 @@ static void wave5_vpu_dec_device_run(void *priv)
* we had a chance to switch, which leads to an invalid state
* change.
*/
- switch_state(inst, VPU_INST_STATE_PIC_RUN);
-
+ set_instance_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.
@@ -1631,14 +1689,12 @@ static void wave5_vpu_dec_device_run(void *priv)
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);
+ set_instance_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;
- }
+ if (q_status.instance_queue_count)
+ goto finish_job_and_return;
fallthrough;
case VPU_INST_STATE_PIC_RUN:
@@ -1647,28 +1703,45 @@ static void wave5_vpu_dec_device_run(void *priv)
dev_err(inst->dev->dev,
"Frame decoding on m2m context (%p), fail: %d (result: %d)\n",
m2m_ctx, ret, fail_res);
- break;
+ goto finish_job_and_return;
+ }
+
+ if (fail_res == WAVE5_SYSERR_QUEUEING_FAIL) {
+ inst->retry = true;
+ inst->queuing_fail = true;
+ } else {
+ inst->retry = false;
+ if (!inst->eos)
+ inst->queuing_num--;
}
- /* 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;
+ default:
+ dev_dbg(inst->dev->dev, "Execution of a job in state %s illegal.\n",
+ state_to_str(inst->state));
}
finish_job_and_return:
dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
pm_runtime_put_autosuspend(inst->dev->dev);
- v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ /*
+ * After receiving CMD_STOP, there is no input, but we have to run device_run
+ * to send DEC_PIC command until display index == -1, so job_finish was always
+ * called in the device_run to archive it, the logic was very wasteful
+ * in power and CPU time.
+ * If EOS is passed, device_run will not call job_finish no more, it is called
+ * only if HW is idle status in order to reduce overhead.
+ */
+ if (!inst->sent_eos)
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
static void wave5_vpu_dec_job_abort(void *priv)
{
struct vpu_instance *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
int ret;
- ret = switch_state(inst, VPU_INST_STATE_STOP);
+ ret = set_instance_state(inst, VPU_INST_STATE_STOP);
if (ret)
return;
@@ -1676,6 +1749,8 @@ static void wave5_vpu_dec_job_abort(void *priv)
if (ret)
dev_warn(inst->dev->dev,
"Setting EOS for the bitstream, fail: %d\n", ret);
+
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
static int wave5_vpu_dec_job_ready(void *priv)
@@ -1711,10 +1786,15 @@ static int wave5_vpu_dec_job_ready(void *priv)
"No capture buffer ready to decode!\n");
break;
} else if (!wave5_is_draining_or_eos(inst) &&
- !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
+ (!v4l2_m2m_num_src_bufs_ready(m2m_ctx) ||
+ inst->empty_queue)) {
dev_dbg(inst->dev->dev,
"No bitstream data to decode!\n");
break;
+ } else if (inst->state == VPU_INST_STATE_PIC_RUN &&
+ !wave5_is_draining_or_eos(inst) &&
+ inst->queuing_fail) {
+ break;
}
ret = 1;
break;
@@ -1751,10 +1831,14 @@ static int wave5_vpu_open_dec(struct file *filp)
inst->ops = &wave5_vpu_dec_inst_ops;
spin_lock_init(&inst->state_spinlock);
+ mutex_init(&inst->feed_lock);
+ INIT_LIST_HEAD(&inst->avail_src_bufs);
inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
- if (!inst->codec_info)
+ if (!inst->codec_info) {
+ kfree(inst);
return -ENOMEM;
+ }
v4l2_fh_init(&inst->v4l2_fh, vdev);
v4l2_fh_add(&inst->v4l2_fh, filp);
@@ -1806,6 +1890,11 @@ static int wave5_vpu_open_dec(struct file *filp)
inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
init_completion(&inst->irq_done);
+ ret = wave5_kfifo_alloc(inst);
+ if (ret) {
+ dev_err(inst->dev->dev, "failed to allocate fifo\n");
+ goto cleanup_inst;
+ }
inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
if (inst->id < 0) {
@@ -1825,9 +1914,6 @@ static int wave5_vpu_open_dec(struct file *filp)
if (ret)
goto cleanup_inst;
- if (list_empty(&dev->instances))
- pm_runtime_use_autosuspend(inst->dev->dev);
-
list_add_tail(&inst->list, &dev->instances);
mutex_unlock(&dev->dev_lock);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index 9bfaa9fb3ceb..24fc0d0d3f4a 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -649,6 +649,8 @@ static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_en
m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
m2m_ctx->is_draining = true;
+
+ v4l2_m2m_try_schedule(m2m_ctx);
break;
case V4L2_ENC_CMD_START:
break;
@@ -1367,7 +1369,8 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count
if (ret)
goto return_buffers;
}
- if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) {
+ if (inst->state == VPU_INST_STATE_OPEN &&
+ (m2m_ctx->cap_q_ctx.q.streaming || q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
ret = initialize_sequence(inst);
if (ret) {
dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret);
@@ -1578,8 +1581,10 @@ static int wave5_vpu_open_enc(struct file *filp)
inst->ops = &wave5_vpu_enc_inst_ops;
inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
- if (!inst->codec_info)
+ if (!inst->codec_info) {
+ kfree(inst);
return -ENOMEM;
+ }
v4l2_fh_init(&inst->v4l2_fh, vdev);
v4l2_fh_add(&inst->v4l2_fh, filp);
@@ -1754,6 +1759,11 @@ static int wave5_vpu_open_enc(struct file *filp)
inst->frame_rate = 30;
init_completion(&inst->irq_done);
+ ret = wave5_kfifo_alloc(inst);
+ if (ret) {
+ dev_err(inst->dev->dev, "failed to allocate fifo\n");
+ goto cleanup_inst;
+ }
inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
if (inst->id < 0) {
@@ -1768,9 +1778,6 @@ static int wave5_vpu_open_enc(struct file *filp)
if (ret)
goto cleanup_inst;
- if (list_empty(&dev->instances))
- pm_runtime_use_autosuspend(inst->dev->dev);
-
list_add_tail(&inst->list, &dev->instances);
mutex_unlock(&dev->dev_lock);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
index e1715d3f43b0..76d57c6b636a 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -51,8 +51,11 @@ static void wave5_vpu_handle_irq(void *dev_id)
u32 seq_done;
u32 cmd_done;
u32 irq_reason;
- struct vpu_instance *inst;
+ u32 irq_subreason;
+ struct vpu_instance *inst, *tmp;
struct vpu_device *dev = dev_id;
+ int val;
+ unsigned long flags;
irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
@@ -60,7 +63,8 @@ static void wave5_vpu_handle_irq(void *dev_id)
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) {
+ spin_lock_irqsave(&dev->irq_spinlock, flags);
+ list_for_each_entry_safe(inst, tmp, &dev->instances, list) {
if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
@@ -82,22 +86,54 @@ static void wave5_vpu_handle_irq(void *dev_id)
irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
if (cmd_done & BIT(inst->id)) {
cmd_done &= ~BIT(inst->id);
- wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
- cmd_done);
- inst->ops->finish_process(inst);
+ if (dev->irq >= 0) {
+ irq_subreason =
+ wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
+ if (!(irq_subreason & BIT(INT_WAVE5_DEC_PIC)))
+ wave5_vdi_write_register(dev,
+ W5_RET_QUEUE_CMD_DONE_INST,
+ cmd_done);
+ }
+ val = BIT(INT_WAVE5_DEC_PIC);
+ kfifo_in(&inst->irq_status, &val, sizeof(int));
}
}
+ }
+ spin_unlock_irqrestore(&dev->irq_spinlock, flags);
+
+ if (dev->irq < 0)
+ up(&dev->irq_sem);
+}
- wave5_vpu_clear_interrupt(inst, irq_reason);
+static irqreturn_t wave5_vpu_irq(int irq, void *dev_id)
+{
+ struct vpu_device *dev = dev_id;
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
+ wave5_vpu_handle_irq(dev);
+ return IRQ_WAKE_THREAD;
}
+
+ return IRQ_HANDLED;
}
static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
{
struct vpu_device *dev = dev_id;
+ struct vpu_instance *inst, *tmp;
+ int irq_status, ret;
- if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS))
- wave5_vpu_handle_irq(dev);
+ mutex_lock(&dev->irq_lock);
+ list_for_each_entry_safe(inst, tmp, &dev->instances, list) {
+ while (kfifo_len(&inst->irq_status)) {
+ ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
+ if (!ret)
+ break;
+
+ inst->ops->finish_process(inst);
+ }
+ }
+ mutex_unlock(&dev->irq_lock);
return IRQ_HANDLED;
}
@@ -121,6 +157,35 @@ static enum hrtimer_restart wave5_vpu_timer_callback(struct hrtimer *timer)
return HRTIMER_RESTART;
}
+static int irq_thread(void *data)
+{
+ struct vpu_device *dev = (struct vpu_device *)data;
+ struct vpu_instance *inst, *tmp;
+ int irq_status, ret;
+
+ while (!kthread_should_stop()) {
+ if (down_interruptible(&dev->irq_sem))
+ continue;
+
+ if (kthread_should_stop())
+ break;
+
+ mutex_lock(&dev->irq_lock);
+ list_for_each_entry_safe(inst, tmp, &dev->instances, list) {
+ while (kfifo_len(&inst->irq_status)) {
+ ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
+ if (!ret)
+ break;
+
+ inst->ops->finish_process(inst);
+ }
+ }
+ mutex_unlock(&dev->irq_lock);
+ }
+
+ return 0;
+}
+
static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
u32 *revision)
{
@@ -224,6 +289,8 @@ static int wave5_vpu_probe(struct platform_device *pdev)
mutex_init(&dev->dev_lock);
mutex_init(&dev->hw_lock);
+ mutex_init(&dev->irq_lock);
+ spin_lock_init(&dev->irq_spinlock);
dev_set_drvdata(&pdev->dev, dev);
dev->dev = &pdev->dev;
@@ -266,9 +333,13 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
dev->product = wave5_vpu_get_product_id(dev);
+ INIT_LIST_HEAD(&dev->instances);
+
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq < 0) {
dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n");
+ sema_init(&dev->irq_sem, 1);
+ dev->irq_thread = kthread_run(irq_thread, dev, "irq thread");
hrtimer_setup(&dev->hrtimer, &wave5_vpu_timer_callback, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_PINNED);
dev->worker = kthread_run_worker(0, "vpu_irq_thread");
@@ -280,7 +351,7 @@ static int wave5_vpu_probe(struct platform_device *pdev)
dev->vpu_poll_interval = vpu_poll_interval;
kthread_init_work(&dev->work, wave5_vpu_irq_work_fn);
} else {
- ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
+ ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave5_vpu_irq,
wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
if (ret) {
dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
@@ -288,11 +359,10 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
}
- 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;
+ goto err_irq_release;
}
if (match_data->flags & WAVE5_IS_DEC) {
@@ -322,7 +392,7 @@ static int wave5_vpu_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
- pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
wave5_vpu_sleep_wake(&pdev->dev, true, NULL, 0);
@@ -337,7 +407,15 @@ err_dec_unreg:
wave5_vpu_dec_unregister_device(dev);
err_v4l2_unregister:
v4l2_device_unregister(&dev->v4l2_dev);
+err_irq_release:
+ if (dev->irq < 0)
+ kthread_destroy_worker(dev->worker);
err_vdi_release:
+ if (dev->irq_thread) {
+ kthread_stop(dev->irq_thread);
+ up(&dev->irq_sem);
+ dev->irq_thread = NULL;
+ }
wave5_vdi_release(&pdev->dev);
err_clk_dis:
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
@@ -351,21 +429,30 @@ static void wave5_vpu_remove(struct platform_device *pdev)
{
struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
+ wave5_vpu_enc_unregister_device(dev);
+ wave5_vpu_dec_unregister_device(dev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+
if (dev->irq < 0) {
- kthread_destroy_worker(dev->worker);
+ if (dev->irq_thread) {
+ kthread_stop(dev->irq_thread);
+ up(&dev->irq_sem);
+ dev->irq_thread = NULL;
+ }
+
hrtimer_cancel(&dev->hrtimer);
+ kthread_cancel_work_sync(&dev->work);
+ kthread_destroy_worker(dev->worker);
}
- pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
mutex_destroy(&dev->dev_lock);
mutex_destroy(&dev->hw_lock);
+ mutex_destroy(&dev->irq_lock);
reset_control_assert(dev->resets);
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);
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
index 5943bdaa9c4c..99c3be882192 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
@@ -22,8 +22,8 @@
struct vpu_src_buffer {
struct v4l2_m2m_buffer v4l2_m2m_buf;
- struct list_head list;
bool consumed;
+ struct list_head list;
};
struct vpu_dst_buffer {
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
index e5e879a13e8b..d26ffc942219 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -52,11 +52,12 @@ int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size)
int wave5_vpu_flush_instance(struct vpu_instance *inst)
{
int ret = 0;
+ int mutex_ret = 0;
int retry = 0;
- ret = mutex_lock_interruptible(&inst->dev->hw_lock);
- if (ret)
- return ret;
+ mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (mutex_ret)
+ return mutex_ret;
do {
/*
* Repeat the FLUSH command until the firmware reports that the
@@ -80,11 +81,16 @@ int wave5_vpu_flush_instance(struct vpu_instance *inst)
mutex_unlock(&inst->dev->hw_lock);
wave5_vpu_dec_get_output_info(inst, &dec_info);
- ret = mutex_lock_interruptible(&inst->dev->hw_lock);
- if (ret)
- return ret;
- if (dec_info.index_frame_display > 0)
+ mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (mutex_ret)
+ return mutex_ret;
+ if (dec_info.index_frame_display >= 0) {
+ mutex_unlock(&inst->dev->hw_lock);
wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display);
+ mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (mutex_ret)
+ return mutex_ret;
+ }
}
} while (ret != 0);
mutex_unlock(&inst->dev->hw_lock);
@@ -207,8 +213,8 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
int retry = 0;
struct vpu_device *vpu_dev = inst->dev;
int i;
- int inst_count = 0;
- struct vpu_instance *inst_elm;
+ struct dec_output_info dec_info;
+ int ret_mutex;
*fail_res = 0;
if (!inst->codec_info)
@@ -216,10 +222,10 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
pm_runtime_resume_and_get(inst->dev->dev);
- ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
- if (ret) {
+ ret_mutex = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret_mutex) {
pm_runtime_put_sync(inst->dev->dev);
- return ret;
+ return ret_mutex;
}
do {
@@ -229,11 +235,26 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
goto unlock_and_return;
}
- if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
- retry++ >= MAX_FIRMWARE_CALL_RETRY) {
+ if (ret == 0)
+ break;
+
+ if (*fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
+ dev_warn(inst->dev->dev, "dec_finish_seq timed out\n");
+ goto unlock_and_return;
+ }
+
+ if (retry++ >= MAX_FIRMWARE_CALL_RETRY) {
ret = -ETIMEDOUT;
goto unlock_and_return;
}
+
+ mutex_unlock(&vpu_dev->hw_lock);
+ wave5_vpu_dec_get_output_info(inst, &dec_info);
+ ret_mutex = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret_mutex) {
+ pm_runtime_put_sync(inst->dev->dev);
+ return ret_mutex;
+ }
} while (ret != 0);
dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__);
@@ -250,10 +271,7 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task);
- list_for_each_entry(inst_elm, &vpu_dev->instances, list)
- inst_count++;
- if (inst_count == 1)
- pm_runtime_dont_use_autosuspend(vpu_dev->dev);
+ mutex_destroy(&inst->feed_lock);
unlock_and_return:
mutex_unlock(&vpu_dev->hw_lock);
@@ -467,11 +485,11 @@ int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int upd
dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst)
{
int ret;
- dma_addr_t rd_ptr;
+ dma_addr_t rd_ptr = 0;
ret = mutex_lock_interruptible(&inst->dev->hw_lock);
if (ret)
- return ret;
+ return rd_ptr;
rd_ptr = wave5_dec_get_rd_ptr(inst);
@@ -492,7 +510,7 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i
struct vpu_device *vpu_dev = inst->dev;
struct dec_output_info *disp_info;
- if (!info)
+ if (WARN_ON(!info))
return -EINVAL;
p_dec_info = &inst->codec_info->dec_info;
@@ -720,8 +738,6 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
int ret;
int retry = 0;
struct vpu_device *vpu_dev = inst->dev;
- int inst_count = 0;
- struct vpu_instance *inst_elm;
*fail_res = 0;
if (!inst->codec_info)
@@ -764,12 +780,6 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
}
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
-
- list_for_each_entry(inst_elm, &vpu_dev->instances, list)
- inst_count++;
- if (inst_count == 1)
- pm_runtime_dont_use_autosuspend(vpu_dev->dev);
-
mutex_unlock(&vpu_dev->hw_lock);
pm_runtime_put_sync(inst->dev->dev);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
index 45615c15beca..c64135769869 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
@@ -8,6 +8,7 @@
#ifndef VPUAPI_H_INCLUDED
#define VPUAPI_H_INCLUDED
+#include <linux/kfifo.h>
#include <linux/idr.h>
#include <linux/genalloc.h>
#include <media/v4l2-device.h>
@@ -747,6 +748,7 @@ struct vpu_device {
struct video_device *video_dev_enc;
struct mutex dev_lock; /* lock for the src, dst v4l2 queues */
struct mutex hw_lock; /* lock hw configurations */
+ struct mutex irq_lock;
int irq;
enum product_id product;
struct vpu_attr attr;
@@ -764,7 +766,10 @@ struct vpu_device {
struct kthread_worker *worker;
int vpu_poll_interval;
int num_clks;
+ struct task_struct *irq_thread;
+ struct semaphore irq_sem; /* signal to irq_thread when interrupt happens*/
struct reset_control *resets;
+ spinlock_t irq_spinlock; /* protect instances list */
};
struct vpu_instance;
@@ -788,6 +793,7 @@ struct vpu_instance {
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_quantization quantization;
+ struct kfifo irq_status;
enum vpu_instance_state state;
enum vpu_instance_type type;
const struct vpu_instance_ops *ops;
@@ -812,6 +818,12 @@ struct vpu_instance {
bool cbcr_interleave;
bool nv21;
bool eos;
+ bool sent_eos; /* check if EOS is sent to application */
+ bool retry; /* retry to feed bitstream if failure reason is WAVE5_SYSERR_QUEUEING_FAIL*/
+ int queuing_num; /* count of bitstream queued */
+ struct mutex feed_lock; /* lock for feeding bitstream buffers */
+ bool queuing_fail; /* if there is the queuing failure */
+ bool empty_queue;
struct vpu_buf bitstream_vbuf;
dma_addr_t last_rd_ptr;
size_t remaining_consumed_bytes;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
index 1ea9f5f31499..4ebd48d5550e 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
@@ -59,6 +59,7 @@
// application specific configuration
#define VPU_ENC_TIMEOUT 60000
#define VPU_DEC_TIMEOUT 60000
+#define VPU_DEC_STOP_TIMEOUT 10
// for WAVE encoder
#define USE_SRC_PRP_AXI 0
diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
index 80fdc6ff57e0..8432833814f3 100644
--- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
@@ -194,11 +194,17 @@ static int mtk_mdp_probe(struct platform_device *pdev)
}
mdp->vpu_dev = vpu_get_plat_device(pdev);
+ if (!mdp->vpu_dev) {
+ dev_err(&pdev->dev, "Failed to get vpu device\n");
+ ret = -ENODEV;
+ goto err_vpu_get_dev;
+ }
+
ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp,
VPU_RST_MDP);
if (ret) {
dev_err(&pdev->dev, "Failed to register reset handler\n");
- goto err_m2m_register;
+ goto err_reg_handler;
}
platform_set_drvdata(pdev, mdp);
@@ -206,7 +212,7 @@ static int mtk_mdp_probe(struct platform_device *pdev)
ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n");
- goto err_m2m_register;
+ goto err_reg_handler;
}
pm_runtime_enable(dev);
@@ -214,6 +220,12 @@ static int mtk_mdp_probe(struct platform_device *pdev)
return 0;
+err_reg_handler:
+ platform_device_put(mdp->vpu_dev);
+
+err_vpu_get_dev:
+ mtk_mdp_unregister_m2m_device(mdp);
+
err_m2m_register:
v4l2_device_unregister(&mdp->v4l2_dev);
@@ -242,6 +254,7 @@ static void mtk_mdp_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
vb2_dma_contig_clear_max_seg_size(&pdev->dev);
+ platform_device_put(mdp->vpu_dev);
mtk_mdp_unregister_m2m_device(mdp);
v4l2_device_unregister(&mdp->v4l2_dev);
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 6d26d4aa1eef..f20d2ed1f2bb 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -268,14 +268,16 @@ static int mdp_probe(struct platform_device *pdev)
goto err_free_mutex;
}
- mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 0);
+ mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME,
+ WQ_FREEZABLE | WQ_PERCPU, 0);
if (!mdp->job_wq) {
dev_err(dev, "Unable to create job workqueue\n");
ret = -ENOMEM;
goto err_deinit_comp;
}
- mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", WQ_FREEZABLE,
+ mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock",
+ WQ_FREEZABLE | WQ_PERCPU,
0);
if (!mdp->clock_wq) {
dev_err(dev, "Unable to create clock workqueue\n");
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 1f32ba11a18c..d76e891f784b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
@@ -882,8 +882,10 @@ void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
src_buf->vb2_buf.req_obj.req;
v4l2_m2m_buf_done(src_buf,
VB2_BUF_STATE_ERROR);
- if (req)
+ if (req) {
v4l2_ctrl_request_complete(req, &ctx->ctrl_hdl);
+ media_request_manual_complete(req);
+ }
}
}
return;
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 9d68808e8f9c..c9d27534c63e 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
@@ -7,6 +7,8 @@
#ifndef _MTK_VCODEC_DEC_DRV_H_
#define _MTK_VCODEC_DEC_DRV_H_
+#include <linux/kref.h>
+
#include "../common/mtk_vcodec_cmn_drv.h"
#include "../common/mtk_vcodec_dbgfs.h"
#include "../common/mtk_vcodec_fw_priv.h"
@@ -129,6 +131,16 @@ struct mtk_vcodec_dec_pdata {
};
/**
+ * struct mtk_vcodec_dec_request - Media request private data.
+ * @refcount: Used to ensure we don't complete the request too soon
+ * @req: Media Request structure
+ */
+struct mtk_vcodec_dec_request {
+ struct kref refcount;
+ struct media_request req;
+};
+
+/**
* struct mtk_vcodec_dec_ctx - Context (instance) private data.
*
* @type: type of decoder instance
@@ -324,6 +336,11 @@ static inline struct mtk_vcodec_dec_ctx *ctrl_to_dec_ctx(struct v4l2_ctrl *ctrl)
return container_of(ctrl->handler, struct mtk_vcodec_dec_ctx, ctrl_hdl);
}
+static inline struct mtk_vcodec_dec_request *req_to_dec_req(struct media_request *req)
+{
+ return container_of(req, struct mtk_vcodec_dec_request, req);
+}
+
/* Wake up context wait_queue */
static inline void
wake_up_dec_ctx(struct mtk_vcodec_dec_ctx *ctx, unsigned int reason, unsigned int hw_id)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c
index aefd3e9e3061..f6f03c61c643 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c
@@ -67,11 +67,7 @@ static int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm)
static void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm)
{
- int ret;
-
- ret = pm_runtime_put(pm->dev);
- if (ret && ret != -EAGAIN)
- dev_err(pm->dev, "pm_runtime_put fail %d", ret);
+ pm_runtime_put(pm->dev);
}
static void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm)
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 d873159b9b30..8e1cf16a168a 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
@@ -242,10 +242,18 @@ static const struct v4l2_frmsize_stepwise stepwise_fhd = {
.step_height = 16
};
+static void mtk_vcodec_dec_request_release(struct kref *ref)
+{
+ struct mtk_vcodec_dec_request *req =
+ container_of(ref, struct mtk_vcodec_dec_request, refcount);
+ media_request_manual_complete(&req->req);
+}
+
static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_dec_ctx *ctx, int error,
- struct media_request *src_buf_req)
+ struct media_request *mreq)
{
- struct vb2_v4l2_buffer *vb2_dst;
+ struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq);
+ struct vb2_v4l2_buffer *dst_buf;
enum vb2_buffer_state state;
if (error)
@@ -253,17 +261,9 @@ static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_dec_ctx *ctx, int e
else
state = VB2_BUF_STATE_DONE;
- vb2_dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
- if (vb2_dst) {
- v4l2_m2m_buf_done(vb2_dst, state);
- mtk_v4l2_vdec_dbg(2, ctx, "free frame buffer id:%d to done list",
- vb2_dst->vb2_buf.index);
- } else {
- mtk_v4l2_vdec_err(ctx, "dst buffer is NULL");
- }
-
- if (src_buf_req)
- v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_buf_done(dst_buf, state);
+ kref_put(&req->refcount, mtk_vcodec_dec_request_release);
}
static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_dec_ctx *ctx)
@@ -306,6 +306,7 @@ static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb)
struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl);
+ media_request_manual_complete(vb->req_obj.req);
}
static void mtk_vdec_worker(struct work_struct *work)
@@ -317,8 +318,9 @@ static void mtk_vdec_worker(struct work_struct *work)
struct vb2_buffer *vb2_src;
struct mtk_vcodec_mem *bs_src;
struct mtk_video_dec_buf *dec_buf_src;
- struct media_request *src_buf_req;
- enum vb2_buffer_state state;
+ struct media_request *mreq;
+ struct mtk_vcodec_dec_request *req;
+ enum vb2_buffer_state buf_state;
bool res_chg = false;
int ret;
@@ -350,14 +352,26 @@ static void mtk_vdec_worker(struct work_struct *work)
mtk_v4l2_vdec_dbg(3, ctx, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p",
ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src);
/* Apply request controls. */
- src_buf_req = vb2_src->req_obj.req;
- if (src_buf_req)
- v4l2_ctrl_request_setup(src_buf_req, &ctx->ctrl_hdl);
- else
+ mreq = vb2_src->req_obj.req;
+ if (WARN_ON(!mreq)) {
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
mtk_v4l2_vdec_err(ctx, "vb2 buffer media request is NULL");
+ return;
+ }
+
+ v4l2_ctrl_request_setup(mreq, &ctx->ctrl_hdl);
+
+ /* Keep a reference so that if the processing completes before this function
+ * ends, we won't accidently update a freshly queued request.
+ */
+ req = req_to_dec_req(mreq);
+ kref_get(&req->refcount);
ret = vdec_if_decode(ctx, bs_src, NULL, &res_chg);
- if (ret && ret != -EAGAIN) {
+ if (ret == -EAGAIN)
+ goto done;
+
+ if (ret) {
mtk_v4l2_vdec_err(ctx,
"[%d] decode src_buf[%d] sz=0x%zx pts=%llu ret=%d res_chg=%d",
ctx->id, vb2_src->index, bs_src->size,
@@ -367,21 +381,23 @@ static void mtk_vdec_worker(struct work_struct *work)
dec_buf_src->error = true;
mutex_unlock(&ctx->lock);
}
- }
- state = ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE;
- if (!IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch) ||
- ctx->current_codec == V4L2_PIX_FMT_VP8_FRAME) {
- v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, state);
- if (src_buf_req)
- v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl);
+ buf_state = VB2_BUF_STATE_ERROR;
} else {
- if (ret != -EAGAIN) {
- v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- v4l2_m2m_buf_done(vb2_v4l2_src, state);
- }
- v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ buf_state = VB2_BUF_STATE_DONE;
}
+
+ v4l2_ctrl_request_complete(mreq, &ctx->ctrl_hdl);
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_buf_done(vb2_v4l2_src, buf_state);
+
+ if (ret || !IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch) ||
+ ctx->current_codec == V4L2_PIX_FMT_VP8_FRAME)
+ mtk_vdec_stateless_cap_to_disp(ctx, ret, mreq);
+
+done:
+ kref_put(&req->refcount, mtk_vcodec_dec_request_release);
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
}
static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb)
@@ -502,6 +518,12 @@ static int mtk_vdec_s_ctrl(struct v4l2_ctrl *ctrl)
mtk_v4l2_vdec_err(ctx, "VP9: bit_depth:%d", frame->bit_depth);
return -EINVAL;
}
+
+ if (!(frame->flags & V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING) ||
+ !(frame->flags & V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING)) {
+ mtk_v4l2_vdec_err(ctx, "VP9: only 420 subsampling is supported");
+ return -EINVAL;
+ }
break;
case V4L2_CID_STATELESS_AV1_SEQUENCE:
seq = (struct v4l2_ctrl_av1_sequence *)hdr_ctrl->p_new.p;
@@ -709,6 +731,22 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx)
return 0;
}
+static struct media_request *fops_media_request_alloc(struct media_device *mdev)
+{
+ struct mtk_vcodec_dec_request *req;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+
+ return &req->req;
+}
+
+static void fops_media_request_free(struct media_request *mreq)
+{
+ struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq);
+
+ kfree(req);
+}
+
static int fops_media_request_validate(struct media_request *mreq)
{
const unsigned int buffer_cnt = vb2_request_buffer_cnt(mreq);
@@ -729,9 +767,20 @@ static int fops_media_request_validate(struct media_request *mreq)
return vb2_request_validate(mreq);
}
+static void fops_media_request_queue(struct media_request *mreq)
+{
+ struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq);
+
+ media_request_mark_manual_completion(mreq);
+ kref_init(&req->refcount);
+ v4l2_m2m_request_queue(mreq);
+}
+
const struct media_device_ops mtk_vcodec_media_ops = {
+ .req_alloc = fops_media_request_alloc,
+ .req_free = fops_media_request_free,
.req_validate = fops_media_request_validate,
- .req_queue = v4l2_m2m_request_queue,
+ .req_queue = fops_media_request_queue,
};
static void mtk_vcodec_add_formats(unsigned int fourcc,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
index eb3354192853..80554b2c26c0 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
@@ -548,10 +548,9 @@ static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst)
static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_dec_ctx *ctx)
{
int result;
- struct mtk_vcodec_mem mem;
+ struct mtk_vcodec_mem mem = { };
struct vdec_vp9_inst *inst;
- memset(&mem, 0, sizeof(mem));
mem.size = sizeof(struct vdec_vp9_inst);
result = mtk_vcodec_mem_alloc(ctx, &mem);
if (result)
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 40b97f114cf6..b35759a0b353 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
@@ -182,12 +182,11 @@ static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len)
static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id)
{
- struct vdec_ap_ipi_cmd msg;
+ struct vdec_ap_ipi_cmd msg = { };
int err = 0;
mtk_vdec_debug(vpu->ctx, "+ id=%X", msg_id);
- memset(&msg, 0, sizeof(msg));
msg.msg_id = msg_id;
if (vpu->fw_abi_version < 2)
msg.vpu_inst_addr = vpu->inst_addr;
@@ -202,7 +201,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id)
int vpu_dec_init(struct vdec_vpu_inst *vpu)
{
- struct vdec_ap_ipi_init msg;
+ struct vdec_ap_ipi_init msg = { };
int err;
init_waitqueue_head(&vpu->wq);
@@ -226,7 +225,6 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
}
}
- memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_INIT;
msg.ap_inst_addr = (unsigned long)vpu;
msg.codec_type = vpu->codec_type;
@@ -246,7 +244,7 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len)
{
- struct vdec_ap_ipi_dec_start msg;
+ struct vdec_ap_ipi_dec_start msg = { };
int i;
int err = 0;
@@ -255,7 +253,6 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len)
return -EINVAL;
}
- memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_START;
if (vpu->fw_abi_version < 2)
msg.vpu_inst_addr = vpu->inst_addr;
@@ -274,7 +271,7 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len)
int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data,
unsigned int len, unsigned int param_type)
{
- struct vdec_ap_ipi_get_param msg;
+ struct vdec_ap_ipi_get_param msg = { };
int err;
if (len > ARRAY_SIZE(msg.data)) {
@@ -282,7 +279,6 @@ int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data,
return -EINVAL;
}
- memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_GET_PARAM;
msg.inst_id = vpu->inst_id;
memcpy(msg.data, data, sizeof(unsigned int) * len);
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 6faf3f659e75..0d4e94463685 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -850,7 +850,7 @@ static void vb2ops_venc_buf_queue(struct vb2_buffer *vb)
static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(q);
- struct venc_enc_param param;
+ struct venc_enc_param param = { };
int ret;
int i;
@@ -1004,7 +1004,7 @@ static int mtk_venc_encode_header(void *priv)
int ret;
struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct mtk_vcodec_mem bs_buf;
- struct venc_done_result enc_result;
+ struct venc_done_result enc_result = { };
dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
if (!dst_buf) {
@@ -1049,7 +1049,7 @@ static int mtk_venc_encode_header(void *priv)
static int mtk_venc_param_change(struct mtk_vcodec_enc_ctx *ctx)
{
- struct venc_enc_param enc_prm;
+ struct venc_enc_param enc_prm = { };
struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
struct mtk_video_enc_buf *mtk_buf;
int ret = 0;
@@ -1060,7 +1060,6 @@ static int mtk_venc_param_change(struct mtk_vcodec_enc_ctx *ctx)
mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, m2m_buf.vb);
- memset(&enc_prm, 0, sizeof(enc_prm));
if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE)
return 0;
@@ -1123,9 +1122,9 @@ static void mtk_venc_worker(struct work_struct *work)
struct mtk_vcodec_enc_ctx *ctx = container_of(work, struct mtk_vcodec_enc_ctx,
encode_work);
struct vb2_v4l2_buffer *src_buf, *dst_buf;
- struct venc_frm_buf frm_buf;
+ struct venc_frm_buf frm_buf = { };
struct mtk_vcodec_mem bs_buf;
- struct venc_done_result enc_result;
+ struct venc_done_result enc_result = { };
int ret, i;
/* check dst_buf, dst_buf may be removed in device_run
@@ -1153,7 +1152,6 @@ static void mtk_venc_worker(struct work_struct *work)
return;
}
- memset(&frm_buf, 0, sizeof(frm_buf));
for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) {
frm_buf.fb_addr[i].dma_addr =
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
index 1a2b14a3e219..edf9133c0673 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
@@ -71,11 +71,7 @@ int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm)
void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm)
{
- int ret;
-
- ret = pm_runtime_put(pm->dev);
- if (ret && ret != -EAGAIN)
- dev_err(pm->dev, "pm_runtime_put fail %d", ret);
+ pm_runtime_put(pm->dev);
}
void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
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 3c229b1f6b21..0c825aa7224d 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
@@ -133,7 +133,7 @@ static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg,
int vpu_enc_init(struct venc_vpu_inst *vpu)
{
int status;
- struct venc_ap_ipi_msg_init out;
+ struct venc_ap_ipi_msg_init out = { };
init_waitqueue_head(&vpu->wq_hd);
vpu->signaled = 0;
@@ -149,7 +149,6 @@ int vpu_enc_init(struct venc_vpu_inst *vpu)
return -EINVAL;
}
- memset(&out, 0, sizeof(out));
out.msg_id = AP_IPIMSG_ENC_INIT;
out.venc_inst = (unsigned long)vpu;
if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
@@ -192,11 +191,10 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu,
size_t msg_size = is_ext ?
sizeof(struct venc_ap_ipi_msg_set_param_ext) :
sizeof(struct venc_ap_ipi_msg_set_param);
- struct venc_ap_ipi_msg_set_param_ext out;
+ struct venc_ap_ipi_msg_set_param_ext out = { };
mtk_venc_debug(vpu->ctx, "id %d ->", id);
- memset(&out, 0, sizeof(out));
out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM;
out.base.vpu_inst_addr = vpu->inst_addr;
out.base.param_id = id;
@@ -259,11 +257,10 @@ static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu,
size_t msg_size = is_ext ?
sizeof(struct venc_ap_ipi_msg_enc_ext) :
sizeof(struct venc_ap_ipi_msg_enc);
- struct venc_ap_ipi_msg_enc_ext out;
+ struct venc_ap_ipi_msg_enc_ext out = { };
mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode);
- memset(&out, 0, sizeof(out));
out.base.msg_id = AP_IPIMSG_ENC_ENCODE;
out.base.vpu_inst_addr = vpu->inst_addr;
out.base.bs_mode = bs_mode;
@@ -303,12 +300,11 @@ static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu,
struct mtk_vcodec_mem *bs_buf,
struct venc_frame_info *frame_info)
{
- struct venc_ap_ipi_msg_enc_ext_34 out;
+ struct venc_ap_ipi_msg_enc_ext_34 out = { };
size_t msg_size = sizeof(struct venc_ap_ipi_msg_enc_ext_34);
mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode);
- memset(&out, 0, sizeof(out));
out.msg_id = AP_IPIMSG_ENC_ENCODE;
out.vpu_inst_addr = vpu->inst_addr;
out.bs_mode = bs_mode;
@@ -368,9 +364,8 @@ int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode,
int vpu_enc_deinit(struct venc_vpu_inst *vpu)
{
- struct venc_ap_ipi_msg_deinit out;
+ struct venc_ap_ipi_msg_deinit out = { };
- memset(&out, 0, sizeof(out));
out.msg_id = AP_IPIMSG_ENC_DEINIT;
out.vpu_inst_addr = vpu->inst_addr;
if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.h b/drivers/media/platform/mediatek/vpu/mtk_vpu.h
index da05f3e74081..3951547e9ec5 100644
--- a/drivers/media/platform/mediatek/vpu/mtk_vpu.h
+++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.h
@@ -119,8 +119,7 @@ int vpu_ipi_send(struct platform_device *pdev,
* @pdev: the platform device of the module requesting VPU platform
* device for using VPU API.
*
- * Return: Return NULL if it is failed.
- * otherwise it is VPU's platform device
+ * Return: a reference to the VPU's platform device, or NULL on failure.
**/
struct platform_device *vpu_get_plat_device(struct platform_device *pdev);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 9e4a813489c0..b558700d1d96 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -44,6 +44,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/genalloc.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -783,32 +784,40 @@ static int mxc_get_free_slot(struct mxc_jpeg_slot_data *slot_data)
return -1;
}
+static void mxc_jpeg_free(struct mxc_jpeg_dev *jpeg, size_t size, void *addr, dma_addr_t handle)
+{
+ if (jpeg->sram_pool)
+ gen_pool_free(jpeg->sram_pool, (unsigned long)addr, size);
+ else
+ dma_free_coherent(jpeg->dev, size, addr, handle);
+}
+
static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
{
/* free descriptor for decoding/encoding phase */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.desc,
- jpeg->slot_data.desc_handle);
+ mxc_jpeg_free(jpeg, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.desc,
+ jpeg->slot_data.desc_handle);
jpeg->slot_data.desc = NULL;
jpeg->slot_data.desc_handle = 0;
/* free descriptor for encoder configuration phase / decoder DHT */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.cfg_desc,
- jpeg->slot_data.cfg_desc_handle);
+ mxc_jpeg_free(jpeg, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.cfg_desc,
+ jpeg->slot_data.cfg_desc_handle);
jpeg->slot_data.cfg_desc_handle = 0;
jpeg->slot_data.cfg_desc = NULL;
/* free configuration stream */
- dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
- jpeg->slot_data.cfg_stream_vaddr,
- jpeg->slot_data.cfg_stream_handle);
+ mxc_jpeg_free(jpeg, MXC_JPEG_MAX_CFG_STREAM,
+ jpeg->slot_data.cfg_stream_vaddr,
+ jpeg->slot_data.cfg_stream_handle);
jpeg->slot_data.cfg_stream_vaddr = NULL;
jpeg->slot_data.cfg_stream_handle = 0;
- dma_free_coherent(jpeg->dev, jpeg->slot_data.cfg_dec_size,
- jpeg->slot_data.cfg_dec_vaddr,
- jpeg->slot_data.cfg_dec_daddr);
+ mxc_jpeg_free(jpeg, jpeg->slot_data.cfg_dec_size,
+ jpeg->slot_data.cfg_dec_vaddr,
+ jpeg->slot_data.cfg_dec_daddr);
jpeg->slot_data.cfg_dec_size = 0;
jpeg->slot_data.cfg_dec_vaddr = NULL;
jpeg->slot_data.cfg_dec_daddr = 0;
@@ -816,6 +825,15 @@ static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
jpeg->slot_data.used = false;
}
+static struct mxc_jpeg_desc *mxc_jpeg_alloc(struct mxc_jpeg_dev *jpeg, size_t size,
+ dma_addr_t *handle)
+{
+ if (jpeg->sram_pool)
+ return gen_pool_dma_zalloc(jpeg->sram_pool, size, handle);
+ else
+ return dma_alloc_coherent(jpeg->dev, size, handle, GFP_ATOMIC);
+}
+
static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
{
struct mxc_jpeg_desc *desc;
@@ -826,37 +844,29 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
goto skip_alloc; /* already allocated, reuse it */
/* allocate descriptor for decoding/encoding phase */
- desc = dma_alloc_coherent(jpeg->dev,
- sizeof(struct mxc_jpeg_desc),
- &jpeg->slot_data.desc_handle,
- GFP_ATOMIC);
+ desc = mxc_jpeg_alloc(jpeg, sizeof(struct mxc_jpeg_desc),
+ &jpeg->slot_data.desc_handle);
if (!desc)
goto err;
jpeg->slot_data.desc = desc;
/* allocate descriptor for configuration phase (encoder only) */
- cfg_desc = dma_alloc_coherent(jpeg->dev,
- sizeof(struct mxc_jpeg_desc),
- &jpeg->slot_data.cfg_desc_handle,
- GFP_ATOMIC);
+ cfg_desc = mxc_jpeg_alloc(jpeg, sizeof(struct mxc_jpeg_desc),
+ &jpeg->slot_data.cfg_desc_handle);
if (!cfg_desc)
goto err;
jpeg->slot_data.cfg_desc = cfg_desc;
/* allocate configuration stream */
- cfg_stm = dma_alloc_coherent(jpeg->dev,
- MXC_JPEG_MAX_CFG_STREAM,
- &jpeg->slot_data.cfg_stream_handle,
- GFP_ATOMIC);
+ cfg_stm = mxc_jpeg_alloc(jpeg, MXC_JPEG_MAX_CFG_STREAM,
+ &jpeg->slot_data.cfg_stream_handle);
if (!cfg_stm)
goto err;
jpeg->slot_data.cfg_stream_vaddr = cfg_stm;
jpeg->slot_data.cfg_dec_size = MXC_JPEG_PATTERN_WIDTH * MXC_JPEG_PATTERN_HEIGHT * 2;
- jpeg->slot_data.cfg_dec_vaddr = dma_alloc_coherent(jpeg->dev,
- jpeg->slot_data.cfg_dec_size,
- &jpeg->slot_data.cfg_dec_daddr,
- GFP_ATOMIC);
+ jpeg->slot_data.cfg_dec_vaddr = mxc_jpeg_alloc(jpeg, jpeg->slot_data.cfg_dec_size,
+ &jpeg->slot_data.cfg_dec_daddr);
if (!jpeg->slot_data.cfg_dec_vaddr)
goto err;
@@ -2884,6 +2894,10 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
jpeg->dev = dev;
jpeg->mode = mode;
+ /* SRAM pool is optional */
+ jpeg->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ dev_info(dev, "Using DMA descriptor pool in %cRAM\n", jpeg->sram_pool ? 'S' : 'D');
+
/* Get clocks */
ret = devm_clk_bulk_get_all(&pdev->dev, &jpeg->clks);
if (ret < 0) {
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 44e46face6d1..9c5b4f053ded 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -141,6 +141,7 @@ struct mxc_jpeg_dev {
int num_domains;
struct device **pd_dev;
struct device_link **pd_link;
+ struct gen_pool *sram_pool;
};
/**
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index 088b2945aee3..9a43fd1eb0bc 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -12,6 +12,7 @@
*
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -1349,27 +1350,26 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
.bus_type = V4L2_MBUS_CSI2_DPHY,
};
struct v4l2_async_connection *asd;
- struct fwnode_handle *ep;
unsigned int i;
int ret;
v4l2_async_subdev_nf_init(&csis->notifier, &csis->sd);
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0,
- FWNODE_GRAPH_ENDPOINT_NEXT);
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
if (!ep)
return -ENOTCONN;
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
- goto err_parse;
+ return ret;
for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
dev_err(csis->dev,
"data lanes reordering is not supported");
- ret = -EINVAL;
- goto err_parse;
+ return -EINVAL;
}
}
@@ -1381,12 +1381,8 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep,
struct v4l2_async_connection);
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- goto err_parse;
- }
-
- fwnode_handle_put(ep);
+ if (IS_ERR(asd))
+ return PTR_ERR(asd);
csis->notifier.ops = &mipi_csis_notify_ops;
@@ -1395,11 +1391,6 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
return ret;
return v4l2_async_register_subdev(&csis->sd);
-
-err_parse:
- fwnode_handle_put(ep);
-
- return ret;
}
/* -----------------------------------------------------------------------------
@@ -1547,10 +1538,8 @@ static int mipi_csis_probe(struct platform_device *pdev)
/* Now that the hardware is initialized, request the interrupt. */
ret = devm_request_irq(dev, irq, mipi_csis_irq_handler, 0,
dev_name(dev), csis);
- if (ret) {
- dev_err(dev, "Interrupt request failed\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Interrupt request failed\n");
/* Initialize and register the subdev. */
ret = mipi_csis_subdev_init(csis);
diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c
index 933a5f39f9f4..7ddc7ba06e3d 100644
--- a/drivers/media/platform/nxp/imx7-media-csi.c
+++ b/drivers/media/platform/nxp/imx7-media-csi.c
@@ -2218,11 +2218,9 @@ static int imx7_csi_probe(struct platform_device *pdev)
/* Acquire resources and install interrupt handler. */
csi->mclk = devm_clk_get(&pdev->dev, "mclk");
- if (IS_ERR(csi->mclk)) {
- ret = PTR_ERR(csi->mclk);
- dev_err(dev, "Failed to get mclk: %d", ret);
- return ret;
- }
+ if (IS_ERR(csi->mclk))
+ return dev_err_probe(dev, PTR_ERR(csi->mclk),
+ "Failed to get mclk\n");
csi->irq = platform_get_irq(pdev, 0);
if (csi->irq < 0)
@@ -2236,10 +2234,8 @@ static int imx7_csi_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi",
(void *)csi);
- if (ret < 0) {
- dev_err(dev, "Request CSI IRQ failed.\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Request CSI IRQ failed.\n");
/* Initialize all the media device infrastructure. */
ret = imx7_csi_media_init(csi);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index c3d411ddf492..18146978bdf5 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -499,13 +499,14 @@ static int mxc_isi_probe(struct platform_device *pdev)
dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = mxc_isi_crossbar_init(isi);
- if (ret) {
- dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
- goto err_pm;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to initialize crossbar\n");
for (i = 0; i < isi->pdata->num_channels; ++i) {
ret = mxc_isi_pipe_init(isi, i);
@@ -528,8 +529,7 @@ static int mxc_isi_probe(struct platform_device *pdev)
err_xbar:
mxc_isi_crossbar_cleanup(&isi->crossbar);
-err_pm:
- pm_runtime_disable(isi->dev);
+
return ret;
}
@@ -548,8 +548,6 @@ static void mxc_isi_remove(struct platform_device *pdev)
mxc_isi_crossbar_cleanup(&isi->crossbar);
mxc_isi_v4l2_cleanup(isi);
-
- pm_runtime_disable(isi->dev);
}
static const struct of_device_id mxc_isi_of_match[] = {
@@ -575,7 +573,6 @@ static struct platform_driver mxc_isi_driver = {
};
module_platform_driver(mxc_isi_driver);
-MODULE_ALIAS("ISI");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver");
MODULE_LICENSE("GPL");
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 ede6cc74c023..3c26cfef93d1 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -114,7 +114,7 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
"invalid route from memory input (%u) to pipe %u\n",
route->sink_pad,
route->source_pad - xbar->num_sinks);
- return -EINVAL;
+ return -ENXIO;
}
}
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index 371b4e81328c..e111b1debd69 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -6,6 +6,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -717,27 +718,26 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state)
.bus_type = V4L2_MBUS_CSI2_DPHY,
};
struct v4l2_async_connection *asd;
- struct fwnode_handle *ep;
unsigned int i;
int ret;
v4l2_async_subdev_nf_init(&state->notifier, &state->sd);
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
- FWNODE_GRAPH_ENDPOINT_NEXT);
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
if (!ep)
return -ENOTCONN;
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
- goto err_parse;
+ return ret;
for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
dev_err(state->dev,
"data lanes reordering is not supported");
- ret = -EINVAL;
- goto err_parse;
+ return -EINVAL;
}
}
@@ -749,12 +749,8 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state)
asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
struct v4l2_async_connection);
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- goto err_parse;
- }
-
- fwnode_handle_put(ep);
+ if (IS_ERR(asd))
+ return PTR_ERR(asd);
state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
@@ -763,11 +759,6 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state)
return ret;
return v4l2_async_register_subdev(&state->sd);
-
-err_parse:
- fwnode_handle_put(ep);
-
- return ret;
}
/* -----------------------------------------------------------------------------
@@ -951,10 +942,9 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
int ret = 0;
state->rst = devm_reset_control_array_get_exclusive(dev);
- if (IS_ERR(state->rst)) {
- dev_err(dev, "Failed to get reset: %pe\n", state->rst);
- return PTR_ERR(state->rst);
- }
+ if (IS_ERR(state->rst))
+ return dev_err_probe(dev, PTR_ERR(state->rst),
+ "Failed to get reset\n");
if (state->pdata->use_reg_csr) {
const struct regmap_config regmap_config = {
@@ -977,24 +967,22 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
ARRAY_SIZE(out_val));
- if (ret) {
- dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "property %s not found\n",
+ "fsl,mipi-phy-gpr");
ph = *out_val;
node = of_find_node_by_phandle(ph);
- if (!node) {
- dev_err(dev, "Error finding node by phandle\n");
- return -ENODEV;
- }
+ if (!node)
+ return dev_err_probe(dev, -ENODEV,
+ "Error finding node by phandle\n");
+
state->phy_gpr = syscon_node_to_regmap(node);
of_node_put(node);
- if (IS_ERR(state->phy_gpr)) {
- dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
- return PTR_ERR(state->phy_gpr);
- }
+ if (IS_ERR(state->phy_gpr))
+ return dev_err_probe(dev, PTR_ERR(state->phy_gpr),
+ "failed to get gpr regmap\n");
state->phy_gpr_reg = out_val[1];
dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
@@ -1017,10 +1005,8 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
state->pdata = of_device_get_match_data(dev);
ret = imx8mq_mipi_csi_parse_dt(state);
- if (ret < 0) {
- dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ if (ret < 0)
return ret;
- }
/* Acquire resources. */
state->regs = devm_platform_ioremap_resource(pdev, 0);
diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c
index 22a30510fb79..2b50f9b96a34 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-340.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-340.c
@@ -55,8 +55,7 @@
#define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0
#define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1
-static void __csid_configure_rx(struct csid_device *csid,
- struct csid_phy_config *phy, int vc)
+static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy)
{
u32 val;
@@ -81,13 +80,9 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
- u8 lane_cnt = csid->phy.lane_cnt;
u8 dt_id;
u32 val;
- if (!lane_cnt)
- lane_cnt = 4;
-
/*
* DT_ID is a two bit bitfield that is concatenated with
* the four least significant bits of the five bit VC
@@ -120,10 +115,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
int i;
+ __csid_configure_rx(csid, &csid->phy);
+
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
if (csid->phy.en_vc & BIT(i)) {
__csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 5284b5857368..ed1820488c98 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1187,24 +1187,12 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
/* Regulator */
for (i = 0; i < ARRAY_SIZE(res->regulators); i++) {
- if (res->regulators[i])
+ if (res->regulators[i].supply)
csid->num_supplies++;
}
- if (csid->num_supplies) {
- csid->supplies = devm_kmalloc_array(camss->dev,
- csid->num_supplies,
- sizeof(*csid->supplies),
- GFP_KERNEL);
- if (!csid->supplies)
- return -ENOMEM;
- }
-
- for (i = 0; i < csid->num_supplies; i++)
- csid->supplies[i].supply = res->regulators[i];
-
- ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies,
- csid->supplies);
+ ret = devm_regulator_bulk_get_const(camss->dev, csid->num_supplies,
+ res->regulators, &csid->supplies);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index 619abbf60781..415483274552 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -46,7 +46,8 @@
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
-#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, common_status_offset, n) \
+ ((offset) + (common_status_offset) + 0x4 * (n))
#define CSIPHY_DEFAULT_PARAMS 0
#define CSIPHY_LANE_ENABLE 1
@@ -810,13 +811,17 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy,
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
hw_version = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset,
+ regs->common_status_offset, 12));
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset,
+ regs->common_status_offset, 13)) << 8;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset,
+ regs->common_status_offset, 14)) << 16;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset,
+ regs->common_status_offset, 15)) << 24;
dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
}
@@ -845,7 +850,8 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
for (i = 0; i < 11; i++) {
int c = i + 22;
u8 val = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset,
+ regs->common_status_offset, i));
writel_relaxed(val, csiphy->base +
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c));
@@ -1004,6 +1010,7 @@ static bool csiphy_is_gen2(u32 version)
switch (version) {
case CAMSS_2290:
+ case CAMSS_6150:
case CAMSS_7280:
case CAMSS_8250:
case CAMSS_8280XP:
@@ -1086,6 +1093,7 @@ static int csiphy_init(struct csiphy_device *csiphy)
csiphy->regs = regs;
regs->offset = 0x800;
+ regs->common_status_offset = 0xb0;
switch (csiphy->camss->res->version) {
case CAMSS_845:
@@ -1093,6 +1101,7 @@ static int csiphy_init(struct csiphy_device *csiphy)
regs->lane_array_size = ARRAY_SIZE(lane_regs_sdm845);
break;
case CAMSS_2290:
+ case CAMSS_6150:
regs->lane_regs = &lane_regs_qcm2290[0];
regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290);
break;
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index a734fb7dde0a..62623393f414 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -695,24 +695,13 @@ int msm_csiphy_subdev_init(struct camss *camss,
/* CSIPHY supplies */
for (i = 0; i < ARRAY_SIZE(res->regulators); i++) {
- if (res->regulators[i])
+ if (res->regulators[i].supply)
csiphy->num_supplies++;
}
- if (csiphy->num_supplies) {
- csiphy->supplies = devm_kmalloc_array(camss->dev,
- csiphy->num_supplies,
- sizeof(*csiphy->supplies),
- GFP_KERNEL);
- if (!csiphy->supplies)
- return -ENOMEM;
- }
-
- for (i = 0; i < csiphy->num_supplies; i++)
- csiphy->supplies[i].supply = res->regulators[i];
-
- ret = devm_regulator_bulk_get(camss->dev, csiphy->num_supplies,
- csiphy->supplies);
+ if (csiphy->num_supplies > 0)
+ ret = devm_regulator_bulk_get_const(camss->dev, csiphy->num_supplies,
+ res->regulators, &csiphy->supplies);
return ret;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index 895f80003c44..2d5054819df7 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -90,6 +90,7 @@ struct csiphy_device_regs {
const struct csiphy_lane_regs *lane_regs;
int lane_array_size;
u32 offset;
+ u32 common_status_offset;
};
struct csiphy_device {
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c
index 4feea590a47b..d73f733fde04 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-480.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c
@@ -202,11 +202,13 @@ static irqreturn_t vfe_isr(int irq, void *dev)
writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0));
writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL);
- /* Loop through all WMs IRQs */
- for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) {
+ for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) {
if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i))
vfe_isr_reg_update(vfe, i);
+ }
+ /* Loop through all WMs IRQs */
+ for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) {
if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)))
vfe_buf_done(vfe, i);
}
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 9c7ad8aa4058..5baf0e3d4bc4 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -342,6 +342,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
break;
case CAMSS_660:
case CAMSS_2290:
+ case CAMSS_6150:
case CAMSS_7280:
case CAMSS_8x96:
case CAMSS_8250:
@@ -2001,6 +2002,7 @@ static int vfe_bpl_align(struct vfe_device *vfe)
int ret = 8;
switch (vfe->camss->res->version) {
+ case CAMSS_6150:
case CAMSS_7280:
case CAMSS_8250:
case CAMSS_8280XP:
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index fcc2b2c3cba0..00b87fd9afbd 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -73,7 +73,9 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = {
static const struct camss_subdev_resources csid_res_8x16[] = {
/* CSID0 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 40000 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
@@ -95,7 +97,9 @@ static const struct camss_subdev_resources csid_res_8x16[] = {
/* CSID1 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 40000 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
@@ -157,7 +161,9 @@ static const struct camss_subdev_resources vfe_res_8x16[] = {
static const struct camss_subdev_resources csiphy_res_8x39[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 40000 }
+ },
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
.clock_rate = { { 0 },
{ 40000000, 80000000 },
@@ -174,7 +180,9 @@ static const struct camss_subdev_resources csiphy_res_8x39[] = {
/* CSIPHY1 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 40000 }
+ },
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
.clock_rate = { { 0 },
{ 40000000, 80000000 },
@@ -300,7 +308,9 @@ static const struct camss_subdev_resources vfe_res_8x39[] = {
static const struct camss_subdev_resources csid_res_8x53[] = {
/* CSID0 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 9900 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
@@ -323,7 +333,9 @@ static const struct camss_subdev_resources csid_res_8x53[] = {
/* CSID1 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 9900 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
@@ -346,7 +358,9 @@ static const struct camss_subdev_resources csid_res_8x53[] = {
/* CSID2 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 9900 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
"csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
.clock_rate = { { 0 },
@@ -507,7 +521,9 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
static const struct camss_subdev_resources csid_res_8x96[] = {
/* CSID0 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 80160 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
@@ -529,7 +545,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
/* CSID1 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 80160 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
@@ -551,7 +569,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
/* CSID2 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 80160 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
"csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
.clock_rate = { { 0 },
@@ -573,7 +593,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
/* CSID3 */
{
- .regulators = { "vdda" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 80160 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb",
"csi3", "csi3_phy", "csi3_pix", "csi3_rdi" },
.clock_rate = { { 0 },
@@ -661,7 +683,10 @@ static const struct camss_subdev_resources vfe_res_8x96[] = {
static const struct camss_subdev_resources csiphy_res_2290[] = {
/* CSIPHY0 */
{
- .regulators = { "vdd-csiphy-1p2", "vdd-csiphy-1p8" },
+ .regulators = {
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 26700 },
+ { .supply = "vdd-csiphy-1p8", .init_load_uA = 2600 }
+ },
.clock = { "top_ahb", "ahb", "csiphy0", "csiphy0_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -678,7 +703,10 @@ static const struct camss_subdev_resources csiphy_res_2290[] = {
/* CSIPHY1 */
{
- .regulators = { "vdd-csiphy-1p2", "vdd-csiphy-1p8" },
+ .regulators = {
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 26700 },
+ { .supply = "vdd-csiphy-1p8", .init_load_uA = 2600 }
+ },
.clock = { "top_ahb", "ahb", "csiphy1", "csiphy1_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -854,7 +882,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
static const struct camss_subdev_resources csid_res_660[] = {
/* CSID0 */
{
- .regulators = { "vdda", "vdd_sec" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 0 },
+ { .supply = "vdd_sec", .init_load_uA = 0 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi",
"cphy_csid0" },
@@ -879,7 +910,10 @@ static const struct camss_subdev_resources csid_res_660[] = {
/* CSID1 */
{
- .regulators = { "vdda", "vdd_sec" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 0 },
+ { .supply = "vdd_sec", .init_load_uA = 0 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi",
"cphy_csid1" },
@@ -904,7 +938,10 @@ static const struct camss_subdev_resources csid_res_660[] = {
/* CSID2 */
{
- .regulators = { "vdda", "vdd_sec" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 0 },
+ { .supply = "vdd_sec", .init_load_uA = 0 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
"csi2", "csi2_phy", "csi2_pix", "csi2_rdi",
"cphy_csid2" },
@@ -929,7 +966,10 @@ static const struct camss_subdev_resources csid_res_660[] = {
/* CSID3 */
{
- .regulators = { "vdda", "vdd_sec" },
+ .regulators = {
+ { .supply = "vdda", .init_load_uA = 0 },
+ { .supply = "vdd_sec", .init_load_uA = 0 }
+ },
.clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb",
"csi3", "csi3_phy", "csi3_pix", "csi3_rdi",
"cphy_csid3" },
@@ -1026,7 +1066,10 @@ static const struct camss_subdev_resources vfe_res_660[] = {
static const struct camss_subdev_resources csiphy_res_670[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 42800 },
+ { .supply = "vdda-pll", .init_load_uA = 13900 }
+ },
.clock = { "soc_ahb", "cpas_ahb",
"csiphy0", "csiphy0_timer" },
.clock_rate = { { 0 },
@@ -1044,7 +1087,10 @@ static const struct camss_subdev_resources csiphy_res_670[] = {
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 42800 },
+ { .supply = "vdda-pll", .init_load_uA = 13900 }
+ },
.clock = { "soc_ahb", "cpas_ahb",
"csiphy1", "csiphy1_timer" },
.clock_rate = { { 0 },
@@ -1062,7 +1108,10 @@ static const struct camss_subdev_resources csiphy_res_670[] = {
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 42800 },
+ { .supply = "vdda-pll", .init_load_uA = 13900 }
+ },
.clock = { "soc_ahb", "cpas_ahb",
"csiphy2", "csiphy2_timer" },
.clock_rate = { { 0 },
@@ -1302,7 +1351,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
static const struct camss_subdev_resources csid_res_845[] = {
/* CSID0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe0", "vfe0_src",
"vfe0_cphy_rx", "csi0",
@@ -1327,7 +1379,10 @@ static const struct camss_subdev_resources csid_res_845[] = {
/* CSID1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe1", "vfe1_src",
"vfe1_cphy_rx", "csi1",
@@ -1352,7 +1407,10 @@ static const struct camss_subdev_resources csid_res_845[] = {
/* CSID2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe_lite", "vfe_lite_src",
"vfe_lite_cphy_rx", "csi2",
@@ -1461,10 +1519,197 @@ static const struct camss_subdev_resources vfe_res_845[] = {
}
};
+static const struct camss_subdev_resources csiphy_res_sm6150[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 },
+ { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 }
+ },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 269333333 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 },
+ { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 }
+ },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 269333333 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 },
+ { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 }
+ },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 269333333 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+};
+
+static const struct camss_subdev_resources csid_res_sm6150[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "vfe0_cphy_rx", "vfe0_csid" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 320000000, 540000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .is_lite = false,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "vfe1_cphy_rx", "vfe1_csid" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 320000000, 540000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .is_lite = false,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_cphy_rx", "vfe_lite_csid" },
+ .clock_rate = { { 269333333, 384000000 },
+ { 320000000, 540000000 } },
+ .reg = { "csid_lite" },
+ .interrupt = { "csid_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+};
+
+static const struct camss_subdev_resources vfe_res_sm6150[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe0", "vfe0_axi"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 37500000, 40000000 },
+ { 360000000, 432000000, 540000000, 600000000 },
+ { 265000000, 426000000 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe1", "vfe1_axi"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 37500000, 40000000 },
+ { 360000000, 432000000, 540000000, 600000000 },
+ { 265000000, 426000000 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe_lite" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 37500000, 40000000 },
+ { 360000000, 432000000, 540000000, 600000000 } },
+ .reg = { "vfe_lite" },
+ .interrupt = { "vfe_lite" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sm6150[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 38400,
+ .icc_bw_tbl.peak = 76800,
+ },
+ {
+ .name = "hf_0",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
static const struct camss_subdev_resources csiphy_res_8250[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1478,7 +1723,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1492,7 +1740,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1506,7 +1757,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
},
/* CSIPHY3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy3", "csiphy3_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1520,7 +1774,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1534,7 +1791,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
},
/* CSIPHY5 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 17500 },
+ { .supply = "vdda-pll", .init_load_uA = 10000 }
+ },
.clock = { "csiphy5", "csiphy5_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
@@ -1748,7 +2008,10 @@ static const struct resources_icc icc_res_sm8250[] = {
static const struct camss_subdev_resources csiphy_res_7280[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 16100 },
+ { .supply = "vdda-pll", .init_load_uA = 9000 }
+ },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 300000000, 400000000 },
@@ -1763,7 +2026,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 16100 },
+ { .supply = "vdda-pll", .init_load_uA = 9000 }
+ },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 300000000, 400000000 },
@@ -1778,7 +2044,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 16100 },
+ { .supply = "vdda-pll", .init_load_uA = 9000 }
+ },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 300000000, 400000000 },
@@ -1793,7 +2062,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
},
/* CSIPHY3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 16100 },
+ { .supply = "vdda-pll", .init_load_uA = 9000 }
+ },
.clock = { "csiphy3", "csiphy3_timer" },
.clock_rate = { { 300000000, 400000000 },
@@ -1808,7 +2080,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 16100 },
+ { .supply = "vdda-pll", .init_load_uA = 9000 }
+ },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 300000000, 400000000 },
@@ -2121,7 +2396,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
static const struct camss_subdev_resources csid_res_sc8280xp[] = {
/* CSID0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_axi" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2137,7 +2415,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_axi" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2153,7 +2434,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2", "vfe2_axi" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2169,7 +2453,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe3_csid", "vfe3_cphy_rx", "vfe3", "vfe3_axi" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2185,7 +2472,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID_LITE0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2201,7 +2491,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID_LITE1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2217,7 +2510,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID_LITE2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe_lite2_csid", "vfe_lite2_cphy_rx", "vfe_lite2" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2233,7 +2529,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = {
},
/* CSID_LITE3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 0 },
+ { .supply = "vdda-pll", .init_load_uA = 0 }
+ },
.clock = { "vfe_lite3_csid", "vfe_lite3_cphy_rx", "vfe_lite3" },
.clock_rate = { { 400000000, 480000000, 600000000 },
{ 0 },
@@ -2434,7 +2733,10 @@ static const struct resources_icc icc_res_sc8280xp[] = {
static const struct camss_subdev_resources csiphy_res_8550[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2448,7 +2750,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2462,7 +2767,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2476,7 +2784,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy3", "csiphy3_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2490,7 +2801,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 37900 },
+ { .supply = "vdda-pll", .init_load_uA = 18600 }
+ },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2504,7 +2818,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY5 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy5", "csiphy5_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2518,7 +2835,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY6 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 37900 },
+ { .supply = "vdda-pll", .init_load_uA = 18600 }
+ },
.clock = { "csiphy6", "csiphy6_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2532,7 +2852,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = {
},
/* CSIPHY7 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 32200 },
+ { .supply = "vdda-pll", .init_load_uA = 18000 }
+ },
.clock = { "csiphy7", "csiphy7_timer" },
.clock_rate = { { 400000000, 480000000 },
{ 400000000 } },
@@ -2704,12 +3027,11 @@ static const struct camss_subdev_resources vfe_res_8550[] = {
/* VFE3 lite */
{
.regulators = {},
- .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ .clock = { "gcc_axi_hf", "cpas_ahb", "vfe_lite_ahb",
"vfe_lite", "cpas_ife_lite", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
- { 300000000, 400000000 },
{ 400000000, 480000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
@@ -2726,12 +3048,11 @@ static const struct camss_subdev_resources vfe_res_8550[] = {
/* VFE4 lite */
{
.regulators = {},
- .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ .clock = { "gcc_axi_hf", "cpas_ahb", "vfe_lite_ahb",
"vfe_lite", "cpas_ife_lite", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
- { 300000000, 400000000 },
{ 400000000, 480000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
@@ -2763,7 +3084,10 @@ static const struct resources_icc icc_res_sm8550[] = {
static const struct camss_subdev_resources csiphy_res_sm8650[] = {
/* CSIPHY0 */
{
- .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy01-0p9", .init_load_uA = 88000 },
+ { .supply = "vdd-csiphy01-1p2", .init_load_uA = 17800 },
+ },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -2777,7 +3101,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy01-0p9", .init_load_uA = 88000 },
+ { .supply = "vdd-csiphy01-1p2", .init_load_uA = 17800 },
+ },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -2791,7 +3118,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy24-0p9", .init_load_uA = 147000 },
+ { .supply = "vdd-csiphy24-1p2", .init_load_uA = 24400 },
+ },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -2805,7 +3135,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = {
},
/* CSIPHY3 */
{
- .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy35-0p9", .init_load_uA = 88000 },
+ { .supply = "vdd-csiphy35-1p2", .init_load_uA = 17800 },
+ },
.clock = { "csiphy3", "csiphy3_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -2819,7 +3152,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy24-0p9", .init_load_uA = 147000 },
+ { .supply = "vdd-csiphy24-1p2", .init_load_uA = 24400 },
+ },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -2833,7 +3169,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = {
},
/* CSIPHY5 */
{
- .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", },
+ .regulators = {
+ { .supply = "vdd-csiphy35-0p9", .init_load_uA = 88000 },
+ { .supply = "vdd-csiphy35-1p2", .init_load_uA = 17800 },
+ },
.clock = { "csiphy5", "csiphy5_timer" },
.clock_rate = { { 400000000 },
{ 400000000 } },
@@ -3074,7 +3413,10 @@ static const struct resources_icc icc_res_sm8650[] = {
static const struct camss_subdev_resources csiphy_res_8300[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy0", "csiphy0_timer" },
.clock_rate = {
@@ -3092,7 +3434,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy1", "csiphy1_timer" },
.clock_rate = {
@@ -3110,7 +3455,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy2", "csiphy2_timer" },
.clock_rate = {
@@ -3131,7 +3479,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = {
static const struct camss_subdev_resources csiphy_res_8775p[] = {
/* CSIPHY0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy0", "csiphy0_timer"},
.clock_rate = {
{ 400000000 },
@@ -3148,7 +3499,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy1", "csiphy1_timer"},
.clock_rate = {
{ 400000000 },
@@ -3165,7 +3519,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy2", "csiphy2_timer"},
.clock_rate = {
{ 400000000 },
@@ -3182,7 +3539,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = {
},
/* CSIPHY3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {
+ { .supply = "vdda-phy", .init_load_uA = 15900 },
+ { .supply = "vdda-pll", .init_load_uA = 8900 }
+ },
.clock = { "csiphy_rx", "csiphy3", "csiphy3_timer"},
.clock_rate = {
{ 400000000 },
@@ -3535,8 +3895,10 @@ static const struct resources_icc icc_res_sa8775p[] = {
static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
/* CSIPHY0 */
{
- .regulators = { "vdd-csiphy-0p8",
- "vdd-csiphy-1p2" },
+ .regulators = {
+ { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 },
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 }
+ },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -3550,8 +3912,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdd-csiphy-0p8",
- "vdd-csiphy-1p2" },
+ .regulators = {
+ { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 },
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 }
+ },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -3565,8 +3929,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdd-csiphy-0p8",
- "vdd-csiphy-1p2" },
+ .regulators = {
+ { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 },
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 }
+ },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -3580,8 +3946,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdd-csiphy-0p8",
- "vdd-csiphy-1p2" },
+ .regulators = {
+ { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 },
+ { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 }
+ },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -4022,16 +4390,16 @@ static const struct parent_dev_ops vfe_parent_dev_ops = {
};
/*
- * camss_of_parse_endpoint_node - Parse port endpoint node
- * @dev: Device
- * @node: Device node to be parsed
+ * camss_parse_endpoint_node - Parse port endpoint node
+ * @dev: CAMSS device
+ * @ep: Device endpoint to be parsed
* @csd: Parsed data from port endpoint node
*
* Return 0 on success or a negative error code on failure
*/
-static int camss_of_parse_endpoint_node(struct device *dev,
- struct device_node *node,
- struct camss_async_subdev *csd)
+static int camss_parse_endpoint_node(struct device *dev,
+ struct fwnode_handle *ep,
+ struct camss_async_subdev *csd)
{
struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg;
struct v4l2_mbus_config_mipi_csi2 *mipi_csi2;
@@ -4039,7 +4407,7 @@ static int camss_of_parse_endpoint_node(struct device *dev,
unsigned int i;
int ret;
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
return ret;
@@ -4074,49 +4442,37 @@ static int camss_of_parse_endpoint_node(struct device *dev,
}
/*
- * camss_of_parse_ports - Parse ports node
- * @dev: Device
- * @notifier: v4l2_device notifier data
+ * camss_parse_ports - Parse ports node
+ * @dev: CAMSS device
*
- * Return number of "port" nodes found in "ports" node
+ * Return 0 on success or a negative error code on failure
*/
-static int camss_of_parse_ports(struct camss *camss)
+static int camss_parse_ports(struct camss *camss)
{
struct device *dev = camss->dev;
- struct device_node *node = NULL;
- struct device_node *remote = NULL;
- int ret, num_subdevs = 0;
+ struct fwnode_handle *fwnode = dev_fwnode(dev), *ep;
+ int ret;
- for_each_endpoint_of_node(dev->of_node, node) {
+ fwnode_graph_for_each_endpoint(fwnode, ep) {
struct camss_async_subdev *csd;
- remote = of_graph_get_remote_port_parent(node);
- if (!remote) {
- dev_err(dev, "Cannot get remote parent\n");
- ret = -EINVAL;
- goto err_cleanup;
- }
-
- csd = v4l2_async_nf_add_fwnode(&camss->notifier,
- of_fwnode_handle(remote),
- struct camss_async_subdev);
- of_node_put(remote);
+ csd = v4l2_async_nf_add_fwnode_remote(&camss->notifier, ep,
+ typeof(*csd));
if (IS_ERR(csd)) {
ret = PTR_ERR(csd);
goto err_cleanup;
}
- ret = camss_of_parse_endpoint_node(dev, node, csd);
+ ret = camss_parse_endpoint_node(dev, ep, csd);
if (ret < 0)
goto err_cleanup;
-
- num_subdevs++;
}
- return num_subdevs;
+ return 0;
err_cleanup:
- of_node_put(node);
+ fwnode_handle_put(ep);
+
return ret;
}
@@ -4673,7 +5029,7 @@ static int camss_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
- ret = camss_of_parse_ports(camss);
+ ret = camss_parse_ports(camss);
if (ret < 0)
goto err_v4l2_device_unregister;
@@ -4864,6 +5220,19 @@ static const struct camss_resources sdm845_resources = {
.vfe_num = ARRAY_SIZE(vfe_res_845),
};
+static const struct camss_resources sm6150_resources = {
+ .version = CAMSS_6150,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_sm6150,
+ .csid_res = csid_res_sm6150,
+ .vfe_res = vfe_res_sm6150,
+ .icc_res = icc_res_sm6150,
+ .icc_path_num = ARRAY_SIZE(icc_res_sm6150),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_sm6150),
+ .csid_num = ARRAY_SIZE(csid_res_sm6150),
+ .vfe_num = ARRAY_SIZE(vfe_res_sm6150),
+};
+
static const struct camss_resources sm8250_resources = {
.version = CAMSS_8250,
.pd_name = "top",
@@ -4959,6 +5328,7 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,sdm660-camss", .data = &sdm660_resources },
{ .compatible = "qcom,sdm670-camss", .data = &sdm670_resources },
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
+ { .compatible = "qcom,sm6150-camss", .data = &sm6150_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
{ .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 9d9a62640e25..6d048414c919 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -44,7 +44,7 @@
#define CAMSS_INIT_BUF_COUNT 2
struct camss_subdev_resources {
- char *regulators[CAMSS_RES_MAX];
+ struct regulator_bulk_data regulators[CAMSS_RES_MAX];
char *clock[CAMSS_RES_MAX];
char *clock_for_reset[CAMSS_RES_MAX];
u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX];
@@ -80,6 +80,7 @@ enum pm_domain {
enum camss_version {
CAMSS_660,
CAMSS_2290,
+ CAMSS_6150,
CAMSS_7280,
CAMSS_8x16,
CAMSS_8x39,
diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
index fad3be044e5f..2abbd3aeb4af 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -22,6 +22,7 @@ qcom-iris-objs += iris_buffer.o \
iris_venc.o \
iris_vpu2.o \
iris_vpu3x.o \
+ iris_vpu4x.o \
iris_vpu_buffer.o \
iris_vpu_common.o \
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index b89b1ee06cce..f1f003a787bf 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -351,12 +351,15 @@ static int iris_create_internal_buffer(struct iris_inst *inst,
buffer->index = index;
buffer->buffer_size = buffers->size;
buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
- list_add_tail(&buffer->list, &buffers->list);
buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
&buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
- if (!buffer->kvaddr)
+ if (!buffer->kvaddr) {
+ kfree(buffer);
return -ENOMEM;
+ }
+
+ list_add_tail(&buffer->list, &buffers->list);
return 0;
}
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
index 325d30fce5c9..75bb76776182 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -27,6 +27,7 @@ struct iris_inst;
* @BUF_SCRATCH_1: buffer to store decoding/encoding context data for HW
* @BUF_SCRATCH_2: buffer to store encoding context data for HW
* @BUF_VPSS: buffer to store VPSS context data for HW
+ * @BUF_PARTIAL: buffer for AV1 IBC data
* @BUF_TYPE_MAX: max buffer types
*/
enum iris_buffer_type {
@@ -42,6 +43,7 @@ enum iris_buffer_type {
BUF_SCRATCH_1,
BUF_SCRATCH_2,
BUF_VPSS,
+ BUF_PARTIAL,
BUF_TYPE_MAX,
};
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index c0b3a09ad3e3..3cec957580f5 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -98,6 +98,20 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
return B_FRAME_QP_H264;
case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP:
return B_FRAME_QP_HEVC;
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE:
+ return PROFILE_AV1;
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL:
+ return LEVEL_AV1;
+ case V4L2_CID_ROTATE:
+ return ROTATION;
+ case V4L2_CID_HFLIP:
+ return HFLIP;
+ case V4L2_CID_VFLIP:
+ return VFLIP;
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE:
+ return IR_TYPE;
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD:
+ return IR_PERIOD;
default:
return INST_FW_CAP_MAX;
}
@@ -185,6 +199,20 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP;
case B_FRAME_QP_HEVC:
return V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP;
+ case PROFILE_AV1:
+ return V4L2_CID_MPEG_VIDEO_AV1_PROFILE;
+ case LEVEL_AV1:
+ return V4L2_CID_MPEG_VIDEO_AV1_LEVEL;
+ case ROTATION:
+ return V4L2_CID_ROTATE;
+ case HFLIP:
+ return V4L2_CID_HFLIP;
+ case VFLIP:
+ return V4L2_CID_VFLIP;
+ case IR_TYPE:
+ return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE;
+ case IR_PERIOD:
+ return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD;
default:
return 0;
}
@@ -893,6 +921,81 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
&range, sizeof(range));
}
+int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 hfi_val;
+
+ switch (inst->fw_caps[cap_id].value) {
+ case 0:
+ hfi_val = HFI_ROTATION_NONE;
+ return 0;
+ case 90:
+ hfi_val = HFI_ROTATION_90;
+ break;
+ case 180:
+ hfi_val = HFI_ROTATION_180;
+ break;
+ case 270:
+ hfi_val = HFI_ROTATION_270;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &hfi_val, sizeof(u32));
+}
+
+int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 hfi_val = HFI_DISABLE_FLIP;
+
+ if (inst->fw_caps[HFLIP].value)
+ hfi_val |= HFI_HORIZONTAL_FLIP;
+
+ if (inst->fw_caps[VFLIP].value)
+ hfi_val |= HFI_VERTICAL_FLIP;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_val, sizeof(u32));
+}
+
+int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 ir_period = inst->fw_caps[cap_id].value;
+ u32 ir_type = 0;
+
+ if (inst->fw_caps[IR_TYPE].value ==
+ V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) {
+ if (vb2_is_streaming(q))
+ return 0;
+ ir_type = HFI_PROP_IR_RANDOM_PERIOD;
+ } else if (inst->fw_caps[IR_TYPE].value ==
+ V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) {
+ ir_type = HFI_PROP_IR_CYCLIC_PERIOD;
+ } else {
+ return -EINVAL;
+ }
+
+ return hfi_ops->session_set_property(inst, ir_type,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &ir_period, sizeof(u32));
+}
+
int iris_set_properties(struct iris_inst *inst, u32 plane)
{
const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
index 30af333cc494..9518803577bc 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.h
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -32,6 +32,9 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i
int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_properties(struct iris_inst *inst, u32 plane);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index 679444327ed7..5f408024e967 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -64,9 +64,9 @@ err_release_fw:
int iris_fw_load(struct iris_core *core)
{
- struct tz_cp_config *cp_config = core->iris_platform_data->tz_cp_config_data;
+ const struct tz_cp_config *cp_config;
const char *fwpath = NULL;
- int ret;
+ int i, ret;
ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
&fwpath);
@@ -85,14 +85,17 @@ int iris_fw_load(struct iris_core *core)
return ret;
}
- ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
- cp_config->cp_size,
- cp_config->cp_nonpixel_start,
- cp_config->cp_nonpixel_size);
- if (ret) {
- dev_err(core->dev, "protect memory failed\n");
- qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
- return ret;
+ for (i = 0; i < core->iris_platform_data->tz_cp_config_data_size; i++) {
+ cp_config = &core->iris_platform_data->tz_cp_config_data[i];
+ ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
+ cp_config->cp_size,
+ cp_config->cp_nonpixel_start,
+ cp_config->cp_nonpixel_size);
+ if (ret) {
+ dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
+ qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ return ret;
+ }
}
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index b51471fb32c7..3edb5ae582b4 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -141,6 +141,9 @@ struct hfi_subscription_params {
u32 profile;
u32 level;
u32 tier;
+ u32 drap;
+ u32 film_grain;
+ u32 super_block;
};
u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index 52da7ef7bab0..11815f6f5bac 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -282,7 +282,7 @@ static int iris_hfi_gen1_queue_input_buffer(struct iris_inst *inst, struct iris_
com_ip_pkt.shdr.session_id = inst->session_id;
com_ip_pkt.time_stamp_hi = upper_32_bits(buf->timestamp);
com_ip_pkt.time_stamp_lo = lower_32_bits(buf->timestamp);
- com_ip_pkt.flags = buf->flags;
+ com_ip_pkt.flags = 0;
com_ip_pkt.mark_target = 0;
com_ip_pkt.mark_data = 0;
com_ip_pkt.offset = buf->data_offset;
@@ -441,6 +441,8 @@ static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct ir
goto exit;
ret = iris_wait_for_session_response(inst, false);
+ if (!ret)
+ ret = iris_destroy_internal_buffer(inst, buf);
exit:
kfree(pkt);
@@ -733,7 +735,7 @@ static int iris_hfi_gen1_set_resolution(struct iris_inst *inst, u32 plane)
struct hfi_framesize fs;
int ret;
- if (!iris_drc_pending(inst)) {
+ if (!iris_drc_pending(inst) && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) {
fs.buffer_type = HFI_BUFFER_INPUT;
fs.width = inst->fmt_src->fmt.pix_mp.width;
fs.height = inst->fmt_src->fmt.pix_mp.height;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index f91295532099..715ec9575b90 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -10,6 +10,7 @@
#define UNSPECIFIED_COLOR_FORMAT 5
#define NUM_SYS_INIT_PACKETS 8
+#define NUM_COMV_AV1 18
#define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \
NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32)))
@@ -121,6 +122,7 @@ static u32 iris_hfi_gen2_get_port_from_buf_type(struct iris_inst *inst,
case BUF_COMV:
case BUF_NON_COMV:
case BUF_LINE:
+ case BUF_PARTIAL:
return HFI_PORT_BITSTREAM;
case BUF_OUTPUT:
case BUF_DPB:
@@ -168,8 +170,7 @@ static int iris_hfi_gen2_session_set_property(struct iris_inst *inst, u32 packet
static int iris_hfi_gen2_set_raw_resolution(struct iris_inst *inst, u32 plane)
{
- u32 resolution = inst->fmt_src->fmt.pix_mp.width << 16 |
- inst->fmt_src->fmt.pix_mp.height;
+ u32 resolution = inst->enc_raw_width << 16 | inst->enc_raw_height;
u32 port = iris_hfi_gen2_get_port(inst, plane);
return iris_hfi_gen2_session_set_property(inst,
@@ -181,22 +182,36 @@ static int iris_hfi_gen2_set_raw_resolution(struct iris_inst *inst, u32 plane)
sizeof(u32));
}
+static inline u32 iris_hfi_get_aligned_resolution(struct iris_inst *inst, u32 width, u32 height)
+{
+ u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
+
+ return (ALIGN(width, codec_align) << 16 | ALIGN(height, codec_align));
+}
+
static int iris_hfi_gen2_set_bitstream_resolution(struct iris_inst *inst, u32 plane)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 port = iris_hfi_gen2_get_port(inst, plane);
enum hfi_packet_payload_info payload_type;
- u32 resolution, codec_align;
+ u32 width, height;
+ u32 resolution;
if (inst->domain == DECODER) {
- resolution = inst->fmt_src->fmt.pix_mp.width << 16 |
- inst->fmt_src->fmt.pix_mp.height;
+ width = inst->fmt_src->fmt.pix_mp.width;
+ height = inst->fmt_src->fmt.pix_mp.height;
+ resolution = iris_hfi_get_aligned_resolution(inst, width, height);
inst_hfi_gen2->src_subcr_params.bitstream_resolution = resolution;
payload_type = HFI_PAYLOAD_U32;
} else {
- codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
- resolution = ALIGN(inst->fmt_dst->fmt.pix_mp.width, codec_align) << 16 |
- ALIGN(inst->fmt_dst->fmt.pix_mp.height, codec_align);
+ if (is_rotation_90_or_270(inst)) {
+ width = inst->enc_scale_height;
+ height = inst->enc_scale_width;
+ } else {
+ width = inst->enc_scale_width;
+ height = inst->enc_scale_height;
+ }
+ resolution = iris_hfi_get_aligned_resolution(inst, width, height);
inst_hfi_gen2->dst_subcr_params.bitstream_resolution = resolution;
payload_type = HFI_PAYLOAD_32_PACKED;
}
@@ -216,7 +231,7 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst, u32 plane)
u32 port = iris_hfi_gen2_get_port(inst, plane);
u32 bottom_offset, right_offset;
u32 left_offset, top_offset;
- u32 payload[2];
+ u32 payload[2], codec_align;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
@@ -231,10 +246,27 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst, u32 plane)
top_offset = inst->compose.top;
}
} else {
- bottom_offset = (inst->fmt_src->fmt.pix_mp.height - inst->crop.height);
- right_offset = (inst->fmt_src->fmt.pix_mp.width - inst->crop.width);
- left_offset = inst->crop.left;
- top_offset = inst->crop.top;
+ codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ bottom_offset = (inst->enc_raw_height - inst->crop.height);
+ right_offset = (inst->enc_raw_width - inst->crop.width);
+ left_offset = inst->crop.left;
+ top_offset = inst->crop.top;
+ } else {
+ if (is_rotation_90_or_270(inst)) {
+ bottom_offset = (ALIGN(inst->enc_scale_width, codec_align) -
+ inst->enc_scale_width);
+ right_offset = (ALIGN(inst->enc_scale_height, codec_align) -
+ inst->enc_scale_height);
+ } else {
+ bottom_offset = (ALIGN(inst->enc_scale_height, codec_align) -
+ inst->enc_scale_height);
+ right_offset = (ALIGN(inst->enc_scale_width, codec_align) -
+ inst->enc_scale_width);
+ }
+ left_offset = 0;
+ top_offset = 0;
+ }
}
payload[0] = FIELD_PREP(GENMASK(31, 16), left_offset) | top_offset;
@@ -380,6 +412,9 @@ static int iris_hfi_gen2_set_profile(struct iris_inst *inst, u32 plane)
case V4L2_PIX_FMT_H264:
profile = inst->fw_caps[PROFILE_H264].value;
break;
+ case V4L2_PIX_FMT_AV1:
+ profile = inst->fw_caps[PROFILE_AV1].value;
+ break;
}
inst_hfi_gen2->src_subcr_params.profile = profile;
@@ -409,6 +444,9 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst, u32 plane)
case V4L2_PIX_FMT_H264:
level = inst->fw_caps[LEVEL_H264].value;
break;
+ case V4L2_PIX_FMT_AV1:
+ level = inst->fw_caps[LEVEL_AV1].value;
+ break;
}
inst_hfi_gen2->src_subcr_params.level = level;
@@ -496,10 +534,12 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32
static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane)
{
- struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 tier = inst->fw_caps[TIER].value;
+ tier = (inst->codec == V4L2_PIX_FMT_AV1) ? inst->fw_caps[TIER_AV1].value :
+ inst->fw_caps[TIER].value;
inst_hfi_gen2->src_subcr_params.tier = tier;
return iris_hfi_gen2_session_set_property(inst,
@@ -525,6 +565,40 @@ static int iris_hfi_gen2_set_frame_rate(struct iris_inst *inst, u32 plane)
sizeof(u32));
}
+static int iris_hfi_gen2_set_film_grain(struct iris_inst *inst, u32 plane)
+{
+ u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 film_grain = inst->fw_caps[FILM_GRAIN].value;
+
+ inst_hfi_gen2->src_subcr_params.film_grain = film_grain;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_AV1_FILM_GRAIN_PRESENT,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &film_grain,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane)
+{
+ u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 super_block = inst->fw_caps[SUPER_BLOCK].value;
+
+ inst_hfi_gen2->src_subcr_params.super_block = super_block;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_AV1_SUPER_BLOCK_ENABLED,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &super_block,
+ sizeof(u32));
+}
+
static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
{
const struct iris_platform_data *pdata = inst->core->iris_platform_data;
@@ -548,6 +622,9 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
{HFI_PROP_TIER, iris_hfi_gen2_set_tier },
{HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate },
+ {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain },
+ {HFI_PROP_AV1_SUPER_BLOCK_ENABLED, iris_hfi_gen2_set_super_block },
+ {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable },
};
if (inst->domain == DECODER) {
@@ -561,6 +638,9 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
} else if (inst->codec == V4L2_PIX_FMT_VP9) {
config_params = pdata->dec_input_config_params_vp9;
config_params_size = pdata->dec_input_config_params_vp9_size;
+ } else if (inst->codec == V4L2_PIX_FMT_AV1) {
+ config_params = pdata->dec_input_config_params_av1;
+ config_params_size = pdata->dec_input_config_params_av1_size;
} else {
return -EINVAL;
}
@@ -615,6 +695,9 @@ static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
break;
case V4L2_PIX_FMT_VP9:
codec = HFI_CODEC_DECODE_VP9;
+ break;
+ case V4L2_PIX_FMT_AV1:
+ codec = HFI_CODEC_DECODE_AV1;
}
iris_hfi_gen2_packet_session_property(inst,
@@ -760,7 +843,7 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
if ((V4L2_TYPE_IS_OUTPUT(plane) && inst_hfi_gen2->ipsc_properties_set) ||
(V4L2_TYPE_IS_CAPTURE(plane) && inst_hfi_gen2->opsc_properties_set)) {
- dev_err(core->dev, "invalid plane\n");
+ dev_dbg(core->dev, "%cPSC already set\n", V4L2_TYPE_IS_OUTPUT(plane) ? 'I' : 'O');
return 0;
}
@@ -780,6 +863,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
change_param_size =
core->iris_platform_data->dec_input_config_params_vp9_size;
break;
+ case V4L2_PIX_FMT_AV1:
+ change_param = core->iris_platform_data->dec_input_config_params_av1;
+ change_param_size =
+ core->iris_platform_data->dec_input_config_params_av1_size;
+ break;
}
payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;
@@ -862,6 +950,16 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
payload_size = sizeof(u32);
payload_type = HFI_PAYLOAD_U32;
break;
+ case HFI_PROP_AV1_FILM_GRAIN_PRESENT:
+ payload[0] = subsc_params.film_grain;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_AV1_SUPER_BLOCK_ENABLED:
+ payload[0] = subsc_params.super_block;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
default:
prop_type = 0;
ret = -EINVAL;
@@ -917,6 +1015,11 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
subscribe_prop_size =
core->iris_platform_data->dec_output_prop_vp9_size;
break;
+ case V4L2_PIX_FMT_AV1:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_av1;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_av1_size;
+ break;
}
}
@@ -963,6 +1066,9 @@ static int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane)
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
int ret = 0;
+ if (!inst_hfi_gen2->packet)
+ return -EINVAL;
+
reinit_completion(&inst->completion);
iris_hfi_gen2_packet_session_command(inst,
@@ -1092,6 +1198,8 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type
return HFI_BUFFER_ARP;
case BUF_VPSS:
return HFI_BUFFER_VPSS;
+ case BUF_PARTIAL:
+ return HFI_BUFFER_PARTIAL_DATA;
default:
return 0;
}
@@ -1104,7 +1212,13 @@ static int iris_set_num_comv(struct iris_inst *inst)
u32 num_comv;
caps = core->iris_platform_data->inst_caps;
- num_comv = caps->num_comv;
+
+ /*
+ * AV1 needs more comv buffers than other codecs.
+ * Update accordingly.
+ */
+ num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ?
+ NUM_COMV_AV1 : caps->num_comv;
return core->hfi_ops->session_set_property(inst,
HFI_PROP_COMV_BUFFER_COUNT,
@@ -1212,5 +1326,10 @@ void iris_hfi_gen2_command_ops_init(struct iris_core *core)
struct iris_inst *iris_hfi_gen2_get_instance(void)
{
- return (struct iris_inst *)kzalloc(sizeof(struct iris_inst_hfi_gen2), GFP_KERNEL);
+ struct iris_inst_hfi_gen2 *out;
+
+ /* The allocation is intentionally larger than struct iris_inst. */
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+
+ return &out->inst;
}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index 1b6a4dbac828..cecf771c55dd 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -70,6 +70,7 @@ enum hfi_rate_control {
#define HFI_PROP_QP_PACKED 0x0300012e
#define HFI_PROP_MIN_QP_PACKED 0x0300012f
#define HFI_PROP_MAX_QP_PACKED 0x03000130
+#define HFI_PROP_IR_RANDOM_PERIOD 0x03000131
#define HFI_PROP_TOTAL_BITRATE 0x0300013b
#define HFI_PROP_MAX_GOP_FRAMES 0x03000146
#define HFI_PROP_MAX_B_FRAMES 0x03000147
@@ -83,16 +84,42 @@ enum hfi_seq_header_mode {
};
#define HFI_PROP_SEQ_HEADER_MODE 0x03000149
+
+enum hfi_rotation {
+ HFI_ROTATION_NONE = 0x00000000,
+ HFI_ROTATION_90 = 0x00000001,
+ HFI_ROTATION_180 = 0x00000002,
+ HFI_ROTATION_270 = 0x00000003,
+};
+
+#define HFI_PROP_ROTATION 0x0300014b
+
+enum hfi_flip {
+ HFI_DISABLE_FLIP = 0x00000000,
+ HFI_HORIZONTAL_FLIP = 0x00000001,
+ HFI_VERTICAL_FLIP = 0x00000002,
+};
+
+#define HFI_PROP_FLIP 0x0300014c
#define HFI_PROP_SIGNAL_COLOR_INFO 0x03000155
#define HFI_PROP_PICTURE_TYPE 0x03000162
#define HFI_PROP_DEC_DEFAULT_HEADER 0x03000168
#define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169
#define HFI_PROP_NO_OUTPUT 0x0300016a
#define HFI_PROP_BUFFER_MARK 0x0300016c
+#define HFI_PROP_WORST_COMPRESSION_RATIO 0x03000174
+#define HFI_PROP_WORST_COMPLEXITY_FACTOR 0x03000175
#define HFI_PROP_RAW_RESOLUTION 0x03000178
#define HFI_PROP_TOTAL_PEAK_BITRATE 0x0300017C
+#define HFI_PROP_AV1_FILM_GRAIN_PRESENT 0x03000180
+#define HFI_PROP_AV1_SUPER_BLOCK_ENABLED 0x03000181
+#define HFI_PROP_AV1_OP_POINT 0x03000182
+#define HFI_PROP_IR_CYCLIC_PERIOD 0x0300017E
#define HFI_PROP_OPB_ENABLE 0x03000184
+#define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187
+#define HFI_PROP_AV1_DRAP_CONFIG 0x03000189
#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
+#define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197
#define HFI_PROP_END 0x03FFFFFF
#define HFI_SESSION_ERROR_BEGIN 0x04000000
@@ -139,6 +166,7 @@ enum hfi_codec_type {
HFI_CODEC_DECODE_HEVC = 3,
HFI_CODEC_ENCODE_HEVC = 4,
HFI_CODEC_DECODE_VP9 = 5,
+ HFI_CODEC_DECODE_AV1 = 7,
};
enum hfi_picture_type {
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
index 2f1f118eae4f..8e19f61bbbf9 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -54,6 +54,10 @@ static u32 iris_hfi_gen2_buf_type_to_driver(struct iris_inst *inst,
return BUF_SCRATCH_2;
case HFI_BUFFER_PERSIST:
return BUF_PERSIST;
+ case HFI_BUFFER_PARTIAL_DATA:
+ return BUF_PARTIAL;
+ case HFI_BUFFER_VPSS:
+ return BUF_VPSS;
default:
return 0;
}
@@ -72,6 +76,7 @@ static bool iris_hfi_gen2_is_valid_hfi_buffer_type(u32 buffer_type)
case HFI_BUFFER_DPB:
case HFI_BUFFER_PERSIST:
case HFI_BUFFER_VPSS:
+ case HFI_BUFFER_PARTIAL_DATA:
return true;
default:
return false;
@@ -596,6 +601,10 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
inst->fw_caps[PROFILE_H264].value = subsc_params.profile;
inst->fw_caps[LEVEL_H264].value = subsc_params.level;
break;
+ case V4L2_PIX_FMT_AV1:
+ inst->fw_caps[PROFILE_AV1].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_AV1].value = subsc_params.level;
+ break;
}
inst->fw_caps[POC].value = subsc_params.pic_order_cnt;
@@ -608,6 +617,11 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
iris_inst_change_state(inst, IRIS_INST_ERROR);
}
+ if (inst->codec == V4L2_PIX_FMT_AV1) {
+ inst->fw_caps[FILM_GRAIN].value = subsc_params.film_grain;
+ inst->fw_caps[SUPER_BLOCK].value = subsc_params.super_block;
+ }
+
inst->fw_min_count = subsc_params.fw_min_count;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
@@ -711,6 +725,12 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
case HFI_PROP_NO_OUTPUT:
inst_hfi_gen2->hfi_frame_info.no_output = 1;
break;
+ case HFI_PROP_AV1_FILM_GRAIN_PRESENT:
+ inst_hfi_gen2->src_subcr_params.film_grain = pkt->payload[0];
+ break;
+ case HFI_PROP_AV1_SUPER_BLOCK_ENABLED:
+ inst_hfi_gen2->src_subcr_params.super_block = pkt->payload[0];
+ break;
case HFI_PROP_QUALITY_MODE:
case HFI_PROP_STAGE:
case HFI_PROP_PIPE:
@@ -841,6 +861,10 @@ static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst)
subsc_params->profile = inst->fw_caps[PROFILE_H264].value;
subsc_params->level = inst->fw_caps[LEVEL_H264].value;
break;
+ case V4L2_PIX_FMT_AV1:
+ subsc_params->profile = inst->fw_caps[PROFILE_AV1].value;
+ subsc_params->level = inst->fw_caps[LEVEL_AV1].value;
+ break;
}
subsc_params->pic_order_cnt = inst->fw_caps[POC].value;
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index 62fbb30691ff..16965150f427 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -19,6 +19,7 @@ enum iris_fmt_type_out {
IRIS_FMT_H264,
IRIS_FMT_HEVC,
IRIS_FMT_VP9,
+ IRIS_FMT_AV1,
};
enum iris_fmt_type_cap {
@@ -69,6 +70,10 @@ struct iris_fmt {
* @frame_rate: frame rate of current instance
* @operating_rate: operating rate of current instance
* @hfi_rc_type: rate control type
+ * @enc_raw_width: source image width for encoder instance
+ * @enc_raw_height: source image height for encoder instance
+ * @enc_scale_width: scale width for encoder instance
+ * @enc_scale_height: scale height for encoder instance
*/
struct iris_inst {
@@ -107,6 +112,10 @@ struct iris_inst {
u32 frame_rate;
u32 operating_rate;
u32 hfi_rc_type;
+ u32 enc_raw_width;
+ u32 enc_raw_height;
+ u32 enc_scale_width;
+ u32 enc_scale_height;
};
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index 8d8cdb56a3c7..5a489917580e 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -57,6 +57,10 @@ enum platform_clk_type {
IRIS_AXI1_CLK,
IRIS_CTRL_FREERUN_CLK,
IRIS_HW_FREERUN_CLK,
+ IRIS_BSE_HW_CLK,
+ IRIS_VPP0_HW_CLK,
+ IRIS_VPP1_HW_CLK,
+ IRIS_APV_HW_CLK,
};
struct platform_clk_data {
@@ -103,6 +107,13 @@ enum platform_inst_fw_cap_type {
LEVEL_H264,
LEVEL_HEVC,
LEVEL_VP9,
+ PROFILE_AV1,
+ LEVEL_AV1,
+ TIER_AV1,
+ DRAP,
+ FILM_GRAIN,
+ SUPER_BLOCK,
+ ENH_LAYER_COUNT,
INPUT_BUF_HOST_MAX_COUNT,
OUTPUT_BUF_HOST_MAX_COUNT,
STAGE,
@@ -143,6 +154,11 @@ enum platform_inst_fw_cap_type {
P_FRAME_QP_HEVC,
B_FRAME_QP_H264,
B_FRAME_QP_HEVC,
+ ROTATION,
+ HFLIP,
+ VFLIP,
+ IR_TYPE,
+ IR_PERIOD,
INST_FW_CAP_MAX,
};
@@ -191,6 +207,9 @@ struct icc_vote_data {
enum platform_pm_domain_type {
IRIS_CTRL_POWER_DOMAIN,
IRIS_HW_POWER_DOMAIN,
+ IRIS_VPP0_HW_POWER_DOMAIN,
+ IRIS_VPP1_HW_POWER_DOMAIN,
+ IRIS_APV_HW_POWER_DOMAIN,
};
struct iris_platform_data {
@@ -209,6 +228,7 @@ struct iris_platform_data {
const char * const *opp_pd_tbl;
unsigned int opp_pd_tbl_size;
const struct platform_clk_data *clk_tbl;
+ const char * const *opp_clk_tbl;
unsigned int clk_tbl_size;
const char * const *clk_rst_tbl;
unsigned int clk_rst_tbl_size;
@@ -217,12 +237,15 @@ struct iris_platform_data {
u64 dma_mask;
const char *fwname;
u32 pas_id;
+ struct iris_fmt *inst_iris_fmts;
+ u32 inst_iris_fmts_size;
struct platform_inst_caps *inst_caps;
const struct platform_inst_fw_cap *inst_fw_caps_dec;
u32 inst_fw_caps_dec_size;
const struct platform_inst_fw_cap *inst_fw_caps_enc;
u32 inst_fw_caps_enc_size;
- struct tz_cp_config *tz_cp_config_data;
+ const struct tz_cp_config *tz_cp_config_data;
+ u32 tz_cp_config_data_size;
u32 core_arch;
u32 hw_response_timeout;
struct ubwc_config_data *ubwc_config;
@@ -239,6 +262,8 @@ struct iris_platform_data {
unsigned int dec_input_config_params_hevc_size;
const u32 *dec_input_config_params_vp9;
unsigned int dec_input_config_params_vp9_size;
+ const u32 *dec_input_config_params_av1;
+ unsigned int dec_input_config_params_av1_size;
const u32 *dec_output_config_params;
unsigned int dec_output_config_params_size;
const u32 *enc_input_config_params;
@@ -253,6 +278,8 @@ struct iris_platform_data {
unsigned int dec_output_prop_hevc_size;
const u32 *dec_output_prop_vp9;
unsigned int dec_output_prop_vp9_size;
+ const u32 *dec_output_prop_av1;
+ unsigned int dec_output_prop_av1_size;
const u32 *dec_ip_int_buf_tbl;
unsigned int dec_ip_int_buf_tbl_size;
const u32 *dec_op_int_buf_tbl;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
index 34cbeb8f52e2..df8e6bf9430e 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
@@ -11,6 +11,7 @@
#include "iris_hfi_gen1_defines.h"
#include "iris_vpu_buffer.h"
#include "iris_vpu_common.h"
+#include "iris_instance.h"
#include "iris_platform_sc7280.h"
@@ -19,7 +20,22 @@
#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2)
#define BITRATE_STEP 100
-static const struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
+static struct iris_fmt platform_fmts_sm8250_dec[] = {
+ [IRIS_FMT_H264] = {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_HEVC] = {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_VP9] = {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
+static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
{
.cap_id = PIPE,
/* .max, .min and .value are set via platform data */
@@ -273,11 +289,18 @@ static const struct platform_clk_data sm8250_clk_table[] = {
{IRIS_HW_CLK, "vcodec0_core" },
};
-static struct tz_cp_config tz_cp_config_sm8250 = {
- .cp_start = 0,
- .cp_size = 0x25800000,
- .cp_nonpixel_start = 0x01000000,
- .cp_nonpixel_size = 0x24800000,
+static const char * const sm8250_opp_clk_table[] = {
+ "vcodec0_core",
+ NULL,
+};
+
+static const struct tz_cp_config tz_cp_config_sm8250[] = {
+ {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+ },
};
static const u32 sm8250_vdec_input_config_param_default[] = {
@@ -333,16 +356,20 @@ const struct iris_platform_data sm8250_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
.clk_tbl = sm8250_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
+ .opp_clk_tbl = sm8250_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu-1.0/venus.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8250_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec),
.inst_caps = &platform_inst_cap_sm8250,
.inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
- .tz_cp_config_data = &tz_cp_config_sm8250,
+ .tz_cp_config_data = tz_cp_config_sm8250,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250),
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.num_vpp_pipe = 4,
.max_session_count = 16,
@@ -382,16 +409,20 @@ const struct iris_platform_data sc7280_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table),
.clk_tbl = sc7280_clk_table,
.clk_tbl_size = ARRAY_SIZE(sc7280_clk_table),
+ .opp_clk_tbl = sc7280_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu/vpu20_p1.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8250_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec),
.inst_caps = &platform_inst_cap_sm8250,
.inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
- .tz_cp_config_data = &tz_cp_config_sm8250,
+ .tz_cp_config_data = tz_cp_config_sm8250,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250),
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.num_vpp_pipe = 1,
.no_aon = true,
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
index c1989240c248..5da90d47f9c6 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -19,6 +19,25 @@
#define VIDEO_ARCH_LX 1
#define BITRATE_MAX 245000000
+static struct iris_fmt platform_fmts_sm8550_dec[] = {
+ [IRIS_FMT_H264] = {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_HEVC] = {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_VP9] = {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_AV1] = {
+ .pixfmt = V4L2_PIX_FMT_AV1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = PROFILE_H264,
@@ -46,6 +65,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
.set = iris_set_u32_enum,
},
{
+ .cap_id = PROFILE_AV1,
+ .min = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN),
+ .value = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
.cap_id = PROFILE_VP9,
.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
.max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
@@ -129,6 +158,33 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
.set = iris_set_u32_enum,
},
{
+ .cap_id = LEVEL_AV1,
+ .min = V4L2_MPEG_VIDEO_AV1_LEVEL_2_0,
+ .max = V4L2_MPEG_VIDEO_AV1_LEVEL_6_1,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_3) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_3) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_3) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_3) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_6_1),
+ .value = V4L2_MPEG_VIDEO_AV1_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
.cap_id = TIER,
.min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
@@ -140,6 +196,53 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
.set = iris_set_u32_enum,
},
{
+ .cap_id = TIER_AV1,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_TIER,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = DRAP,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_AV1_DRAP_CONFIG,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = FILM_GRAIN,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_AV1_FILM_GRAIN_PRESENT,
+ .flags = CAP_FLAG_VOLATILE,
+ },
+ {
+ .cap_id = SUPER_BLOCK,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_AV1_SUPER_BLOCK_ENABLED,
+ },
+ {
+ .cap_id = ENH_LAYER_COUNT,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_AV1_OP_POINT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
.cap_id = INPUT_BUF_HOST_MAX_COUNT,
.min = DEFAULT_MAX_HOST_BUF_COUNT,
.max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
@@ -586,6 +689,57 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_u32,
},
+ {
+ .cap_id = ROTATION,
+ .min = 0,
+ .max = 270,
+ .step_or_mask = 90,
+ .value = 0,
+ .hfi_id = HFI_PROP_ROTATION,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_rotation,
+ },
+ {
+ .cap_id = HFLIP,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_FLIP,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_flip,
+ },
+ {
+ .cap_id = VFLIP,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_FLIP,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_flip,
+ },
+ {
+ .cap_id = IR_TYPE,
+ .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC,
+ .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) |
+ BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC),
+ .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ },
+ {
+ .cap_id = IR_PERIOD,
+ .min = 0,
+ .max = INT_MAX,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_ir_period,
+ },
};
static struct platform_inst_caps platform_inst_cap_sm8550 = {
@@ -631,6 +785,11 @@ static const struct platform_clk_data sm8550_clk_table[] = {
{IRIS_HW_CLK, "vcodec0_core" },
};
+static const char * const sm8550_opp_clk_table[] = {
+ "vcodec0_core",
+ NULL,
+};
+
static struct ubwc_config_data ubwc_config_sm8550 = {
.max_channels = 8,
.mal_length = 32,
@@ -641,11 +800,13 @@ static struct ubwc_config_data ubwc_config_sm8550 = {
.bank_spreading = 1,
};
-static struct tz_cp_config tz_cp_config_sm8550 = {
- .cp_start = 0,
- .cp_size = 0x25800000,
- .cp_nonpixel_start = 0x01000000,
- .cp_nonpixel_size = 0x24800000,
+static const struct tz_cp_config tz_cp_config_sm8550[] = {
+ {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+ },
};
static const u32 sm8550_vdec_input_config_params_default[] = {
@@ -680,6 +841,19 @@ static const u32 sm8550_vdec_input_config_param_vp9[] = {
HFI_PROP_LEVEL,
};
+static const u32 sm8550_vdec_input_config_param_av1[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+ HFI_PROP_TIER,
+ HFI_PROP_AV1_FILM_GRAIN_PRESENT,
+ HFI_PROP_AV1_SUPER_BLOCK_ENABLED,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
static const u32 sm8550_venc_input_config_params[] = {
HFI_PROP_COLOR_FORMAT,
HFI_PROP_RAW_RESOLUTION,
@@ -717,17 +891,28 @@ static const u32 sm8550_vdec_subscribe_output_properties_vp9[] = {
HFI_PROP_PICTURE_TYPE,
};
+static const u32 sm8550_vdec_subscribe_output_properties_av1[] = {
+ HFI_PROP_PICTURE_TYPE,
+ HFI_PROP_WORST_COMPRESSION_RATIO,
+ HFI_PROP_WORST_COMPLEXITY_FACTOR,
+};
+
static const u32 sm8550_dec_ip_int_buf_tbl[] = {
BUF_BIN,
BUF_COMV,
BUF_NON_COMV,
BUF_LINE,
+ BUF_PARTIAL,
};
static const u32 sm8550_dec_op_int_buf_tbl[] = {
BUF_DPB,
};
+static const u32 sm8550_enc_ip_int_buf_tbl[] = {
+ BUF_VPSS,
+};
+
static const u32 sm8550_enc_op_int_buf_tbl[] = {
BUF_BIN,
BUF_COMV,
@@ -755,16 +940,20 @@ const struct iris_platform_data sm8550_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = sm8550_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu/vpu30_p4.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8550_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
.inst_caps = &platform_inst_cap_sm8550,
.inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = &tz_cp_config_sm8550,
+ .tz_cp_config_data = tz_cp_config_sm8550,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
.core_arch = VIDEO_ARCH_LX,
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.ubwc_config = &ubwc_config_sm8550,
@@ -784,6 +973,10 @@ const struct iris_platform_data sm8550_data = {
sm8550_vdec_input_config_param_vp9,
.dec_input_config_params_vp9_size =
ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
+ .dec_input_config_params_av1 =
+ sm8550_vdec_input_config_param_av1,
+ .dec_input_config_params_av1_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
.dec_output_config_params =
sm8550_vdec_output_config_params,
.dec_output_config_params_size =
@@ -809,12 +1002,17 @@ const struct iris_platform_data sm8550_data = {
.dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
.dec_output_prop_vp9_size =
ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
+ .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
+ .dec_output_prop_av1_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
.dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
.dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
.dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
.dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+ .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
.enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
@@ -847,16 +1045,20 @@ const struct iris_platform_data sm8650_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = sm8550_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu/vpu33_p4.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8550_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
.inst_caps = &platform_inst_cap_sm8550,
.inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = &tz_cp_config_sm8550,
+ .tz_cp_config_data = tz_cp_config_sm8550,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
.core_arch = VIDEO_ARCH_LX,
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.ubwc_config = &ubwc_config_sm8550,
@@ -876,6 +1078,10 @@ const struct iris_platform_data sm8650_data = {
sm8550_vdec_input_config_param_vp9,
.dec_input_config_params_vp9_size =
ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
+ .dec_input_config_params_av1 =
+ sm8550_vdec_input_config_param_av1,
+ .dec_input_config_params_av1_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
.dec_output_config_params =
sm8550_vdec_output_config_params,
.dec_output_config_params_size =
@@ -901,12 +1107,17 @@ const struct iris_platform_data sm8650_data = {
.dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
.dec_output_prop_vp9_size =
ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
+ .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
+ .dec_output_prop_av1_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
.dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
.dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
.dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
.dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+ .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
.enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
@@ -915,6 +1126,7 @@ const struct iris_platform_data sm8750_data = {
.get_instance = iris_hfi_gen2_get_instance,
.init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .get_vpu_buffer_size = iris_vpu33_buf_size,
.vpu_ops = &iris_vpu35_ops,
.set_preset_registers = iris_set_sm8550_preset_registers,
.icc_tbl = sm8550_icc_table,
@@ -929,22 +1141,27 @@ const struct iris_platform_data sm8750_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
.clk_tbl = sm8750_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8750_clk_table),
+ .opp_clk_tbl = sm8550_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu/vpu35_p4.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8550_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
.inst_caps = &platform_inst_cap_sm8550,
.inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = &tz_cp_config_sm8550,
+ .tz_cp_config_data = tz_cp_config_sm8550,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
.core_arch = VIDEO_ARCH_LX,
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.ubwc_config = &ubwc_config_sm8550,
.num_vpp_pipe = 4,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K * 2,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
.dec_input_config_params_default =
sm8550_vdec_input_config_params_default,
.dec_input_config_params_default_size =
@@ -957,6 +1174,10 @@ const struct iris_platform_data sm8750_data = {
sm8550_vdec_input_config_param_vp9,
.dec_input_config_params_vp9_size =
ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
+ .dec_input_config_params_av1 =
+ sm8550_vdec_input_config_param_av1,
+ .dec_input_config_params_av1_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
.dec_output_config_params =
sm8550_vdec_output_config_params,
.dec_output_config_params_size =
@@ -982,12 +1203,17 @@ const struct iris_platform_data sm8750_data = {
.dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
.dec_output_prop_vp9_size =
ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
+ .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
+ .dec_output_prop_av1_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
.dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
.dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
.dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
.dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+ .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
.enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
@@ -1015,16 +1241,20 @@ const struct iris_platform_data qcs8300_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = sm8550_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu/vpu30_p4_s6.mbn",
.pas_id = IRIS_PAS_ID,
+ .inst_iris_fmts = platform_fmts_sm8550_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
.inst_caps = &platform_inst_cap_qcs8300,
.inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = &tz_cp_config_sm8550,
+ .tz_cp_config_data = tz_cp_config_sm8550,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
.core_arch = VIDEO_ARCH_LX,
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.ubwc_config = &ubwc_config_sm8550,
@@ -1044,6 +1274,10 @@ const struct iris_platform_data qcs8300_data = {
sm8550_vdec_input_config_param_vp9,
.dec_input_config_params_vp9_size =
ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
+ .dec_input_config_params_av1 =
+ sm8550_vdec_input_config_param_av1,
+ .dec_input_config_params_av1_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
.dec_output_config_params =
sm8550_vdec_output_config_params,
.dec_output_config_params_size =
@@ -1069,12 +1303,17 @@ const struct iris_platform_data qcs8300_data = {
.dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
.dec_output_prop_vp9_size =
ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
+ .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
+ .dec_output_prop_av1_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
.dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
.dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
.dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
.dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+ .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
.enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h
index f1bef4d4bcfe..0ec8f334df67 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h
@@ -23,4 +23,9 @@ static const struct platform_clk_data sc7280_clk_table[] = {
{IRIS_HW_AHB_CLK, "vcodec_bus" },
};
+static const char * const sc7280_opp_clk_table[] = {
+ "vcodec_core",
+ NULL,
+};
+
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_power.c b/drivers/media/platform/qcom/iris/iris_power.c
index dbca42df0910..91aa21d4070e 100644
--- a/drivers/media/platform/qcom/iris/iris_power.c
+++ b/drivers/media/platform/qcom/iris/iris_power.c
@@ -91,7 +91,7 @@ static int iris_set_clocks(struct iris_inst *inst)
}
core->power.clk_freq = freq;
- ret = dev_pm_opp_set_rate(core->dev, freq);
+ ret = iris_opp_set_rate(core->dev, freq);
mutex_unlock(&core->lock);
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 9bc9b34c2576..ddaacda523ec 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -40,8 +40,6 @@ static int iris_init_icc(struct iris_core *core)
static int iris_init_power_domains(struct iris_core *core)
{
- const struct platform_clk_data *clk_tbl;
- u32 clk_cnt, i;
int ret;
struct dev_pm_domain_attach_data iris_pd_data = {
@@ -56,6 +54,11 @@ static int iris_init_power_domains(struct iris_core *core)
.pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
};
+ struct dev_pm_opp_config iris_opp_clk_data = {
+ .clk_names = core->iris_platform_data->opp_clk_tbl,
+ .config_clks = dev_pm_opp_config_clks_simple,
+ };
+
ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
if (ret < 0)
return ret;
@@ -64,16 +67,9 @@ static int iris_init_power_domains(struct iris_core *core)
if (ret < 0)
return ret;
- clk_tbl = core->iris_platform_data->clk_tbl;
- clk_cnt = core->iris_platform_data->clk_tbl_size;
-
- for (i = 0; i < clk_cnt; i++) {
- if (clk_tbl[i].clk_type == IRIS_HW_CLK) {
- ret = devm_pm_opp_set_clkname(core->dev, clk_tbl[i].clk_name);
- if (ret)
- return ret;
- }
- }
+ ret = devm_pm_opp_set_config(core->dev, &iris_opp_clk_data);
+ if (ret)
+ return ret;
return devm_pm_opp_of_add_table(core->dev);
}
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
index 164490c49c95..773f6548370a 100644
--- a/drivers/media/platform/qcom/iris/iris_resources.c
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -4,6 +4,7 @@
*/
#include <linux/clk.h>
+#include <linux/devfreq.h>
#include <linux/interconnect.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
@@ -58,11 +59,22 @@ int iris_unset_icc_bw(struct iris_core *core)
return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
}
+int iris_opp_set_rate(struct device *dev, unsigned long freq)
+{
+ struct dev_pm_opp *opp __free(put_opp);
+
+ opp = devfreq_recommended_opp(dev, &freq, 0);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ return dev_pm_opp_set_opp(dev, opp);
+}
+
int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
{
int ret;
- ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX);
+ ret = iris_opp_set_rate(core->dev, ULONG_MAX);
if (ret)
return ret;
@@ -77,7 +89,7 @@ int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
{
int ret;
- ret = dev_pm_opp_set_rate(core->dev, 0);
+ ret = iris_opp_set_rate(core->dev, 0);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_resources.h b/drivers/media/platform/qcom/iris/iris_resources.h
index f723dfe5bd81..6bfbd2dc6db0 100644
--- a/drivers/media/platform/qcom/iris/iris_resources.h
+++ b/drivers/media/platform/qcom/iris/iris_resources.h
@@ -8,6 +8,7 @@
struct iris_core;
+int iris_opp_set_rate(struct device *dev, unsigned long freq);
int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev);
int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev);
int iris_unset_icc_bw(struct iris_core *core);
diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
index e2f1131de431..cfc5b576ec56 100644
--- a/drivers/media/platform/qcom/iris/iris_utils.c
+++ b/drivers/media/platform/qcom/iris/iris_utils.c
@@ -125,3 +125,9 @@ int iris_check_core_mbps(struct iris_inst *inst)
return 0;
}
+
+bool is_rotation_90_or_270(struct iris_inst *inst)
+{
+ return inst->fw_caps[ROTATION].value == 90 ||
+ inst->fw_caps[ROTATION].value == 270;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h
index 75740181122f..b5705d156431 100644
--- a/drivers/media/platform/qcom/iris/iris_utils.h
+++ b/drivers/media/platform/qcom/iris/iris_utils.h
@@ -51,5 +51,6 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
int iris_check_core_mbpf(struct iris_inst *inst);
int iris_check_core_mbps(struct iris_inst *inst);
+bool is_rotation_90_or_270(struct iris_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index db8768d8a8f6..bf0b8400996e 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -193,10 +193,14 @@ int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
buf_type = iris_v4l2_type_to_driver(q->type);
if (inst->domain == DECODER) {
- if (inst->state == IRIS_INST_STREAMING)
+ if (buf_type == BUF_INPUT)
+ ret = iris_queue_deferred_buffers(inst, BUF_INPUT);
+
+ if (!ret && inst->state == IRIS_INST_STREAMING) {
ret = iris_queue_internal_deferred_buffers(inst, BUF_DPB);
- if (!ret)
- ret = iris_queue_deferred_buffers(inst, buf_type);
+ if (!ret)
+ ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT);
+ }
} else {
if (inst->state == IRIS_INST_STREAMING) {
ret = iris_queue_deferred_buffers(inst, BUF_INPUT);
@@ -231,8 +235,6 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
return;
mutex_lock(&inst->lock);
- if (inst->state == IRIS_INST_ERROR)
- goto exit;
if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
!V4L2_TYPE_IS_CAPTURE(q->type))
@@ -243,10 +245,10 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
goto exit;
exit:
- if (ret) {
- iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
+ if (ret)
iris_inst_change_state(inst, IRIS_INST_ERROR);
- }
+
mutex_unlock(&inst->lock);
}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index 69ffe52590d3..467d00044a2f 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -67,21 +67,6 @@ void iris_vdec_inst_deinit(struct iris_inst *inst)
kfree(inst->fmt_src);
}
-static const struct iris_fmt iris_vdec_formats_out[] = {
- [IRIS_FMT_H264] = {
- .pixfmt = V4L2_PIX_FMT_H264,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
- [IRIS_FMT_HEVC] = {
- .pixfmt = V4L2_PIX_FMT_HEVC,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
- [IRIS_FMT_VP9] = {
- .pixfmt = V4L2_PIX_FMT_VP9,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
-};
-
static const struct iris_fmt iris_vdec_formats_cap[] = {
[IRIS_FMT_NV12] = {
.pixfmt = V4L2_PIX_FMT_NV12,
@@ -101,8 +86,8 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
unsigned int i;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- fmt = iris_vdec_formats_out;
- size = ARRAY_SIZE(iris_vdec_formats_out);
+ fmt = inst->core->iris_platform_data->inst_iris_fmts;
+ size = inst->core->iris_platform_data->inst_iris_fmts_size;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_vdec_formats_cap;
@@ -131,8 +116,8 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- fmt = iris_vdec_formats_out;
- size = ARRAY_SIZE(iris_vdec_formats_out);
+ fmt = inst->core->iris_platform_data->inst_iris_fmts;
+ size = inst->core->iris_platform_data->inst_iris_fmts_size;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_vdec_formats_cap;
@@ -231,6 +216,14 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
if (vb2_is_busy(q))
return -EBUSY;
+ /* Width and height are optional, so fall back to a valid placeholder
+ * resolution until the real one is decoded from the bitstream.
+ */
+ if (f->fmt.pix_mp.width == 0 && f->fmt.pix_mp.height == 0) {
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ }
+
iris_vdec_try_fmt(inst, f);
switch (f->type) {
diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c
index 5830eba93c68..6461d9c9d598 100644
--- a/drivers/media/platform/qcom/iris/iris_venc.c
+++ b/drivers/media/platform/qcom/iris/iris_venc.c
@@ -62,12 +62,17 @@ int iris_venc_inst_init(struct iris_inst *inst)
inst->crop.left = 0;
inst->crop.top = 0;
- inst->crop.width = f->fmt.pix_mp.width;
- inst->crop.height = f->fmt.pix_mp.height;
+ inst->crop.width = DEFAULT_WIDTH;
+ inst->crop.height = DEFAULT_HEIGHT;
inst->operating_rate = DEFAULT_FPS;
inst->frame_rate = DEFAULT_FPS;
+ inst->enc_raw_width = DEFAULT_WIDTH;
+ inst->enc_raw_height = DEFAULT_HEIGHT;
+ inst->enc_scale_width = DEFAULT_WIDTH;
+ inst->enc_scale_height = DEFAULT_HEIGHT;
+
memcpy(&inst->fw_caps[0], &core->inst_fw_caps_enc[0],
INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
@@ -223,15 +228,32 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f)
{
+ const struct iris_fmt *venc_fmt;
struct v4l2_format *fmt;
+ u32 codec_align;
iris_venc_try_fmt(inst, f);
- if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
+ venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type);
+ if (!venc_fmt)
return -EINVAL;
+ codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16;
+
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ /*
+ * If output format size != input format size,
+ * it is considered a scaling case,
+ * and the scaled size needs to be saved.
+ */
+ if (f->fmt.pix_mp.width != inst->fmt_src->fmt.pix_mp.width ||
+ f->fmt.pix_mp.height != inst->fmt_src->fmt.pix_mp.height) {
+ inst->enc_scale_width = f->fmt.pix_mp.width;
+ inst->enc_scale_height = f->fmt.pix_mp.height;
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
+ }
fmt->fmt.pix_mp.num_planes = 1;
fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
@@ -287,6 +309,11 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f)
inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+ inst->enc_raw_width = f->fmt.pix_mp.width;
+ inst->enc_raw_height = f->fmt.pix_mp.height;
+ inst->enc_scale_width = f->fmt.pix_mp.width;
+ inst->enc_scale_height = f->fmt.pix_mp.height;
+
if (f->fmt.pix_mp.width != inst->crop.width ||
f->fmt.pix_mp.height != inst->crop.height) {
inst->crop.top = 0;
@@ -382,8 +409,7 @@ int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm)
struct v4l2_fract *timeperframe = NULL;
u32 default_rate = DEFAULT_FPS;
bool is_frame_rate = false;
- u64 us_per_frame, fps;
- u32 max_rate;
+ u32 fps, max_rate;
int ret = 0;
@@ -405,23 +431,19 @@ int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm)
timeperframe->denominator = default_rate;
}
- us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
- do_div(us_per_frame, timeperframe->denominator);
-
- if (!us_per_frame)
+ fps = timeperframe->denominator / timeperframe->numerator;
+ if (!fps)
return -EINVAL;
- fps = (u64)USEC_PER_SEC;
- do_div(fps, us_per_frame);
if (fps > max_rate) {
ret = -ENOMEM;
goto reset_rate;
}
if (is_frame_rate)
- inst->frame_rate = (u32)fps;
+ inst->frame_rate = fps;
else
- inst->operating_rate = (u32)fps;
+ inst->operating_rate = fps;
if ((s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_is_streaming(src_q)) ||
(s_parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vb2_is_streaming(dst_q))) {
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index c9b881923ef1..bd38d84c9cc7 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -178,6 +178,7 @@ int iris_open(struct file *filp)
INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_1].list);
INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_2].list);
INIT_LIST_HEAD(&inst->buffers[BUF_VPSS].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_PARTIAL].list);
init_completion(&inst->completion);
init_completion(&inst->flush_completion);
@@ -572,9 +573,10 @@ static int iris_dec_cmd(struct file *filp, void *fh,
mutex_lock(&inst->lock);
- ret = v4l2_m2m_ioctl_decoder_cmd(filp, fh, dec);
- if (ret)
+ if (dec->cmd != V4L2_DEC_CMD_STOP && dec->cmd != V4L2_DEC_CMD_START) {
+ ret = -EINVAL;
goto unlock;
+ }
if (inst->state == IRIS_INST_DEINIT)
goto unlock;
@@ -605,9 +607,10 @@ static int iris_enc_cmd(struct file *filp, void *fh,
mutex_lock(&inst->lock);
- ret = v4l2_m2m_ioctl_encoder_cmd(filp, fh, enc);
- if (ret)
+ if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START) {
+ ret = -EINVAL;
goto unlock;
+ }
if (inst->state == IRIS_INST_DEINIT)
goto unlock;
diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c
index 339776a0b467..fe4423b951b1 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu3x.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c
@@ -11,55 +11,11 @@
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
-#define WRAPPER_TZ_BASE_OFFS 0x000C0000
-#define AON_BASE_OFFS 0x000E0000
-#define AON_MVP_NOC_RESET 0x0001F000
-
-#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
-#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
-#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
-#define REQ_POWER_DOWN_PREP BIT(0)
-#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
-#define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */
-#define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */
-#define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */
-#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
-#define CORE_CLK_RUN 0x0
-/* VPU v3.5 */
-#define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78)
-
-#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
-#define CTL_AXI_CLK_HALT BIT(0)
-#define CTL_CLK_HALT BIT(1)
-
-#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
-#define RESET_HIGH BIT(0)
-
-#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
-#define CORE_BRIDGE_SW_RESET BIT(0)
-#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
-
-#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
-#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
-#define MSK_CORE_POWER_ON BIT(1)
-
-#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
-#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
-
-#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
-
-#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
-
-#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
-#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
-
#define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18)
#define SW_RESET BIT(0)
#define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20)
#define NOC_HALT BIT(0)
#define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28)
-#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C)
-#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30)
static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core)
{
@@ -304,155 +260,12 @@ static void iris_vpu35_power_off_hw(struct iris_core *core)
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
}
-static int iris_vpu35_power_off_controller(struct iris_core *core)
-{
- u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
- unsigned int count = 0;
- u32 val = 0;
- bool handshake_done, handshake_busy;
- int ret;
-
- writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
-
- writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
-
- ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
- val, val & BIT(0), 200, 2000);
- if (ret)
- goto disable_power;
-
- writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
-
- /* Retry up to 1000 times as recommended by hardware documentation */
- do {
- /* set MNoC to low power */
- writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
-
- udelay(15);
-
- val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS);
-
- handshake_done = val & NOC_LPI_STATUS_DONE;
- handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
-
- if (handshake_done || !handshake_busy)
- break;
-
- writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
-
- udelay(15);
-
- } while (++count < 1000);
-
- if (!handshake_done && handshake_busy)
- dev_err(core->dev, "LPI handshake timeout\n");
-
- ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS,
- val, val & BIT(0), 200, 2000);
- if (ret)
- goto disable_power;
-
- writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
-
- writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
-
- ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
- val, val == 0, 200, 2000);
- if (ret)
- goto disable_power;
-
-disable_power:
- iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
- iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
- iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
-
- iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
-
- reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
-
- return 0;
-}
-
-static int iris_vpu35_power_on_controller(struct iris_core *core)
-{
- int ret;
-
- ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
- if (ret)
- return ret;
-
- ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK);
- if (ret)
- goto err_disable_power;
-
- ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK);
- if (ret)
- goto err_disable_axi1_clk;
-
- ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
- if (ret)
- goto err_disable_ctrl_free_clk;
-
- return 0;
-
-err_disable_ctrl_free_clk:
- iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
-err_disable_axi1_clk:
- iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
-err_disable_power:
- iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
-
- return ret;
-}
-
-static void iris_vpu35_program_bootup_registers(struct iris_core *core)
-{
- writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0);
-}
-
-static u64 iris_vpu3x_calculate_frequency(struct iris_inst *inst, size_t data_size)
-{
- struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
- struct v4l2_format *inp_f = inst->fmt_src;
- u32 height, width, mbs_per_second, mbpf;
- u64 fw_cycles, fw_vpp_cycles;
- u64 vsp_cycles, vpp_cycles;
- u32 fps = DEFAULT_FPS;
-
- width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
- height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
-
- mbpf = NUM_MBS_PER_FRAME(height, width);
- mbs_per_second = mbpf * fps;
-
- fw_cycles = fps * caps->mb_cycles_fw;
- fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp;
-
- vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value);
- /* 21 / 20 is minimum overhead factor */
- vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles);
-
- /* 1.059 is multi-pipe overhead */
- if (inst->fw_caps[PIPE].value > 1)
- vpp_cycles += div_u64(vpp_cycles * 59, 1000);
-
- vsp_cycles = fps * data_size * 8;
- vsp_cycles = div_u64(vsp_cycles, 2);
- /* VSP FW overhead 1.05 */
- vsp_cycles = div_u64(vsp_cycles * 21, 20);
-
- if (inst->fw_caps[STAGE].value == STAGE_1)
- vsp_cycles = vsp_cycles * 3;
-
- return max3(vpp_cycles, vsp_cycles, fw_cycles);
-}
-
const struct vpu_ops iris_vpu3_ops = {
.power_off_hw = iris_vpu3_power_off_hardware,
.power_on_hw = iris_vpu_power_on_hw,
.power_off_controller = iris_vpu_power_off_controller,
.power_on_controller = iris_vpu_power_on_controller,
- .calc_freq = iris_vpu3x_calculate_frequency,
+ .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
};
const struct vpu_ops iris_vpu33_ops = {
@@ -460,14 +273,14 @@ const struct vpu_ops iris_vpu33_ops = {
.power_on_hw = iris_vpu_power_on_hw,
.power_off_controller = iris_vpu33_power_off_controller,
.power_on_controller = iris_vpu_power_on_controller,
- .calc_freq = iris_vpu3x_calculate_frequency,
+ .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
};
const struct vpu_ops iris_vpu35_ops = {
.power_off_hw = iris_vpu35_power_off_hw,
.power_on_hw = iris_vpu35_power_on_hw,
- .power_off_controller = iris_vpu35_power_off_controller,
- .power_on_controller = iris_vpu35_power_on_controller,
- .program_bootup_registers = iris_vpu35_program_bootup_registers,
- .calc_freq = iris_vpu3x_calculate_frequency,
+ .power_off_controller = iris_vpu35_vpu4x_power_off_controller,
+ .power_on_controller = iris_vpu35_vpu4x_power_on_controller,
+ .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers,
+ .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu4x.c b/drivers/media/platform/qcom/iris/iris_vpu4x.c
new file mode 100644
index 000000000000..a8db02ce5c5e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu4x.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define AON_WRAPPER_MVP_NOC_RESET_SYNCRST (AON_MVP_NOC_RESET + 0x08)
+#define CPU_CS_APV_BRIDGE_SYNC_RESET (CPU_BASE_OFFS + 0x174)
+#define MVP_NOC_RESET_REQ_MASK 0x70103
+#define VPU_IDLE_BITS 0x7103
+#define WRAPPER_EFUSE_MONITOR (WRAPPER_BASE_OFFS + 0x08)
+
+#define APV_CLK_HALT BIT(1)
+#define CORE_CLK_HALT BIT(0)
+#define CORE_PWR_ON BIT(1)
+#define DISABLE_VIDEO_APV_BIT BIT(27)
+#define DISABLE_VIDEO_VPP1_BIT BIT(28)
+#define DISABLE_VIDEO_VPP0_BIT BIT(29)
+
+static int iris_vpu4x_genpd_set_hwmode(struct iris_core *core, bool hw_mode, u32 efuse_value)
+{
+ int ret;
+
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], hw_mode);
+ if (ret)
+ return ret;
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
+ [IRIS_VPP0_HW_POWER_DOMAIN], hw_mode);
+ if (ret)
+ goto restore_hw_domain_mode;
+ }
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
+ [IRIS_VPP1_HW_POWER_DOMAIN], hw_mode);
+ if (ret)
+ goto restore_vpp0_domain_mode;
+ }
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) {
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
+ [IRIS_APV_HW_POWER_DOMAIN], hw_mode);
+ if (ret)
+ goto restore_vpp1_domain_mode;
+ }
+
+ return 0;
+
+restore_vpp1_domain_mode:
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP1_HW_POWER_DOMAIN],
+ !hw_mode);
+restore_vpp0_domain_mode:
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP0_HW_POWER_DOMAIN],
+ !hw_mode);
+restore_hw_domain_mode:
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], !hw_mode);
+
+ return ret;
+}
+
+static int iris_vpu4x_power_on_apv(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_enable_power_domains(core,
+ core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_APV_HW_CLK);
+ if (ret)
+ goto disable_apv_hw_power_domain;
+
+ return 0;
+
+disable_apv_hw_power_domain:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static void iris_vpu4x_power_off_apv(struct iris_core *core)
+{
+ bool handshake_done, handshake_busy;
+ u32 value, count = 0;
+ int ret;
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ if (value & APV_CLK_HALT)
+ writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ do {
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+ value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS);
+
+ handshake_done = value & NOC_LPI_STATUS_DONE;
+ handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
+
+ if (handshake_done || !handshake_busy)
+ break;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+
+ } while (++count < 1000);
+
+ if (!handshake_done && handshake_busy)
+ dev_err(core->dev, "LPI handshake timeout\n");
+
+ writel(0x080200, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ value, value & 0x080200, 200, 2000);
+ if (ret)
+ goto disable_clocks_and_power;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST);
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ value, value == 0x0, 200, 2000);
+ if (ret)
+ goto disable_clocks_and_power;
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
+ CPU_CS_APV_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
+
+disable_clocks_and_power:
+ iris_disable_unprepare_clock(core, IRIS_APV_HW_CLK);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
+}
+
+static void iris_vpu4x_ahb_sync_reset_apv(struct iris_core *core)
+{
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
+ CPU_CS_APV_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
+}
+
+static void iris_vpu4x_ahb_sync_reset_hardware(struct iris_core *core)
+{
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
+ CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+}
+
+static int iris_vpu4x_enable_hardware_clocks(struct iris_core *core, u32 efuse_value)
+{
+ int ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK);
+ if (ret)
+ goto disable_axi_clock;
+
+ ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
+ if (ret)
+ goto disable_hw_free_run_clock;
+
+ ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK);
+ if (ret)
+ goto disable_hw_clock;
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
+ ret = iris_prepare_enable_clock(core, IRIS_VPP0_HW_CLK);
+ if (ret)
+ goto disable_bse_hw_clock;
+ }
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
+ ret = iris_prepare_enable_clock(core, IRIS_VPP1_HW_CLK);
+ if (ret)
+ goto disable_vpp0_hw_clock;
+ }
+
+ return 0;
+
+disable_vpp0_hw_clock:
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK);
+disable_bse_hw_clock:
+ iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
+disable_hw_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+disable_hw_free_run_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
+disable_axi_clock:
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+
+ return ret;
+}
+
+static void iris_vpu4x_disable_hardware_clocks(struct iris_core *core, u32 efuse_value)
+{
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ iris_disable_unprepare_clock(core, IRIS_VPP1_HW_CLK);
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK);
+
+ iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+ iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+}
+
+static int iris_vpu4x_power_on_hardware(struct iris_core *core)
+{
+ u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP0_HW_POWER_DOMAIN]);
+ if (ret)
+ goto disable_hw_power_domain;
+ }
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP1_HW_POWER_DOMAIN]);
+ if (ret)
+ goto disable_vpp0_power_domain;
+ }
+
+ ret = iris_vpu4x_enable_hardware_clocks(core, efuse_value);
+ if (ret)
+ goto disable_vpp1_power_domain;
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) {
+ ret = iris_vpu4x_power_on_apv(core);
+ if (ret)
+ goto disable_hw_clocks;
+
+ iris_vpu4x_ahb_sync_reset_apv(core);
+ }
+
+ iris_vpu4x_ahb_sync_reset_hardware(core);
+
+ ret = iris_vpu4x_genpd_set_hwmode(core, true, efuse_value);
+ if (ret)
+ goto disable_apv_power_domain;
+
+ return 0;
+
+disable_apv_power_domain:
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
+ iris_vpu4x_power_off_apv(core);
+disable_hw_clocks:
+ iris_vpu4x_disable_hardware_clocks(core, efuse_value);
+disable_vpp1_power_domain:
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP1_HW_POWER_DOMAIN]);
+disable_vpp0_power_domain:
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP0_HW_POWER_DOMAIN]);
+disable_hw_power_domain:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static void iris_vpu4x_power_off_hardware(struct iris_core *core)
+{
+ u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
+ bool handshake_done, handshake_busy;
+ u32 value, count = 0;
+ int ret;
+
+ iris_vpu4x_genpd_set_hwmode(core, false, efuse_value);
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
+ iris_vpu4x_power_off_apv(core);
+
+ value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
+
+ if (!(value & CORE_PWR_ON))
+ goto disable_clocks_and_power;
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ if (value & CORE_CLK_HALT)
+ writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN, value,
+ value & VPU_IDLE_BITS, 2000, 20000);
+
+ do {
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+ value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS);
+
+ handshake_done = value & NOC_LPI_STATUS_DONE;
+ handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
+
+ if (handshake_done || !handshake_busy)
+ break;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+
+ } while (++count < 1000);
+
+ if (!handshake_done && handshake_busy)
+ dev_err(core->dev, "LPI handshake timeout\n");
+
+ writel(MVP_NOC_RESET_REQ_MASK, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ value, value & MVP_NOC_RESET_REQ_MASK, 200, 2000);
+ if (ret)
+ goto disable_clocks_and_power;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST);
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ value, value == 0x0, 200, 2000);
+ if (ret)
+ goto disable_clocks_and_power;
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
+ CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+
+disable_clocks_and_power:
+ iris_vpu4x_disable_hardware_clocks(core, efuse_value);
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP1_HW_POWER_DOMAIN]);
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP0_HW_POWER_DOMAIN]);
+
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+}
+
+const struct vpu_ops iris_vpu4x_ops = {
+ .power_off_hw = iris_vpu4x_power_off_hardware,
+ .power_on_hw = iris_vpu4x_power_on_hardware,
+ .power_off_controller = iris_vpu35_vpu4x_power_off_controller,
+ .power_on_controller = iris_vpu35_vpu4x_power_on_controller,
+ .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers,
+ .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index 4463be05ce16..9270422c1601 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -9,6 +9,17 @@
#include "iris_hfi_gen2_defines.h"
#define HFI_MAX_COL_FRAME 6
+#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_HEIGHT (8)
+#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH (32)
+#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_HEIGHT (8)
+#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_WIDTH (16)
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_HEIGHT (4)
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH (48)
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_HEIGHT (4)
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_WIDTH (24)
+#define AV1D_SIZE_BSE_COL_MV_64x64 512
+#define AV1D_SIZE_BSE_COL_MV_128x128 2816
+#define UBWC_TILE_SIZE 256
#ifndef SYSTEM_LAL_TILE10
#define SYSTEM_LAL_TILE10 192
@@ -39,6 +50,31 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p
return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
}
+static u32 size_av1d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+
+ size_yuv = ((frame_width * frame_height) <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) :
+ ((frame_width * frame_height * 3) >> 1);
+ size_bin_hdr = size_yuv * AV1_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * AV1_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+ size_bin_res = ALIGN(size_bin_res / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+
+ return size_bin_hdr + size_bin_res;
+}
+
+static u32 hfi_buffer_bin_av1d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 n_aligned_h = ALIGN(frame_height, 16);
+ u32 n_aligned_w = ALIGN(frame_width, 16);
+
+ return size_av1d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
+}
+
static u32 size_h265d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
{
u32 product = frame_width * frame_height;
@@ -110,6 +146,26 @@ static u32 hfi_buffer_comv_h265d(u32 frame_width, u32 frame_height, u32 _comv_bu
return (_size * (_comv_bufcount)) + 512;
}
+static u32 num_lcu(u32 frame_width, u32 frame_height, u32 lcu_size)
+{
+ return ((frame_width + lcu_size - 1) / lcu_size) *
+ ((frame_height + lcu_size - 1) / lcu_size);
+}
+
+static u32 hfi_buffer_comv_av1d(u32 frame_width, u32 frame_height, u32 comv_bufcount)
+{
+ u32 size;
+
+ size = 2 * ALIGN(max(num_lcu(frame_width, frame_height, 64) *
+ AV1D_SIZE_BSE_COL_MV_64x64,
+ num_lcu(frame_width, frame_height, 128) *
+ AV1D_SIZE_BSE_COL_MV_128x128),
+ DMA_ALIGNMENT);
+ size *= comv_bufcount;
+
+ return size;
+}
+
static u32 size_h264d_bse_cmd_buf(u32 frame_height)
{
u32 height = ALIGN(frame_height, 32);
@@ -122,7 +178,7 @@ static u32 size_h265d_bse_cmd_buf(u32 frame_width, u32 frame_height)
{
u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
(ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) *
- NUM_HW_PIC_BUF, DMA_ALIGNMENT);
+ NUM_HW_PIC_BUF, DMA_ALIGNMENT);
_size = min_t(u32, _size, H265D_MAX_SLICE + 1);
_size = 2 * _size * SIZE_H265D_BSE_CMD_PER_BUF;
@@ -174,6 +230,20 @@ static u32 hfi_buffer_persist_h264d(void)
DMA_ALIGNMENT);
}
+static u32 hfi_buffer_persist_av1d(u32 max_width, u32 max_height, u32 total_ref_count)
+{
+ u32 comv_size, size;
+
+ comv_size = hfi_buffer_comv_av1d(max_width, max_height, total_ref_count);
+ size = ALIGN((SIZE_AV1D_SEQUENCE_HEADER * 2 + SIZE_AV1D_METADATA +
+ AV1D_NUM_HW_PIC_BUF * (SIZE_AV1D_TILE_OFFSET + SIZE_AV1D_QM) +
+ AV1D_NUM_FRAME_HEADERS * (SIZE_AV1D_FRAME_HEADER +
+ 2 * SIZE_AV1D_PROB_TABLE) + comv_size + HDR10_HIST_EXTRADATA_SIZE +
+ SIZE_AV1D_METADATA * AV1D_NUM_HW_PIC_BUF), DMA_ALIGNMENT);
+
+ return ALIGN(size, DMA_ALIGNMENT);
+}
+
static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
{
u32 size_bse = size_h264d_bse_cmd_buf(frame_height);
@@ -459,6 +529,182 @@ static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
return ALIGN((size + vpss_lb_size), DMA_ALIGNMENT);
}
+static u32 size_av1d_lb_opb_wr1_nv12_ubwc(u32 frame_width, u32 frame_height)
+{
+ u32 size, y_width, y_width_a = 128;
+
+ y_width = ALIGN(frame_width, y_width_a);
+
+ size = ((y_width + HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH - 1) /
+ HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH +
+ (AV1D_MAX_TILE_COLS - 1));
+ return size * UBWC_TILE_SIZE;
+}
+
+static u32 size_av1d_lb_opb_wr1_tp10_ubwc(u32 frame_width, u32 frame_height)
+{
+ u32 size, y_width, y_width_a = 256;
+
+ y_width = ALIGN(frame_width, y_width_a);
+
+ size = ((y_width + HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH - 1) /
+ HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH +
+ (AV1D_MAX_TILE_COLS - 1));
+
+ return size * UBWC_TILE_SIZE;
+}
+
+static u32 hfi_buffer_line_av1d(u32 frame_width, u32 frame_height,
+ bool is_opb, u32 num_vpp_pipes)
+{
+ u32 size, vpss_lb_size, opbwrbufsize, opbwr8, opbwr10;
+
+ size = ALIGN(size_av1d_lb_fe_top_data(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_av1d_lb_fe_top_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_av1d_lb_fe_left_data(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_av1d_lb_fe_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_av1d_lb_se_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_av1d_lb_se_top_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_av1d_lb_pe_top_data(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_av1d_lb_vsp_top(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_av1d_lb_recon_dma_metadata_wr
+ (frame_width, frame_height), DMA_ALIGNMENT) * 2 +
+ ALIGN(size_av1d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+ opbwr8 = size_av1d_lb_opb_wr1_nv12_ubwc(frame_width, frame_height);
+ opbwr10 = size_av1d_lb_opb_wr1_tp10_ubwc(frame_width, frame_height);
+ opbwrbufsize = opbwr8 >= opbwr10 ? opbwr8 : opbwr10;
+ size = ALIGN((size + opbwrbufsize), DMA_ALIGNMENT);
+ if (is_opb) {
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+ size = ALIGN((size + vpss_lb_size) * 2, DMA_ALIGNMENT);
+ }
+
+ return size;
+}
+
+static u32 size_av1d_ibc_nv12_ubwc(u32 frame_width, u32 frame_height)
+{
+ u32 size;
+ u32 y_width_a = 128, y_height_a = 32;
+ u32 uv_width_a = 128, uv_height_a = 32;
+ u32 ybufsize, uvbufsize, y_width, y_height, uv_width, uv_height;
+ u32 y_meta_width_a = 64, y_meta_height_a = 16;
+ u32 uv_meta_width_a = 64, uv_meta_height_a = 16;
+ u32 meta_height, meta_stride, meta_size;
+ u32 tile_width_y = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH;
+ u32 tile_height_y = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_HEIGHT;
+ u32 tile_width_uv = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_WIDTH;
+ u32 tile_height_uv = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_HEIGHT;
+
+ y_width = ALIGN(frame_width, y_width_a);
+ y_height = ALIGN(frame_height, y_height_a);
+ uv_width = ALIGN(frame_width, uv_width_a);
+ uv_height = ALIGN(((frame_height + 1) >> 1), uv_height_a);
+ ybufsize = ALIGN((y_width * y_height), HFI_ALIGNMENT_4096);
+ uvbufsize = ALIGN(uv_width * uv_height, HFI_ALIGNMENT_4096);
+ size = ybufsize + uvbufsize;
+ meta_stride = ALIGN(((frame_width + (tile_width_y - 1)) / tile_width_y),
+ y_meta_width_a);
+ meta_height = ALIGN(((frame_height + (tile_height_y - 1)) / tile_height_y),
+ y_meta_height_a);
+ meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096);
+ size += meta_size;
+ meta_stride = ALIGN(((((frame_width + 1) >> 1) + (tile_width_uv - 1)) /
+ tile_width_uv), uv_meta_width_a);
+ meta_height = ALIGN(((((frame_height + 1) >> 1) + (tile_height_uv - 1)) /
+ tile_height_uv), uv_meta_height_a);
+ meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096);
+ size += meta_size;
+
+ return size;
+}
+
+static u32 hfi_yuv420_tp10_calc_y_stride(u32 frame_width, u32 stride_multiple)
+{
+ u32 stride;
+
+ stride = ALIGN(frame_width, 192);
+ stride = ALIGN(stride * 4 / 3, stride_multiple);
+
+ return stride;
+}
+
+static u32 hfi_yuv420_tp10_calc_y_bufheight(u32 frame_height, u32 min_buf_height_multiple)
+{
+ return ALIGN(frame_height, min_buf_height_multiple);
+}
+
+static u32 hfi_yuv420_tp10_calc_uv_stride(u32 frame_width, u32 stride_multiple)
+{
+ u32 stride;
+
+ stride = ALIGN(frame_width, 192);
+ stride = ALIGN(stride * 4 / 3, stride_multiple);
+
+ return stride;
+}
+
+static u32 hfi_yuv420_tp10_calc_uv_bufheight(u32 frame_height, u32 min_buf_height_multiple)
+{
+ return ALIGN(((frame_height + 1) >> 1), min_buf_height_multiple);
+}
+
+static u32 size_av1d_ibc_tp10_ubwc(u32 frame_width, u32 frame_height)
+{
+ u32 size;
+ u32 y_width_a = 256, y_height_a = 16,
+ uv_width_a = 256, uv_height_a = 16;
+ u32 ybufsize, uvbufsize, y_width, y_height, uv_width, uv_height;
+ u32 y_meta_width_a = 64, y_meta_height_a = 16,
+ uv_meta_width_a = 64, uv_meta_height_a = 16;
+ u32 meta_height, meta_stride, meta_size;
+ u32 tile_width_y = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH;
+ u32 tile_height_y = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_HEIGHT;
+ u32 tile_width_uv = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_WIDTH;
+ u32 tile_height_uv = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_HEIGHT;
+
+ y_width = hfi_yuv420_tp10_calc_y_stride(frame_width, y_width_a);
+ y_height = hfi_yuv420_tp10_calc_y_bufheight(frame_height, y_height_a);
+ uv_width = hfi_yuv420_tp10_calc_uv_stride(frame_width, uv_width_a);
+ uv_height = hfi_yuv420_tp10_calc_uv_bufheight(frame_height, uv_height_a);
+ ybufsize = ALIGN(y_width * y_height, HFI_ALIGNMENT_4096);
+ uvbufsize = ALIGN(uv_width * uv_height, HFI_ALIGNMENT_4096);
+ size = ybufsize + uvbufsize;
+ meta_stride = ALIGN(((frame_width + (tile_width_y - 1)) / tile_width_y),
+ y_meta_width_a);
+ meta_height = ALIGN(((frame_height + (tile_height_y - 1)) / tile_height_y),
+ y_meta_height_a);
+ meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096);
+ size += meta_size;
+ meta_stride = ALIGN(((((frame_width + 1) >> 1) + (tile_width_uv - 1)) /
+ tile_width_uv), uv_meta_width_a);
+ meta_height = ALIGN(((((frame_height + 1) >> 1) + (tile_height_uv - 1)) /
+ tile_height_uv), uv_meta_height_a);
+ meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096);
+ size += meta_size;
+
+ return size;
+}
+
+static u32 hfi_buffer_ibc_av1d(u32 frame_width, u32 frame_height)
+{
+ u32 size, ibc8, ibc10;
+
+ ibc8 = size_av1d_ibc_nv12_ubwc(frame_width, frame_height);
+ ibc10 = size_av1d_ibc_tp10_ubwc(frame_width, frame_height);
+ size = ibc8 >= ibc10 ? ibc8 : ibc10;
+
+ return ALIGN(size, DMA_ALIGNMENT);
+}
+
static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
@@ -472,6 +718,8 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
return hfi_buffer_bin_h265d(width, height, num_vpp_pipes);
else if (inst->codec == V4L2_PIX_FMT_VP9)
return hfi_buffer_bin_vp9d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_AV1)
+ return hfi_buffer_bin_av1d(width, height, num_vpp_pipes);
return 0;
}
@@ -487,18 +735,34 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
return hfi_buffer_comv_h264d(width, height, num_comv);
else if (inst->codec == V4L2_PIX_FMT_HEVC)
return hfi_buffer_comv_h265d(width, height, num_comv);
+ else if (inst->codec == V4L2_PIX_FMT_AV1) {
+ if (inst->fw_caps[DRAP].value)
+ return 0;
+ else
+ return hfi_buffer_comv_av1d(width, height, num_comv);
+ }
return 0;
}
static u32 iris_vpu_dec_persist_size(struct iris_inst *inst)
{
+ struct platform_inst_caps *caps;
+
if (inst->codec == V4L2_PIX_FMT_H264)
return hfi_buffer_persist_h264d();
else if (inst->codec == V4L2_PIX_FMT_HEVC)
return hfi_buffer_persist_h265d(0);
else if (inst->codec == V4L2_PIX_FMT_VP9)
return hfi_buffer_persist_vp9d();
+ else if (inst->codec == V4L2_PIX_FMT_AV1) {
+ caps = inst->core->iris_platform_data->inst_caps;
+ if (inst->fw_caps[DRAP].value)
+ return hfi_buffer_persist_av1d(caps->max_frame_width,
+ caps->max_frame_height, 16);
+ else
+ return hfi_buffer_persist_av1d(0, 0, 0);
+ }
return 0;
}
@@ -545,6 +809,8 @@ static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
else if (inst->codec == V4L2_PIX_FMT_VP9)
return hfi_buffer_line_vp9d(width, height, out_min_count, is_opb,
num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_AV1)
+ return hfi_buffer_line_av1d(width, height, is_opb, num_vpp_pipes);
return 0;
}
@@ -556,6 +822,22 @@ static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
iris_vpu_dec_line_size(inst);
}
+static inline u32 iris_vpu_enc_get_bitstream_width(struct iris_inst *inst)
+{
+ if (is_rotation_90_or_270(inst))
+ return inst->fmt_dst->fmt.pix_mp.height;
+ else
+ return inst->fmt_dst->fmt.pix_mp.width;
+}
+
+static inline u32 iris_vpu_enc_get_bitstream_height(struct iris_inst *inst)
+{
+ if (is_rotation_90_or_270(inst))
+ return inst->fmt_dst->fmt.pix_mp.width;
+ else
+ return inst->fmt_dst->fmt.pix_mp.height;
+}
+
static inline u32 size_bin_bitstream_enc(u32 width, u32 height,
u32 rc_type)
{
@@ -638,10 +920,9 @@ static inline u32 hfi_buffer_bin_enc(u32 width, u32 height,
static u32 iris_vpu_enc_bin_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ u32 height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 width = iris_vpu_enc_get_bitstream_width(inst);
u32 stage = inst->fw_caps[STAGE].value;
- struct v4l2_format *f = inst->fmt_dst;
- u32 height = f->fmt.pix_mp.height;
- u32 width = f->fmt.pix_mp.width;
u32 lcu_size;
if (inst->codec == V4L2_PIX_FMT_HEVC)
@@ -653,6 +934,15 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst)
num_vpp_pipes, inst->hfi_rc_type);
}
+static u32 iris_vpu_dec_partial_size(struct iris_inst *inst)
+{
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_ibc_av1d(width, height);
+}
+
static inline
u32 hfi_buffer_comv_enc(u32 frame_width, u32 frame_height, u32 lcu_size,
u32 num_recon, u32 standard)
@@ -676,9 +966,8 @@ u32 hfi_buffer_comv_enc(u32 frame_width, u32 frame_height, u32 lcu_size,
static u32 iris_vpu_enc_comv_size(struct iris_inst *inst)
{
- struct v4l2_format *f = inst->fmt_dst;
- u32 height = f->fmt.pix_mp.height;
- u32 width = f->fmt.pix_mp.width;
+ u32 height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 width = iris_vpu_enc_get_bitstream_width(inst);
u32 num_recon = 1;
u32 lcu_size = 16;
@@ -958,9 +1247,8 @@ u32 hfi_buffer_non_comv_enc(u32 frame_width, u32 frame_height,
static u32 iris_vpu_enc_non_comv_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
- struct v4l2_format *f = inst->fmt_dst;
- u32 height = f->fmt.pix_mp.height;
- u32 width = f->fmt.pix_mp.width;
+ u32 height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 width = iris_vpu_enc_get_bitstream_width(inst);
u32 lcu_size = 16;
if (inst->codec == V4L2_PIX_FMT_HEVC) {
@@ -1051,9 +1339,8 @@ u32 hfi_buffer_line_enc_vpu33(u32 frame_width, u32 frame_height, bool is_ten_bit
static u32 iris_vpu_enc_line_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
- struct v4l2_format *f = inst->fmt_dst;
- u32 height = f->fmt.pix_mp.height;
- u32 width = f->fmt.pix_mp.width;
+ u32 height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 width = iris_vpu_enc_get_bitstream_width(inst);
u32 lcu_size = 16;
if (inst->codec == V4L2_PIX_FMT_HEVC) {
@@ -1069,9 +1356,8 @@ static u32 iris_vpu_enc_line_size(struct iris_inst *inst)
static u32 iris_vpu33_enc_line_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
- struct v4l2_format *f = inst->fmt_dst;
- u32 height = f->fmt.pix_mp.height;
- u32 width = f->fmt.pix_mp.width;
+ u32 height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 width = iris_vpu_enc_get_bitstream_width(inst);
u32 lcu_size = 16;
if (inst->codec == V4L2_PIX_FMT_HEVC) {
@@ -1131,10 +1417,11 @@ static u32 iris_vpu_enc_arp_size(struct iris_inst *inst)
inline bool is_scaling_enabled(struct iris_inst *inst)
{
- return inst->crop.left != inst->compose.left ||
- inst->crop.top != inst->compose.top ||
- inst->crop.width != inst->compose.width ||
- inst->crop.height != inst->compose.height;
+ struct v4l2_pix_format_mplane *dst_fmt = &inst->fmt_dst->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *src_fmt = &inst->fmt_src->fmt.pix_mp;
+
+ return dst_fmt->width != src_fmt->width ||
+ dst_fmt->height != src_fmt->height;
}
static inline
@@ -1291,9 +1578,8 @@ static inline u32 hfi_buffer_scratch1_enc(u32 frame_width, u32 frame_height,
static u32 iris_vpu_enc_scratch1_size(struct iris_inst *inst)
{
u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
- struct v4l2_format *f = inst->fmt_dst;
- u32 frame_height = f->fmt.pix_mp.height;
- u32 frame_width = f->fmt.pix_mp.width;
+ u32 frame_height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 frame_width = iris_vpu_enc_get_bitstream_width(inst);
u32 num_ref = 1;
u32 lcu_size;
bool is_h265;
@@ -1389,9 +1675,8 @@ static inline u32 hfi_buffer_scratch2_enc(u32 frame_width, u32 frame_height,
static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst)
{
- struct v4l2_format *f = inst->fmt_dst;
- u32 frame_width = f->fmt.pix_mp.width;
- u32 frame_height = f->fmt.pix_mp.height;
+ u32 frame_height = iris_vpu_enc_get_bitstream_height(inst);
+ u32 frame_width = iris_vpu_enc_get_bitstream_width(inst);
u32 num_ref = 1;
return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref,
@@ -1408,13 +1693,313 @@ static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst)
return hfi_buffer_vpss_enc(width, height, ds_enable, 0, 0);
}
+static inline u32 size_dpb_opb(u32 height, u32 lcu_size)
+{
+ u32 max_tile_height = ((height + lcu_size - 1) / lcu_size) * lcu_size + 8;
+ u32 dpb_opb = 3 * ((max_tile_height >> 3) * DMA_ALIGNMENT);
+ u32 num_luma_chrome_plane = 2;
+
+ return ALIGN(dpb_opb, DMA_ALIGNMENT) * num_luma_chrome_plane;
+}
+
+static u32 hfi_vpu4x_vp9d_lb_size(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 vp9_top_lb, vp9_fe_left_lb, vp9_se_left_lb, dpb_opb, vp9d_qp, num_lcu_per_pipe;
+ u32 lcu_size = 64;
+
+ vp9_top_lb = ALIGN(size_vp9d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT);
+ vp9_top_lb += ALIGN(size_vpxd_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT);
+ vp9_top_lb += max3(DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_16_BYTES) *
+ MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE,
+ DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_32_BYTES) *
+ MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE,
+ DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_64_BYTES) *
+ MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE);
+ vp9_top_lb = ALIGN(vp9_top_lb, DMA_ALIGNMENT);
+ vp9_top_lb += ALIGN((DMA_ALIGNMENT * DIV_ROUND_UP(frame_width, lcu_size)),
+ DMA_ALIGNMENT) * FE_TOP_CTRL_LINE_NUMBERS;
+ vp9_top_lb += ALIGN(DMA_ALIGNMENT * 8 * DIV_ROUND_UP(frame_width, lcu_size),
+ DMA_ALIGNMENT) * (FE_TOP_DATA_LUMA_LINE_NUMBERS +
+ FE_TOP_DATA_CHROMA_LINE_NUMBERS);
+
+ num_lcu_per_pipe = (DIV_ROUND_UP(frame_height, lcu_size) / num_vpp_pipes) +
+ (DIV_ROUND_UP(frame_height, lcu_size) % num_vpp_pipes);
+ vp9_fe_left_lb = ALIGN((DMA_ALIGNMENT * num_lcu_per_pipe), DMA_ALIGNMENT) *
+ FE_LFT_CTRL_LINE_NUMBERS;
+ vp9_fe_left_lb += ((ALIGN((DMA_ALIGNMENT * 8 * num_lcu_per_pipe), DMA_ALIGNMENT) *
+ FE_LFT_DB_DATA_LINE_NUMBERS) +
+ ALIGN((DMA_ALIGNMENT * 3 * num_lcu_per_pipe), DMA_ALIGNMENT) +
+ ALIGN((DMA_ALIGNMENT * 4 * num_lcu_per_pipe), DMA_ALIGNMENT) +
+ (ALIGN((DMA_ALIGNMENT * 24 * num_lcu_per_pipe), DMA_ALIGNMENT) *
+ FE_LFT_LR_DATA_LINE_NUMBERS));
+ vp9_fe_left_lb = vp9_fe_left_lb * num_vpp_pipes;
+
+ vp9_se_left_lb = ALIGN(size_vpxd_lb_se_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT);
+ dpb_opb = size_dpb_opb(frame_height, lcu_size);
+ vp9d_qp = ALIGN(size_vp9d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+
+ return vp9_top_lb + vp9_fe_left_lb + (vp9_se_left_lb * num_vpp_pipes) +
+ (dpb_opb * num_vpp_pipes) + vp9d_qp;
+}
+
+static u32 hfi_vpu4x_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min,
+ bool is_opb, u32 num_vpp_pipes)
+{
+ u32 lb_size = hfi_vpu4x_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes);
+ u32 dpb_obp_size = 0, lcu_size = 64;
+
+ if (is_opb)
+ dpb_obp_size = size_dpb_opb(frame_height, lcu_size) * num_vpp_pipes;
+
+ return lb_size + dpb_obp_size;
+}
+
+static u32 iris_vpu4x_dec_line_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ u32 out_min_count = inst->buffers[BUF_OUTPUT].min_count;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+ bool is_opb = false;
+
+ if (iris_split_mode_enabled(inst))
+ is_opb = true;
+
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_line_h265d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_vpu4x_buffer_line_vp9d(width, height, out_min_count, is_opb,
+ num_vpp_pipes);
+
+ return 0;
+}
+
+static u32 hfi_vpu4x_buffer_persist_h265d(u32 rpu_enabled)
+{
+ return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_FRM_INFO *
+ H265_DISPLAY_BUF_SIZE + (H265_NUM_TILE * sizeof(u32)) + (NUM_HW_PIC_BUF *
+ (SIZE_SEI_USERDATA + SIZE_H265D_ARP + SIZE_THREE_DIMENSION_USERDATA)) +
+ rpu_enabled * NUM_HW_PIC_BUF * SIZE_DOLBY_RPU_METADATA), DMA_ALIGNMENT);
+}
+
+static u32 hfi_vpu4x_buffer_persist_vp9d(void)
+{
+ return ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, DMA_ALIGNMENT) +
+ (ALIGN(hfi_iris3_vp9d_comv_size(), DMA_ALIGNMENT) * 2) +
+ ALIGN(MAX_SUPERFRAME_HEADER_LEN, DMA_ALIGNMENT) +
+ ALIGN(VP9_UDC_HEADER_BUF_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * VP9_FRAME_INFO_BUF_SIZE_VPU4X, DMA_ALIGNMENT) +
+ HDR10_HIST_EXTRADATA_SIZE;
+}
+
+static u32 iris_vpu4x_dec_persist_size(struct iris_inst *inst)
+{
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_persist_h264d();
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_vpu4x_buffer_persist_h265d(0);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_vpu4x_buffer_persist_vp9d();
+
+ return 0;
+}
+
+static u32 size_se_lb(u32 standard, u32 num_vpp_pipes_enc,
+ u32 frame_width_coded, u32 frame_height_coded)
+{
+ u32 se_tlb_size = ALIGN(frame_width_coded, DMA_ALIGNMENT);
+ u32 se_llb_size = (standard == HFI_CODEC_ENCODE_HEVC) ?
+ ((frame_height_coded + BUFFER_ALIGNMENT_32_BYTES - 1) /
+ BUFFER_ALIGNMENT_32_BYTES) * LOG2_16 * LLB_UNIT_SIZE :
+ ((frame_height_coded + BUFFER_ALIGNMENT_16_BYTES - 1) /
+ BUFFER_ALIGNMENT_16_BYTES) * LOG2_32 * LLB_UNIT_SIZE;
+
+ se_llb_size = ALIGN(se_llb_size, BUFFER_ALIGNMENT_32_BYTES);
+
+ if (num_vpp_pipes_enc > 1)
+ se_llb_size = ALIGN(se_llb_size + BUFFER_ALIGNMENT_512_BYTES,
+ DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ return ALIGN(se_tlb_size + se_llb_size, DMA_ALIGNMENT);
+}
+
+static u32 size_te_lb(bool is_ten_bit, u32 num_vpp_pipes_enc, u32 width_in_lcus,
+ u32 frame_height_coded, u32 frame_width_coded)
+{
+ u32 num_pixel_10_bit = 3, num_pixel_8_bit = 2, num_pixel_te_llb = 3;
+ u32 te_llb_col_rc_size = ALIGN(32 * width_in_lcus / num_vpp_pipes_enc,
+ DMA_ALIGNMENT) * num_vpp_pipes_enc;
+ u32 te_tlb_recon_data_size = ALIGN((is_ten_bit ? num_pixel_10_bit : num_pixel_8_bit) *
+ frame_width_coded, DMA_ALIGNMENT);
+ u32 te_llb_recon_data_size = ((1 + is_ten_bit) * num_pixel_te_llb * frame_height_coded +
+ num_vpp_pipes_enc - 1) / num_vpp_pipes_enc;
+ te_llb_recon_data_size = ALIGN(te_llb_recon_data_size, DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ return ALIGN(te_llb_recon_data_size + te_llb_col_rc_size + te_tlb_recon_data_size,
+ DMA_ALIGNMENT);
+}
+
+static inline u32 calc_fe_tlb_size(u32 size_per_lcu, bool is_ten_bit)
+{
+ u32 num_pixels_fe_tlb_10_bit = 128, num_pixels_fe_tlb_8_bit = 64;
+
+ return is_ten_bit ? (num_pixels_fe_tlb_10_bit * (size_per_lcu + 1)) :
+ (size_per_lcu * num_pixels_fe_tlb_8_bit);
+}
+
+static u32 size_fe_lb(bool is_ten_bit, u32 standard, u32 num_vpp_pipes_enc,
+ u32 frame_height_coded, u32 frame_width_coded)
+{
+ u32 log2_lcu_size, num_cu_in_height_pipe, num_cu_in_width,
+ fb_llb_db_ctrl_size, fb_llb_db_luma_size, fb_llb_db_chroma_size,
+ fb_tlb_db_ctrl_size, fb_tlb_db_luma_size, fb_tlb_db_chroma_size,
+ fb_llb_sao_ctrl_size, fb_llb_sao_luma_size, fb_llb_sao_chroma_size,
+ fb_tlb_sao_ctrl_size, fb_tlb_sao_luma_size, fb_tlb_sao_chroma_size,
+ fb_lb_top_sdc_size, fb_lb_se_ctrl_size, fe_tlb_size, size_per_lcu;
+
+ log2_lcu_size = (standard == HFI_CODEC_ENCODE_HEVC) ? 5 : 4;
+ num_cu_in_height_pipe = ((frame_height_coded >> log2_lcu_size) + num_vpp_pipes_enc - 1) /
+ num_vpp_pipes_enc;
+ num_cu_in_width = frame_width_coded >> log2_lcu_size;
+
+ size_per_lcu = 2;
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, 1);
+ fb_llb_db_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe;
+ fb_llb_db_ctrl_size = ALIGN(fb_llb_db_ctrl_size, DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ size_per_lcu = (1 << (log2_lcu_size - 3));
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit);
+ fb_llb_db_luma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe;
+ fb_llb_db_luma_size = ALIGN(fb_llb_db_luma_size, DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ size_per_lcu = ((1 << (log2_lcu_size - 4)) * 2);
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit);
+ fb_llb_db_chroma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe;
+ fb_llb_db_chroma_size = ALIGN(fb_llb_db_chroma_size, DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ size_per_lcu = 1;
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, 1);
+ fb_tlb_db_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width;
+ fb_llb_sao_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe;
+ fb_llb_sao_ctrl_size = fb_llb_sao_ctrl_size * num_vpp_pipes_enc;
+ fb_tlb_sao_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width;
+
+ size_per_lcu = ((1 << (log2_lcu_size - 3)) + 1);
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit);
+ fb_tlb_db_luma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width;
+
+ size_per_lcu = (2 * ((1 << (log2_lcu_size - 4)) + 1));
+ fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit);
+ fb_tlb_db_chroma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width;
+
+ fb_llb_sao_luma_size = BUFFER_ALIGNMENT_256_BYTES * num_vpp_pipes_enc;
+ fb_llb_sao_chroma_size = BUFFER_ALIGNMENT_256_BYTES * num_vpp_pipes_enc;
+ fb_tlb_sao_luma_size = BUFFER_ALIGNMENT_256_BYTES;
+ fb_tlb_sao_chroma_size = BUFFER_ALIGNMENT_256_BYTES;
+ fb_lb_top_sdc_size = ALIGN((FE_SDC_DATA_PER_BLOCK * (frame_width_coded >> 5)),
+ DMA_ALIGNMENT);
+ fb_lb_se_ctrl_size = ALIGN((SE_CTRL_DATA_PER_BLOCK * (frame_width_coded >> 5)),
+ DMA_ALIGNMENT);
+
+ return fb_llb_db_ctrl_size + fb_llb_db_luma_size + fb_llb_db_chroma_size +
+ fb_tlb_db_ctrl_size + fb_tlb_db_luma_size + fb_tlb_db_chroma_size +
+ fb_llb_sao_ctrl_size + fb_llb_sao_luma_size + fb_llb_sao_chroma_size +
+ fb_tlb_sao_ctrl_size + fb_tlb_sao_luma_size + fb_tlb_sao_chroma_size +
+ fb_lb_top_sdc_size + fb_lb_se_ctrl_size;
+}
+
+static u32 size_md_lb(u32 standard, u32 frame_width_coded,
+ u32 frame_height_coded, u32 num_vpp_pipes_enc)
+{
+ u32 md_tlb_size = ALIGN(frame_width_coded, DMA_ALIGNMENT);
+ u32 md_llb_size = (standard == HFI_CODEC_ENCODE_HEVC) ?
+ ((frame_height_coded + BUFFER_ALIGNMENT_32_BYTES - 1) /
+ BUFFER_ALIGNMENT_32_BYTES) * LOG2_16 * LLB_UNIT_SIZE :
+ ((frame_height_coded + BUFFER_ALIGNMENT_16_BYTES - 1) /
+ BUFFER_ALIGNMENT_16_BYTES) * LOG2_32 * LLB_UNIT_SIZE;
+
+ md_llb_size = ALIGN(md_llb_size, BUFFER_ALIGNMENT_32_BYTES);
+
+ if (num_vpp_pipes_enc > 1)
+ md_llb_size = ALIGN(md_llb_size + BUFFER_ALIGNMENT_512_BYTES,
+ DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ md_llb_size = ALIGN(md_llb_size, DMA_ALIGNMENT);
+
+ return ALIGN(md_tlb_size + md_llb_size, DMA_ALIGNMENT);
+}
+
+static u32 size_dma_opb_lb(u32 num_vpp_pipes_enc, u32 frame_width_coded,
+ u32 frame_height_coded)
+{
+ u32 opb_packet_bytes = 128, opb_bpp = 128, opb_size_per_row = 6;
+ u32 dma_opb_wr_tlb_y_size = DIV_ROUND_UP(frame_width_coded, 16) * opb_packet_bytes;
+ u32 dma_opb_wr_tlb_uv_size = DIV_ROUND_UP(frame_width_coded, 16) * opb_packet_bytes;
+ u32 dma_opb_wr2_tlb_y_size = ALIGN((opb_bpp * opb_size_per_row * frame_height_coded / 8),
+ DMA_ALIGNMENT) * num_vpp_pipes_enc;
+ u32 dma_opb_wr2_tlb_uv_size = ALIGN((opb_bpp * opb_size_per_row * frame_height_coded / 8),
+ DMA_ALIGNMENT) * num_vpp_pipes_enc;
+
+ dma_opb_wr2_tlb_y_size = max(dma_opb_wr2_tlb_y_size, dma_opb_wr_tlb_y_size << 1);
+ dma_opb_wr2_tlb_uv_size = max(dma_opb_wr2_tlb_uv_size, dma_opb_wr_tlb_uv_size << 1);
+
+ return ALIGN(dma_opb_wr_tlb_y_size + dma_opb_wr_tlb_uv_size + dma_opb_wr2_tlb_y_size +
+ dma_opb_wr2_tlb_uv_size, DMA_ALIGNMENT);
+}
+
+static u32 hfi_vpu4x_buffer_line_enc(u32 frame_width, u32 frame_height,
+ bool is_ten_bit, u32 num_vpp_pipes_enc,
+ u32 lcu_size, u32 standard)
+{
+ u32 width_in_lcus = (frame_width + lcu_size - 1) / lcu_size;
+ u32 height_in_lcus = (frame_height + lcu_size - 1) / lcu_size;
+ u32 frame_width_coded = width_in_lcus * lcu_size;
+ u32 frame_height_coded = height_in_lcus * lcu_size;
+
+ u32 se_lb_size = size_se_lb(standard, num_vpp_pipes_enc, frame_width_coded,
+ frame_height_coded);
+ u32 te_lb_size = size_te_lb(is_ten_bit, num_vpp_pipes_enc, width_in_lcus,
+ frame_height_coded, frame_width_coded);
+ u32 fe_lb_size = size_fe_lb(is_ten_bit, standard, num_vpp_pipes_enc, frame_height_coded,
+ frame_width_coded);
+ u32 md_lb_size = size_md_lb(standard, frame_width_coded, frame_height_coded,
+ num_vpp_pipes_enc);
+ u32 dma_opb_lb_size = size_dma_opb_lb(num_vpp_pipes_enc, frame_width_coded,
+ frame_height_coded);
+ u32 dse_lb_size = ALIGN((256 + (16 * (frame_width_coded >> 4))), DMA_ALIGNMENT);
+ u32 size_vpss_lb_enc = size_vpss_line_buf_vpu33(num_vpp_pipes_enc, frame_width_coded,
+ frame_height_coded);
+
+ return se_lb_size + te_lb_size + fe_lb_size + md_lb_size + dma_opb_lb_size +
+ dse_lb_size + size_vpss_lb_enc;
+}
+
+static u32 iris_vpu4x_enc_line_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ u32 lcu_size = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
+ struct v4l2_format *f = inst->fmt_dst;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_vpu4x_buffer_line_enc(width, height, 0, num_vpp_pipes,
+ lcu_size, inst->codec);
+}
+
static int output_min_count(struct iris_inst *inst)
{
int output_min_count = 4;
/* fw_min_count > 0 indicates reconfig event has already arrived */
if (inst->fw_min_count) {
- if (iris_split_mode_enabled(inst) && inst->codec == V4L2_PIX_FMT_VP9)
+ if (iris_split_mode_enabled(inst) &&
+ (inst->codec == V4L2_PIX_FMT_VP9 ||
+ inst->codec == V4L2_PIX_FMT_AV1))
return min_t(u32, 4, inst->fw_min_count);
else
return inst->fw_min_count;
@@ -1422,6 +2007,8 @@ static int output_min_count(struct iris_inst *inst)
if (inst->codec == V4L2_PIX_FMT_VP9)
output_min_count = 9;
+ else if (inst->codec == V4L2_PIX_FMT_AV1)
+ output_min_count = 11;
return output_min_count;
}
@@ -1444,6 +2031,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
{BUF_PERSIST, iris_vpu_dec_persist_size },
{BUF_DPB, iris_vpu_dec_dpb_size },
{BUF_SCRATCH_1, iris_vpu_dec_scratch1_size },
+ {BUF_PARTIAL, iris_vpu_dec_partial_size },
};
static const struct iris_vpu_buf_type_handle enc_internal_buf_type_handle[] = {
@@ -1503,6 +2091,50 @@ u32 iris_vpu33_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ
return size;
}
+u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
+{
+ const struct iris_vpu_buf_type_handle *buf_type_handle_arr = NULL;
+ u32 size = 0, buf_type_handle_size = 0, i;
+
+ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = {
+ {BUF_BIN, iris_vpu_dec_bin_size },
+ {BUF_COMV, iris_vpu_dec_comv_size },
+ {BUF_NON_COMV, iris_vpu_dec_non_comv_size },
+ {BUF_LINE, iris_vpu4x_dec_line_size },
+ {BUF_PERSIST, iris_vpu4x_dec_persist_size },
+ {BUF_DPB, iris_vpu_dec_dpb_size },
+ {BUF_SCRATCH_1, iris_vpu_dec_scratch1_size },
+ };
+
+ static const struct iris_vpu_buf_type_handle enc_internal_buf_type_handle[] = {
+ {BUF_BIN, iris_vpu_enc_bin_size },
+ {BUF_COMV, iris_vpu_enc_comv_size },
+ {BUF_NON_COMV, iris_vpu_enc_non_comv_size },
+ {BUF_LINE, iris_vpu4x_enc_line_size },
+ {BUF_ARP, iris_vpu_enc_arp_size },
+ {BUF_VPSS, iris_vpu_enc_vpss_size },
+ {BUF_SCRATCH_1, iris_vpu_enc_scratch1_size },
+ {BUF_SCRATCH_2, iris_vpu_enc_scratch2_size },
+ };
+
+ if (inst->domain == DECODER) {
+ buf_type_handle_size = ARRAY_SIZE(dec_internal_buf_type_handle);
+ buf_type_handle_arr = dec_internal_buf_type_handle;
+ } else if (inst->domain == ENCODER) {
+ buf_type_handle_size = ARRAY_SIZE(enc_internal_buf_type_handle);
+ buf_type_handle_arr = enc_internal_buf_type_handle;
+ }
+
+ for (i = 0; i < buf_type_handle_size; i++) {
+ if (buf_type_handle_arr[i].type == buffer_type) {
+ size = buf_type_handle_arr[i].handle(inst);
+ break;
+ }
+ }
+
+ return size;
+}
+
static u32 internal_buffer_count(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
@@ -1510,14 +2142,20 @@ static u32 internal_buffer_count(struct iris_inst *inst,
buffer_type == BUF_PERSIST) {
return 1;
} else if (buffer_type == BUF_COMV || buffer_type == BUF_NON_COMV) {
- if (inst->codec == V4L2_PIX_FMT_H264 || inst->codec == V4L2_PIX_FMT_HEVC)
+ if (inst->codec == V4L2_PIX_FMT_H264 ||
+ inst->codec == V4L2_PIX_FMT_HEVC ||
+ inst->codec == V4L2_PIX_FMT_AV1)
return 1;
}
+
return 0;
}
static inline int iris_vpu_dpb_count(struct iris_inst *inst)
{
+ if (inst->codec == V4L2_PIX_FMT_AV1)
+ return 11;
+
if (iris_split_mode_enabled(inst)) {
return inst->fw_min_count ?
inst->fw_min_count : inst->buffers[BUF_OUTPUT].min_count;
@@ -1536,9 +2174,13 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type
return MIN_BUFFERS;
else
return output_min_count(inst);
+ case BUF_NON_COMV:
+ if (inst->codec == V4L2_PIX_FMT_AV1)
+ return 0;
+ else
+ return 1;
case BUF_BIN:
case BUF_COMV:
- case BUF_NON_COMV:
case BUF_LINE:
case BUF_PERSIST:
return internal_buffer_count(inst, buffer_type);
@@ -1546,6 +2188,7 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type
case BUF_SCRATCH_2:
case BUF_VPSS:
case BUF_ARP:
+ case BUF_PARTIAL:
return 1; /* internal buffer count needed by firmware is 1 */
case BUF_DPB:
return iris_vpu_dpb_count(inst);
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
index 04f0b7400a1e..12640eb5ed8c 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
@@ -11,6 +11,7 @@ struct iris_inst;
#define MIN_BUFFERS 4
#define DMA_ALIGNMENT 256
+#define HFI_ALIGNMENT_4096 4096
#define NUM_HW_PIC_BUF 32
#define LCU_MAX_SIZE_PELS 64
@@ -47,7 +48,12 @@ struct iris_inst;
#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
#define VP9_PROB_TABLE_SIZE (3840)
#define VP9_FRAME_INFO_BUF_SIZE (6144)
+#define VP9_FRAME_INFO_BUF_SIZE_VPU4X (6400)
+#define BUFFER_ALIGNMENT_16_BYTES 16
#define BUFFER_ALIGNMENT_32_BYTES 32
+#define BUFFER_ALIGNMENT_64_BYTES 64
+#define BUFFER_ALIGNMENT_256_BYTES 256
+#define BUFFER_ALIGNMENT_512_BYTES 512
#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES)
#define MAX_SUPERFRAME_HEADER_LEN (34)
#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
@@ -66,6 +72,8 @@ struct iris_inst;
#define H265_CABAC_HDR_RATIO_HD_TOT 2
#define H265_CABAC_RES_RATIO_HD_TOT 2
#define SIZE_H265D_VPP_CMD_PER_BUF (256)
+#define SIZE_THREE_DIMENSION_USERDATA 768
+#define SIZE_H265D_ARP 9728
#define VPX_DECODER_FRAME_CONCURENCY_LVL (2)
#define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1
@@ -76,11 +84,39 @@ struct iris_inst;
#define SIZE_H264D_HW_PIC_T (BIT(11))
+#define FE_LFT_CTRL_LINE_NUMBERS 4
+#define FE_LFT_DB_DATA_LINE_NUMBERS 2
+#define FE_LFT_LR_DATA_LINE_NUMBERS 4
+#define FE_TOP_CTRL_LINE_NUMBERS 3
+#define FE_TOP_DATA_LUMA_LINE_NUMBERS 2
+#define FE_TOP_DATA_CHROMA_LINE_NUMBERS 3
+#define FE_SDC_DATA_PER_BLOCK 16
+#define SE_CTRL_DATA_PER_BLOCK 2020
+
+#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96
+#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192
+
#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 16
#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE 384
#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640
+#define AV1_CABAC_HDR_RATIO_HD_TOT 2
+#define AV1_CABAC_RES_RATIO_HD_TOT 2
+#define AV1D_LCU_MAX_SIZE_PELS 128
+#define AV1D_LCU_MIN_SIZE_PELS 64
+#define AV1D_MAX_TILE_COLS 64
+#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192
+#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96
+#define AV1D_NUM_HW_PIC_BUF 16
+#define AV1D_NUM_FRAME_HEADERS 16
+#define SIZE_AV1D_SEQUENCE_HEADER 768
+#define SIZE_AV1D_METADATA 512
+#define SIZE_AV1D_FRAME_HEADER 1280
+#define SIZE_AV1D_TILE_OFFSET 65536
+#define SIZE_AV1D_QM 3328
+#define SIZE_AV1D_PROB_TABLE 22784
+
#define SIZE_SLICE_CMD_BUFFER (ALIGN(20480, 256))
#define SIZE_SPS_PPS_SLICE_HDR (2048 + 4096)
#define SIZE_BSE_SLICE_CMD_BUF ((((8192 << 2) + 7) & (~7)) * 3)
@@ -96,11 +132,24 @@ struct iris_inst;
#define HFI_BUFFER_ARP_ENC 204800
+#define LOG2_16 4
+#define LOG2_32 5
+#define LLB_UNIT_SIZE 16
+
#define MAX_WIDTH 4096
#define MAX_HEIGHT 2304
#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))
#define NUM_MBS_720P (((ALIGN(1280, 16)) >> 4) * ((ALIGN(736, 16)) >> 4))
+#define BITS_PER_PIX 16
+#define NUM_LINES_LUMA 10
+#define NUM_LINES_CHROMA 6
+#define AV1D_LCU_MAX_SIZE_PELS 128
+#define AV1D_LCU_MIN_SIZE_PELS 64
+#define AV1D_MAX_TILE_COLS 64
+#define BITS_PER_CTRL_PACK 128
+#define NUM_CTRL_PACK_LCU 10
+
static inline u32 size_h264d_lb_fe_top_data(u32 frame_width)
{
return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN(frame_width, 16) * 3;
@@ -146,8 +195,99 @@ static inline u32 size_h264d_qp(u32 frame_width, u32 frame_height)
return DIV_ROUND_UP(frame_width, 64) * DIV_ROUND_UP(frame_height, 64) * 128;
}
+static inline u32 size_av1d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return (ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) *
+ ((BITS_PER_PIX * NUM_LINES_LUMA) >> 3) +
+ ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) / 2 *
+ ((BITS_PER_PIX * NUM_LINES_CHROMA) >> 3) * 2);
+}
+
+static inline u32 size_av1d_lb_fe_left_data(u32 frame_width, u32 frame_height)
+{
+ return (32 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 16) +
+ 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 8) * 2 +
+ 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 16) +
+ 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 12) * 2 +
+ 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 16) +
+ 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 16) +
+ 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
+ ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS * 12) * 2);
+}
+
+static inline u32 size_av1d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (NUM_CTRL_PACK_LCU * ((frame_width + AV1D_LCU_MIN_SIZE_PELS - 1) /
+ AV1D_LCU_MIN_SIZE_PELS) * BITS_PER_CTRL_PACK / 8);
+}
+
+static inline u32 size_av1d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (16 * ((ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 16) +
+ (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS)) +
+ 3 * 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
+ AV1D_LCU_MIN_SIZE_PELS));
+}
+
+static inline u32 size_av1d_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (((frame_width + 7) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE);
+}
+
+static inline u32 size_av1d_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (max(((frame_height + 15) / 16) *
+ MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max(((frame_height + 31) / 32) *
+ MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 63) / 64) *
+ MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE)));
+}
+
+static inline u32 size_av1d_lb_pe_top_data(u32 frame_width, u32 frame_height)
+{
+ return (max(((frame_width + 15) / 16) *
+ MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE,
+ max(((frame_width + 31) / 32) *
+ MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE,
+ ((frame_width + 63) / 64) *
+ MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE)));
+}
+
+static inline u32 size_av1d_lb_vsp_top(u32 frame_width, u32 frame_height)
+{
+ return (max(((frame_width + 63) / 64) * 1280,
+ ((frame_width + 127) / 128) * MAX_HEIGHT));
+}
+
+static inline u32 size_av1d_lb_recon_dma_metadata_wr(u32 frame_width,
+ u32 frame_height)
+{
+ return ((ALIGN(frame_height, 8) / (4 / 2)) * 64);
+}
+
+static inline u32 size_av1d_qp(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_qp(frame_width, frame_height);
+}
+
u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
u32 iris_vpu33_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index 515dd55a3377..548e5f1727fd 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -8,16 +8,12 @@
#include <linux/reset.h>
#include "iris_core.h"
+#include "iris_instance.h"
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
-#define WRAPPER_TZ_BASE_OFFS 0x000C0000
-#define AON_BASE_OFFS 0x000E0000
-
-#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS)
-
-#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C)
-#define CLEAR_XTENSA2HOST_INTR BIT(0)
+#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C)
+#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30)
#define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48)
#define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C)
@@ -35,42 +31,6 @@
#define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64)
#define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68)
-#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148)
-#define HOST2XTENSA_INTR_ENABLE BIT(0)
-
-#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
-#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
-#define MSK_CORE_POWER_ON BIT(1)
-
-#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150)
-#define CPU_IC_SOFTINT_H2A_SHFT 0x0
-
-#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C)
-#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3)
-#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2)
-
-#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10)
-#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3)
-#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2)
-
-#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
-#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
-#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
-#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
-
-#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
-#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
-#define CTL_AXI_CLK_HALT BIT(0)
-#define CTL_CLK_HALT BIT(1)
-
-#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
-#define RESET_HIGH BIT(0)
-
-#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
-#define REQ_POWER_DOWN_PREP BIT(0)
-
-#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
-
static void iris_vpu_interrupt_init(struct iris_core *core)
{
u32 mask_val;
@@ -270,7 +230,7 @@ void iris_vpu_power_off_hw(struct iris_core *core)
void iris_vpu_power_off(struct iris_core *core)
{
- dev_pm_opp_set_rate(core->dev, 0);
+ iris_opp_set_rate(core->dev, 0);
core->iris_platform_data->vpu_ops->power_off_hw(core);
core->iris_platform_data->vpu_ops->power_off_controller(core);
iris_unset_icc_bw(core);
@@ -348,6 +308,144 @@ err_disable_power:
return ret;
}
+int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core)
+{
+ u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+ bool handshake_done, handshake_busy;
+ u32 count = 0, val = 0;
+ int ret;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ /* Retry up to 1000 times as recommended by hardware documentation */
+ do {
+ /* set MNoC to low power */
+ writel(REQ_POWER_DOWN_PREP, core->reg_base +
+ AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+ val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS);
+
+ handshake_done = val & NOC_LPI_STATUS_DONE;
+ handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
+
+ if (handshake_done || !handshake_busy)
+ break;
+
+ writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
+ usleep_range(10, 20);
+
+ } while (++count < 1000);
+
+ if (!handshake_done && handshake_busy)
+ dev_err(core->dev, "LPI handshake timeout\n");
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
+
+ writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+ readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ val, val == 0, 200, 2000);
+
+disable_power:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+ iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
+ iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
+
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
+
+ return 0;
+}
+
+int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK);
+ if (ret)
+ goto err_disable_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK);
+ if (ret)
+ goto err_disable_axi1_clk;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
+ if (ret)
+ goto err_disable_ctrl_free_clk;
+
+ return 0;
+
+err_disable_ctrl_free_clk:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
+err_disable_axi1_clk:
+ iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
+err_disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return ret;
+}
+
+void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core)
+{
+ writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0);
+}
+
+u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 height, width, mbs_per_second, mbpf;
+ u64 fw_cycles, fw_vpp_cycles;
+ u64 vsp_cycles, vpp_cycles;
+ u32 fps = DEFAULT_FPS;
+
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ mbs_per_second = mbpf * fps;
+
+ fw_cycles = fps * caps->mb_cycles_fw;
+ fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp;
+
+ vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value);
+ /* 21 / 20 is minimum overhead factor */
+ vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles);
+
+ /* 1.059 is multi-pipe overhead */
+ if (inst->fw_caps[PIPE].value > 1)
+ vpp_cycles += div_u64(vpp_cycles * 59, 1000);
+
+ vsp_cycles = fps * data_size * 8;
+ vsp_cycles = div_u64(vsp_cycles, 2);
+ /* VSP FW overhead 1.05 */
+ vsp_cycles = div_u64(vsp_cycles * 21, 20);
+
+ if (inst->fw_caps[STAGE].value == STAGE_1)
+ vsp_cycles = vsp_cycles * 3;
+
+ return max3(vpp_cycles, vsp_cycles, fw_cycles);
+}
+
int iris_vpu_power_on(struct iris_core *core)
{
u32 freq;
@@ -368,7 +466,7 @@ int iris_vpu_power_on(struct iris_core *core)
freq = core->power.clk_freq ? core->power.clk_freq :
(u32)ULONG_MAX;
- dev_pm_opp_set_rate(core->dev, freq);
+ iris_opp_set_rate(core->dev, freq);
core->iris_platform_data->set_preset_registers(core);
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
index d636e287457a..f6dffc613b82 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -12,6 +12,7 @@ extern const struct vpu_ops iris_vpu2_ops;
extern const struct vpu_ops iris_vpu3_ops;
extern const struct vpu_ops iris_vpu33_ops;
extern const struct vpu_ops iris_vpu35_ops;
+extern const struct vpu_ops iris_vpu4x_ops;
struct vpu_ops {
void (*power_off_hw)(struct iris_core *core);
@@ -33,5 +34,9 @@ int iris_vpu_power_on(struct iris_core *core);
int iris_vpu_power_off_controller(struct iris_core *core);
void iris_vpu_power_off_hw(struct iris_core *core);
void iris_vpu_power_off(struct iris_core *core);
+int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core);
+int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core);
+void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core);
+u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
index fe8a39e5e5a3..72168b9ffa73 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
@@ -7,11 +7,72 @@
#define __IRIS_VPU_REGISTER_DEFINES_H__
#define VCODEC_BASE_OFFS 0x00000000
+#define AON_MVP_NOC_RESET 0x0001F000
#define CPU_BASE_OFFS 0x000A0000
#define WRAPPER_BASE_OFFS 0x000B0000
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define AON_BASE_OFFS 0x000E0000
+
+#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
+
+#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
+#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
+
+#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
#define CPU_CS_BASE_OFFS (CPU_BASE_OFFS)
+#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS)
+
+#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C)
+#define CLEAR_XTENSA2HOST_INTR BIT(0)
+
+#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148)
+#define HOST2XTENSA_INTR_ENABLE BIT(0)
+
+#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT 0x0
+
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
+#define CORE_BRIDGE_SW_RESET BIT(0)
+#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
+
+#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
+#define MSK_CORE_POWER_ON BIT(1)
+#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C)
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2)
+
+#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2)
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
+#define REQ_POWER_DOWN_PREP BIT(0)
+
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
+#define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */
+#define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */
+#define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */
+
+#define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78)
#define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80)
+#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
+#define CORE_CLK_RUN 0x0
+
+#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
+
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define CTL_AXI_CLK_HALT BIT(0)
+#define CTL_CLK_HALT BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
#endif
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 4a6641fdffcf..21ca4947a849 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -433,9 +433,13 @@ vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
static int
vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
+ struct venus_inst *inst = to_inst(file);
+ struct venus_core *core = inst->core;
+
strscpy(cap->driver, "qcom-venus", sizeof(cap->driver));
strscpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card));
- strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "plat:%s:dec", dev_name(core->dev));
return 0;
}
@@ -565,7 +569,13 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
fdata.buffer_type = HFI_BUFFER_INPUT;
fdata.flags |= HFI_BUFFERFLAG_EOS;
- if (IS_V6(inst->core) && is_fw_rev_or_older(inst->core, 1, 0, 87))
+
+ /* Send NULL EOS addr for only IRIS2 (SM8250),for firmware <= 1.0.87.
+ * SC7280 also reports "1.0.<hash>" parsed as 1.0.0; restricting to IRIS2
+ * avoids misapplying this quirk and breaking VP9 decode on SC7280.
+ */
+
+ if (IS_IRIS2(inst->core) && is_fw_rev_or_older(inst->core, 1, 0, 87))
fdata.device_addr = 0;
else
fdata.device_addr = 0xdeadb000;
@@ -1440,10 +1450,10 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
inst->drain_active = false;
inst->codec_state = VENUS_DEC_STATE_STOPPED;
}
+ } else {
+ if (!bytesused)
+ state = VB2_BUF_STATE_ERROR;
}
-
- if (!bytesused)
- state = VB2_BUF_STATE_ERROR;
} else {
vbuf->sequence = inst->sequence_out++;
}
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index b478b982a80d..0b5843ba536f 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -144,9 +144,13 @@ static int venc_v4l2_to_hfi(int id, int value)
static int
venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
+ struct venus_inst *inst = to_inst(file);
+ struct venus_core *core = inst->core;
+
strscpy(cap->driver, "qcom-venus", sizeof(cap->driver));
strscpy(cap->card, "Qualcomm Venus video encoder", sizeof(cap->card));
- strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "plat:%s:enc", dev_name(core->dev));
return 0;
}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
index 2c5b4d24b4e6..104908afbf41 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
@@ -492,7 +492,7 @@ static int csi2_set_routing(struct v4l2_subdev *sd,
const struct v4l2_subdev_route *route = &routing->routes[i];
if (route->source_stream != 0)
- return -EINVAL;
+ return -ENXIO;
}
ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 730bdf98565a..bb575873f2b2 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -80,6 +80,9 @@ static int rga_buf_init(struct vb2_buffer *vb)
struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
size_t n_desc = 0;
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE);
rbuf->n_desc = n_desc;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
index 6dcefd144d5a..867cdddf9f89 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -1123,7 +1123,6 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue)
struct rkisp1_capture *cap = queue->drv_priv;
struct rkisp1_vdev_node *node = &cap->vnode;
struct rkisp1_device *rkisp1 = cap->rkisp1;
- int ret;
mutex_lock(&cap->rkisp1->stream_lock);
@@ -1132,9 +1131,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue)
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR);
v4l2_pipeline_pm_put(&node->vdev.entity);
- ret = pm_runtime_put(rkisp1->dev);
- if (ret < 0)
- dev_err(rkisp1->dev, "power down failed error:%d\n", ret);
+ pm_runtime_put(rkisp1->dev);
rkisp1_dummy_buf_destroy(cap);
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index c9f88635224c..6442436a5e42 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -411,12 +411,6 @@ static void rkisp1_flt_config(struct rkisp1_params *params,
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_LUM_WEIGHT,
arg->lum_weight);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE,
- (arg->mode ? RKISP1_CIF_ISP_FLT_MODE_DNR : 0) |
- RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) |
- RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) |
- RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1));
-
/* avoid to override the old enable value */
filt_mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE);
filt_mode &= RKISP1_CIF_ISP_FLT_ENA;
diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
index a77122641d14..e629d571e4d8 100644
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
@@ -1,3 +1,15 @@
obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o
-rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-hevc.o rkvdec-vp9.o
+rockchip-vdec-y += \
+ rkvdec.o \
+ rkvdec-cabac.o \
+ rkvdec-h264.o \
+ rkvdec-h264-common.o \
+ rkvdec-hevc.o \
+ rkvdec-hevc-common.o \
+ rkvdec-rcb.o \
+ rkvdec-vdpu381-h264.o \
+ rkvdec-vdpu381-hevc.o \
+ rkvdec-vdpu383-h264.o \
+ rkvdec-vdpu383-hevc.o \
+ rkvdec-vp9.o
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.c
index eac4ea604949..068bc0cd5f43 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.c
@@ -1,19 +1,515 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Rockchip Video Decoder driver
+ * Rockchip Video Decoder CABAC tables
*
* Copyright (C) 2023 Collabora, Ltd.
* Sebastian Fricke <sebastian.fricke@collabora.com>
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
*/
-#include <linux/types.h>
+#include "rkvdec-cabac.h"
-#define RKV_CABAC_TABLE_SIZE 27456
+#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \
+ idc2_m, idc2_n, intra_m, intra_n) \
+ [0][(ctxidx)] = {idc0_m, idc0_n}, \
+ [1][(ctxidx)] = {idc1_m, idc1_n}, \
+ [2][(ctxidx)] = {idc2_m, idc2_n}, \
+ [3][(ctxidx)] = {intra_m, intra_n}
/*
- * This file is #include from rkvdec-hevc.c and not compiled.
+ * Constant CABAC table.
+ * Built from the tables described in section '9.3.1.1 Initialisation process
+ * for context variables' of the H264 spec.
*/
-static const u8 rkvdec_hevc_cabac_table[RKV_CABAC_TABLE_SIZE] = {
+const s8 rkvdec_h264_cabac_table[4][464][2] = {
+ /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */
+ CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15),
+ CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54),
+ CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74),
+ CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15),
+ CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54),
+ CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74),
+ CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127),
+ CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104),
+ CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53),
+ CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54),
+ CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51),
+
+ /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */
+ CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0),
+ CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0),
+ CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0),
+ CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0),
+ CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0),
+ CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0),
+ CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0),
+ CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0),
+ CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0),
+ CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0),
+ CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0),
+ CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0),
+ CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0),
+
+ /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */
+ CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0),
+ CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0),
+ CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0),
+ CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0),
+ CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0),
+ CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0),
+ CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0),
+ CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0),
+ CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0),
+ CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0),
+ CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0),
+ CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0),
+ CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0),
+ CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0),
+ CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0),
+ CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0),
+
+ /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */
+ CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0),
+ CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0),
+ CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0),
+ CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0),
+ CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0),
+ CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0),
+ CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0),
+ CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0),
+ CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0),
+ CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0),
+ CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0),
+ CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0),
+ CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0),
+ CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0),
+
+ /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */
+ CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0),
+ CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0),
+ CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0),
+ CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0),
+ CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0),
+ CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0),
+
+ /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */
+ CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41),
+ CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83),
+ CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86),
+ CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97),
+ CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72),
+ CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41),
+ CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62),
+
+ /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */
+ CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11),
+ CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55),
+ CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69),
+ CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127),
+ CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102),
+ CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82),
+ CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74),
+ CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107),
+ CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127),
+ CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127),
+ CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127),
+ CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95),
+ CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127),
+ CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114),
+ CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127),
+ CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123),
+ CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115),
+ CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122),
+ CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115),
+ CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63),
+ CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68),
+ CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84),
+ CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104),
+ CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70),
+ CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93),
+ CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90),
+ CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127),
+ CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74),
+ CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97),
+ CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91),
+ CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127),
+ CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56),
+ CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82),
+ CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76),
+ CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125),
+
+ /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */
+ CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93),
+ CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87),
+ CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77),
+ CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71),
+ CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63),
+ CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68),
+ CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84),
+ CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62),
+ CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65),
+ CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61),
+ CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56),
+ CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66),
+ CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64),
+ CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61),
+ CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78),
+ CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50),
+ CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52),
+ CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35),
+ CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44),
+ CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38),
+ CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45),
+ CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46),
+ CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44),
+ CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17),
+ CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51),
+ CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50),
+ CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19),
+ CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33),
+ CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62),
+ CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108),
+ CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100),
+ CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101),
+ CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91),
+ CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94),
+ CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88),
+ CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84),
+ CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86),
+ CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83),
+ CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87),
+ CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94),
+ CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70),
+ CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72),
+ CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74),
+ CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59),
+ CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102),
+ CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100),
+ CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95),
+ CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75),
+ CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72),
+ CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75),
+ CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71),
+ CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46),
+ CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69),
+ CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62),
+ CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65),
+ CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37),
+ CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72),
+ CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57),
+ CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54),
+ CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62),
+ CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72),
+
+ /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */
+ CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0),
+ CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9),
+ CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25),
+ CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18),
+ CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9),
+ CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19),
+ CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37),
+ CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18),
+ CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29),
+ CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33),
+ CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30),
+ CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45),
+ CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58),
+ CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62),
+ CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61),
+ CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38),
+ CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45),
+ CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39),
+ CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42),
+ CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44),
+ CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45),
+ CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41),
+ CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49),
+ CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34),
+ CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42),
+ CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55),
+ CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51),
+ CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46),
+ CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89),
+ CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19),
+ CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17),
+ CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17),
+ CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25),
+ CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20),
+ CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23),
+ CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27),
+ CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23),
+ CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28),
+ CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17),
+ CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11),
+ CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15),
+ CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6),
+ CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1),
+ CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17),
+ CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6),
+ CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3),
+ CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22),
+ CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16),
+ CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4),
+ CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8),
+ CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3),
+ CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3),
+ CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5),
+ CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0),
+ CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16),
+ CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22),
+ CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48),
+ CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37),
+ CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60),
+ CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68),
+ CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97),
+
+ /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */
+ CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71),
+ CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42),
+ CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50),
+ CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54),
+ CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62),
+ CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58),
+ CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63),
+ CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72),
+ CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74),
+ CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91),
+ CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67),
+ CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27),
+ CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39),
+ CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44),
+ CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46),
+ CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64),
+ CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68),
+ CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78),
+ CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77),
+ CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86),
+ CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92),
+ CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55),
+ CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60),
+ CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62),
+ CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65),
+ CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73),
+ CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76),
+ CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80),
+ CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88),
+ CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110),
+ CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97),
+ CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84),
+ CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79),
+ CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73),
+ CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74),
+ CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86),
+ CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96),
+ CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97),
+ CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117),
+ CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78),
+ CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33),
+ CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48),
+ CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53),
+ CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62),
+ CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71),
+ CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79),
+ CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86),
+ CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90),
+ CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97),
+
+ /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */
+ CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93),
+ CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84),
+ CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79),
+ CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66),
+ CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71),
+ CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62),
+ CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60),
+ CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59),
+ CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75),
+ CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62),
+ CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58),
+ CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66),
+ CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79),
+ CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71),
+ CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68),
+ CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44),
+ CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62),
+ CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36),
+ CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40),
+ CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27),
+ CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29),
+ CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44),
+ CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36),
+ CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32),
+ CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42),
+ CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48),
+ CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62),
+ CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46),
+ CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64),
+ CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104),
+ CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97),
+ CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96),
+ CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88),
+ CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85),
+ CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85),
+ CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85),
+ CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88),
+ CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66),
+ CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77),
+ CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76),
+ CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76),
+ CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58),
+ CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76),
+ CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83),
+ CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99),
+ CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95),
+ CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95),
+ CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76),
+ CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74),
+ CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70),
+ CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75),
+ CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68),
+ CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65),
+ CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73),
+ CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62),
+ CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62),
+ CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68),
+ CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75),
+ CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55),
+ CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64),
+ CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70),
+
+ /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */
+ CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6),
+ CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19),
+ CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16),
+ CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14),
+ CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13),
+ CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11),
+ CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15),
+ CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16),
+ CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23),
+ CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23),
+ CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20),
+ CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26),
+ CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44),
+ CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40),
+ CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47),
+ CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17),
+ CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21),
+ CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22),
+ CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27),
+ CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29),
+ CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35),
+ CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50),
+ CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57),
+ CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63),
+ CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77),
+ CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82),
+ CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94),
+ CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69),
+ CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109),
+ CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35),
+ CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34),
+ CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26),
+ CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30),
+ CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32),
+ CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18),
+ CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15),
+ CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15),
+ CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7),
+ CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5),
+ CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0),
+ CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2),
+ CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13),
+ CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35),
+ CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58),
+ CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3),
+ CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0),
+ CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30),
+ CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7),
+ CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15),
+ CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3),
+ CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3),
+ CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1),
+ CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5),
+ CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11),
+ CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5),
+ CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12),
+ CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11),
+ CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29),
+ CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26),
+ CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39),
+ CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66),
+
+ /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */
+ CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21),
+ CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31),
+ CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50),
+ CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120),
+ CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112),
+ CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114),
+ CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85),
+ CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92),
+ CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89),
+ CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71),
+ CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81),
+ CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80),
+ CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68),
+ CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70),
+ CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56),
+ CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68),
+ CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50),
+ CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74),
+ CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13),
+ CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13),
+ CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15),
+ CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14),
+ CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3),
+ CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6),
+ CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34),
+ CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54),
+ CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82),
+ CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75),
+ CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23),
+ CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34),
+ CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43),
+ CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54),
+ CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55),
+ CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61),
+ CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64),
+ CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68),
+ CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92),
+ CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106),
+ CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97),
+ CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90),
+ CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90),
+ CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88),
+ CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73),
+ CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79),
+ CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86),
+ CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73),
+ CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70),
+ CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69),
+ CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66),
+ CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64),
+ CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58),
+ CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59),
+ CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10),
+ CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11),
+ CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8),
+ CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1),
+ CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3),
+ CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9),
+ CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20),
+ CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36),
+ CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67),
+};
+
+const u8 rkvdec_hevc_cabac_table[RKV_HEVC_CABAC_TABLE_SIZE] = {
0x07, 0x0f, 0x48, 0x58, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f,
0x68, 0x48, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x40,
0x40, 0x68, 0x58, 0x60, 0x40, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x60,
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h
new file mode 100644
index 000000000000..3b721c854b9b
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Video Decoder CABAC tables
+ *
+ * Copyright (C) 2023 Collabora, Ltd.
+ * Sebastian Fricke <sebastian.fricke@collabora.com>
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ */
+
+#ifndef RKVDEC_CABAC_H_
+#define RKVDEC_CABAC_H_
+
+#include <linux/types.h>
+
+#define RKV_HEVC_CABAC_TABLE_SIZE 27456
+
+extern const s8 rkvdec_h264_cabac_table[4][464][2];
+extern const u8 rkvdec_hevc_cabac_table[RKV_HEVC_CABAC_TABLE_SIZE];
+
+#endif /* RKVDEC_CABAC_H_ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c
new file mode 100644
index 000000000000..e28f06394470
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip video decoder h264 common functions
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <linux/v4l2-common.h>
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-h264-common.h"
+
+#define RKVDEC_NUM_REFLIST 3
+
+static void set_dpb_info(struct rkvdec_rps_entry *entries,
+ u8 reflist,
+ u8 refnum,
+ u8 info,
+ bool bottom)
+{
+ struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8];
+ u8 idx = refnum % 8;
+
+ switch (idx) {
+ case 0:
+ entry->dpb_info0 = info;
+ entry->bottom_flag0 = bottom;
+ break;
+ case 1:
+ entry->dpb_info1 = info;
+ entry->bottom_flag1 = bottom;
+ break;
+ case 2:
+ entry->dpb_info2 = info;
+ entry->bottom_flag2 = bottom;
+ break;
+ case 3:
+ entry->dpb_info3 = info;
+ entry->bottom_flag3 = bottom;
+ break;
+ case 4:
+ entry->dpb_info4 = info;
+ entry->bottom_flag4 = bottom;
+ break;
+ case 5:
+ entry->dpb_info5 = info;
+ entry->bottom_flag5 = bottom;
+ break;
+ case 6:
+ entry->dpb_info6 = info;
+ entry->bottom_flag6 = bottom;
+ break;
+ case 7:
+ entry->dpb_info7 = info;
+ entry->bottom_flag7 = bottom;
+ break;
+ }
+}
+
+void lookup_ref_buf_idx(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb;
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
+ struct vb2_buffer *buf = NULL;
+
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) {
+ buf = vb2_find_buffer(cap_q, dpb[i].reference_ts);
+ if (!buf)
+ pr_debug("No buffer for reference_ts %llu",
+ dpb[i].reference_ts);
+ }
+
+ run->ref_buf[i] = buf;
+ }
+}
+
+void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
+ struct rkvdec_h264_run *run,
+ struct rkvdec_h264_reflists *reflists,
+ struct rkvdec_rps *hw_rps)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+
+ u32 i, j;
+
+ memset(hw_rps, 0, sizeof(*hw_rps));
+
+ /*
+ * Assign an invalid pic_num if DPB entry at that position is inactive.
+ * If we assign 0 in that position hardware will treat that as a real
+ * reference picture with pic_num 0, triggering output picture
+ * corruption.
+ */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
+ continue;
+
+ hw_rps->frame_num[i] = builder->refs[i].frame_num;
+ }
+
+ for (j = 0; j < RKVDEC_NUM_REFLIST; j++) {
+ for (i = 0; i < builder->num_valid; i++) {
+ struct v4l2_h264_reference *ref;
+ bool dpb_valid;
+ bool bottom;
+
+ switch (j) {
+ case 0:
+ ref = &reflists->p[i];
+ break;
+ case 1:
+ ref = &reflists->b0[i];
+ break;
+ case 2:
+ ref = &reflists->b1[i];
+ break;
+ }
+
+ if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb)))
+ continue;
+
+ dpb_valid = !!(run->ref_buf[ref->index]);
+ bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF;
+
+ set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom);
+ }
+ }
+}
+
+void assemble_hw_scaling_list(struct rkvdec_h264_run *run,
+ struct rkvdec_h264_scaling_list *scaling_list)
+{
+ const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix;
+ const struct v4l2_ctrl_h264_pps *pps = run->pps;
+
+ if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT))
+ return;
+
+ BUILD_BUG_ON(sizeof(scaling_list->scaling_list_4x4) !=
+ sizeof(scaling->scaling_list_4x4));
+ BUILD_BUG_ON(sizeof(scaling_list->scaling_list_8x8) !=
+ sizeof(scaling->scaling_list_8x8));
+
+ memcpy(scaling_list->scaling_list_4x4,
+ scaling->scaling_list_4x4,
+ sizeof(scaling->scaling_list_4x4));
+
+ memcpy(scaling_list->scaling_list_8x8,
+ scaling->scaling_list_8x8,
+ sizeof(scaling->scaling_list_8x8));
+}
+
+#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2
+
+int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+ fmt->num_planes = 1;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
+ RKVDEC_H264_MAX_DEPTH_IN_BYTES;
+ return 0;
+}
+
+enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
+
+ if (ctrl->id != V4L2_CID_STATELESS_H264_SPS)
+ return RKVDEC_IMG_FMT_ANY;
+
+ if (sps->bit_depth_luma_minus8 == 0) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_8BIT;
+ else
+ return RKVDEC_IMG_FMT_420_8BIT;
+ } else if (sps->bit_depth_luma_minus8 == 2) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_10BIT;
+ else
+ return RKVDEC_IMG_FMT_420_10BIT;
+ }
+
+ return RKVDEC_IMG_FMT_ANY;
+}
+
+int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_h264_sps *sps)
+{
+ unsigned int width, height;
+
+ if (sps->chroma_format_idc > 2)
+ /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit is supported */
+ return -EINVAL;
+
+ width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
+ height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
+
+ /*
+ * When frame_mbs_only_flag is not set, this is field height,
+ * which is half the final height (see (7-18) in the
+ * specification)
+ */
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
+ height *= 2;
+
+ if (width > ctx->coded_fmt.fmt.pix_mp.width ||
+ height > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_DECODE_PARAMS);
+ run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SPS);
+ run->sps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_PPS);
+ run->pps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SCALING_MATRIX);
+ run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
+
+ rkvdec_run_preamble(ctx, &run->base);
+}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h
new file mode 100644
index 000000000000..5336370507d6
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip video decoder h264 common functions
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+
+struct rkvdec_h264_scaling_list {
+ u8 scaling_list_4x4[6][16];
+ u8 scaling_list_8x8[6][64];
+ u8 padding[128];
+};
+
+struct rkvdec_h264_reflists {
+ struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN];
+};
+
+struct rkvdec_h264_run {
+ struct rkvdec_run base;
+ const struct v4l2_ctrl_h264_decode_params *decode_params;
+ const struct v4l2_ctrl_h264_sps *sps;
+ const struct v4l2_ctrl_h264_pps *pps;
+ const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix;
+ struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES];
+};
+
+struct rkvdec_rps_entry {
+ u32 dpb_info0: 5;
+ u32 bottom_flag0: 1;
+ u32 view_index_off0: 1;
+ u32 dpb_info1: 5;
+ u32 bottom_flag1: 1;
+ u32 view_index_off1: 1;
+ u32 dpb_info2: 5;
+ u32 bottom_flag2: 1;
+ u32 view_index_off2: 1;
+ u32 dpb_info3: 5;
+ u32 bottom_flag3: 1;
+ u32 view_index_off3: 1;
+ u32 dpb_info4: 5;
+ u32 bottom_flag4: 1;
+ u32 view_index_off4: 1;
+ u32 dpb_info5: 5;
+ u32 bottom_flag5: 1;
+ u32 view_index_off5: 1;
+ u32 dpb_info6: 5;
+ u32 bottom_flag6: 1;
+ u32 view_index_off6: 1;
+ u32 dpb_info7: 5;
+ u32 bottom_flag7: 1;
+ u32 view_index_off7: 1;
+} __packed;
+
+struct rkvdec_rps {
+ u16 frame_num[16];
+ u32 reserved0;
+ struct rkvdec_rps_entry entries[12];
+ u32 reserved1[66];
+} __packed;
+
+void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run);
+void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
+ struct rkvdec_h264_run *run,
+ struct rkvdec_h264_reflists *reflists,
+ struct rkvdec_rps *hw_rps);
+void assemble_hw_scaling_list(struct rkvdec_h264_run *run,
+ struct rkvdec_h264_scaling_list *scaling_list);
+int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f);
+enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
+int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, const struct v4l2_ctrl_h264_sps *sps);
+void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
index d14b4d173448..a1af12c97914 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
@@ -14,20 +14,13 @@
#include "rkvdec.h"
#include "rkvdec-regs.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-h264-common.h"
/* Size with u32 units. */
#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128)
-#define RKV_RPS_SIZE ((128 + 128) / 4)
#define RKV_ERROR_INFO_SIZE (256 * 144 * 4)
-#define RKVDEC_NUM_REFLIST 3
-
-struct rkvdec_h264_scaling_list {
- u8 scaling_list_4x4[6][16];
- u8 scaling_list_8x8[6][64];
- u8 padding[128];
-};
-
struct rkvdec_sps_pps_packet {
u32 info[8];
};
@@ -83,537 +76,19 @@ struct rkvdec_ps_field {
#define SCALING_LIST_ADDRESS PS_FIELD(184, 32)
#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1)
-#define DPB_OFFS(i, j) (288 + ((j) * 32 * 7) + ((i) * 7))
-#define DPB_INFO(i, j) PS_FIELD(DPB_OFFS(i, j), 5)
-#define BOTTOM_FLAG(i, j) PS_FIELD(DPB_OFFS(i, j) + 5, 1)
-#define VIEW_INDEX_OFF(i, j) PS_FIELD(DPB_OFFS(i, j) + 6, 1)
-
/* Data structure describing auxiliary buffer format. */
struct rkvdec_h264_priv_tbl {
s8 cabac_table[4][464][2];
struct rkvdec_h264_scaling_list scaling_list;
- u32 rps[RKV_RPS_SIZE];
+ struct rkvdec_rps rps;
struct rkvdec_sps_pps_packet param_set[256];
u8 err_info[RKV_ERROR_INFO_SIZE];
};
-struct rkvdec_h264_reflists {
- struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN];
- struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN];
- struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN];
-};
-
-struct rkvdec_h264_run {
- struct rkvdec_run base;
- const struct v4l2_ctrl_h264_decode_params *decode_params;
- const struct v4l2_ctrl_h264_sps *sps;
- const struct v4l2_ctrl_h264_pps *pps;
- const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix;
- struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES];
-};
-
struct rkvdec_h264_ctx {
struct rkvdec_aux_buf priv_tbl;
struct rkvdec_h264_reflists reflists;
-};
-
-#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \
- idc2_m, idc2_n, intra_m, intra_n) \
- [0][(ctxidx)] = {idc0_m, idc0_n}, \
- [1][(ctxidx)] = {idc1_m, idc1_n}, \
- [2][(ctxidx)] = {idc2_m, idc2_n}, \
- [3][(ctxidx)] = {intra_m, intra_n}
-
-/*
- * Constant CABAC table.
- * Built from the tables described in section '9.3.1.1 Initialisation process
- * for context variables' of the H264 spec.
- */
-static const s8 rkvdec_h264_cabac_table[4][464][2] = {
- /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */
- CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15),
- CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54),
- CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74),
- CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15),
- CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54),
- CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74),
- CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127),
- CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104),
- CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53),
- CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54),
- CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51),
-
- /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */
- CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0),
- CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0),
- CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0),
- CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0),
- CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0),
- CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0),
- CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0),
- CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0),
- CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0),
- CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0),
- CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0),
- CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0),
- CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0),
-
- /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */
- CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0),
- CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0),
- CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0),
- CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0),
- CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0),
- CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0),
- CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0),
- CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0),
- CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0),
- CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0),
- CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0),
- CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0),
- CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0),
- CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0),
- CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0),
- CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0),
-
- /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */
- CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0),
- CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0),
- CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0),
- CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0),
- CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0),
- CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0),
- CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0),
- CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0),
- CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0),
- CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0),
- CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0),
- CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0),
- CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0),
- CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0),
-
- /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */
- CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0),
- CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0),
- CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0),
- CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0),
- CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0),
- CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0),
-
- /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */
- CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41),
- CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63),
- CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63),
- CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63),
- CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83),
- CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86),
- CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97),
- CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72),
- CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41),
- CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62),
-
- /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */
- CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11),
- CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55),
- CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69),
- CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127),
- CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102),
- CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82),
- CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74),
- CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107),
- CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127),
- CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127),
- CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127),
- CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95),
- CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127),
- CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114),
- CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127),
- CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123),
- CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115),
- CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122),
- CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115),
- CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63),
- CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68),
- CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84),
- CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104),
- CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70),
- CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93),
- CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90),
- CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127),
- CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74),
- CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97),
- CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91),
- CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127),
- CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56),
- CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82),
- CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76),
- CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125),
-
- /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */
- CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93),
- CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87),
- CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77),
- CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71),
- CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63),
- CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68),
- CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84),
- CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62),
- CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65),
- CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61),
- CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56),
- CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66),
- CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64),
- CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61),
- CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78),
- CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50),
- CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52),
- CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35),
- CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44),
- CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38),
- CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45),
- CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46),
- CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44),
- CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17),
- CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51),
- CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50),
- CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19),
- CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33),
- CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62),
- CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108),
- CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100),
- CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101),
- CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91),
- CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94),
- CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88),
- CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84),
- CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86),
- CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83),
- CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87),
- CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94),
- CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70),
- CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72),
- CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74),
- CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59),
- CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102),
- CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100),
- CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95),
- CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75),
- CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72),
- CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75),
- CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71),
- CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46),
- CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69),
- CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62),
- CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65),
- CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37),
- CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72),
- CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57),
- CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54),
- CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62),
- CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72),
-
- /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */
- CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0),
- CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9),
- CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25),
- CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18),
- CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9),
- CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19),
- CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37),
- CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18),
- CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29),
- CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33),
- CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30),
- CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45),
- CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58),
- CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62),
- CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61),
- CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38),
- CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45),
- CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39),
- CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42),
- CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44),
- CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45),
- CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41),
- CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49),
- CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34),
- CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42),
- CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55),
- CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51),
- CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46),
- CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89),
- CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19),
- CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17),
- CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17),
- CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25),
- CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20),
- CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23),
- CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27),
- CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23),
- CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28),
- CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17),
- CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11),
- CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15),
- CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6),
- CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1),
- CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17),
- CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6),
- CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3),
- CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22),
- CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16),
- CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4),
- CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8),
- CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3),
- CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3),
- CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5),
- CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0),
- CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16),
- CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22),
- CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48),
- CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37),
- CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60),
- CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68),
- CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97),
-
- /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */
- CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71),
- CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42),
- CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50),
- CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54),
- CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62),
- CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58),
- CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63),
- CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72),
- CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74),
- CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91),
- CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67),
- CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27),
- CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39),
- CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44),
- CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46),
- CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64),
- CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68),
- CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78),
- CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77),
- CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86),
- CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92),
- CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55),
- CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60),
- CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62),
- CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65),
- CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73),
- CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76),
- CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80),
- CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88),
- CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110),
- CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97),
- CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84),
- CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79),
- CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73),
- CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74),
- CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86),
- CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96),
- CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97),
- CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117),
- CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78),
- CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33),
- CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48),
- CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53),
- CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62),
- CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71),
- CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79),
- CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86),
- CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90),
- CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97),
-
- /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */
- CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93),
- CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84),
- CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79),
- CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66),
- CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71),
- CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62),
- CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60),
- CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59),
- CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75),
- CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62),
- CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58),
- CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66),
- CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79),
- CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71),
- CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68),
- CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44),
- CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62),
- CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36),
- CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40),
- CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27),
- CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29),
- CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44),
- CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36),
- CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32),
- CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42),
- CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48),
- CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62),
- CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46),
- CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64),
- CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104),
- CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97),
- CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96),
- CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88),
- CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85),
- CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85),
- CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85),
- CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88),
- CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66),
- CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77),
- CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76),
- CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76),
- CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58),
- CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76),
- CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83),
- CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99),
- CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95),
- CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95),
- CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76),
- CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74),
- CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70),
- CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75),
- CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68),
- CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65),
- CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73),
- CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62),
- CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62),
- CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68),
- CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75),
- CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55),
- CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64),
- CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70),
-
- /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */
- CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6),
- CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19),
- CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16),
- CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14),
- CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13),
- CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11),
- CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15),
- CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16),
- CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23),
- CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23),
- CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20),
- CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26),
- CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44),
- CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40),
- CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47),
- CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17),
- CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21),
- CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22),
- CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27),
- CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29),
- CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35),
- CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50),
- CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57),
- CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63),
- CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77),
- CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82),
- CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94),
- CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69),
- CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109),
- CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35),
- CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34),
- CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26),
- CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30),
- CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32),
- CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18),
- CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15),
- CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15),
- CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7),
- CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5),
- CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0),
- CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2),
- CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13),
- CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35),
- CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58),
- CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3),
- CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0),
- CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30),
- CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7),
- CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15),
- CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3),
- CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3),
- CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1),
- CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5),
- CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11),
- CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5),
- CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12),
- CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11),
- CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29),
- CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26),
- CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39),
- CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66),
-
- /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */
- CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21),
- CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31),
- CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50),
- CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120),
- CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112),
- CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114),
- CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85),
- CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92),
- CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89),
- CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71),
- CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81),
- CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80),
- CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68),
- CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70),
- CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56),
- CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68),
- CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50),
- CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74),
- CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13),
- CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13),
- CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15),
- CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14),
- CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3),
- CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6),
- CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34),
- CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54),
- CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82),
- CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75),
- CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23),
- CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34),
- CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43),
- CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54),
- CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55),
- CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61),
- CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64),
- CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68),
- CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92),
- CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106),
- CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97),
- CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90),
- CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90),
- CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88),
- CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73),
- CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79),
- CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86),
- CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73),
- CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70),
- CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69),
- CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66),
- CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64),
- CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58),
- CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59),
- CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10),
- CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11),
- CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8),
- CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1),
- CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3),
- CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9),
- CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20),
- CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36),
- CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67),
+ struct rkvdec_regs regs;
};
static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
@@ -733,154 +208,43 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
}
}
-static void lookup_ref_buf_idx(struct rkvdec_ctx *ctx,
- struct rkvdec_h264_run *run)
+/*
+ * Set the ref POC in the correct register.
+ *
+ * The 32 registers are spread across 3 regions, each alternating top and bottom ref POCs:
+ * - 1: ref 0 to 14 contain top 0 to 7 and bottoms 0 to 6
+ * - 2: ref 15 to 29 contain top 8 to 14 and bottoms 7 to 14
+ * - 3: ref 30 and 31 which correspond to top 15 and bottom 15 respectively.
+ */
+static void set_poc_reg(struct rkvdec_regs *regs, uint32_t poc, int id, bool bottom)
{
- const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
- u32 i;
-
- for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
- struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
- const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb;
- struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
- struct vb2_buffer *buf = NULL;
-
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) {
- buf = vb2_find_buffer(cap_q, dpb[i].reference_ts);
- if (!buf)
- pr_debug("No buffer for reference_ts %llu",
- dpb[i].reference_ts);
+ if (!bottom) {
+ switch (id) {
+ case 0 ... 7:
+ regs->h26x.ref0_14_poc[id * 2] = poc;
+ break;
+ case 8 ... 14:
+ regs->h26x.ref15_29_poc[(id - 8) * 2 + 1] = poc;
+ break;
+ case 15:
+ regs->h26x.ref30_poc = poc;
+ break;
}
-
- run->ref_buf[i] = buf;
- }
-}
-
-static void assemble_hw_rps(struct rkvdec_ctx *ctx,
- struct v4l2_h264_reflist_builder *builder,
- struct rkvdec_h264_run *run)
-{
- const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
- const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
- struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
- struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
-
- u32 *hw_rps = priv_tbl->rps;
- u32 i, j;
- u16 *p = (u16 *)hw_rps;
-
- memset(hw_rps, 0, sizeof(priv_tbl->rps));
-
- /*
- * Assign an invalid pic_num if DPB entry at that position is inactive.
- * If we assign 0 in that position hardware will treat that as a real
- * reference picture with pic_num 0, triggering output picture
- * corruption.
- */
- for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
- if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
- continue;
-
- p[i] = builder->refs[i].frame_num;
- }
-
- for (j = 0; j < RKVDEC_NUM_REFLIST; j++) {
- for (i = 0; i < builder->num_valid; i++) {
- struct v4l2_h264_reference *ref;
- bool dpb_valid;
- bool bottom;
-
- switch (j) {
- case 0:
- ref = &h264_ctx->reflists.p[i];
- break;
- case 1:
- ref = &h264_ctx->reflists.b0[i];
- break;
- case 2:
- ref = &h264_ctx->reflists.b1[i];
- break;
- }
-
- if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb)))
- continue;
-
- dpb_valid = run->ref_buf[ref->index] != NULL;
- bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF;
-
- set_ps_field(hw_rps, DPB_INFO(i, j),
- ref->index | dpb_valid << 4);
- set_ps_field(hw_rps, BOTTOM_FLAG(i, j), bottom);
+ } else {
+ switch (id) {
+ case 0 ... 6:
+ regs->h26x.ref0_14_poc[id * 2 + 1] = poc;
+ break;
+ case 7 ... 14:
+ regs->h26x.ref15_29_poc[(id - 7) * 2] = poc;
+ break;
+ case 15:
+ regs->h26x.ref31_poc = poc;
+ break;
}
}
}
-static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
- struct rkvdec_h264_run *run)
-{
- const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix;
- const struct v4l2_ctrl_h264_pps *pps = run->pps;
- struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
- struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
-
- if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT))
- return;
-
- BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_4x4) !=
- sizeof(scaling->scaling_list_4x4));
- BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_8x8) !=
- sizeof(scaling->scaling_list_8x8));
-
- memcpy(tbl->scaling_list.scaling_list_4x4,
- scaling->scaling_list_4x4,
- sizeof(scaling->scaling_list_4x4));
-
- memcpy(tbl->scaling_list.scaling_list_8x8,
- scaling->scaling_list_8x8,
- sizeof(scaling->scaling_list_8x8));
-}
-
-/*
- * dpb poc related registers table
- */
-static const u32 poc_reg_tbl_top_field[16] = {
- RKVDEC_REG_H264_POC_REFER0(0),
- RKVDEC_REG_H264_POC_REFER0(2),
- RKVDEC_REG_H264_POC_REFER0(4),
- RKVDEC_REG_H264_POC_REFER0(6),
- RKVDEC_REG_H264_POC_REFER0(8),
- RKVDEC_REG_H264_POC_REFER0(10),
- RKVDEC_REG_H264_POC_REFER0(12),
- RKVDEC_REG_H264_POC_REFER0(14),
- RKVDEC_REG_H264_POC_REFER1(1),
- RKVDEC_REG_H264_POC_REFER1(3),
- RKVDEC_REG_H264_POC_REFER1(5),
- RKVDEC_REG_H264_POC_REFER1(7),
- RKVDEC_REG_H264_POC_REFER1(9),
- RKVDEC_REG_H264_POC_REFER1(11),
- RKVDEC_REG_H264_POC_REFER1(13),
- RKVDEC_REG_H264_POC_REFER2(0)
-};
-
-static const u32 poc_reg_tbl_bottom_field[16] = {
- RKVDEC_REG_H264_POC_REFER0(1),
- RKVDEC_REG_H264_POC_REFER0(3),
- RKVDEC_REG_H264_POC_REFER0(5),
- RKVDEC_REG_H264_POC_REFER0(7),
- RKVDEC_REG_H264_POC_REFER0(9),
- RKVDEC_REG_H264_POC_REFER0(11),
- RKVDEC_REG_H264_POC_REFER0(13),
- RKVDEC_REG_H264_POC_REFER1(0),
- RKVDEC_REG_H264_POC_REFER1(2),
- RKVDEC_REG_H264_POC_REFER1(4),
- RKVDEC_REG_H264_POC_REFER1(6),
- RKVDEC_REG_H264_POC_REFER1(8),
- RKVDEC_REG_H264_POC_REFER1(10),
- RKVDEC_REG_H264_POC_REFER1(12),
- RKVDEC_REG_H264_POC_REFER1(14),
- RKVDEC_REG_H264_POC_REFER2(1)
-};
-
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
@@ -894,6 +258,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
const struct v4l2_format *f;
+ struct rkvdec_regs *regs = &h264_ctx->regs;
dma_addr_t rlc_addr;
dma_addr_t refer_addr;
u32 rlc_len;
@@ -903,10 +268,11 @@ static void config_registers(struct rkvdec_ctx *ctx,
u32 yuv_virstride = 0;
u32 offset;
dma_addr_t dst_addr;
- u32 reg, i;
+ u32 i;
+
+ memset(regs, 0, sizeof(*regs));
- reg = RKVDEC_MODE(RKVDEC_MODE_H264);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL);
+ regs->common.reg02.dec_mode = RKVDEC_MODE_H264;
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
@@ -921,39 +287,35 @@ static void config_registers(struct rkvdec_ctx *ctx,
else if (sps->chroma_format_idc == 2)
yuv_virstride = 2 * y_virstride;
- reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) |
- RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) |
- RKVDEC_SLICE_NUM_HIGHBIT |
- RKVDEC_SLICE_NUM_LOWBITS(0x7ff);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR);
+ regs->common.reg03.uv_hor_virstride = hor_virstride / 16;
+ regs->common.reg03.y_hor_virstride = hor_virstride / 16;
+ regs->common.reg03.slice_num_highbit = 1;
+ regs->common.reg03.slice_num_lowbits = 0x7ff;
/* config rlc base address */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
- writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
- writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_RLCWRITE_BASE);
+ regs->common.strm_rlc_base = rlc_addr;
+ regs->h26x.rlcwrite_base = rlc_addr;
rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
- reg = RKVDEC_STRM_LEN(rlc_len);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN);
+ regs->common.stream_len = rlc_len;
/* config cabac table */
offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
+ regs->common.cabactbl_base = priv_start_addr + offset;
/* config output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
- writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+ regs->common.decout_base = dst_addr;
- reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
+ regs->common.reg08.y_virstride = y_virstride / 16;
- reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+ regs->common.reg09.yuv_virstride = yuv_virstride / 16;
/* config ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct vb2_buffer *vb_buf = run->ref_buf[i];
+ struct ref_base *base;
/*
* If a DPB entry is unused or invalid, address of current destination
@@ -963,124 +325,37 @@ static void config_registers(struct rkvdec_ctx *ctx,
vb_buf = &dst_buf->vb2_buf;
refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)
- refer_addr |= RKVDEC_COLMV_USED_FLAG_REF;
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)
- refer_addr |= RKVDEC_FIELD_REF;
-
- if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF)
- refer_addr |= RKVDEC_TOPFIELD_USED_REF;
- if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
- refer_addr |= RKVDEC_BOTFIELD_USED_REF;
-
- writel_relaxed(dpb[i].top_field_order_cnt,
- rkvdec->regs + poc_reg_tbl_top_field[i]);
- writel_relaxed(dpb[i].bottom_field_order_cnt,
- rkvdec->regs + poc_reg_tbl_bottom_field[i]);
-
if (i < V4L2_H264_NUM_DPB_ENTRIES - 1)
- writel_relaxed(refer_addr,
- rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i));
+ base = &regs->h26x.ref0_14_base[i];
else
- writel_relaxed(refer_addr,
- rkvdec->regs + RKVDEC_REG_H264_BASE_REFER15);
- }
+ base = &regs->h26x.ref15_base;
+
+ base->base_addr = refer_addr >> 4;
+ base->field_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD);
+ base->colmv_use_flag_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE);
+ base->topfield_used_ref = !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF);
+ base->botfield_used_ref = !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF);
- reg = RKVDEC_CUR_POC(dec_params->top_field_order_cnt);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0);
+ set_poc_reg(regs, dpb[i].top_field_order_cnt, i, false);
+ set_poc_reg(regs, dpb[i].bottom_field_order_cnt, i, true);
+ }
- reg = RKVDEC_CUR_POC(dec_params->bottom_field_order_cnt);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC1);
+ regs->h26x.cur_poc = dec_params->top_field_order_cnt;
+ regs->h26x.cur_poc1 = dec_params->bottom_field_order_cnt;
/* config hw pps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_PPS_BASE);
+ regs->h26x.pps_base = priv_start_addr + offset;
/* config hw rps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_RPS_BASE);
-
- reg = RKVDEC_AXI_DDR_RDATA(0);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA);
-
- reg = RKVDEC_AXI_DDR_WDATA(0);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA);
+ regs->h26x.rps_base = priv_start_addr + offset;
offset = offsetof(struct rkvdec_h264_priv_tbl, err_info);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_H264_ERRINFO_BASE);
-}
-
-#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2
+ regs->h26x.errorinfo_base = priv_start_addr + offset;
-static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
-
- fmt->num_planes = 1;
- if (!fmt->plane_fmt[0].sizeimage)
- fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
- RKVDEC_H264_MAX_DEPTH_IN_BYTES;
- return 0;
-}
-
-static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx,
- struct v4l2_ctrl *ctrl)
-{
- const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
-
- if (ctrl->id != V4L2_CID_STATELESS_H264_SPS)
- return RKVDEC_IMG_FMT_ANY;
-
- if (sps->bit_depth_luma_minus8 == 0) {
- if (sps->chroma_format_idc == 2)
- return RKVDEC_IMG_FMT_422_8BIT;
- else
- return RKVDEC_IMG_FMT_420_8BIT;
- } else if (sps->bit_depth_luma_minus8 == 2) {
- if (sps->chroma_format_idc == 2)
- return RKVDEC_IMG_FMT_422_10BIT;
- else
- return RKVDEC_IMG_FMT_420_10BIT;
- }
-
- return RKVDEC_IMG_FMT_ANY;
-}
-
-static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx,
- const struct v4l2_ctrl_h264_sps *sps)
-{
- unsigned int width, height;
-
- if (sps->chroma_format_idc > 2)
- /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */
- return -EINVAL;
- if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
- /* Luma and chroma bit depth mismatch */
- return -EINVAL;
- if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
- /* Only 8-bit and 10-bit is supported */
- return -EINVAL;
-
- width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
- height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
-
- /*
- * When frame_mbs_only_flag is not set, this is field height,
- * which is half the final height (see (7-18) in the
- * specification)
- */
- if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
- height *= 2;
-
- if (width > ctx->coded_fmt.fmt.pix_mp.width ||
- height > ctx->coded_fmt.fmt.pix_mp.height)
- return -EINVAL;
-
- return 0;
+ rkvdec_memcpy_toio(rkvdec->regs, regs,
+ MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
}
static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
@@ -1134,33 +409,13 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
kfree(h264_ctx);
}
-static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx,
- struct rkvdec_h264_run *run)
-{
- struct v4l2_ctrl *ctrl;
-
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_H264_DECODE_PARAMS);
- run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_H264_SPS);
- run->sps = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_H264_PPS);
- run->pps = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_H264_SCALING_MATRIX);
- run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
-
- rkvdec_run_preamble(ctx, &run->base);
-}
-
static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
{
struct v4l2_h264_reflist_builder reflist_builder;
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_h264_run run;
+ struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
rkvdec_h264_run_preamble(ctx, &run);
@@ -1171,18 +426,16 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
h264_ctx->reflists.b1);
- assemble_hw_scaling_list(ctx, &run);
+ assemble_hw_scaling_list(&run, &tbl->scaling_list);
assemble_hw_pps(ctx, &run);
lookup_ref_buf_idx(ctx, &run);
- assemble_hw_rps(ctx, &reflist_builder, &run);
+ assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
- writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
- writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
new file mode 100644
index 000000000000..28267ee30190
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip video decoder hevc common functions
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ *
+ * Copyright (C) 2023 Collabora, Ltd.
+ * Sebastian Fricke <sebastian.fricke@collabora.com>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <linux/v4l2-common.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-hevc-common.h"
+
+/* Store the Short term ref pic set calculated values */
+struct calculated_rps_st_set {
+ u8 num_delta_pocs;
+ u8 num_negative_pics;
+ u8 num_positive_pics;
+ u8 used_by_curr_pic_s0[16];
+ u8 used_by_curr_pic_s1[16];
+ s32 delta_poc_s0[16];
+ s32 delta_poc_s1[16];
+};
+
+void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
+ u16 width, u16 height, s32 pic_in_cts_width,
+ s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
+{
+ const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+ int i;
+
+ for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++)
+ column_width[i] = ((i + 1) * pic_in_cts_width) /
+ (pps->num_tile_columns_minus1 + 1) -
+ (i * pic_in_cts_width) /
+ (pps->num_tile_columns_minus1 + 1);
+
+ for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++)
+ row_height[i] = ((i + 1) * pic_in_cts_height) /
+ (pps->num_tile_rows_minus1 + 1) -
+ (i * pic_in_cts_height) /
+ (pps->num_tile_rows_minus1 + 1);
+}
+
+void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
+ u16 width, u16 height, s32 pic_in_cts_width,
+ s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
+{
+ const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+ s32 sum = 0;
+ int i;
+
+ for (i = 0; i < pps->num_tile_columns_minus1; i++) {
+ column_width[i] = pps->column_width_minus1[i] + 1;
+ sum += column_width[i];
+ }
+ column_width[i] = pic_in_cts_width - sum;
+
+ sum = 0;
+ for (i = 0; i < pps->num_tile_rows_minus1; i++) {
+ row_height[i] = pps->row_height_minus1[i] + 1;
+ sum += row_height[i];
+ }
+ row_height[i] = pic_in_cts_height - sum;
+}
+
+static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag)
+{
+ switch (poc) {
+ case 0:
+ set->delta_poc0 = value;
+ set->used_flag0 = flag;
+ break;
+ case 1:
+ set->delta_poc1 = value;
+ set->used_flag1 = flag;
+ break;
+ case 2:
+ set->delta_poc2 = value;
+ set->used_flag2 = flag;
+ break;
+ case 3:
+ set->delta_poc3 = value;
+ set->used_flag3 = flag;
+ break;
+ case 4:
+ set->delta_poc4 = value;
+ set->used_flag4 = flag;
+ break;
+ case 5:
+ set->delta_poc5 = value;
+ set->used_flag5 = flag;
+ break;
+ case 6:
+ set->delta_poc6 = value;
+ set->used_flag6 = flag;
+ break;
+ case 7:
+ set->delta_poc7 = value;
+ set->used_flag7 = flag;
+ break;
+ case 8:
+ set->delta_poc8 = value;
+ set->used_flag8 = flag;
+ break;
+ case 9:
+ set->delta_poc9 = value;
+ set->used_flag9 = flag;
+ break;
+ case 10:
+ set->delta_poc10 = value;
+ set->used_flag10 = flag;
+ break;
+ case 11:
+ set->delta_poc11 = value;
+ set->used_flag11 = flag;
+ break;
+ case 12:
+ set->delta_poc12 = value;
+ set->used_flag12 = flag;
+ break;
+ case 13:
+ set->delta_poc13 = value;
+ set->used_flag13 = flag;
+ break;
+ case 14:
+ set->delta_poc14 = value;
+ set->used_flag14 = flag;
+ break;
+ }
+}
+
+static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output,
+ const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ const struct rkvdec_variant *variant = ctx->dev->variant;
+ int offset = 0;
+
+ variant->ops->flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4);
+ offset = 6 * 16 * sizeof(u8);
+ variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8);
+ offset += 6 * 64 * sizeof(u8);
+ variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_16x16,
+ 6, 8);
+ offset += 6 * 64 * sizeof(u8);
+ /* Add a 128 byte padding with 0s between the two 32x32 matrices */
+ variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_32x32,
+ 1, 8);
+ offset += 64 * sizeof(u8);
+ memset(output + offset, 0, 128);
+ offset += 128 * sizeof(u8);
+ variant->ops->flatten_matrices(output + offset,
+ (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)),
+ 1, 8);
+ offset += 64 * sizeof(u8);
+ memset(output + offset, 0, 128);
+}
+
+/*
+ * Required layout:
+ * A = scaling_list_dc_coef_16x16
+ * B = scaling_list_dc_coef_32x32
+ * 0 = Padding
+ *
+ * A, A, A, A, A, A, B, 0, 0, B, 0, 0
+ */
+static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ u8 list_32x32[6] = {0};
+
+ memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8));
+ list_32x32[0] = input->scaling_list_dc_coef_32x32[0];
+ list_32x32[3] = input->scaling_list_dc_coef_32x32[1];
+ memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8));
+}
+
+static void translate_scaling_list(struct rkvdec_ctx *ctx, struct scaling_factor *output,
+ const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ assemble_scalingfactor0(ctx, output->scalingfactor0, input);
+ memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96);
+ assemble_scalingdc(output->scalingdc, input);
+ memset(output->reserved, 0, 4 * sizeof(u8));
+}
+
+void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run,
+ struct scaling_factor *scaling_factor,
+ struct v4l2_ctrl_hevc_scaling_matrix *cache)
+{
+ const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix;
+
+ if (!memcmp(cache, scaling,
+ sizeof(struct v4l2_ctrl_hevc_scaling_matrix)))
+ return;
+
+ translate_scaling_list(ctx, scaling_factor, scaling);
+
+ memcpy(cache, scaling,
+ sizeof(struct v4l2_ctrl_hevc_scaling_matrix));
+}
+
+static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps)
+{
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+
+ if (!run->ext_sps_lt_rps)
+ return;
+
+ for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
+ rps->refs[i].lt_ref_pic_poc_lsb =
+ run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps;
+ rps->refs[i].used_by_curr_pic_lt_flag =
+ !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT);
+ }
+}
+
+static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
+ struct calculated_rps_st_set *calculated_rps_st_sets)
+{
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+
+ for (int i = 0; i < sps->num_short_term_ref_pic_sets; i++) {
+ int poc = 0;
+ int j = 0;
+ const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i];
+
+ rps->short_term_ref_sets[i].num_negative = set->num_negative_pics;
+ rps->short_term_ref_sets[i].num_positive = set->num_positive_pics;
+
+ for (; j < set->num_negative_pics; j++) {
+ set_ref_poc(&rps->short_term_ref_sets[i], j,
+ set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]);
+ }
+ poc = j;
+
+ for (j = 0; j < set->num_positive_pics; j++) {
+ set_ref_poc(&rps->short_term_ref_sets[i], poc + j,
+ set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]);
+ }
+ }
+}
+
+/*
+ * Compute the short term ref pic set parameters based on its reference short term ref pic
+ */
+static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx,
+ struct calculated_rps_st_set *calculated_rps_st_sets)
+{
+ const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
+ struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
+ struct calculated_rps_st_set *ref_rps;
+ u8 st_rps_idx = idx;
+ u8 ref_rps_idx = 0;
+ s16 delta_rps = 0;
+ u8 use_delta_flag[16] = { 0 };
+ u8 used_by_curr_pic_flag[16] = { 0 };
+ int i, j;
+ int dPoc;
+
+ ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */
+ delta_rps = (1 - 2 * rps_data->delta_rps_sign) *
+ (rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */
+
+ ref_rps = &calculated_rps_st_sets[ref_rps_idx];
+
+ for (j = 0; j <= ref_rps->num_delta_pocs; j++) {
+ used_by_curr_pic_flag[j] = !!(rps_data->used_by_curr_pic & (1 << j));
+ use_delta_flag[j] = !!(rps_data->use_delta_flag & (1 << j));
+ }
+
+ /* 7-61: calculate num_negative_pics, delta_poc_s0 and used_by_curr_pic_s0 */
+ i = 0;
+ for (j = (ref_rps->num_positive_pics - 1); j >= 0; j--) {
+ dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
+ if (dPoc < 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
+ st_rps->delta_poc_s0[i] = dPoc;
+ st_rps->used_by_curr_pic_s0[i++] =
+ used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
+ }
+ }
+ if (delta_rps < 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
+ st_rps->delta_poc_s0[i] = delta_rps;
+ st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
+ }
+ for (j = 0; j < ref_rps->num_negative_pics; j++) {
+ dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
+ if (dPoc < 0 && use_delta_flag[j]) {
+ st_rps->delta_poc_s0[i] = dPoc;
+ st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j];
+ }
+ }
+ st_rps->num_negative_pics = i;
+
+ /* 7-62: calculate num_positive_pics, delta_poc_s1 and used_by_curr_pic_s1 */
+ i = 0;
+ for (j = (ref_rps->num_negative_pics - 1); j >= 0; j--) {
+ dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
+ if (dPoc > 0 && use_delta_flag[j]) {
+ st_rps->delta_poc_s1[i] = dPoc;
+ st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j];
+ }
+ }
+ if (delta_rps > 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
+ st_rps->delta_poc_s1[i] = delta_rps;
+ st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
+ }
+ for (j = 0; j < ref_rps->num_positive_pics; j++) {
+ dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
+ if (dPoc > 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
+ st_rps->delta_poc_s1[i] = dPoc;
+ st_rps->used_by_curr_pic_s1[i++] =
+ used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
+ }
+ }
+ st_rps->num_positive_pics = i;
+
+ st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
+}
+
+/*
+ * Compute the short term ref pic set parameters based on the control's data.
+ */
+static void st_ref_pic_set_calculate(struct rkvdec_hevc_run *run, int idx,
+ struct calculated_rps_st_set *calculated_rps_st_sets)
+{
+ const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
+ struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
+ int j, i = 0;
+
+ /* 7-63 */
+ st_rps->num_negative_pics = rps_data->num_negative_pics;
+ /* 7-64 */
+ st_rps->num_positive_pics = rps_data->num_positive_pics;
+
+ for (i = 0; i < st_rps->num_negative_pics; i++) {
+ /* 7-65 */
+ st_rps->used_by_curr_pic_s0[i] = !!(rps_data->used_by_curr_pic & (1 << i));
+
+ if (i == 0) {
+ /* 7-67 */
+ st_rps->delta_poc_s0[i] = -(rps_data->delta_poc_s0_minus1[i] + 1);
+ } else {
+ /* 7-69 */
+ st_rps->delta_poc_s0[i] =
+ st_rps->delta_poc_s0[i - 1] -
+ (rps_data->delta_poc_s0_minus1[i] + 1);
+ }
+ }
+
+ for (j = 0; j < st_rps->num_positive_pics; j++) {
+ /* 7-66 */
+ st_rps->used_by_curr_pic_s1[j] = !!(rps_data->used_by_curr_pic & (1 << (i + j)));
+
+ if (j == 0) {
+ /* 7-68 */
+ st_rps->delta_poc_s1[j] = rps_data->delta_poc_s1_minus1[j] + 1;
+ } else {
+ /* 7-70 */
+ st_rps->delta_poc_s1[j] =
+ st_rps->delta_poc_s1[j - 1] +
+ (rps_data->delta_poc_s1_minus1[j] + 1);
+ }
+ }
+
+ /* 7-71 */
+ st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
+}
+
+static void rkvdec_hevc_prepare_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
+ struct v4l2_ctrl_hevc_ext_sps_st_rps *cache)
+{
+ int idx;
+
+ if (!run->ext_sps_st_rps)
+ return;
+
+ if (!memcmp(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps)))
+ return;
+
+ struct calculated_rps_st_set *calculated_rps_st_sets =
+ kzalloc(sizeof(struct calculated_rps_st_set) *
+ run->sps->num_short_term_ref_pic_sets, GFP_KERNEL);
+
+ for (idx = 0; idx < run->sps->num_short_term_ref_pic_sets; idx++) {
+ const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
+
+ if (rps_data->flags & V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED)
+ st_ref_pic_set_prediction(run, idx, calculated_rps_st_sets);
+ else
+ st_ref_pic_set_calculate(run, idx, calculated_rps_st_sets);
+ }
+
+ rkvdec_hevc_assemble_hw_st_rps(run, rps, calculated_rps_st_sets);
+
+ kfree(calculated_rps_st_sets);
+
+ memcpy(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps));
+}
+
+void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
+ struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache)
+{
+ rkvdec_hevc_prepare_hw_st_rps(run, rps, st_cache);
+ rkvdec_hevc_assemble_hw_lt_rps(run, rps);
+}
+
+struct vb2_buffer *
+get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run,
+ unsigned int dpb_idx)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
+ const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
+ struct vb2_buffer *buf = NULL;
+
+ if (dpb_idx < decode_params->num_active_dpb_entries)
+ buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp);
+
+ /*
+ * If a DPB entry is unused or invalid, the address of current destination
+ * buffer is returned.
+ */
+ if (!buf)
+ return &run->base.bufs.dst->vb2_buf;
+
+ return buf;
+}
+
+#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2
+
+int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+ fmt->num_planes = 1;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
+ RKVDEC_HEVC_MAX_DEPTH_IN_BYTES;
+ return 0;
+}
+
+enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
+
+ if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS)
+ return RKVDEC_IMG_FMT_ANY;
+
+ if (sps->bit_depth_luma_minus8 == 0) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_8BIT;
+ else
+ return RKVDEC_IMG_FMT_420_8BIT;
+ } else if (sps->bit_depth_luma_minus8 == 2) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_10BIT;
+ else
+ return RKVDEC_IMG_FMT_420_10BIT;
+ }
+
+ return RKVDEC_IMG_FMT_ANY;
+}
+
+void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_DECODE_PARAMS);
+ run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SLICE_PARAMS);
+ run->slices_params = ctrl ? ctrl->p_cur.p : NULL;
+ run->num_slices = ctrl ? ctrl->new_elems : 0;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SPS);
+ run->sps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_PPS);
+ run->pps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SCALING_MATRIX);
+ run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
+
+ if (ctx->has_sps_st_rps) {
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS);
+ run->ext_sps_st_rps = ctrl ? ctrl->p_cur.p : NULL;
+ }
+ if (ctx->has_sps_lt_rps) {
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS);
+ run->ext_sps_lt_rps = ctrl ? ctrl->p_cur.p : NULL;
+ }
+
+ rkvdec_run_preamble(ctx, &run->base);
+}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h
new file mode 100644
index 000000000000..6f4faca4c091
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip video decoder hevc common functions
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ *
+ * Copyright (C) 2023 Collabora, Ltd.
+ * Sebastian Fricke <sebastian.fricke@collabora.com>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <media/v4l2-mem2mem.h>
+#include <linux/types.h>
+
+#include "rkvdec.h"
+
+struct rkvdec_rps_refs {
+ u16 lt_ref_pic_poc_lsb;
+ u16 used_by_curr_pic_lt_flag : 1;
+ u16 reserved : 15;
+} __packed;
+
+struct rkvdec_rps_short_term_ref_set {
+ u32 num_negative : 4;
+ u32 num_positive : 4;
+ u32 delta_poc0 : 16;
+ u32 used_flag0 : 1;
+ u32 delta_poc1 : 16;
+ u32 used_flag1 : 1;
+ u32 delta_poc2 : 16;
+ u32 used_flag2 : 1;
+ u32 delta_poc3 : 16;
+ u32 used_flag3 : 1;
+ u32 delta_poc4 : 16;
+ u32 used_flag4 : 1;
+ u32 delta_poc5 : 16;
+ u32 used_flag5 : 1;
+ u32 delta_poc6 : 16;
+ u32 used_flag6 : 1;
+ u32 delta_poc7 : 16;
+ u32 used_flag7 : 1;
+ u32 delta_poc8 : 16;
+ u32 used_flag8 : 1;
+ u32 delta_poc9 : 16;
+ u32 used_flag9 : 1;
+ u32 delta_poc10 : 16;
+ u32 used_flag10 : 1;
+ u32 delta_poc11 : 16;
+ u32 used_flag11 : 1;
+ u32 delta_poc12 : 16;
+ u32 used_flag12 : 1;
+ u32 delta_poc13 : 16;
+ u32 used_flag13 : 1;
+ u32 delta_poc14 : 16;
+ u32 used_flag14 : 1;
+ u32 reserved_bits : 25;
+ u32 reserved[3];
+} __packed;
+
+struct rkvdec_rps {
+ struct rkvdec_rps_refs refs[32];
+ struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64];
+} __packed;
+
+struct rkvdec_hevc_run {
+ struct rkvdec_run base;
+ const struct v4l2_ctrl_hevc_slice_params *slices_params;
+ const struct v4l2_ctrl_hevc_decode_params *decode_params;
+ const struct v4l2_ctrl_hevc_sps *sps;
+ const struct v4l2_ctrl_hevc_pps *pps;
+ const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
+ const struct v4l2_ctrl_hevc_ext_sps_st_rps *ext_sps_st_rps;
+ const struct v4l2_ctrl_hevc_ext_sps_lt_rps *ext_sps_lt_rps;
+ int num_slices;
+};
+
+struct scaling_factor {
+ u8 scalingfactor0[1248];
+ u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/
+ u8 scalingdc[12]; /*N1005 Vienna Meeting*/
+ u8 reserved[4]; /*16Bytes align*/
+};
+
+void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
+ u16 width, u16 height, s32 pic_in_cts_width,
+ s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
+void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
+ u16 width, u16 height, s32 pic_in_cts_width,
+ s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
+void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
+ struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache);
+void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run,
+ struct scaling_factor *scaling_factor,
+ struct v4l2_ctrl_hevc_scaling_matrix *cache);
+struct vb2_buffer *get_ref_buf(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run,
+ unsigned int dpb_idx);
+int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f);
+enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
+void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
index fc7e6a260b0a..3276f584f5c7 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -16,7 +16,8 @@
#include "rkvdec.h"
#include "rkvdec-regs.h"
-#include "rkvdec-hevc-data.c"
+#include "rkvdec-cabac.h"
+#include "rkvdec-hevc-common.h"
/* Size in u8/u32 units. */
#define RKV_SCALING_LIST_SIZE 1360
@@ -110,32 +111,16 @@ struct rkvdec_ps_field {
/* Data structure describing auxiliary buffer format. */
struct rkvdec_hevc_priv_tbl {
- u8 cabac_table[RKV_CABAC_TABLE_SIZE];
- u8 scaling_list[RKV_SCALING_LIST_SIZE];
+ u8 cabac_table[RKV_HEVC_CABAC_TABLE_SIZE];
+ struct scaling_factor scaling_list;
struct rkvdec_sps_pps_packet param_set[RKV_PPS_LEN];
struct rkvdec_rps_packet rps[RKV_RPS_LEN];
};
-struct rkvdec_hevc_run {
- struct rkvdec_run base;
- const struct v4l2_ctrl_hevc_slice_params *slices_params;
- const struct v4l2_ctrl_hevc_decode_params *decode_params;
- const struct v4l2_ctrl_hevc_sps *sps;
- const struct v4l2_ctrl_hevc_pps *pps;
- const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
- int num_slices;
-};
-
struct rkvdec_hevc_ctx {
struct rkvdec_aux_buf priv_tbl;
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
-};
-
-struct scaling_factor {
- u8 scalingfactor0[1248];
- u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/
- u8 scalingdc[12]; /*N1005 Vienna Meeting*/
- u8 reserved[4]; /*16Bytes align*/
+ struct rkvdec_regs regs;
};
static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
@@ -414,131 +399,6 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx,
}
}
-/*
- * Flip one or more matrices along their main diagonal and flatten them
- * before writing it to the memory.
- * Convert:
- * ABCD AEIM
- * EFGH => BFJN => AEIMBFJNCGKODHLP
- * IJKL CGKO
- * MNOP DHLP
- */
-static void transpose_and_flatten_matrices(u8 *output, const u8 *input,
- int matrices, int row_length)
-{
- int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value;
-
- matrix_size = row_length * row_length;
- for (i = 0; i < matrices; i++) {
- row = 0;
- x_offset = 0;
- matrix_offset = i * matrix_size;
- for (j = 0; j < matrix_size; j++) {
- y_offset = j - (row * row_length);
- rot_index = y_offset * row_length + x_offset;
- new_value = *(input + i * matrix_size + j);
- output[matrix_offset + rot_index] = new_value;
- if ((j + 1) % row_length == 0) {
- row += 1;
- x_offset += 1;
- }
- }
- }
-}
-
-static void assemble_scalingfactor0(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
-{
- int offset = 0;
-
- transpose_and_flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4);
- offset = 6 * 16 * sizeof(u8);
- transpose_and_flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8);
- offset += 6 * 64 * sizeof(u8);
- transpose_and_flatten_matrices(output + offset,
- (const u8 *)input->scaling_list_16x16, 6, 8);
- offset += 6 * 64 * sizeof(u8);
- /* Add a 128 byte padding with 0s between the two 32x32 matrices */
- transpose_and_flatten_matrices(output + offset,
- (const u8 *)input->scaling_list_32x32, 1, 8);
- offset += 64 * sizeof(u8);
- memset(output + offset, 0, 128);
- offset += 128 * sizeof(u8);
- transpose_and_flatten_matrices(output + offset,
- (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)),
- 1, 8);
- offset += 64 * sizeof(u8);
- memset(output + offset, 0, 128);
-}
-
-/*
- * Required layout:
- * A = scaling_list_dc_coef_16x16
- * B = scaling_list_dc_coef_32x32
- * 0 = Padding
- *
- * A, A, A, A, A, A, B, 0, 0, B, 0, 0
- */
-static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
-{
- u8 list_32x32[6] = {0};
-
- memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8));
- list_32x32[0] = input->scaling_list_dc_coef_32x32[0];
- list_32x32[3] = input->scaling_list_dc_coef_32x32[1];
- memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8));
-}
-
-static void translate_scaling_list(struct scaling_factor *output,
- const struct v4l2_ctrl_hevc_scaling_matrix *input)
-{
- assemble_scalingfactor0(output->scalingfactor0, input);
- memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96);
- assemble_scalingdc(output->scalingdc, input);
- memset(output->reserved, 0, 4 * sizeof(u8));
-}
-
-static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
- struct rkvdec_hevc_run *run)
-{
- const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix;
- struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
- struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
- u8 *dst;
-
- if (!memcmp((void *)&hevc_ctx->scaling_matrix_cache, scaling,
- sizeof(struct v4l2_ctrl_hevc_scaling_matrix)))
- return;
-
- dst = tbl->scaling_list;
- translate_scaling_list((struct scaling_factor *)dst, scaling);
-
- memcpy((void *)&hevc_ctx->scaling_matrix_cache, scaling,
- sizeof(struct v4l2_ctrl_hevc_scaling_matrix));
-}
-
-static struct vb2_buffer *
-get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run,
- unsigned int dpb_idx)
-{
- struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
- const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
- const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
- struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
- struct vb2_buffer *buf = NULL;
-
- if (dpb_idx < decode_params->num_active_dpb_entries)
- buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp);
-
- /*
- * If a DPB entry is unused or invalid, the address of current destination
- * buffer is returned.
- */
- if (!buf)
- return &run->base.bufs.dst->vb2_buf;
-
- return buf;
-}
-
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
@@ -548,6 +408,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
const struct v4l2_ctrl_hevc_slice_params *sl_params = &run->slices_params[0];
const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_regs *regs = &hevc_ctx->regs;
dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
@@ -564,8 +425,9 @@ static void config_registers(struct rkvdec_ctx *ctx,
dma_addr_t dst_addr;
u32 reg, i;
- reg = RKVDEC_MODE(RKVDEC_MODE_HEVC);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL);
+ memset(regs, 0, sizeof(*regs));
+
+ regs->common.reg02.dec_mode = RKVDEC_MODE_HEVC;
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
@@ -580,33 +442,27 @@ static void config_registers(struct rkvdec_ctx *ctx,
else if (sps->chroma_format_idc == 2)
yuv_virstride = 2 * y_virstride;
- reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) |
- RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) |
- RKVDEC_SLICE_NUM_LOWBITS(run->num_slices);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR);
+ regs->common.reg03.slice_num_lowbits = run->num_slices;
+ regs->common.reg03.uv_hor_virstride = hor_virstride / 16;
+ regs->common.reg03.y_hor_virstride = hor_virstride / 16;
/* config rlc base address */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
- writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
+ regs->common.strm_rlc_base = rlc_addr;
rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
- reg = RKVDEC_STRM_LEN(round_up(rlc_len, 16) + 64);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN);
+ regs->common.stream_len = round_up(rlc_len, 16) + 64;
/* config cabac table */
offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
+ regs->common.cabactbl_base = priv_start_addr + offset;
/* config output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
- writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+ regs->common.decout_base = dst_addr;
- reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
-
- reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+ regs->common.reg08.y_virstride = y_virstride / 16;
+ regs->common.reg09.yuv_virstride = yuv_virstride / 16;
/* config ref pic address */
for (i = 0; i < 15; i++) {
@@ -620,70 +476,30 @@ static void config_registers(struct rkvdec_ctx *ctx,
}
refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
- writel_relaxed(refer_addr | reg,
- rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i));
- reg = RKVDEC_POC_REFER(i < decode_params->num_active_dpb_entries ?
- dpb[i].pic_order_cnt_val : 0);
- writel_relaxed(reg,
- rkvdec->regs + RKVDEC_REG_H264_POC_REFER0(i));
+ regs->h26x.ref0_14_base[i].base_addr = refer_addr >> 4;
+ regs->h26x.ref0_14_base[i].field_ref = !!(reg & 1);
+ regs->h26x.ref0_14_base[i].topfield_used_ref = !!(reg & 2);
+ regs->h26x.ref0_14_base[i].botfield_used_ref = !!(reg & 4);
+ regs->h26x.ref0_14_base[i].colmv_use_flag_ref = !!(reg & 8);
+
+ regs->h26x.ref0_14_poc[i] = i < decode_params->num_active_dpb_entries
+ ? dpb[i].pic_order_cnt_val
+ : 0;
}
- reg = RKVDEC_CUR_POC(sl_params->slice_pic_order_cnt);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0);
+ regs->h26x.cur_poc = sl_params->slice_pic_order_cnt;
/* config hw pps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_PPS_BASE);
+ regs->h26x.pps_base = priv_start_addr + offset;
/* config hw rps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
- writel_relaxed(priv_start_addr + offset,
- rkvdec->regs + RKVDEC_REG_RPS_BASE);
+ regs->h26x.rps_base = priv_start_addr + offset;
- reg = RKVDEC_AXI_DDR_RDATA(0);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA);
-
- reg = RKVDEC_AXI_DDR_WDATA(0);
- writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA);
-}
-
-#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2
-
-static int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
-
- fmt->num_planes = 1;
- if (!fmt->plane_fmt[0].sizeimage)
- fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
- RKVDEC_HEVC_MAX_DEPTH_IN_BYTES;
- return 0;
-}
-
-static enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx,
- struct v4l2_ctrl *ctrl)
-{
- const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
-
- if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS)
- return RKVDEC_IMG_FMT_ANY;
-
- if (sps->bit_depth_luma_minus8 == 0) {
- if (sps->chroma_format_idc == 2)
- return RKVDEC_IMG_FMT_422_8BIT;
- else
- return RKVDEC_IMG_FMT_420_8BIT;
- } else if (sps->bit_depth_luma_minus8 == 2) {
- if (sps->chroma_format_idc == 2)
- return RKVDEC_IMG_FMT_422_10BIT;
- else
- return RKVDEC_IMG_FMT_420_10BIT;
- }
-
- return RKVDEC_IMG_FMT_ANY;
+ rkvdec_memcpy_toio(rkvdec->regs, regs,
+ MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
}
static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
@@ -696,7 +512,7 @@ static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
- /* Only 8-bit and 10-bit is supported */
+ /* Only 8-bit and 10-bit are supported */
return -EINVAL;
if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
@@ -742,40 +558,18 @@ static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
kfree(hevc_ctx);
}
-static void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx,
- struct rkvdec_hevc_run *run)
-{
- struct v4l2_ctrl *ctrl;
-
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_HEVC_DECODE_PARAMS);
- run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_HEVC_SLICE_PARAMS);
- run->slices_params = ctrl ? ctrl->p_cur.p : NULL;
- run->num_slices = ctrl ? ctrl->new_elems : 0;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_HEVC_SPS);
- run->sps = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_HEVC_PPS);
- run->pps = ctrl ? ctrl->p_cur.p : NULL;
- ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
- V4L2_CID_STATELESS_HEVC_SCALING_MATRIX);
- run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
-
- rkvdec_run_preamble(ctx, &run->base);
-}
-
static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_run run;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
u32 reg;
rkvdec_hevc_run_preamble(ctx, &run);
- assemble_hw_scaling_list(ctx, &run);
+ rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
+ &hevc_ctx->scaling_matrix_cache);
assemble_hw_pps(ctx, &run);
assemble_sw_rps(ctx, &run);
config_registers(ctx, &run);
@@ -784,8 +578,6 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
- writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
- writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
new file mode 100644
index 000000000000..fdcf1f177379
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip video decoder Rows and Cols Buffers manager
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include "rkvdec.h"
+#include "rkvdec-rcb.h"
+
+#include <linux/iommu.h>
+#include <linux/genalloc.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+struct rkvdec_rcb_config {
+ struct rkvdec_aux_buf *rcb_bufs;
+ size_t rcb_count;
+};
+
+static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
+ unsigned int width, unsigned int height)
+{
+ return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width);
+}
+
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id)
+{
+ return ctx->rcb_config->rcb_bufs[id].dma;
+}
+
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id)
+{
+ return ctx->rcb_config->rcb_bufs[id].size;
+}
+
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
+{
+ return ctx->rcb_config->rcb_count;
+}
+
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *dev = ctx->dev;
+ struct rkvdec_rcb_config *cfg = ctx->rcb_config;
+ unsigned long virt_addr;
+ int i;
+
+ if (!cfg)
+ return;
+
+ for (i = 0; i < cfg->rcb_count; i++) {
+ size_t rcb_size = cfg->rcb_bufs[i].size;
+
+ if (!cfg->rcb_bufs[i].cpu)
+ continue;
+
+ switch (cfg->rcb_bufs[i].type) {
+ case RKVDEC_ALLOC_SRAM:
+ virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
+
+ if (dev->iommu_domain)
+ iommu_unmap(dev->iommu_domain, virt_addr, rcb_size);
+ gen_pool_free(dev->sram_pool, virt_addr, rcb_size);
+ break;
+ case RKVDEC_ALLOC_DMA:
+ dma_free_coherent(dev->dev,
+ rcb_size,
+ cfg->rcb_bufs[i].cpu,
+ cfg->rcb_bufs[i].dma);
+ break;
+ }
+ }
+
+ if (cfg->rcb_bufs)
+ devm_kfree(dev->dev, cfg->rcb_bufs);
+
+ devm_kfree(dev->dev, cfg);
+}
+
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+ const struct rcb_size_info *size_info,
+ size_t rcb_count)
+{
+ int ret, i;
+ u32 width, height;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_rcb_config *cfg;
+
+ if (!size_info || !rcb_count) {
+ ctx->rcb_config = NULL;
+ return 0;
+ }
+
+ ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL);
+ if (!ctx->rcb_config)
+ return -ENOMEM;
+
+ cfg = ctx->rcb_config;
+
+ cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL);
+ if (!cfg->rcb_bufs) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ width = ctx->decoded_fmt.fmt.pix_mp.width;
+ height = ctx->decoded_fmt.fmt.pix_mp.height;
+
+ for (i = 0; i < rcb_count; i++) {
+ void *cpu = NULL;
+ dma_addr_t dma;
+ size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height);
+ enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM;
+
+ /* Try allocating an SRAM buffer */
+ if (ctx->dev->sram_pool) {
+ if (rkvdec->iommu_domain)
+ rcb_size = ALIGN(rcb_size, SZ_4K);
+
+ cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool,
+ rcb_size,
+ &dma,
+ SZ_4K);
+ }
+
+ /* If an IOMMU is used, map the SRAM address through it */
+ if (cpu && rkvdec->iommu_domain) {
+ unsigned long virt_addr = (unsigned long)cpu;
+ phys_addr_t phys_addr = dma;
+
+ ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr,
+ rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
+ if (ret) {
+ gen_pool_free(ctx->dev->sram_pool,
+ (unsigned long)cpu,
+ rcb_size);
+ cpu = NULL;
+ goto ram_fallback;
+ }
+
+ /*
+ * The registers will be configured with the virtual
+ * address so that it goes through the IOMMU
+ */
+ dma = virt_addr;
+ }
+
+ram_fallback:
+ /* Fallback to RAM */
+ if (!cpu) {
+ cpu = dma_alloc_coherent(ctx->dev->dev,
+ rcb_size,
+ &dma,
+ GFP_KERNEL);
+ alloc_type = RKVDEC_ALLOC_DMA;
+ }
+
+ if (!cpu) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ cfg->rcb_bufs[i].cpu = cpu;
+ cfg->rcb_bufs[i].dma = dma;
+ cfg->rcb_bufs[i].size = rcb_size;
+ cfg->rcb_bufs[i].type = alloc_type;
+
+ cfg->rcb_count += 1;
+ }
+
+ return 0;
+
+err_alloc:
+ rkvdec_free_rcb(ctx);
+
+ return ret;
+}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
new file mode 100644
index 000000000000..30e8002555c8
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip video decoder Rows and Cols Buffers manager
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <linux/types.h>
+
+struct rkvdec_ctx;
+
+enum rcb_axis {
+ PIC_WIDTH = 0,
+ PIC_HEIGHT = 1
+};
+
+struct rcb_size_info {
+ u8 multiplier;
+ enum rcb_axis axis;
+};
+
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+ const struct rcb_size_info *size_info,
+ size_t rcb_count);
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
index c627b6b6f53a..1af66c5f1c9b 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
@@ -3,7 +3,12 @@
#ifndef RKVDEC_REGS_H_
#define RKVDEC_REGS_H_
-/* rkvcodec registers */
+#include <linux/types.h>
+
+/*
+ * REG_INTERRUPT is accessed via writel to enable the decoder after
+ * configuring it and clear interrupt strmd_error_status
+ */
#define RKVDEC_REG_INTERRUPT 0x004
#define RKVDEC_INTERRUPT_DEC_E BIT(0)
#define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1)
@@ -30,198 +35,399 @@
#define RKVDEC_SOFTRESET_RDY BIT(22)
#define RKVDEC_WR_DDR_ALIGN_EN BIT(23)
-#define RKVDEC_REG_SYSCTRL 0x008
-#define RKVDEC_IN_ENDIAN BIT(0)
-#define RKVDEC_IN_SWAP32_E BIT(1)
-#define RKVDEC_IN_SWAP64_E BIT(2)
-#define RKVDEC_STR_ENDIAN BIT(3)
-#define RKVDEC_STR_SWAP32_E BIT(4)
-#define RKVDEC_STR_SWAP64_E BIT(5)
-#define RKVDEC_OUT_ENDIAN BIT(6)
-#define RKVDEC_OUT_SWAP32_E BIT(7)
-#define RKVDEC_OUT_CBCR_SWAP BIT(8)
-#define RKVDEC_RLC_MODE_DIRECT_WRITE BIT(10)
-#define RKVDEC_RLC_MODE BIT(11)
-#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12)
-#define RKVDEC_MODE(x) (((x) & 0x03) << 20)
-#define RKVDEC_MODE_HEVC 0
-#define RKVDEC_MODE_H264 1
-#define RKVDEC_MODE_VP9 2
-#define RKVDEC_RPS_MODE BIT(24)
-#define RKVDEC_STRM_MODE BIT(25)
-#define RKVDEC_H264_STRM_LASTPKT BIT(26)
-#define RKVDEC_H264_FIRSTSLICE_FLAG BIT(27)
-#define RKVDEC_H264_FRAME_ORSLICE BIT(28)
-#define RKVDEC_BUSPR_SLOT_DIS BIT(29)
-
-#define RKVDEC_REG_PICPAR 0x00C
-#define RKVDEC_Y_HOR_VIRSTRIDE(x) ((x) & 0x1ff)
-#define RKVDEC_SLICE_NUM_HIGHBIT BIT(11)
-#define RKVDEC_UV_HOR_VIRSTRIDE(x) (((x) & 0x1ff) << 12)
-#define RKVDEC_SLICE_NUM_LOWBITS(x) (((x) & 0x7ff) << 21)
-
-#define RKVDEC_REG_STRM_RLC_BASE 0x010
-
-#define RKVDEC_REG_STRM_LEN 0x014
-#define RKVDEC_STRM_LEN(x) ((x) & 0x7ffffff)
-
-#define RKVDEC_REG_CABACTBL_PROB_BASE 0x018
-#define RKVDEC_REG_DECOUT_BASE 0x01C
-
-#define RKVDEC_REG_Y_VIRSTRIDE 0x020
-#define RKVDEC_Y_VIRSTRIDE(x) ((x) & 0xfffff)
-
-#define RKVDEC_REG_YUV_VIRSTRIDE 0x024
-#define RKVDEC_YUV_VIRSTRIDE(x) ((x) & 0x1fffff)
-#define RKVDEC_REG_H264_BASE_REFER(i) (((i) * 0x04) + 0x028)
-
-#define RKVDEC_REG_H264_BASE_REFER15 0x0C0
-#define RKVDEC_FIELD_REF BIT(0)
-#define RKVDEC_TOPFIELD_USED_REF BIT(1)
-#define RKVDEC_BOTFIELD_USED_REF BIT(2)
-#define RKVDEC_COLMV_USED_FLAG_REF BIT(3)
-
-#define RKVDEC_REG_VP9_LAST_FRAME_BASE 0x02c
-#define RKVDEC_REG_VP9_GOLDEN_FRAME_BASE 0x030
-#define RKVDEC_REG_VP9_ALTREF_FRAME_BASE 0x034
-
-#define RKVDEC_REG_VP9_CPRHEADER_OFFSET 0x028
-#define RKVDEC_VP9_CPRHEADER_OFFSET(x) ((x) & 0xffff)
-
-#define RKVDEC_REG_VP9_REFERLAST_BASE 0x02C
-#define RKVDEC_REG_VP9_REFERGOLDEN_BASE 0x030
-#define RKVDEC_REG_VP9_REFERALFTER_BASE 0x034
-
-#define RKVDEC_REG_VP9COUNT_BASE 0x038
-#define RKVDEC_VP9COUNT_UPDATE_EN BIT(0)
-
-#define RKVDEC_REG_VP9_SEGIDLAST_BASE 0x03C
-#define RKVDEC_REG_VP9_SEGIDCUR_BASE 0x040
-#define RKVDEC_REG_VP9_FRAME_SIZE(i) ((i) * 0x04 + 0x044)
-#define RKVDEC_VP9_FRAMEWIDTH(x) (((x) & 0xffff) << 0)
-#define RKVDEC_VP9_FRAMEHEIGHT(x) (((x) & 0xffff) << 16)
-
-#define RKVDEC_VP9_SEGID_GRP(i) ((i) * 0x04 + 0x050)
-#define RKVDEC_SEGID_ABS_DELTA(x) ((x) & 0x1)
-#define RKVDEC_SEGID_FRAME_QP_DELTA_EN(x) (((x) & 0x1) << 1)
-#define RKVDEC_SEGID_FRAME_QP_DELTA(x) (((x) & 0x1ff) << 2)
-#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(x) (((x) & 0x1) << 11)
-#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(x) (((x) & 0x7f) << 12)
-#define RKVDEC_SEGID_REFERINFO_EN(x) (((x) & 0x1) << 19)
-#define RKVDEC_SEGID_REFERINFO(x) (((x) & 0x03) << 20)
-#define RKVDEC_SEGID_FRAME_SKIP_EN(x) (((x) & 0x1) << 22)
-
-#define RKVDEC_VP9_CPRHEADER_CONFIG 0x070
-#define RKVDEC_VP9_TX_MODE(x) ((x) & 0x07)
-#define RKVDEC_VP9_FRAME_REF_MODE(x) (((x) & 0x03) << 3)
-
-#define RKVDEC_VP9_REF_SCALE(i) ((i) * 0x04 + 0x074)
-#define RKVDEC_VP9_REF_HOR_SCALE(x) ((x) & 0xffff)
-#define RKVDEC_VP9_REF_VER_SCALE(x) (((x) & 0xffff) << 16)
-
-#define RKVDEC_VP9_REF_DELTAS_LASTFRAME 0x080
-#define RKVDEC_REF_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7))
-
-#define RKVDEC_VP9_INFO_LASTFRAME 0x084
-#define RKVDEC_MODE_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7))
-#define RKVDEC_SEG_EN_LASTFRAME BIT(16)
-#define RKVDEC_LAST_SHOW_FRAME BIT(17)
-#define RKVDEC_LAST_INTRA_ONLY BIT(18)
-#define RKVDEC_LAST_WIDHHEIGHT_EQCUR BIT(19)
-#define RKVDEC_COLOR_SPACE_LASTKEYFRAME(x) (((x) & 0x07) << 20)
-
-#define RKVDEC_VP9_INTERCMD_BASE 0x088
-
-#define RKVDEC_VP9_INTERCMD_NUM 0x08C
-#define RKVDEC_INTERCMD_NUM(x) ((x) & 0xffffff)
-
-#define RKVDEC_VP9_LASTTILE_SIZE 0x090
-#define RKVDEC_LASTTILE_SIZE(x) ((x) & 0xffffff)
-
-#define RKVDEC_VP9_HOR_VIRSTRIDE(i) ((i) * 0x04 + 0x094)
-#define RKVDEC_HOR_Y_VIRSTRIDE(x) ((x) & 0x1ff)
-#define RKVDEC_HOR_UV_VIRSTRIDE(x) (((x) & 0x1ff) << 16)
-
-#define RKVDEC_REG_H264_POC_REFER0(i) (((i) * 0x04) + 0x064)
-#define RKVDEC_REG_H264_POC_REFER1(i) (((i) * 0x04) + 0x0C4)
-#define RKVDEC_REG_H264_POC_REFER2(i) (((i) * 0x04) + 0x120)
-#define RKVDEC_POC_REFER(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_CUR_POC0 0x0A0
-#define RKVDEC_REG_CUR_POC1 0x128
-#define RKVDEC_CUR_POC(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_RLCWRITE_BASE 0x0A4
-#define RKVDEC_REG_PPS_BASE 0x0A8
-#define RKVDEC_REG_RPS_BASE 0x0AC
-
-#define RKVDEC_REG_STRMD_ERR_EN 0x0B0
-#define RKVDEC_STRMD_ERR_EN(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_STRMD_ERR_STA 0x0B4
-#define RKVDEC_STRMD_ERR_STA(x) ((x) & 0xfffffff)
-#define RKVDEC_COLMV_ERR_REF_PICIDX(x) (((x) & 0x0f) << 28)
-
-#define RKVDEC_REG_STRMD_ERR_CTU 0x0B8
-#define RKVDEC_STRMD_ERR_CTU(x) ((x) & 0xff)
-#define RKVDEC_STRMD_ERR_CTU_YOFFSET(x) (((x) & 0xff) << 8)
-#define RKVDEC_STRMFIFO_SPACE2FULL(x) (((x) & 0x7f) << 16)
-#define RKVDEC_VP9_ERR_EN_CTU0 BIT(24)
-
-#define RKVDEC_REG_SAO_CTU_POS 0x0BC
-#define RKVDEC_SAOWR_XOFFSET(x) ((x) & 0x1ff)
-#define RKVDEC_SAOWR_YOFFSET(x) (((x) & 0x3ff) << 16)
-
-#define RKVDEC_VP9_LAST_FRAME_YSTRIDE 0x0C0
-#define RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE 0x0C4
-#define RKVDEC_VP9_ALTREF_FRAME_YSTRIDE 0x0C8
-#define RKVDEC_VP9_REF_YSTRIDE(x) (((x) & 0xfffff) << 0)
-
-#define RKVDEC_VP9_LAST_FRAME_YUVSTRIDE 0x0CC
-#define RKVDEC_VP9_REF_YUVSTRIDE(x) (((x) & 0x1fffff) << 0)
-
-#define RKVDEC_VP9_REF_COLMV_BASE 0x0D0
-
-#define RKVDEC_REG_PERFORMANCE_CYCLE 0x100
-#define RKVDEC_PERFORMANCE_CYCLE(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_AXI_DDR_RDATA 0x104
-#define RKVDEC_AXI_DDR_RDATA(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_AXI_DDR_WDATA 0x108
-#define RKVDEC_AXI_DDR_WDATA(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_FPGADEBUG_RESET 0x10C
-#define RKVDEC_BUSIFD_RESETN BIT(0)
-#define RKVDEC_CABAC_RESETN BIT(1)
-#define RKVDEC_DEC_CTRL_RESETN BIT(2)
-#define RKVDEC_TRANSD_RESETN BIT(3)
-#define RKVDEC_INTRA_RESETN BIT(4)
-#define RKVDEC_INTER_RESETN BIT(5)
-#define RKVDEC_RECON_RESETN BIT(6)
-#define RKVDEC_FILER_RESETN BIT(7)
-
-#define RKVDEC_REG_PERFORMANCE_SEL 0x110
-#define RKVDEC_PERF_SEL_CNT0(x) ((x) & 0x3f)
-#define RKVDEC_PERF_SEL_CNT1(x) (((x) & 0x3f) << 8)
-#define RKVDEC_PERF_SEL_CNT2(x) (((x) & 0x3f) << 16)
-
-#define RKVDEC_REG_PERFORMANCE_CNT(i) ((i) * 0x04 + 0x114)
-#define RKVDEC_PERF_CNT(x) ((x) & 0xffffffff)
-
-#define RKVDEC_REG_H264_ERRINFO_BASE 0x12C
-
-#define RKVDEC_REG_H264_ERRINFO_NUM 0x130
-#define RKVDEC_SLICEDEC_NUM(x) ((x) & 0x3fff)
-#define RKVDEC_STRMD_DECT_ERR_FLAG BIT(15)
-#define RKVDEC_ERR_PKT_NUM(x) (((x) & 0x3fff) << 16)
-
-#define RKVDEC_REG_H264_ERR_E 0x134
-#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff)
-
#define RKVDEC_REG_QOS_CTRL 0x18C
+/*
+ * Cache configuration is not covered in the range of the register struct
+ */
#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410
#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450
+/*
+ * Define the mode values
+ */
+#define RKVDEC_MODE_HEVC 0
+#define RKVDEC_MODE_H264 1
+#define RKVDEC_MODE_VP9 2
+
+/* rkvcodec registers */
+struct rkvdec_common_regs {
+ struct rkvdec_id {
+ u32 minor_ver : 8;
+ u32 level : 1;
+ u32 dec_support : 3;
+ u32 profile : 1;
+ u32 reserved0 : 1;
+ u32 codec_flag : 1;
+ u32 reserved1 : 1;
+ u32 prod_num : 16;
+ } reg00;
+
+ struct rkvdec_int {
+ u32 dec_e : 1;
+ u32 dec_clkgate_e : 1;
+ u32 dec_e_strmd_clkgate_dis : 1;
+ u32 timeout_mode : 1;
+ u32 dec_irq_dis : 1;
+ u32 dec_timeout_e : 1;
+ u32 buf_empty_en : 1;
+ u32 stmerror_waitdecfifo_empty : 1;
+ u32 dec_irq : 1;
+ u32 dec_irq_raw : 1;
+ u32 reserved2 : 2;
+ u32 dec_rdy_sta : 1;
+ u32 dec_bus_sta : 1;
+ u32 dec_error_sta : 1;
+ u32 dec_timeout_sta : 1;
+ u32 dec_empty_sta : 1;
+ u32 colmv_ref_error_sta : 1;
+ u32 cabu_end_sta : 1;
+ u32 h264orvp9_error_mode : 1;
+ u32 softrst_en_p : 1;
+ u32 force_softreset_valid : 1;
+ u32 softreset_rdy : 1;
+ u32 wr_ddr_align_en : 1;
+ u32 scl_down_en : 1;
+ u32 allow_not_wr_unref_bframe : 1;
+ u32 reserved1 : 6;
+ } reg01;
+
+ struct rkvdec_sysctrl {
+ u32 in_endian : 1;
+ u32 in_swap32_e : 1;
+ u32 in_swap64_e : 1;
+ u32 str_endian : 1;
+ u32 str_swap32_e : 1;
+ u32 str_swap64_e : 1;
+ u32 out_endian : 1;
+ u32 out_swap32_e : 1;
+ u32 out_cbcr_swap : 1;
+ u32 reserved0 : 1;
+ u32 rlc_mode_direct_write : 1;
+ u32 rlc_mode : 1;
+ u32 strm_start_bit : 7;
+ u32 reserved1 : 1;
+ u32 dec_mode : 2;
+ u32 reserved2 : 2;
+ u32 rps_mode : 1;
+ u32 stream_mode : 1;
+ u32 stream_lastpacket : 1;
+ u32 firstslice_flag : 1;
+ u32 frame_orslice : 1;
+ u32 buspr_slot_disable : 1;
+ u32 colmv_mode : 1;
+ u32 ycacherd_prior : 1;
+ } reg02;
+
+ struct rkvdec_picpar {
+ u32 y_hor_virstride : 9;
+ u32 reserved : 2;
+ u32 slice_num_highbit : 1;
+ u32 uv_hor_virstride : 9;
+ u32 slice_num_lowbits : 11;
+ } reg03;
+
+ u32 strm_rlc_base;
+ u32 stream_len;
+ u32 cabactbl_base;
+ u32 decout_base;
+
+ struct rkvdec_y_virstride {
+ u32 y_virstride : 20;
+ u32 reserved0 : 12;
+ } reg08;
+
+ struct rkvdec_yuv_virstride {
+ u32 yuv_virstride : 21;
+ u32 reserved0 : 11;
+ } reg09;
+} __packed;
+
+struct ref_base {
+ u32 field_ref : 1;
+ u32 topfield_used_ref : 1;
+ u32 botfield_used_ref : 1;
+ u32 colmv_use_flag_ref : 1;
+ u32 base_addr : 28;
+};
+
+struct rkvdec_h26x_regs {
+ struct ref_base ref0_14_base[15];
+ u32 ref0_14_poc[15];
+
+ u32 cur_poc;
+ u32 rlcwrite_base;
+ u32 pps_base;
+ u32 rps_base;
+
+ u32 strmd_error_e;
+
+ struct {
+ u32 strmd_error_status : 28;
+ u32 colmv_error_ref_picidx : 4;
+ } reg45;
+
+ struct {
+ u32 strmd_error_ctu_xoffset : 8;
+ u32 strmd_error_ctu_yoffset : 8;
+ u32 streamfifo_space2full : 7;
+ u32 reserved0 : 1;
+ u32 vp9_error_ctu0_en : 1;
+ u32 reserved1 : 7;
+ } reg46;
+
+ struct {
+ u32 saowr_xoffet : 9;
+ u32 reserved0 : 7;
+ u32 saowr_yoffset : 10;
+ u32 reserved1 : 6;
+ } reg47;
+
+ struct ref_base ref15_base;
+
+ u32 ref15_29_poc[15];
+
+ u32 performance_cycle;
+ u32 axi_ddr_rdata;
+ u32 axi_ddr_wdata;
+
+ struct {
+ u32 busifd_resetn : 1;
+ u32 cabac_resetn : 1;
+ u32 dec_ctrl_resetn : 1;
+ u32 transd_resetn : 1;
+ u32 intra_resetn : 1;
+ u32 inter_resetn : 1;
+ u32 recon_resetn : 1;
+ u32 filer_resetn : 1;
+ u32 reserved0 : 24;
+ } reg67;
+
+ struct {
+ u32 perf_cnt0_sel : 6;
+ u32 reserved0 : 2;
+ u32 perf_cnt1_sel : 6;
+ u32 reserved1 : 2;
+ u32 perf_cnt2_sel : 6;
+ u32 reserved2 : 10;
+ } reg68;
+
+ u32 perf_cnt0;
+ u32 perf_cnt1;
+ u32 perf_cnt2;
+ u32 ref30_poc;
+ u32 ref31_poc;
+ u32 cur_poc1;
+ u32 errorinfo_base;
+
+ struct {
+ u32 slicedec_num : 14;
+ u32 reserved0 : 1;
+ u32 strmd_detect_error_flag : 1;
+ u32 error_packet_num : 14;
+ u32 reserved1 : 2;
+ } reg76;
+
+ struct {
+ u32 error_en_highbits : 30;
+ u32 strmd_error_slice_en : 1;
+ u32 strmd_error_frame_en : 1;
+ } reg77;
+
+ u32 colmv_cur_base;
+ u32 colmv_ref_base[16];
+ u32 scanlist_addr;
+ u32 reg96_sd_decout_base;
+ u32 sd_y_virstride;
+ u32 sd_hor_stride;
+ u32 qos_ctrl;
+ u32 perf[8];
+ u32 qos1;
+} __packed;
+
+struct rkvdec_vp9_regs {
+ struct cprheader_offset {
+ u32 cprheader_offset : 16;
+ u32 reserved : 16;
+ } reg10;
+
+ u32 refer_bases[3];
+ u32 count_base;
+ u32 segidlast_base;
+ u32 segidcur_base;
+
+ struct frame_sizes {
+ u32 framewidth : 16;
+ u32 frameheight : 16;
+ } reg17_19[3];
+
+ struct segid_grp {
+ u32 segid_abs_delta : 1;
+ u32 segid_frame_qp_delta_en : 1;
+ u32 segid_frame_qp_delta : 9;
+ u32 segid_frame_loopfilter_value_en : 1;
+ u32 segid_frame_loopfilter_value : 7;
+ u32 segid_referinfo_en : 1;
+ u32 segid_referinfo : 2;
+ u32 segid_frame_skip_en : 1;
+ u32 reserved : 9;
+ } reg20_27[8];
+
+ struct cprheader_config {
+ u32 tx_mode : 3;
+ u32 frame_reference_mode : 2;
+ u32 reserved : 27;
+ } reg28;
+
+ struct ref_scale {
+ u32 ref_hor_scale : 16;
+ u32 ref_ver_scale : 16;
+ } reg29_31[3];
+
+ struct ref_deltas_lastframe {
+ u32 ref_deltas_lastframe0 : 7;
+ u32 ref_deltas_lastframe1 : 7;
+ u32 ref_deltas_lastframe2 : 7;
+ u32 ref_deltas_lastframe3 : 7;
+ u32 reserved : 4;
+ } reg32;
+
+ struct info_lastframe {
+ u32 mode_deltas_lastframe0 : 7;
+ u32 mode_deltas_lastframe1 : 7;
+ u32 reserved0 : 2;
+ u32 segmentation_enable_lstframe : 1;
+ u32 last_show_frame : 1;
+ u32 last_intra_only : 1;
+ u32 last_widthheight_eqcur : 1;
+ u32 color_space_lastkeyframe : 3;
+ u32 reserved1 : 9;
+ } reg33;
+
+ u32 intercmd_base;
+
+ struct intercmd_num {
+ u32 intercmd_num : 24;
+ u32 reserved : 8;
+ } reg35;
+
+ struct lasttile_size {
+ u32 lasttile_size : 24;
+ u32 reserved : 8;
+ } reg36;
+
+ struct hor_virstride {
+ u32 y_hor_virstride : 9;
+ u32 reserved0 : 7;
+ u32 uv_hor_virstride : 9;
+ u32 reserved1 : 7;
+ } reg37_39[3];
+
+ u32 cur_poc;
+
+ struct rlcwrite_base {
+ u32 reserved : 3;
+ u32 rlcwrite_base : 29;
+ } reg41;
+
+ struct pps_base {
+ u32 reserved : 4;
+ u32 pps_base : 28;
+ } reg42;
+
+ struct rps_base {
+ u32 reserved : 4;
+ u32 rps_base : 28;
+ } reg43;
+
+ struct strmd_error_en {
+ u32 strmd_error_e : 28;
+ u32 reserved : 4;
+ } reg44;
+
+ u32 vp9_error_info0;
+
+ struct strmd_error_ctu {
+ u32 strmd_error_ctu_xoffset : 8;
+ u32 strmd_error_ctu_yoffset : 8;
+ u32 streamfifo_space2full : 7;
+ u32 reserved0 : 1;
+ u32 error_ctu0_en : 1;
+ u32 reserved1 : 7;
+ } reg46;
+
+ struct sao_ctu_position {
+ u32 saowr_xoffet : 9;
+ u32 reserved0 : 7;
+ u32 saowr_yoffset : 10;
+ u32 reserved1 : 6;
+ } reg47;
+
+ struct ystride {
+ u32 virstride : 20;
+ u32 reserved : 12;
+ } reg48_50[3];
+
+ struct lastref_yuvstride {
+ u32 lastref_yuv_virstride : 21;
+ u32 reserved : 11;
+ } reg51;
+
+ u32 refcolmv_base;
+
+ u32 reserved0[11];
+
+ u32 performance_cycle;
+ u32 axi_ddr_rdata;
+ u32 axi_ddr_wdata;
+
+ struct fpgadebug_reset {
+ u32 busifd_resetn : 1;
+ u32 cabac_resetn : 1;
+ u32 dec_ctrl_resetn : 1;
+ u32 transd_resetn : 1;
+ u32 intra_resetn : 1;
+ u32 inter_resetn : 1;
+ u32 recon_resetn : 1;
+ u32 filer_resetn : 1;
+ u32 reserved : 24;
+ } reg67;
+
+ struct performance_sel {
+ u32 perf_cnt0_sel : 6;
+ u32 reserved0 : 2;
+ u32 perf_cnt1_sel : 6;
+ u32 reserved1 : 2;
+ u32 perf_cnt2_sel : 6;
+ u32 reserved : 10;
+ } reg68;
+
+ u32 perf_cnt0;
+ u32 perf_cnt1;
+ u32 perf_cnt2;
+
+ u32 reserved1[3];
+
+ u32 vp9_error_info1;
+
+ struct error_ctu1 {
+ u32 vp9_error_ctu1_x : 6;
+ u32 reserved0 : 2;
+ u32 vp9_error_ctu1_y : 6;
+ u32 reserved1 : 1;
+ u32 vp9_error_ctu1_en : 1;
+ u32 reserved2 : 16;
+ } reg76;
+
+ u32 reserved2;
+} __packed;
+
+struct rkvdec_regs {
+ struct rkvdec_common_regs common;
+ union {
+ struct rkvdec_h26x_regs h26x;
+ struct rkvdec_vp9_regs vp9;
+ };
+} __packed;
+
#endif /* RKVDEC_REGS_H_ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
new file mode 100644
index 000000000000..cd0aa3f3a13d
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip VDPU381 Video Decoder H264 backend
+ *
+ * Copyright (C) 2024 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-rcb.h"
+#include "rkvdec-h264-common.h"
+#include "rkvdec-vdpu381-regs.h"
+
+struct rkvdec_sps {
+ u16 seq_parameter_set_id: 4;
+ u16 profile_idc: 8;
+ u16 constraint_set3_flag: 1;
+ u16 chroma_format_idc: 2;
+ u16 bit_depth_luma: 3;
+ u16 bit_depth_chroma: 3;
+ u16 qpprime_y_zero_transform_bypass_flag: 1;
+ u16 log2_max_frame_num_minus4: 4;
+ u16 max_num_ref_frames: 5;
+ u16 pic_order_cnt_type: 2;
+ u16 log2_max_pic_order_cnt_lsb_minus4: 4;
+ u16 delta_pic_order_always_zero_flag: 1;
+ u16 pic_width_in_mbs: 12;
+ u16 pic_height_in_mbs: 12;
+ u16 frame_mbs_only_flag: 1;
+ u16 mb_adaptive_frame_field_flag: 1;
+ u16 direct_8x8_inference_flag: 1;
+ u16 mvc_extension_enable: 1;
+ u16 num_views: 2;
+
+ u16 reserved_bits: 12;
+ u16 reserved[11];
+} __packed;
+
+struct rkvdec_pps {
+ u16 pic_parameter_set_id: 8;
+ u16 pps_seq_parameter_set_id: 5;
+ u16 entropy_coding_mode_flag: 1;
+ u16 bottom_field_pic_order_in_frame_present_flag: 1;
+ u16 num_ref_idx_l0_default_active_minus1: 5;
+ u16 num_ref_idx_l1_default_active_minus1: 5;
+ u16 weighted_pred_flag: 1;
+ u16 weighted_bipred_idc: 2;
+ u16 pic_init_qp_minus26: 7;
+ u16 pic_init_qs_minus26: 6;
+ u16 chroma_qp_index_offset: 5;
+ u16 deblocking_filter_control_present_flag: 1;
+ u16 constrained_intra_pred_flag: 1;
+ u16 redundant_pic_cnt_present: 1;
+ u16 transform_8x8_mode_flag: 1;
+ u16 second_chroma_qp_index_offset: 5;
+ u16 scaling_list_enable_flag: 1;
+ u32 scaling_list_address;
+ u16 is_longterm;
+
+ u8 reserved[3];
+} __packed;
+
+struct rkvdec_sps_pps {
+ struct rkvdec_sps sps;
+ struct rkvdec_pps pps;
+} __packed;
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_h264_priv_tbl {
+ s8 cabac_table[4][464][2];
+ struct rkvdec_h264_scaling_list scaling_list;
+ struct rkvdec_sps_pps param_set[256];
+ struct rkvdec_rps rps;
+};
+
+struct rkvdec_h264_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct rkvdec_h264_reflists reflists;
+ struct rkvdec_vdpu381_regs_h264 regs;
+};
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ const struct v4l2_ctrl_h264_sps *sps = run->sps;
+ const struct v4l2_ctrl_h264_pps *pps = run->pps;
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+ struct rkvdec_sps_pps *hw_ps;
+ dma_addr_t scaling_list_address;
+ u32 scaling_distance;
+ u32 i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 32 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+ /* write sps */
+ hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id;
+ hw_ps->sps.profile_idc = sps->profile_idc;
+ hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3));
+ hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
+ hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8;
+ hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8;
+ hw_ps->sps.qpprime_y_zero_transform_bypass_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
+ hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
+ hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames;
+ hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type;
+ hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 =
+ sps->log2_max_pic_order_cnt_lsb_minus4;
+ hw_ps->sps.delta_pic_order_always_zero_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
+ hw_ps->sps.mvc_extension_enable = 1;
+ hw_ps->sps.num_views = 1;
+
+ /*
+ * Use the SPS values since they are already in macroblocks
+ * dimensions, height can be field height (halved) if
+ * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
+ * decoding smaller images into larger allocation which can be used
+ * to implementing SVC spatial layer support.
+ */
+ hw_ps->sps.pic_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1;
+ hw_ps->sps.pic_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1;
+ hw_ps->sps.frame_mbs_only_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
+ hw_ps->sps.mb_adaptive_frame_field_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
+ hw_ps->sps.direct_8x8_inference_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
+
+ /* write pps */
+ hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id;
+ hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id;
+ hw_ps->pps.entropy_coding_mode_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
+ hw_ps->pps.bottom_field_pic_order_in_frame_present_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
+ hw_ps->pps.num_ref_idx_l0_default_active_minus1 =
+ pps->num_ref_idx_l0_default_active_minus1;
+ hw_ps->pps.num_ref_idx_l1_default_active_minus1 =
+ pps->num_ref_idx_l1_default_active_minus1;
+ hw_ps->pps.weighted_pred_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
+ hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc;
+ hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26;
+ hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26;
+ hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset;
+ hw_ps->pps.deblocking_filter_control_present_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
+ hw_ps->pps.constrained_intra_pred_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+ hw_ps->pps.redundant_pic_cnt_present =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
+ hw_ps->pps.transform_8x8_mode_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
+ hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
+ hw_ps->pps.scaling_list_enable_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT);
+
+ /*
+ * To be on the safe side, program the scaling matrix address
+ */
+ scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+ scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance;
+ hw_ps->pps.scaling_list_address = scaling_list_address;
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ hw_ps->pps.is_longterm |= (1 << i);
+ }
+}
+
+static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
+ &h264_ctx->regs.common,
+ sizeof(h264_ctx->regs.common));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
+ &h264_ctx->regs.h264_param,
+ sizeof(h264_ctx->regs.h264_param));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
+ &h264_ctx->regs.common_addr,
+ sizeof(h264_ctx->regs.common_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
+ &h264_ctx->regs.h264_addr,
+ sizeof(h264_ctx->regs.h264_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
+ &h264_ctx->regs.h264_highpoc,
+ sizeof(h264_ctx->regs.h264_highpoc));
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ struct rkvdec_vdpu381_regs_h264 *regs = &h264_ctx->regs;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ dma_addr_t dst_addr;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
+ u32 offset;
+ u32 pixels;
+ u32 i;
+
+ memset(regs, 0, sizeof(*regs));
+
+ /* Set H264 mode */
+ regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_H264;
+
+ /* Set config */
+ regs->common.reg011_important_en.buf_empty_en = 1;
+ regs->common.reg011_important_en.dec_clkgate_e = 1;
+ regs->common.reg011_important_en.dec_timeout_e = 1;
+ regs->common.reg011_important_en.pix_range_det_e = 1;
+
+ /*
+ * Even though the scan list address can be set in RPS,
+ * with some frames, it will try to use the address set in the register.
+ */
+ regs->common.reg012_secondary_en.scanlist_addr_valid_en = 1;
+
+ /* Set IDR flag */
+ regs->common.reg013_en_mode_set.cur_pic_is_idr =
+ !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC);
+
+ /* Set input stream length */
+ regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ /* Set max slice number */
+ regs->common.reg017_slice_number.slice_num = MAX_SLICE_NUMBER;
+
+ /* Set strides */
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16;
+ regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16;
+ regs->common.reg020_y_stride.y_virstride = y_virstride / 16;
+
+ /* Activate block gating */
+ regs->common.reg026_block_gating_en.inter_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0;
+ regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.intra_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.mc_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.transd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.sram_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.cru_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1;
+
+ /* Set timeout threshold */
+ pixels = dst_fmt->height * dst_fmt->width;
+ if (pixels < RKVDEC_1080P_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p;
+ else if (pixels < RKVDEC_4K_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K;
+ else if (pixels < RKVDEC_8K_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K;
+ else
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX;
+
+ /* Set TOP and BOTTOM POCs */
+ regs->h264_param.reg065_cur_top_poc = dec_params->top_field_order_cnt;
+ regs->h264_param.reg066_cur_bot_poc = dec_params->bottom_field_order_cnt;
+
+ /* Set ref pic address & poc */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct vb2_buffer *vb_buf = run->ref_buf[i];
+ dma_addr_t buf_dma;
+
+ /*
+ * If a DPB entry is unused or invalid, address of current destination
+ * buffer is returned.
+ */
+ if (!vb_buf)
+ vb_buf = &dst_buf->vb2_buf;
+
+ buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+
+ /* Set reference addresses */
+ regs->h264_addr.reg164_180_ref_base[i] = buf_dma;
+
+ /* Set COLMV addresses */
+ regs->h264_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset;
+
+ struct rkvdec_vdpu381_h264_ref_info *ref_info =
+ &regs->h264_param.reg099_102_ref_info_regs[i / 4].ref_info[i % 4];
+
+ ref_info->ref_field =
+ !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD);
+ ref_info->ref_colmv_use_flag =
+ !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE);
+ ref_info->ref_topfield_used =
+ !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF);
+ ref_info->ref_botfield_used =
+ !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF);
+
+ regs->h264_param.reg067_098_ref_poc[i * 2] =
+ dpb[i].top_field_order_cnt;
+ regs->h264_param.reg067_098_ref_poc[i * 2 + 1] =
+ dpb[i].bottom_field_order_cnt;
+ }
+
+ /* Set rlc base address (input stream) */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ regs->common_addr.rlc_base = rlc_addr;
+ regs->common_addr.rlcwrite_base = rlc_addr;
+
+ /* Set output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ regs->common_addr.decout_base = dst_addr;
+ regs->common_addr.error_ref_base = dst_addr;
+
+ /* Set colmv address */
+ regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset;
+
+ /* Set RCB addresses */
+ for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++)
+ regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i);
+
+ /* Set hw pps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
+ regs->h264_addr.reg161_pps_base = priv_start_addr + offset;
+
+ /* Set hw rps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
+ regs->h264_addr.reg163_rps_base = priv_start_addr + offset;
+
+ /* Set cabac table */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
+ regs->h264_addr.reg199_cabactbl_base = priv_start_addr + offset;
+
+ offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+ regs->h264_addr.reg181_scanlist_addr = priv_start_addr + offset;
+
+ rkvdec_write_regs(ctx);
+}
+
+static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_priv_tbl *priv_tbl;
+ struct rkvdec_h264_ctx *h264_ctx;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+ if (ret)
+ return ret;
+
+ h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL);
+ if (!h264_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &h264_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ h264_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
+ sizeof(rkvdec_h264_cabac_table));
+
+ ctx->priv = h264_ctx;
+ return 0;
+
+err_free_ctx:
+ kfree(h264_ctx);
+ return ret;
+}
+
+static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+ h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
+ kfree(h264_ctx);
+}
+
+static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
+{
+ struct v4l2_h264_reflist_builder reflist_builder;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
+ struct rkvdec_h264_run run;
+
+ rkvdec_h264_run_preamble(ctx, &run);
+
+ /* Build the P/B{0,1} ref lists. */
+ v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
+ run.sps, run.decode_params->dpb);
+ v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
+ v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
+ h264_ctx->reflists.b1);
+
+ assemble_hw_scaling_list(&run, &tbl->scaling_list);
+ assemble_hw_pps(ctx, &run);
+ lookup_ref_buf_idx(ctx, &run);
+ assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
+
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ rkvdec_schedule_watchdog(rkvdec, h264_ctx->regs.common.reg032_timeout_threshold);
+
+ /* Start decoding! */
+ writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
+
+ return 0;
+}
+
+static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
+ return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops = {
+ .adjust_fmt = rkvdec_h264_adjust_fmt,
+ .get_image_fmt = rkvdec_h264_get_image_fmt,
+ .start = rkvdec_h264_start,
+ .stop = rkvdec_h264_stop,
+ .run = rkvdec_h264_run,
+ .try_ctrl = rkvdec_h264_try_ctrl,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
new file mode 100644
index 000000000000..5f1c11ffb9f0
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip VDPU381 HEVC backend
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-rcb.h"
+#include "rkvdec-hevc-common.h"
+#include "rkvdec-vdpu381-regs.h"
+
+// SPS
+struct rkvdec_hevc_sps {
+ u16 video_parameters_set_id : 4;
+ u16 seq_parameters_set_id_sps : 4;
+ u16 chroma_format_idc : 2;
+ u16 width : 16;
+ u16 height : 16;
+ u16 bit_depth_luma : 4;
+ u16 bit_depth_chroma : 4;
+ u16 max_pic_order_count_lsb : 5;
+ u16 diff_max_min_luma_coding_block_size : 2;
+ u16 min_luma_coding_block_size : 3;
+ u16 min_transform_block_size : 3;
+ u16 diff_max_min_transform_block_size : 2;
+ u16 max_transform_hierarchy_depth_inter : 3;
+ u16 max_transform_hierarchy_depth_intra : 3;
+ u16 scaling_list_enabled_flag : 1;
+ u16 amp_enabled_flag : 1;
+ u16 sample_adaptive_offset_enabled_flag : 1;
+ u16 pcm_enabled_flag : 1;
+ u16 pcm_sample_bit_depth_luma : 4;
+ u16 pcm_sample_bit_depth_chroma : 4;
+ u16 pcm_loop_filter_disabled_flag : 1;
+ u16 diff_max_min_pcm_luma_coding_block_size : 3;
+ u16 min_pcm_luma_coding_block_size : 3;
+ u16 num_short_term_ref_pic_sets : 7;
+ u16 long_term_ref_pics_present_flag : 1;
+ u16 num_long_term_ref_pics_sps : 6;
+ u16 sps_temporal_mvp_enabled_flag : 1;
+ u16 strong_intra_smoothing_enabled_flag : 1;
+ u16 reserved_0 : 7;
+ u16 sps_max_dec_pic_buffering_minus1 : 4;
+ u16 reserved_0_2 : 3;
+ u16 reserved_f : 8;
+} __packed;
+
+//PPS
+struct rkvdec_hevc_pps {
+ u16 picture_parameters_set_id : 6;
+ u16 seq_parameters_set_id_pps : 4;
+ u16 dependent_slice_segments_enabled_flag : 1;
+ u16 output_flag_present_flag : 1;
+ u16 num_extra_slice_header_bits : 13;
+ u16 sign_data_hiding_enabled_flag : 1;
+ u16 cabac_init_present_flag : 1;
+ u16 num_ref_idx_l0_default_active : 4;
+ u16 num_ref_idx_l1_default_active : 4;
+ u16 init_qp_minus26 : 7;
+ u16 constrained_intra_pred_flag : 1;
+ u16 transform_skip_enabled_flag : 1;
+ u16 cu_qp_delta_enabled_flag : 1;
+ u16 log2_min_cb_size : 3;
+ u16 pps_cb_qp_offset : 5;
+ u16 pps_cr_qp_offset : 5;
+ u16 pps_slice_chroma_qp_offsets_present_flag : 1;
+ u16 weighted_pred_flag : 1;
+ u16 weighted_bipred_flag : 1;
+ u16 transquant_bypass_enabled_flag : 1;
+ u16 tiles_enabled_flag : 1;
+ u16 entropy_coding_sync_enabled_flag : 1;
+ u16 pps_loop_filter_across_slices_enabled_flag : 1;
+ u16 loop_filter_across_tiles_enabled_flag : 1;
+ u16 deblocking_filter_override_enabled_flag : 1;
+ u16 pps_deblocking_filter_disabled_flag : 1;
+ u16 pps_beta_offset_div2 : 4;
+ u16 pps_tc_offset_div2 : 4;
+ u16 lists_modification_present_flag : 1;
+ u16 log2_parallel_merge_level : 3;
+ u16 slice_segment_header_extension_present_flag : 1;
+ u16 zeroes : 3;
+ u16 num_tile_columns : 5;
+ u16 num_tile_rows : 5;
+ u16 sps_pps_mode : 4;
+ u16 reserved_bits : 14;
+ u16 reserved;
+} __packed;
+
+struct rkvdec_hevc_tile {
+ u16 value0 : 12;
+ u16 value1 : 12;
+} __packed;
+
+struct rkvdec_sps_pps_packet {
+ struct rkvdec_hevc_sps sps;
+ struct rkvdec_hevc_pps pps;
+ struct rkvdec_hevc_tile column_width[10];
+ struct rkvdec_hevc_tile row_height[11];
+ u32 zeroes[3];
+ u32 zeroes_bits : 6;
+ u32 padding_bits : 2;
+ u32 padding;
+} __packed;
+
+struct rkvdec_hevc_priv_tbl {
+ struct rkvdec_sps_pps_packet param_set[64];
+ struct rkvdec_rps rps;
+ struct scaling_factor scaling_list;
+ u8 cabac_table[27456];
+};
+
+struct rkvdec_hevc_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
+ struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache;
+ struct rkvdec_vdpu381_regs_hevc regs;
+};
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+ struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
+ struct rkvdec_sps_pps_packet *hw_ps;
+ bool tiles_enabled;
+ s32 max_cu_width;
+ s32 pic_in_cts_width;
+ s32 pic_in_cts_height;
+ u16 log2_min_cb_size, width, height;
+ u16 column_width[20];
+ u16 row_height[22];
+ u8 pcm_enabled;
+ u32 i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 32 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+ /* write sps */
+ hw_ps->sps.video_parameters_set_id = sps->video_parameter_set_id;
+ hw_ps->sps.seq_parameters_set_id_sps = sps->seq_parameter_set_id;
+ hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
+
+ log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ width = sps->pic_width_in_luma_samples;
+ height = sps->pic_height_in_luma_samples;
+ hw_ps->sps.width = width;
+ hw_ps->sps.height = height;
+ hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
+ hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
+ hw_ps->sps.max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
+ hw_ps->sps.diff_max_min_luma_coding_block_size =
+ sps->log2_diff_max_min_luma_coding_block_size;
+ hw_ps->sps.min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ hw_ps->sps.min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2;
+ hw_ps->sps.diff_max_min_transform_block_size =
+ sps->log2_diff_max_min_luma_transform_block_size;
+ hw_ps->sps.max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter;
+ hw_ps->sps.max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra;
+ hw_ps->sps.scaling_list_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED);
+ hw_ps->sps.amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED);
+ hw_ps->sps.sample_adaptive_offset_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET);
+
+ pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
+ hw_ps->sps.pcm_enabled_flag = pcm_enabled;
+ hw_ps->sps.pcm_sample_bit_depth_luma =
+ pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0;
+ hw_ps->sps.pcm_sample_bit_depth_chroma =
+ pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0;
+ hw_ps->sps.pcm_loop_filter_disabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED);
+ hw_ps->sps.diff_max_min_pcm_luma_coding_block_size =
+ sps->log2_diff_max_min_pcm_luma_coding_block_size;
+ hw_ps->sps.min_pcm_luma_coding_block_size =
+ pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0;
+ hw_ps->sps.num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
+ hw_ps->sps.long_term_ref_pics_present_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT);
+ hw_ps->sps.num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps;
+ hw_ps->sps.sps_temporal_mvp_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED);
+ hw_ps->sps.strong_intra_smoothing_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED);
+ hw_ps->sps.sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1;
+ hw_ps->sps.reserved_f = 0xff;
+
+ /* write pps */
+ hw_ps->pps.picture_parameters_set_id = pps->pic_parameter_set_id;
+ hw_ps->pps.seq_parameters_set_id_pps = sps->seq_parameter_set_id;
+ hw_ps->pps.dependent_slice_segments_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED);
+ hw_ps->pps.output_flag_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT);
+ hw_ps->pps.num_extra_slice_header_bits = pps->num_extra_slice_header_bits;
+ hw_ps->pps.sign_data_hiding_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED);
+ hw_ps->pps.cabac_init_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT);
+ hw_ps->pps.num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1;
+ hw_ps->pps.num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1;
+ hw_ps->pps.init_qp_minus26 = pps->init_qp_minus26;
+ hw_ps->pps.constrained_intra_pred_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+ hw_ps->pps.transform_skip_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED);
+ hw_ps->pps.cu_qp_delta_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED);
+ hw_ps->pps.log2_min_cb_size = log2_min_cb_size +
+ sps->log2_diff_max_min_luma_coding_block_size -
+ pps->diff_cu_qp_delta_depth;
+ hw_ps->pps.pps_cb_qp_offset = pps->pps_cb_qp_offset;
+ hw_ps->pps.pps_cr_qp_offset = pps->pps_cr_qp_offset;
+ hw_ps->pps.pps_slice_chroma_qp_offsets_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT);
+ hw_ps->pps.weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED);
+ hw_ps->pps.weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
+ hw_ps->pps.transquant_bypass_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED);
+
+ tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
+ hw_ps->pps.tiles_enabled_flag = tiles_enabled;
+ hw_ps->pps.entropy_coding_sync_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED);
+ hw_ps->pps.pps_loop_filter_across_slices_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED);
+ hw_ps->pps.loop_filter_across_tiles_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED);
+ hw_ps->pps.deblocking_filter_override_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED);
+ hw_ps->pps.pps_deblocking_filter_disabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER);
+ hw_ps->pps.pps_beta_offset_div2 = pps->pps_beta_offset_div2;
+ hw_ps->pps.pps_tc_offset_div2 = pps->pps_tc_offset_div2;
+ hw_ps->pps.lists_modification_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT);
+ hw_ps->pps.log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2;
+ hw_ps->pps.slice_segment_header_extension_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT);
+ hw_ps->pps.num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 0;
+ hw_ps->pps.num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 0;
+ hw_ps->pps.sps_pps_mode = 0;
+ hw_ps->pps.reserved_bits = 0x3fff;
+ hw_ps->pps.reserved = 0xffff;
+
+ // Setup tiles information
+ memset(column_width, 0, sizeof(column_width));
+ memset(row_height, 0, sizeof(row_height));
+
+ max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
+ pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
+ pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
+
+ if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
+ if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
+ compute_tiles_uniform(run, log2_min_cb_size, width, height,
+ pic_in_cts_width, pic_in_cts_height,
+ column_width, row_height);
+ } else {
+ compute_tiles_non_uniform(run, log2_min_cb_size, width, height,
+ pic_in_cts_width, pic_in_cts_height,
+ column_width, row_height);
+ }
+ } else {
+ column_width[0] = (width + max_cu_width - 1) / max_cu_width;
+ row_height[0] = (height + max_cu_width - 1) / max_cu_width;
+ }
+
+ for (i = 0; i < 20; i++) {
+ if (column_width[i] > 0)
+ column_width[i]--;
+
+ if (i & 1)
+ hw_ps->column_width[i / 2].value1 = column_width[i];
+ else
+ hw_ps->column_width[i / 2].value0 = column_width[i];
+ }
+
+ for (i = 0; i < 22; i++) {
+ if (row_height[i] > 0)
+ row_height[i]--;
+
+ if (i & 1)
+ hw_ps->row_height[i / 2].value1 = row_height[i];
+ else
+ hw_ps->row_height[i / 2].value0 = row_height[i];
+ }
+
+ hw_ps->padding = 0xffffffff;
+ hw_ps->padding_bits = 0x3;
+}
+
+static void set_ref_valid(struct rkvdec_vdpu381_regs_hevc *regs, int id, u32 valid)
+{
+ switch (id) {
+ case 0:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_0 = valid;
+ break;
+ case 1:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_1 = valid;
+ break;
+ case 2:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_2 = valid;
+ break;
+ case 3:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_3 = valid;
+ break;
+ case 4:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_4 = valid;
+ break;
+ case 5:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_5 = valid;
+ break;
+ case 6:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_6 = valid;
+ break;
+ case 7:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_7 = valid;
+ break;
+ case 8:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_8 = valid;
+ break;
+ case 9:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_9 = valid;
+ break;
+ case 10:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_10 = valid;
+ break;
+ case 11:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_11 = valid;
+ break;
+ case 12:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_12 = valid;
+ break;
+ case 13:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_13 = valid;
+ break;
+ case 14:
+ regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_14 = valid;
+ break;
+ }
+}
+
+static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
+ &hevc_ctx->regs.common,
+ sizeof(hevc_ctx->regs.common));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
+ &hevc_ctx->regs.hevc_param,
+ sizeof(hevc_ctx->regs.hevc_param));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
+ &hevc_ctx->regs.common_addr,
+ sizeof(hevc_ctx->regs.common_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
+ &hevc_ctx->regs.hevc_addr,
+ sizeof(hevc_ctx->regs.hevc_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
+ &hevc_ctx->regs.hevc_highpoc,
+ sizeof(hevc_ctx->regs.hevc_highpoc));
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
+ const struct v4l2_hevc_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_vdpu381_regs_hevc *regs = &hevc_ctx->regs;
+ dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ u32 hor_virstride = 0;
+ u32 ver_virstride = 0;
+ u32 y_virstride = 0;
+ u32 offset;
+ u32 pixels;
+ dma_addr_t dst_addr;
+ u32 i;
+
+ memset(regs, 0, sizeof(*regs));
+
+ /* Set HEVC mode */
+ regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_HEVC;
+
+ /* Set config */
+ regs->common.reg011_important_en.buf_empty_en = 1;
+ regs->common.reg011_important_en.dec_clkgate_e = 1;
+ regs->common.reg011_important_en.dec_timeout_e = 1;
+ regs->common.reg011_important_en.pix_range_det_e = 1;
+
+ /* Set IDR flag */
+ regs->common.reg013_en_mode_set.cur_pic_is_idr =
+ !!(dec_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC);
+
+ /* Set input stream length */
+ regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ /* Set max slice number */
+ regs->common.reg017_slice_number.slice_num = 1;
+
+ /* Set strides */
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16;
+ regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16;
+ regs->common.reg020_y_stride.y_virstride = y_virstride / 16;
+
+ /* Activate block gating */
+ regs->common.reg026_block_gating_en.inter_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0;
+ regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.intra_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.mc_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.transd_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.sram_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.cru_auto_gating_e = 1;
+ regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1;
+
+ /* Set timeout threshold */
+ pixels = dst_fmt->height * dst_fmt->width;
+ if (pixels < RKVDEC_1080P_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p;
+ else if (pixels < RKVDEC_4K_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K;
+ else if (pixels < RKVDEC_8K_PIXELS)
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K;
+ else
+ regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX;
+
+ /* Set POC val */
+ regs->hevc_param.reg065_cur_top_poc = dec_params->pic_order_cnt_val;
+
+ /* Set ref pic address & poc */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
+ dma_addr_t buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+ u32 valid = !!(dec_params->num_active_dpb_entries > i);
+
+ /* Set reference addresses */
+ regs->hevc_addr.reg164_180_ref_base[i] = buf_dma;
+
+ /* Set COLMV addresses */
+ regs->hevc_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset;
+
+ regs->hevc_param.reg067_082_ref_poc[i] =
+ dpb[i].pic_order_cnt_val;
+
+ set_ref_valid(regs, i, valid);
+ regs->hevc_param.reg103_hevc_mvc0.ref_pic_layer_same_with_cur |= 1 << i;
+ }
+
+ /* Set rlc base address (input stream) */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ regs->common_addr.rlc_base = rlc_addr;
+ regs->common_addr.rlcwrite_base = rlc_addr;
+
+ /* Set output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ regs->common_addr.decout_base = dst_addr;
+ regs->common_addr.error_ref_base = dst_addr;
+
+ /* Set colmv address */
+ regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset;
+
+ /* Set RCB addresses */
+ for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++)
+ regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i);
+
+ /* Set hw pps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
+ regs->hevc_addr.reg161_pps_base = priv_start_addr + offset;
+
+ /* Set hw rps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
+ regs->hevc_addr.reg163_rps_base = priv_start_addr + offset;
+
+ /* Set cabac table */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
+ regs->hevc_addr.reg199_cabactbl_base = priv_start_addr + offset;
+
+ /* Set scaling matrix */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
+ regs->hevc_addr.reg181_scanlist_addr = priv_start_addr + offset;
+
+ rkvdec_write_regs(ctx);
+}
+
+static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_hevc_sps *sps)
+{
+ if (sps->chroma_format_idc != 1)
+ /* Only 4:2:0 is supported */
+ return -EINVAL;
+
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit are supported */
+ return -EINVAL;
+
+ if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
+ sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_priv_tbl *priv_tbl;
+ struct rkvdec_hevc_ctx *hevc_ctx;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
+ if (ret)
+ return ret;
+
+ hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL);
+ if (!hevc_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ hevc_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
+ sizeof(rkvdec_hevc_cabac_table));
+
+ ctx->priv = hevc_ctx;
+ return 0;
+
+err_free_ctx:
+ kfree(hevc_ctx);
+ return ret;
+}
+
+static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+ hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
+ kfree(hevc_ctx);
+}
+
+static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_run run;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
+
+ rkvdec_hevc_run_preamble(ctx, &run);
+
+ rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
+ &hevc_ctx->scaling_matrix_cache);
+ assemble_hw_pps(ctx, &run);
+
+ /*
+ * On vdpu381, not setting the long and short term ref sets will just output wrong frames.
+ * Let's just warn about it and let the decoder run anyway.
+ */
+ if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
+ (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
+ dev_warn_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
+ } else {
+ rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache);
+ }
+
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ rkvdec_schedule_watchdog(rkvdec, hevc_ctx->regs.common.reg032_timeout_threshold);
+
+ /* Start decoding! */
+ writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
+
+ return 0;
+}
+
+static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
+ return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops = {
+ .adjust_fmt = rkvdec_hevc_adjust_fmt,
+ .start = rkvdec_hevc_start,
+ .stop = rkvdec_hevc_stop,
+ .run = rkvdec_hevc_run,
+ .try_ctrl = rkvdec_hevc_try_ctrl,
+ .get_image_fmt = rkvdec_hevc_get_image_fmt,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h
new file mode 100644
index 000000000000..6da36031df2d
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h
@@ -0,0 +1,430 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip VDPU381 Video Decoder driver registers description
+ *
+ * Copyright (C) 2024 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <linux/types.h>
+
+#ifndef _RKVDEC_REGS_H_
+#define _RKVDEC_REGS_H_
+
+#define OFFSET_COMMON_REGS (8 * sizeof(u32))
+#define OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32))
+#define OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32))
+#define OFFSET_CODEC_ADDR_REGS (160 * sizeof(u32))
+#define OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32))
+
+#define VDPU381_MODE_HEVC 0
+#define VDPU381_MODE_H264 1
+#define VDPU381_MODE_VP9 2
+#define VDPU381_MODE_AVS2 3
+
+#define MAX_SLICE_NUMBER 0x3fff
+
+#define RKVDEC_TIMEOUT_1080p (0xefffff)
+#define RKVDEC_TIMEOUT_4K (0x2cfffff)
+#define RKVDEC_TIMEOUT_8K (0x4ffffff)
+#define RKVDEC_TIMEOUT_MAX (0xffffffff)
+
+#define VDPU381_REG_DEC_E 0x028
+#define VDPU381_DEC_E_BIT 1
+
+#define VDPU381_REG_IMPORTANT_EN 0x02c
+#define VDPU381_DEC_IRQ_DISABLE BIT(4)
+
+#define VDPU381_REG_STA_INT 0x380
+#define VDPU381_STA_INT_DEC_RDY_STA BIT(2)
+#define VDPU381_STA_INT_ERROR BIT(4)
+#define VDPU381_STA_INT_TIMEOUT BIT(5)
+#define VDPU381_STA_INT_SOFTRESET_RDY BIT(9)
+
+/* base: OFFSET_COMMON_REGS */
+struct rkvdec_vdpu381_regs_common {
+ struct {
+ u32 in_endian : 1;
+ u32 in_swap32_e : 1;
+ u32 in_swap64_e : 1;
+ u32 str_endian : 1;
+ u32 str_swap32_e : 1;
+ u32 str_swap64_e : 1;
+ u32 out_endian : 1;
+ u32 out_swap32_e : 1;
+ u32 out_cbcr_swap : 1;
+ u32 out_swap64_e : 1;
+ u32 reserved : 22;
+ } reg008_in_out;
+
+ struct {
+ u32 dec_mode : 10;
+ u32 reserved : 22;
+ } reg009_dec_mode;
+
+ struct {
+ u32 dec_e : 1;
+ u32 reserved : 31;
+ } reg010_dec_e;
+
+ struct {
+ u32 reserved0 : 1;
+ u32 dec_clkgate_e : 1;
+ u32 dec_e_strmd_clkgate_dis : 1;
+ u32 reserved1 : 1;
+
+ u32 dec_irq_dis : 1;
+ u32 dec_timeout_e : 1;
+ u32 buf_empty_en : 1;
+ u32 reserved2 : 3;
+
+ u32 dec_e_rewrite_valid : 1;
+ u32 reserved3 : 9;
+ u32 softrst_en_p : 1;
+ u32 force_softreset_valid : 1;
+ u32 reserved4 : 2;
+ u32 pix_range_det_e : 1;
+ u32 reserved5 : 7;
+ } reg011_important_en;
+
+ struct {
+ u32 reserved0 : 1;
+ u32 colmv_compress_en : 1;
+ u32 fbc_e : 1;
+ u32 reserved1 : 1;
+
+ u32 buspr_slot_disable : 1;
+ u32 error_info_en : 1;
+ u32 collect_info_en : 1;
+ u32 error_auto_rst_disable : 1;
+
+ u32 scanlist_addr_valid_en : 1;
+ u32 scale_down_en : 1;
+ u32 error_cfg_wr_disable : 1;
+ u32 reserved2 : 21;
+ } reg012_secondary_en;
+
+ struct {
+ u32 reserved0 : 1;
+ u32 req_timeout_rst_sel : 1;
+ u32 reserved1 : 1;
+ u32 dec_commonirq_mode : 1;
+ u32 reserved2 : 2;
+ u32 stmerror_waitdecfifo_empty : 1;
+ u32 reserved3 : 5;
+ u32 allow_not_wr_unref_bframe : 1;
+ u32 fbc_output_wr_disable : 1;
+ u32 reserved4 : 4;
+ u32 error_mode : 1;
+ u32 reserved5 : 2;
+ u32 ycacherd_prior : 1;
+ u32 reserved6 : 2;
+ u32 cur_pic_is_idr : 1;
+ u32 reserved7 : 1;
+ u32 right_auto_rst_disable : 1;
+ u32 frame_end_err_rst_flag : 1;
+ u32 rd_prior_mode : 1;
+ u32 rd_ctrl_prior_mode : 1;
+ u32 reserved8 : 1;
+ u32 filter_outbuf_mode : 1;
+ } reg013_en_mode_set;
+
+ struct {
+ u32 fbc_force_uncompress : 1;
+
+ u32 reserved0 : 2;
+ u32 allow_16x8_cp_flag : 1;
+ u32 reserved1 : 2;
+
+ u32 fbc_h264_exten_4or8_flag : 1;
+ u32 reserved2 : 25;
+ } reg014_fbc_param_set;
+
+ struct {
+ u32 rlc_mode_direct_write : 1;
+ u32 rlc_mode : 1;
+ u32 reserved0 : 3;
+
+ u32 strm_start_bit : 7;
+ u32 reserved1 : 20;
+ } reg015_stream_param_set;
+
+ u32 reg016_stream_len;
+
+ struct {
+ u32 slice_num : 25;
+ u32 reserved : 7;
+ } reg017_slice_number;
+
+ struct {
+ u32 y_hor_virstride : 16;
+ u32 reserved : 16;
+ } reg018_y_hor_stride;
+
+ struct {
+ u32 uv_hor_virstride : 16;
+ u32 reserved : 16;
+ } reg019_uv_hor_stride;
+
+ struct {
+ u32 y_virstride : 28;
+ u32 reserved : 4;
+ } reg020_y_stride;
+
+ struct {
+ u32 inter_error_prc_mode : 1;
+ u32 error_intra_mode : 1;
+ u32 error_deb_en : 1;
+ u32 picidx_replace : 5;
+ u32 error_spread_e : 1;
+ u32 reserved0 : 3;
+ u32 error_inter_pred_cross_slice : 1;
+ u32 reserved1 : 11;
+ u32 roi_error_ctu_cal_en : 1;
+ u32 reserved2 : 7;
+ } reg021_error_ctrl_set;
+
+ struct {
+ u32 roi_x_ctu_offset_st : 12;
+ u32 reserved0 : 4;
+ u32 roi_y_ctu_offset_st : 12;
+ u32 reserved1 : 4;
+ } reg022_err_roi_ctu_offset_start;
+
+ struct {
+ u32 roi_x_ctu_offset_end : 12;
+ u32 reserved0 : 4;
+ u32 roi_y_ctu_offset_end : 12;
+ u32 reserved1 : 4;
+ } reg023_err_roi_ctu_offset_end;
+
+ struct {
+ u32 cabac_err_en_lowbits : 32;
+ } reg024_cabac_error_en_lowbits;
+
+ struct {
+ u32 cabac_err_en_highbits : 30;
+ u32 reserved : 2;
+ } reg025_cabac_error_en_highbits;
+
+ struct {
+ u32 inter_auto_gating_e : 1;
+ u32 filterd_auto_gating_e : 1;
+ u32 strmd_auto_gating_e : 1;
+ u32 mcp_auto_gating_e : 1;
+ u32 busifd_auto_gating_e : 1;
+ u32 reserved0 : 3;
+ u32 dec_ctrl_auto_gating_e : 1;
+ u32 intra_auto_gating_e : 1;
+ u32 mc_auto_gating_e : 1;
+ u32 transd_auto_gating_e : 1;
+ u32 reserved1 : 4;
+ u32 sram_auto_gating_e : 1;
+ u32 cru_auto_gating_e : 1;
+ u32 reserved2 : 13;
+ u32 reg_cfg_gating_en : 1;
+ } reg026_block_gating_en;
+
+ struct {
+ u32 core_safe_x_pixels : 16;
+ u32 core_safe_y_pixels : 16;
+ } reg027_core_safe_pixels;
+
+ struct {
+ u32 vp9_wr_prob_idx : 3;
+ u32 reserved0 : 1;
+ u32 vp9_rd_prob_idx : 3;
+ u32 reserved1 : 1;
+
+ u32 ref_req_advance_flag : 1;
+ u32 colmv_req_advance_flag : 1;
+ u32 poc_only_highbit_flag : 1;
+ u32 poc_arb_flag : 1;
+
+ u32 reserved2 : 4;
+ u32 film_idx : 10;
+ u32 reserved3 : 2;
+ u32 pu_req_mismatch_dis : 1;
+ u32 colmv_req_mismatch_dis : 1;
+ u32 reserved4 : 2;
+ } reg028_multiply_core_ctrl;
+
+ struct {
+ u32 scale_down_hor_ratio : 2;
+ u32 reserved0 : 6;
+ u32 scale_down_vrz_ratio : 2;
+ u32 reserved1 : 22;
+ } reg029_scale_down_ctrl;
+
+ struct {
+ u32 y_scale_down_tile8x8_hor_stride : 20;
+ u32 reserved0 : 12;
+ } reg030_y_scale_down_tile8x8_hor_stride;
+
+ struct {
+ u32 uv_scale_down8x8_tile_hor_stride : 20;
+ u32 reserved0 : 12;
+ } reg031_uv_scale_down_tile8x8_hor_stride;
+
+ u32 reg032_timeout_threshold;
+} __packed;
+
+/* base: OFFSET_COMMON_ADDR_REGS */
+struct rkvdec_vdpu381_regs_common_addr {
+ u32 rlc_base;
+ u32 rlcwrite_base;
+ u32 decout_base;
+ u32 colmv_cur_base;
+ u32 error_ref_base;
+ u32 rcb_base[10];
+} __packed;
+
+struct rkvdec_vdpu381_h26x_set {
+ u32 h26x_frame_orslice : 1;
+ u32 h26x_rps_mode : 1;
+ u32 h26x_stream_mode : 1;
+ u32 h26x_stream_lastpacket : 1;
+ u32 h264_firstslice_flag : 1;
+ u32 reserved : 27;
+} __packed;
+
+/* base: OFFSET_CODEC_PARAMS_REGS */
+struct rkvdec_vdpu381_regs_h264_params {
+ struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
+
+ u32 reg065_cur_top_poc;
+ u32 reg066_cur_bot_poc;
+ u32 reg067_098_ref_poc[32];
+
+ struct rkvdec_vdpu381_h264_info {
+ struct rkvdec_vdpu381_h264_ref_info {
+ u32 ref_field : 1;
+ u32 ref_topfield_used : 1;
+ u32 ref_botfield_used : 1;
+ u32 ref_colmv_use_flag : 1;
+ u32 reserved : 4;
+ } __packed ref_info[4];
+ } __packed reg099_102_ref_info_regs[4];
+
+ u32 reserved_103_111[9];
+
+ struct {
+ u32 avs2_ref_error_field : 1;
+ u32 avs2_ref_error_topfield : 1;
+ u32 ref_error_topfield_used : 1;
+ u32 ref_error_botfield_used : 1;
+ u32 reserved : 28;
+ } reg112_error_ref_info;
+} __packed;
+
+struct rkvdec_vdpu381_regs_hevc_params {
+ struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
+
+ u32 reg065_cur_top_poc;
+ u32 reg066_cur_bot_poc;
+ u32 reg067_082_ref_poc[16];
+
+ u32 reserved_083_098[16];
+
+ struct {
+ u32 hevc_ref_valid_0 : 1;
+ u32 hevc_ref_valid_1 : 1;
+ u32 hevc_ref_valid_2 : 1;
+ u32 hevc_ref_valid_3 : 1;
+ u32 reserve0 : 4;
+ u32 hevc_ref_valid_4 : 1;
+ u32 hevc_ref_valid_5 : 1;
+ u32 hevc_ref_valid_6 : 1;
+ u32 hevc_ref_valid_7 : 1;
+ u32 reserve1 : 4;
+ u32 hevc_ref_valid_8 : 1;
+ u32 hevc_ref_valid_9 : 1;
+ u32 hevc_ref_valid_10 : 1;
+ u32 hevc_ref_valid_11 : 1;
+ u32 reserve2 : 4;
+ u32 hevc_ref_valid_12 : 1;
+ u32 hevc_ref_valid_13 : 1;
+ u32 hevc_ref_valid_14 : 1;
+ u32 reserve3 : 5;
+ } reg099_hevc_ref_valid;
+
+ u32 reserved_100_102[3];
+
+ struct {
+ u32 ref_pic_layer_same_with_cur : 16;
+ u32 reserve : 16;
+ } reg103_hevc_mvc0;
+
+ struct {
+ u32 poc_lsb_not_present_flag : 1;
+ u32 num_direct_ref_layers : 6;
+ u32 reserve0 : 1;
+
+ u32 num_reflayer_pics : 6;
+ u32 default_ref_layers_active_flag : 1;
+ u32 max_one_active_ref_layer_flag : 1;
+
+ u32 poc_reset_info_present_flag : 1;
+ u32 vps_poc_lsb_aligned_flag : 1;
+ u32 mvc_poc15_valid_flag : 1;
+ u32 reserve1 : 13;
+ } reg104_hevc_mvc1;
+
+ u32 reserved_105_111[7];
+
+ struct {
+ u32 avs2_ref_error_field : 1;
+ u32 avs2_ref_error_topfield : 1;
+ u32 ref_error_topfield_used : 1;
+ u32 ref_error_botfield_used : 1;
+ u32 reserve : 28;
+ } reg112_hevc_ref_info;
+
+} __packed;
+
+/* base: OFFSET_CODEC_ADDR_REGS */
+struct rkvdec_vdpu381_regs_h26x_addr {
+ u32 reserved_160;
+ u32 reg161_pps_base;
+ u32 reserved_162;
+ u32 reg163_rps_base;
+ u32 reg164_180_ref_base[16];
+ u32 reg181_scanlist_addr;
+ u32 reg182_198_colmv_base[16];
+ u32 reg199_cabactbl_base;
+} __packed;
+
+struct rkvdec_vdpu381_regs_h26x_highpoc {
+ struct {
+ u32 ref0_poc_highbit : 4;
+ u32 ref1_poc_highbit : 4;
+ u32 ref2_poc_highbit : 4;
+ u32 ref3_poc_highbit : 4;
+ u32 ref4_poc_highbit : 4;
+ u32 ref5_poc_highbit : 4;
+ u32 ref6_poc_highbit : 4;
+ u32 ref7_poc_highbit : 4;
+ } reg200_203_ref_poc_highbit[4];
+ struct {
+ u32 cur_poc_highbit : 4;
+ u32 reserved : 28;
+ } reg204_cur_poc_highbit;
+} __packed;
+
+struct rkvdec_vdpu381_regs_h264 {
+ struct rkvdec_vdpu381_regs_common common;
+ struct rkvdec_vdpu381_regs_h264_params h264_param;
+ struct rkvdec_vdpu381_regs_common_addr common_addr;
+ struct rkvdec_vdpu381_regs_h26x_addr h264_addr;
+ struct rkvdec_vdpu381_regs_h26x_highpoc h264_highpoc;
+} __packed;
+
+struct rkvdec_vdpu381_regs_hevc {
+ struct rkvdec_vdpu381_regs_common common;
+ struct rkvdec_vdpu381_regs_hevc_params hevc_param;
+ struct rkvdec_vdpu381_regs_common_addr common_addr;
+ struct rkvdec_vdpu381_regs_h26x_addr hevc_addr;
+ struct rkvdec_vdpu381_regs_h26x_highpoc hevc_highpoc;
+} __packed;
+
+#endif /* __RKVDEC_REGS_H__ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
new file mode 100644
index 000000000000..6ab3167addc8
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder VDPU383 H264 backend
+ *
+ * Copyright (C) 2024 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include <linux/iopoll.h>
+
+#include "rkvdec-rcb.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-vdpu383-regs.h"
+#include "rkvdec-h264-common.h"
+
+struct rkvdec_sps {
+ u16 seq_parameter_set_id: 4;
+ u16 profile_idc: 8;
+ u16 constraint_set3_flag: 1;
+ u16 chroma_format_idc: 2;
+ u16 bit_depth_luma: 3;
+ u16 bit_depth_chroma: 3;
+ u16 qpprime_y_zero_transform_bypass_flag: 1;
+ u16 log2_max_frame_num_minus4: 4;
+ u16 max_num_ref_frames: 5;
+ u16 pic_order_cnt_type: 2;
+ u16 log2_max_pic_order_cnt_lsb_minus4: 4;
+ u16 delta_pic_order_always_zero_flag: 1;
+
+ u16 pic_width_in_mbs: 16;
+ u16 pic_height_in_mbs: 16;
+
+ u16 frame_mbs_only_flag: 1;
+ u16 mb_adaptive_frame_field_flag: 1;
+ u16 direct_8x8_inference_flag: 1;
+ u16 mvc_extension_enable: 1;
+ u16 num_views: 2;
+ u16 view_id0: 10;
+ u16 view_id1: 10;
+} __packed;
+
+struct rkvdec_pps {
+ u32 pic_parameter_set_id: 8;
+ u32 pps_seq_parameter_set_id: 5;
+ u32 entropy_coding_mode_flag: 1;
+ u32 bottom_field_pic_order_in_frame_present_flag: 1;
+ u32 num_ref_idx_l0_default_active_minus1: 5;
+ u32 num_ref_idx_l1_default_active_minus1: 5;
+ u32 weighted_pred_flag: 1;
+ u32 weighted_bipred_idc: 2;
+ u32 pic_init_qp_minus26: 7;
+ u32 pic_init_qs_minus26: 6;
+ u32 chroma_qp_index_offset: 5;
+ u32 deblocking_filter_control_present_flag: 1;
+ u32 constrained_intra_pred_flag: 1;
+ u32 redundant_pic_cnt_present: 1;
+ u32 transform_8x8_mode_flag: 1;
+ u32 second_chroma_qp_index_offset: 5;
+ u32 scaling_list_enable_flag: 1;
+ u32 is_longterm: 16;
+ u32 voidx: 16;
+
+ // dpb
+ u32 pic_field_flag: 1;
+ u32 pic_associated_flag: 1;
+ u32 cur_top_field: 32;
+ u32 cur_bot_field: 32;
+
+ u32 top_field_order_cnt0: 32;
+ u32 bot_field_order_cnt0: 32;
+ u32 top_field_order_cnt1: 32;
+ u32 bot_field_order_cnt1: 32;
+ u32 top_field_order_cnt2: 32;
+ u32 bot_field_order_cnt2: 32;
+ u32 top_field_order_cnt3: 32;
+ u32 bot_field_order_cnt3: 32;
+ u32 top_field_order_cnt4: 32;
+ u32 bot_field_order_cnt4: 32;
+ u32 top_field_order_cnt5: 32;
+ u32 bot_field_order_cnt5: 32;
+ u32 top_field_order_cnt6: 32;
+ u32 bot_field_order_cnt6: 32;
+ u32 top_field_order_cnt7: 32;
+ u32 bot_field_order_cnt7: 32;
+ u32 top_field_order_cnt8: 32;
+ u32 bot_field_order_cnt8: 32;
+ u32 top_field_order_cnt9: 32;
+ u32 bot_field_order_cnt9: 32;
+ u32 top_field_order_cnt10: 32;
+ u32 bot_field_order_cnt10: 32;
+ u32 top_field_order_cnt11: 32;
+ u32 bot_field_order_cnt11: 32;
+ u32 top_field_order_cnt12: 32;
+ u32 bot_field_order_cnt12: 32;
+ u32 top_field_order_cnt13: 32;
+ u32 bot_field_order_cnt13: 32;
+ u32 top_field_order_cnt14: 32;
+ u32 bot_field_order_cnt14: 32;
+ u32 top_field_order_cnt15: 32;
+ u32 bot_field_order_cnt15: 32;
+
+ u32 ref_field_flags: 16;
+ u32 ref_topfield_used: 16;
+ u32 ref_botfield_used: 16;
+ u32 ref_colmv_use_flag: 16;
+
+ u32 reserved0: 30;
+ u32 reserved[3];
+} __packed;
+
+struct rkvdec_sps_pps {
+ struct rkvdec_sps sps;
+ struct rkvdec_pps pps;
+} __packed;
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_h264_priv_tbl {
+ s8 cabac_table[4][464][2];
+ struct rkvdec_h264_scaling_list scaling_list;
+ struct rkvdec_sps_pps param_set[256];
+ struct rkvdec_rps rps;
+} __packed;
+
+struct rkvdec_h264_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct rkvdec_h264_reflists reflists;
+ struct vdpu383_regs_h26x regs;
+};
+
+static void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb)
+{
+ pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt;
+ pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt;
+ pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt;
+ pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt;
+ pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt;
+ pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt;
+ pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt;
+ pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt;
+ pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt;
+ pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt;
+ pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt;
+ pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt;
+ pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt;
+ pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt;
+ pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt;
+ pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt;
+ pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt;
+ pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt;
+ pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt;
+ pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt;
+ pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt;
+ pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt;
+ pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt;
+ pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt;
+ pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt;
+ pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt;
+ pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt;
+ pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt;
+ pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt;
+ pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt;
+ pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt;
+ pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt;
+}
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ const struct v4l2_ctrl_h264_sps *sps = run->sps;
+ const struct v4l2_ctrl_h264_pps *pps = run->pps;
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+ struct rkvdec_sps_pps *hw_ps;
+ u32 pic_width, pic_height;
+ u32 i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 32 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+ /* write sps */
+ hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id;
+ hw_ps->sps.profile_idc = sps->profile_idc;
+ hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3));
+ hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
+ hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8;
+ hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8;
+ hw_ps->sps.qpprime_y_zero_transform_bypass_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
+ hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
+ hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames;
+ hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type;
+ hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 =
+ sps->log2_max_pic_order_cnt_lsb_minus4;
+ hw_ps->sps.delta_pic_order_always_zero_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
+ hw_ps->sps.mvc_extension_enable = 0;
+ hw_ps->sps.num_views = 0;
+
+ /*
+ * Use the SPS values since they are already in macroblocks
+ * dimensions, height can be field height (halved) if
+ * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
+ * decoding smaller images into larger allocation which can be used
+ * to implementing SVC spatial layer support.
+ */
+ pic_width = 16 * (sps->pic_width_in_mbs_minus1 + 1);
+ pic_height = 16 * (sps->pic_height_in_map_units_minus1 + 1);
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
+ pic_height *= 2;
+ if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC))
+ pic_height /= 2;
+
+ hw_ps->sps.pic_width_in_mbs = pic_width;
+ hw_ps->sps.pic_height_in_mbs = pic_height;
+
+ hw_ps->sps.frame_mbs_only_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
+ hw_ps->sps.mb_adaptive_frame_field_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
+ hw_ps->sps.direct_8x8_inference_flag =
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
+
+ /* write pps */
+ hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id;
+ hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id;
+ hw_ps->pps.entropy_coding_mode_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
+ hw_ps->pps.bottom_field_pic_order_in_frame_present_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
+ hw_ps->pps.num_ref_idx_l0_default_active_minus1 =
+ pps->num_ref_idx_l0_default_active_minus1;
+ hw_ps->pps.num_ref_idx_l1_default_active_minus1 =
+ pps->num_ref_idx_l1_default_active_minus1;
+ hw_ps->pps.weighted_pred_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
+ hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc;
+ hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26;
+ hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26;
+ hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset;
+ hw_ps->pps.deblocking_filter_control_present_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
+ hw_ps->pps.constrained_intra_pred_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+ hw_ps->pps.redundant_pic_cnt_present =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
+ hw_ps->pps.transform_8x8_mode_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
+ hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
+ hw_ps->pps.scaling_list_enable_flag =
+ !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT);
+
+ set_field_order_cnt(&hw_ps->pps, dpb);
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ hw_ps->pps.is_longterm |= (1 << i);
+
+ hw_ps->pps.ref_field_flags |=
+ (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i;
+ hw_ps->pps.ref_colmv_use_flag |=
+ (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i;
+ hw_ps->pps.ref_topfield_used |=
+ (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i;
+ hw_ps->pps.ref_botfield_used |=
+ (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i;
+ }
+
+ hw_ps->pps.pic_field_flag =
+ !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC);
+ hw_ps->pps.pic_associated_flag =
+ !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD);
+
+ hw_ps->pps.cur_top_field = dec_params->top_field_order_cnt;
+ hw_ps->pps.cur_bot_field = dec_params->bottom_field_order_cnt;
+}
+
+static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
+ &h264_ctx->regs.common,
+ sizeof(h264_ctx->regs.common));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
+ &h264_ctx->regs.common_addr,
+ sizeof(h264_ctx->regs.common_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
+ &h264_ctx->regs.h26x_params,
+ sizeof(h264_ctx->regs.h26x_params));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
+ &h264_ctx->regs.h26x_addr,
+ sizeof(h264_ctx->regs.h26x_addr));
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ struct vdpu383_regs_h26x *regs = &h264_ctx->regs;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ dma_addr_t dst_addr;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
+ u32 offset;
+ u32 pixels;
+ u32 i;
+
+ memset(regs, 0, sizeof(*regs));
+
+ /* Set H264 mode */
+ regs->common.reg008_dec_mode = VDPU383_MODE_H264;
+
+ /* Set input stream length */
+ regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ /* Set strides */
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ pixels = dst_fmt->height * dst_fmt->width;
+
+ regs->h26x_params.reg068_hor_virstride = hor_virstride / 16;
+ regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16;
+ regs->h26x_params.reg070_y_virstride = y_virstride / 16;
+
+ /* Activate block gating */
+ regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.inter_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.intra_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.transd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.recon_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.bus_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1;
+
+ /* Set timeout threshold */
+ if (pixels < RKVDEC_1080P_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p;
+ else if (pixels < RKVDEC_4K_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K;
+ else if (pixels < RKVDEC_8K_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K;
+ else
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX;
+
+ regs->common.reg016_error_ctrl_set.error_proc_disable = 1;
+
+ /* Set ref pic address & poc */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct vb2_buffer *vb_buf = run->ref_buf[i];
+ dma_addr_t buf_dma;
+
+ /*
+ * If a DPB entry is unused or invalid, address of current destination
+ * buffer is returned.
+ */
+ if (!vb_buf)
+ vb_buf = &dst_buf->vb2_buf;
+
+ buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+
+ /* Set reference addresses */
+ regs->h26x_addr.reg170_185_ref_base[i] = buf_dma;
+ regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma;
+
+ /* Set COLMV addresses */
+ regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset;
+ }
+
+ /* Set rlc base address (input stream) */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ regs->common_addr.reg128_strm_base = rlc_addr;
+
+ /* Set output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ regs->h26x_addr.reg168_decout_base = dst_addr;
+ regs->h26x_addr.reg169_error_ref_base = dst_addr;
+ regs->h26x_addr.reg192_payload_st_cur_base = dst_addr;
+
+ /* Set colmv address */
+ regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset;
+
+ /* Set RCB addresses */
+ for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) {
+ regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i);
+ regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i);
+ }
+
+ /* Set hw pps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
+ regs->common_addr.reg131_gbl_base = priv_start_addr + offset;
+ regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_sps_pps) / 16;
+
+ /* Set hw rps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
+ regs->common_addr.reg129_rps_base = priv_start_addr + offset;
+
+ /* Set cabac table */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
+ regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset;
+
+ /* Set scaling list address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+ regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset;
+
+ rkvdec_write_regs(ctx);
+}
+
+static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_priv_tbl *priv_tbl;
+ struct rkvdec_h264_ctx *h264_ctx;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+ if (ret)
+ return ret;
+
+ h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL);
+ if (!h264_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &h264_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ h264_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
+ sizeof(rkvdec_h264_cabac_table));
+
+ ctx->priv = h264_ctx;
+
+ return 0;
+
+err_free_ctx:
+ kfree(h264_ctx);
+ return ret;
+}
+
+static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+ h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
+ kfree(h264_ctx);
+}
+
+static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
+{
+ struct v4l2_h264_reflist_builder reflist_builder;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_h264_run run;
+ struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
+ u32 timeout_threshold;
+
+ rkvdec_h264_run_preamble(ctx, &run);
+
+ /* Build the P/B{0,1} ref lists. */
+ v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
+ run.sps, run.decode_params->dpb);
+ v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
+ v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
+ h264_ctx->reflists.b1);
+
+ assemble_hw_scaling_list(&run, &tbl->scaling_list);
+ assemble_hw_pps(ctx, &run);
+ lookup_ref_buf_idx(ctx, &run);
+ assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
+
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ timeout_threshold = h264_ctx->regs.common.reg013_core_timeout_threshold;
+ rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
+
+ /* Start decoding! */
+ writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
+ writel(0, rkvdec->link + VDPU383_LINK_IP_ENABLE);
+ writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
+
+ return 0;
+}
+
+static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
+ return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops = {
+ .adjust_fmt = rkvdec_h264_adjust_fmt,
+ .get_image_fmt = rkvdec_h264_get_image_fmt,
+ .start = rkvdec_h264_start,
+ .stop = rkvdec_h264_stop,
+ .run = rkvdec_h264_run,
+ .try_ctrl = rkvdec_h264_try_ctrl,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
new file mode 100644
index 000000000000..085c0a63a80c
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip VDPU383 HEVC backend
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-rcb.h"
+#include "rkvdec-hevc-common.h"
+#include "rkvdec-vdpu383-regs.h"
+
+struct rkvdec_hevc_sps_pps {
+ // SPS
+ u16 video_parameters_set_id : 4;
+ u16 seq_parameters_set_id_sps : 4;
+ u16 chroma_format_idc : 2;
+ u16 width : 16;
+ u16 height : 16;
+ u16 bit_depth_luma : 3;
+ u16 bit_depth_chroma : 3;
+ u16 max_pic_order_count_lsb : 5;
+ u16 diff_max_min_luma_coding_block_size : 2;
+ u16 min_luma_coding_block_size : 3;
+ u16 min_transform_block_size : 3;
+ u16 diff_max_min_transform_block_size : 2;
+ u16 max_transform_hierarchy_depth_inter : 3;
+ u16 max_transform_hierarchy_depth_intra : 3;
+ u16 scaling_list_enabled_flag : 1;
+ u16 amp_enabled_flag : 1;
+ u16 sample_adaptive_offset_enabled_flag : 1;
+ u16 pcm_enabled_flag : 1;
+ u16 pcm_sample_bit_depth_luma : 4;
+ u16 pcm_sample_bit_depth_chroma : 4;
+ u16 pcm_loop_filter_disabled_flag : 1;
+ u16 diff_max_min_pcm_luma_coding_block_size : 3;
+ u16 min_pcm_luma_coding_block_size : 3;
+ u16 num_short_term_ref_pic_sets : 7;
+ u16 long_term_ref_pics_present_flag : 1;
+ u16 num_long_term_ref_pics_sps : 6;
+ u16 sps_temporal_mvp_enabled_flag : 1;
+ u16 strong_intra_smoothing_enabled_flag : 1;
+ u16 reserved0 : 7;
+ u16 sps_max_dec_pic_buffering_minus1 : 4;
+ u16 separate_colour_plane_flag : 1;
+ u16 high_precision_offsets_enabled_flag : 1;
+ u16 persistent_rice_adaptation_enabled_flag : 1;
+
+ // PPS
+ u16 picture_parameters_set_id : 6;
+ u16 seq_parameters_set_id_pps : 4;
+ u16 dependent_slice_segments_enabled_flag : 1;
+ u16 output_flag_present_flag : 1;
+ u16 num_extra_slice_header_bits : 13;
+ u16 sign_data_hiding_enabled_flag : 1;
+ u16 cabac_init_present_flag : 1;
+ u16 num_ref_idx_l0_default_active : 4;
+ u16 num_ref_idx_l1_default_active : 4;
+ u16 init_qp_minus26 : 7;
+ u16 constrained_intra_pred_flag : 1;
+ u16 transform_skip_enabled_flag : 1;
+ u16 cu_qp_delta_enabled_flag : 1;
+ u16 log2_min_cb_size : 3;
+ u16 pps_cb_qp_offset : 5;
+ u16 pps_cr_qp_offset : 5;
+ u16 pps_slice_chroma_qp_offsets_present_flag : 1;
+ u16 weighted_pred_flag : 1;
+ u16 weighted_bipred_flag : 1;
+ u16 transquant_bypass_enabled_flag : 1;
+ u16 tiles_enabled_flag : 1;
+ u16 entropy_coding_sync_enabled_flag : 1;
+ u16 pps_loop_filter_across_slices_enabled_flag : 1;
+ u16 loop_filter_across_tiles_enabled_flag : 1;
+ u16 deblocking_filter_override_enabled_flag : 1;
+ u16 pps_deblocking_filter_disabled_flag : 1;
+ u16 pps_beta_offset_div2 : 4;
+ u16 pps_tc_offset_div2 : 4;
+ u16 lists_modification_present_flag : 1;
+ u16 log2_parallel_merge_level : 3;
+ u16 slice_segment_header_extension_present_flag : 1;
+ u16 reserved1 : 3;
+
+ // pps extensions
+ u16 log2_max_transform_skip_block_size : 2;
+ u16 cross_component_prediction_enabled_flag : 1;
+ u16 chroma_qp_offset_list_enabled_flag : 1;
+ u16 log2_min_cu_chroma_qp_delta_size : 3;
+ u16 cb_qp_offset_list0 : 5;
+ u16 cb_qp_offset_list1 : 5;
+ u16 cb_qp_offset_list2 : 5;
+ u16 cb_qp_offset_list3 : 5;
+ u16 cb_qp_offset_list4 : 5;
+ u16 cb_qp_offset_list5 : 5;
+ u16 cb_cr_offset_list0 : 5;
+ u16 cb_cr_offset_list1 : 5;
+ u16 cb_cr_offset_list2 : 5;
+ u16 cb_cr_offset_list3 : 5;
+ u16 cb_cr_offset_list4 : 5;
+ u16 cb_cr_offset_list5 : 5;
+ u16 chroma_qp_offset_list_len_minus1 : 3;
+
+ /* mvc0 && mvc1 */
+ u16 mvc_ff : 16;
+ u16 mvc_00 : 9;
+
+ /* poc info */
+ u16 reserved2 : 3;
+ u32 current_poc : 32;
+ u32 ref_pic_poc0 : 32;
+ u32 ref_pic_poc1 : 32;
+ u32 ref_pic_poc2 : 32;
+ u32 ref_pic_poc3 : 32;
+ u32 ref_pic_poc4 : 32;
+ u32 ref_pic_poc5 : 32;
+ u32 ref_pic_poc6 : 32;
+ u32 ref_pic_poc7 : 32;
+ u32 ref_pic_poc8 : 32;
+ u32 ref_pic_poc9 : 32;
+ u32 ref_pic_poc10 : 32;
+ u32 ref_pic_poc11 : 32;
+ u32 ref_pic_poc12 : 32;
+ u32 ref_pic_poc13 : 32;
+ u32 ref_pic_poc14 : 32;
+ u32 reserved3 : 32;
+ u32 ref_is_valid : 15;
+ u32 reserved4 : 1;
+
+ /* tile info*/
+ u16 num_tile_columns : 5;
+ u16 num_tile_rows : 5;
+ u32 column_width0 : 24;
+ u32 column_width1 : 24;
+ u32 column_width2 : 24;
+ u32 column_width3 : 24;
+ u32 column_width4 : 24;
+ u32 column_width5 : 24;
+ u32 column_width6 : 24;
+ u32 column_width7 : 24;
+ u32 column_width8 : 24;
+ u32 column_width9 : 24;
+ u32 row_height0 : 24;
+ u32 row_height1 : 24;
+ u32 row_height2 : 24;
+ u32 row_height3 : 24;
+ u32 row_height4 : 24;
+ u32 row_height5 : 24;
+ u32 row_height6 : 24;
+ u32 row_height7 : 24;
+ u32 row_height8 : 24;
+ u32 row_height9 : 24;
+ u32 row_height10 : 24;
+ u32 reserved5 : 2;
+ u32 padding;
+} __packed;
+
+struct rkvdec_hevc_priv_tbl {
+ struct rkvdec_hevc_sps_pps param_set;
+ struct rkvdec_rps rps;
+ struct scaling_factor scaling_list;
+ u8 cabac_table[27456];
+} __packed;
+
+struct rkvdec_hevc_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
+ struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache;
+ struct vdpu383_regs_h26x regs;
+};
+
+static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row)
+{
+ hw_ps->column_width0 = column[0] | (column[1] << 12);
+ hw_ps->row_height0 = row[0] | (row[1] << 12);
+ hw_ps->column_width1 = column[2] | (column[3] << 12);
+ hw_ps->row_height1 = row[2] | (row[3] << 12);
+ hw_ps->column_width2 = column[4] | (column[5] << 12);
+ hw_ps->row_height2 = row[4] | (row[5] << 12);
+ hw_ps->column_width3 = column[6] | (column[7] << 12);
+ hw_ps->row_height3 = row[6] | (row[7] << 12);
+ hw_ps->column_width4 = column[8] | (column[9] << 12);
+ hw_ps->row_height4 = row[8] | (row[9] << 12);
+ hw_ps->column_width5 = column[10] | (column[11] << 12);
+ hw_ps->row_height5 = row[10] | (row[11] << 12);
+ hw_ps->column_width6 = column[12] | (column[13] << 12);
+ hw_ps->row_height6 = row[12] | (row[13] << 12);
+ hw_ps->column_width7 = column[14] | (column[15] << 12);
+ hw_ps->row_height7 = row[14] | (row[15] << 12);
+ hw_ps->column_width8 = column[16] | (column[17] << 12);
+ hw_ps->row_height8 = row[16] | (row[17] << 12);
+ hw_ps->column_width9 = column[18] | (column[19] << 12);
+ hw_ps->row_height9 = row[18] | (row[19] << 12);
+
+ hw_ps->row_height10 = row[20] | (row[21] << 12);
+}
+
+static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb)
+{
+ hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val;
+ hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val;
+ hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val;
+ hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val;
+ hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val;
+ hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val;
+ hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val;
+ hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val;
+ hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val;
+ hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val;
+ hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val;
+ hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val;
+ hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val;
+ hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val;
+ hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val;
+}
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct rkvdec_hevc_ctx *h264_ctx = ctx->priv;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+ const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
+ struct rkvdec_hevc_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+ struct rkvdec_hevc_sps_pps *hw_ps;
+ bool tiles_enabled;
+ s32 max_cu_width;
+ s32 pic_in_cts_width;
+ s32 pic_in_cts_height;
+ u16 log2_min_cb_size, width, height;
+ u16 column_width[22];
+ u16 row_height[22];
+ u8 pcm_enabled;
+ u32 i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 32 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set;
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+ /* write sps */
+ hw_ps->video_parameters_set_id = sps->video_parameter_set_id;
+ hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id;
+ hw_ps->chroma_format_idc = sps->chroma_format_idc;
+
+ log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ width = sps->pic_width_in_luma_samples;
+ height = sps->pic_height_in_luma_samples;
+ hw_ps->width = width;
+ hw_ps->height = height;
+ hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
+ hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
+ hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
+ hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size;
+ hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2;
+ hw_ps->diff_max_min_transform_block_size =
+ sps->log2_diff_max_min_luma_transform_block_size;
+ hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter;
+ hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra;
+ hw_ps->scaling_list_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED);
+ hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED);
+ hw_ps->sample_adaptive_offset_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET);
+
+ pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
+ hw_ps->pcm_enabled_flag = pcm_enabled;
+ hw_ps->pcm_sample_bit_depth_luma =
+ pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0;
+ hw_ps->pcm_sample_bit_depth_chroma =
+ pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0;
+ hw_ps->pcm_loop_filter_disabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED);
+ hw_ps->diff_max_min_pcm_luma_coding_block_size =
+ sps->log2_diff_max_min_pcm_luma_coding_block_size;
+ hw_ps->min_pcm_luma_coding_block_size =
+ pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0;
+ hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
+ hw_ps->long_term_ref_pics_present_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT);
+ hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps;
+ hw_ps->sps_temporal_mvp_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED);
+ hw_ps->strong_intra_smoothing_enabled_flag =
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED);
+ hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1;
+
+ /* write pps */
+ hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id;
+ hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id;
+ hw_ps->dependent_slice_segments_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED);
+ hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT);
+ hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits;
+ hw_ps->sign_data_hiding_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED);
+ hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT);
+ hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1;
+ hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1;
+ hw_ps->init_qp_minus26 = pps->init_qp_minus26;
+ hw_ps->constrained_intra_pred_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+ hw_ps->transform_skip_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED);
+ hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED);
+ hw_ps->log2_min_cb_size = log2_min_cb_size +
+ sps->log2_diff_max_min_luma_coding_block_size -
+ pps->diff_cu_qp_delta_depth;
+ hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset;
+ hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset;
+ hw_ps->pps_slice_chroma_qp_offsets_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT);
+ hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED);
+ hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
+ hw_ps->transquant_bypass_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED);
+ tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
+ hw_ps->tiles_enabled_flag = tiles_enabled;
+ hw_ps->entropy_coding_sync_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED);
+ hw_ps->pps_loop_filter_across_slices_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED);
+ hw_ps->loop_filter_across_tiles_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED);
+ hw_ps->deblocking_filter_override_enabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED);
+ hw_ps->pps_deblocking_filter_disabled_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER);
+ hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2;
+ hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2;
+ hw_ps->lists_modification_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT);
+ hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2;
+ hw_ps->slice_segment_header_extension_present_flag =
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT);
+ hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1;
+ hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1;
+ hw_ps->mvc_ff = 0xffff;
+
+ // Setup tiles information
+ memset(column_width, 0, sizeof(column_width));
+ memset(row_height, 0, sizeof(row_height));
+
+ max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
+ pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
+ pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
+
+ if (tiles_enabled) {
+ if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
+ compute_tiles_uniform(run, log2_min_cb_size, width, height,
+ pic_in_cts_width, pic_in_cts_height,
+ column_width, row_height);
+ } else {
+ compute_tiles_non_uniform(run, log2_min_cb_size, width, height,
+ pic_in_cts_width, pic_in_cts_height,
+ column_width, row_height);
+ }
+ } else {
+ column_width[0] = (width + max_cu_width - 1) / max_cu_width;
+ row_height[0] = (height + max_cu_width - 1) / max_cu_width;
+ }
+
+ set_column_row(hw_ps, column_width, row_height);
+
+ // Setup POC information
+ hw_ps->current_poc = dec_params->pic_order_cnt_val;
+
+ set_pps_ref_pic_poc(hw_ps, dec_params->dpb);
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ u32 valid = !!(dec_params->num_active_dpb_entries > i);
+ hw_ps->ref_is_valid |= valid << i;
+ }
+}
+
+static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_ctx *h265_ctx = ctx->priv;
+
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
+ &h265_ctx->regs.common,
+ sizeof(h265_ctx->regs.common));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
+ &h265_ctx->regs.common_addr,
+ sizeof(h265_ctx->regs.common_addr));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
+ &h265_ctx->regs.h26x_params,
+ sizeof(h265_ctx->regs.h26x_params));
+ rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
+ &h265_ctx->regs.h26x_addr,
+ sizeof(h265_ctx->regs.h26x_addr));
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
+ struct rkvdec_hevc_ctx *h265_ctx = ctx->priv;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ dma_addr_t priv_start_addr = h265_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ struct vdpu383_regs_h26x *regs = &h265_ctx->regs;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ dma_addr_t dst_addr;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
+ u32 offset;
+ u32 pixels;
+ u32 i;
+
+ memset(regs, 0, sizeof(*regs));
+
+ /* Set HEVC mode */
+ regs->common.reg008_dec_mode = VDPU383_MODE_HEVC;
+
+ /* Set input stream length */
+ regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ /* Set strides */
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ pixels = dst_fmt->height * dst_fmt->width;
+
+ regs->h26x_params.reg068_hor_virstride = hor_virstride / 16;
+ regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16;
+ regs->h26x_params.reg070_y_virstride = y_virstride / 16;
+
+ /* Activate block gating */
+ regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.inter_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.intra_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.transd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.recon_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.bus_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1;
+ regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1;
+
+ /* Set timeout threshold */
+ if (pixels < RKVDEC_1080P_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p;
+ else if (pixels < RKVDEC_4K_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K;
+ else if (pixels < RKVDEC_8K_PIXELS)
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K;
+ else
+ regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX;
+
+ regs->common.reg016_error_ctrl_set.error_proc_disable = 1;
+
+ /* Set ref pic address & poc */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb) - 1; i++) {
+ struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
+ dma_addr_t buf_dma;
+
+ buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+
+ /* Set reference addresses */
+ regs->h26x_addr.reg170_185_ref_base[i] = buf_dma;
+ regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma;
+
+ /* Set COLMV addresses */
+ regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset;
+ }
+
+ /* Set rlc base address (input stream) */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ regs->common_addr.reg128_strm_base = rlc_addr;
+
+ /* Set output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ regs->h26x_addr.reg168_decout_base = dst_addr;
+ regs->h26x_addr.reg169_error_ref_base = dst_addr;
+ regs->h26x_addr.reg192_payload_st_cur_base = dst_addr;
+
+ /* Set colmv address */
+ regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset;
+
+ /* Set RCB addresses */
+ for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) {
+ regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i);
+ regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i);
+ }
+
+ if (sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) {
+ /* Set scaling matrix */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
+ regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset;
+ }
+
+ /* Set hw pps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
+ regs->common_addr.reg131_gbl_base = priv_start_addr + offset;
+ regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_hevc_sps_pps) / 16;
+
+ /* Set hw rps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
+ regs->common_addr.reg129_rps_base = priv_start_addr + offset;
+
+ /* Set cabac table */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
+ regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset;
+
+ rkvdec_write_regs(ctx);
+}
+
+static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_hevc_sps *sps)
+{
+ if (sps->chroma_format_idc != 1)
+ /* Only 4:2:0 is supported */
+ return -EINVAL;
+
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit are supported */
+ return -EINVAL;
+
+ if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
+ sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_priv_tbl *priv_tbl;
+ struct rkvdec_hevc_ctx *hevc_ctx;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
+ if (ret)
+ return ret;
+
+ hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL);
+ if (!hevc_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ hevc_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
+ sizeof(rkvdec_hevc_cabac_table));
+
+ ctx->priv = hevc_ctx;
+ return 0;
+
+err_free_ctx:
+ kfree(hevc_ctx);
+ return ret;
+}
+
+static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+ hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
+ kfree(hevc_ctx);
+}
+
+static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_run run;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
+ u32 timeout_threshold;
+
+ rkvdec_hevc_run_preamble(ctx, &run);
+
+ /*
+ * On vdpu383, not setting the long and short term ref sets leads to IOMMU page faults.
+ * To be on the safe side for this new v4l2 control, write an error in the log and mark
+ * the buffer as failed by returning an error here.
+ */
+ if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
+ (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
+ dev_err_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
+ return -EINVAL;
+ }
+
+ rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
+ &hevc_ctx->scaling_matrix_cache);
+ assemble_hw_pps(ctx, &run);
+ rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache);
+
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ timeout_threshold = hevc_ctx->regs.common.reg013_core_timeout_threshold;
+ rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
+
+ /* Start decoding! */
+ writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
+ writel(VDPU383_IP_CRU_MODE, rkvdec->link + VDPU383_LINK_IP_ENABLE);
+ writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
+
+ return 0;
+}
+
+static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
+ return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops = {
+ .adjust_fmt = rkvdec_hevc_adjust_fmt,
+ .start = rkvdec_hevc_start,
+ .stop = rkvdec_hevc_stop,
+ .run = rkvdec_hevc_run,
+ .try_ctrl = rkvdec_hevc_try_ctrl,
+ .get_image_fmt = rkvdec_hevc_get_image_fmt,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h
new file mode 100644
index 000000000000..04eb23297e37
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Video Decoder VDPU383 driver registers description
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ */
+
+#ifndef _RKVDEC_VDPU838_REGS_H_
+#define _RKVDEC_VDPU838_REGS_H_
+
+#include <linux/types.h>
+
+#define VDPU383_OFFSET_COMMON_REGS (8 * sizeof(u32))
+#define VDPU383_OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32))
+#define VDPU383_OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32))
+#define VDPU383_OFFSET_CODEC_ADDR_REGS (168 * sizeof(u32))
+#define VDPU383_OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32))
+
+#define VDPU383_MODE_HEVC 0
+#define VDPU383_MODE_H264 1
+
+#define VDPU383_TIMEOUT_1080p (0xffffff)
+#define VDPU383_TIMEOUT_4K (0x2cfffff)
+#define VDPU383_TIMEOUT_8K (0x4ffffff)
+#define VDPU383_TIMEOUT_MAX (0xffffffff)
+
+#define VDPU383_LINK_TIMEOUT_THRESHOLD 0x54
+
+#define VDPU383_LINK_IP_ENABLE 0x58
+#define VDPU383_IP_CRU_MODE BIT(24)
+
+#define VDPU383_LINK_DEC_ENABLE 0x40
+#define VDPU383_DEC_E_BIT BIT(0)
+
+#define VDPU383_LINK_INT_EN 0x048
+#define VDPU383_INT_EN_IRQ BIT(0)
+#define VDPU383_INT_EN_LINE_IRQ BIT(1)
+
+#define VDPU383_LINK_STA_INT 0x04c
+#define VDPU383_STA_INT_DEC_RDY_STA BIT(0)
+#define VDPU383_STA_INT_SOFTRESET_RDY (BIT(10) | BIT(11))
+#define VDPU383_STA_INT_ALL 0x3ff
+
+struct vdpu383_regs_common {
+ u32 reg008_dec_mode;
+
+ struct {
+ u32 fbc_e : 1;
+ u32 tile_e : 1;
+ u32 reserve0 : 2;
+ u32 buf_empty_en : 1;
+ u32 scale_down_en : 1;
+ u32 reserve1 : 1;
+ u32 pix_range_det_e : 1;
+ u32 av1_fgs_en : 1;
+ u32 reserve2 : 7;
+ u32 line_irq_en : 1;
+ u32 out_cbcr_swap : 1;
+ u32 fbc_force_uncompress : 1;
+ u32 fbc_sparse_mode : 1;
+ u32 reserve3 : 12;
+ } reg009_important_en;
+
+ struct {
+ u32 strmd_auto_gating_e : 1;
+ u32 inter_auto_gating_e : 1;
+ u32 intra_auto_gating_e : 1;
+ u32 transd_auto_gating_e : 1;
+ u32 recon_auto_gating_e : 1;
+ u32 filterd_auto_gating_e : 1;
+ u32 bus_auto_gating_e : 1;
+ u32 ctrl_auto_gating_e : 1;
+ u32 rcb_auto_gating_e : 1;
+ u32 err_prc_auto_gating_e : 1;
+ u32 reserve0 : 22;
+ } reg010_block_gating_en;
+
+ struct {
+ u32 reserve0 : 9;
+ u32 dec_timeout_dis : 1;
+ u32 reserve1 : 22;
+ } reg011_cfg_para;
+
+ struct {
+ u32 reserve0 : 7;
+ u32 cache_hash_mask : 25;
+ } reg012_cache_hash_mask;
+
+ u32 reg013_core_timeout_threshold;
+
+ struct {
+ u32 dec_line_irq_step : 16;
+ u32 dec_line_offset_y_st : 16;
+ } reg014_line_irq_ctrl;
+
+ struct {
+ u32 rkvdec_frame_rdy_sta : 1;
+ u32 rkvdec_strm_error_sta : 1;
+ u32 rkvdec_core_timeout_sta : 1;
+ u32 rkvdec_ip_timeout_sta : 1;
+ u32 rkvdec_bus_error_sta : 1;
+ u32 rkvdec_buffer_empty_sta : 1;
+ u32 rkvdec_colmv_ref_error_sta : 1;
+ u32 rkvdec_error_spread_sta : 1;
+ u32 create_core_timeout_sta : 1;
+ u32 wlast_miss_match_sta : 1;
+ u32 rkvdec_core_rst_rdy_sta : 1;
+ u32 rkvdec_ip_rst_rdy_sta : 1;
+ u32 force_busidle_rdy_sta : 1;
+ u32 ltb_pause_rdy_sta : 1;
+ u32 ltb_end_flag : 1;
+ u32 unsupport_decmode_error_sta : 1;
+ u32 wmask_bits : 15;
+ u32 reserve0 : 1;
+ } reg015_irq_sta;
+
+ struct {
+ u32 error_proc_disable : 1;
+ u32 reserve0 : 7;
+ u32 error_spread_disable : 1;
+ u32 reserve1 : 15;
+ u32 roi_error_ctu_cal_en : 1;
+ u32 reserve2 : 7;
+ } reg016_error_ctrl_set;
+
+ struct {
+ u32 roi_x_ctu_offset_st : 12;
+ u32 reserve0 : 4;
+ u32 roi_y_ctu_offset_st : 12;
+ u32 reserve1 : 4;
+ } reg017_err_roi_ctu_offset_start;
+
+ struct {
+ u32 roi_x_ctu_offset_end : 12;
+ u32 reserve0 : 4;
+ u32 roi_y_ctu_offset_end : 12;
+ u32 reserve1 : 4;
+ } reg018_err_roi_ctu_offset_end;
+
+ struct {
+ u32 avs2_ref_error_field : 1;
+ u32 avs2_ref_error_topfield : 1;
+ u32 ref_error_topfield_used : 1;
+ u32 ref_error_botfield_used : 1;
+ u32 reserve0 : 28;
+ } reg019_error_ref_info;
+
+ u32 reg020_cabac_error_en_lowbits;
+ u32 reg021_cabac_error_en_highbits;
+
+ u32 reg022_reserved;
+
+ struct {
+ u32 fill_y : 10;
+ u32 fill_u : 10;
+ u32 fill_v : 10;
+ u32 reserve0 : 2;
+ } reg023_invalid_pixel_fill;
+
+ u32 reg024_026_reserved[3];
+
+ struct {
+ u32 reserve0 : 4;
+ u32 ctu_align_wr_en : 1;
+ u32 reserve1 : 27;
+ } reg027_align_en;
+
+ struct {
+ u32 axi_perf_work_e : 1;
+ u32 reserve0 : 2;
+ u32 axi_cnt_type : 1;
+ u32 rd_latency_id : 8;
+ u32 reserve1 : 4;
+ u32 rd_latency_thr : 12;
+ u32 reserve2 : 4;
+ } reg028_debug_perf_latency_ctrl0;
+
+ struct {
+ u32 addr_align_type : 2;
+ u32 ar_cnt_id_type : 1;
+ u32 aw_cnt_id_type : 1;
+ u32 ar_count_id : 8;
+ u32 reserve0 : 4;
+ u32 aw_count_id : 8;
+ u32 rd_band_width_mode : 1;
+ u32 reserve1 : 7;
+ } reg029_debug_perf_latency_ctrl1;
+
+ struct {
+ u32 axi_wr_qos_level : 4;
+ u32 reserve0 : 4;
+ u32 axi_wr_qos : 4;
+ u32 reserve1 : 4;
+ u32 axi_rd_qos_level : 4;
+ u32 reserve2 : 4;
+ u32 axi_rd_qos : 4;
+ u32 reserve3 : 4;
+ } reg030_qos_ctrl;
+};
+
+struct vdpu383_regs_common_addr {
+ u32 reg128_strm_base;
+ u32 reg129_rps_base;
+ u32 reg130_cabactbl_base;
+ u32 reg131_gbl_base;
+ u32 reg132_scanlist_addr;
+ u32 reg133_scale_down_base;
+ u32 reg134_fgs_base;
+ u32 reg135_139_reserved[5];
+
+ struct rcb_info {
+ u32 offset;
+ u32 size;
+ } reg140_162_rcb_info[11];
+};
+
+struct vdpu383_regs_h26x_addr {
+ u32 reg168_decout_base;
+ u32 reg169_error_ref_base;
+ u32 reg170_185_ref_base[16];
+ u32 reg186_191_reserved[6];
+ u32 reg192_payload_st_cur_base;
+ u32 reg193_fbc_payload_offset;
+ u32 reg194_payload_st_error_ref_base;
+ u32 reg195_210_payload_st_ref_base[16];
+ u32 reg211_215_reserved[5];
+ u32 reg216_colmv_cur_base;
+ u32 reg217_232_colmv_ref_base[16];
+};
+
+struct vdpu383_regs_h26x_params {
+ u32 reg064_start_decoder;
+ u32 reg065_strm_start_bit;
+ u32 reg066_stream_len;
+ u32 reg067_global_len;
+ u32 reg068_hor_virstride;
+ u32 reg069_raster_uv_hor_virstride;
+ u32 reg070_y_virstride;
+ u32 reg071_scl_ref_hor_virstride;
+ u32 reg072_scl_ref_raster_uv_hor_virstride;
+ u32 reg073_scl_ref_virstride;
+ u32 reg074_fgs_ref_hor_virstride;
+ u32 reg075_079_reserved[5];
+ u32 reg080_error_ref_hor_virstride;
+ u32 reg081_error_ref_raster_uv_hor_virstride;
+ u32 reg082_error_ref_virstride;
+ u32 reg083_ref0_hor_virstride;
+ u32 reg084_ref0_raster_uv_hor_virstride;
+ u32 reg085_ref0_virstride;
+ u32 reg086_ref1_hor_virstride;
+ u32 reg087_ref1_raster_uv_hor_virstride;
+ u32 reg088_ref1_virstride;
+ u32 reg089_ref2_hor_virstride;
+ u32 reg090_ref2_raster_uv_hor_virstride;
+ u32 reg091_ref2_virstride;
+ u32 reg092_ref3_hor_virstride;
+ u32 reg093_ref3_raster_uv_hor_virstride;
+ u32 reg094_ref3_virstride;
+ u32 reg095_ref4_hor_virstride;
+ u32 reg096_ref4_raster_uv_hor_virstride;
+ u32 reg097_ref4_virstride;
+ u32 reg098_ref5_hor_virstride;
+ u32 reg099_ref5_raster_uv_hor_virstride;
+ u32 reg100_ref5_virstride;
+ u32 reg101_ref6_hor_virstride;
+ u32 reg102_ref6_raster_uv_hor_virstride;
+ u32 reg103_ref6_virstride;
+ u32 reg104_ref7_hor_virstride;
+ u32 reg105_ref7_raster_uv_hor_virstride;
+ u32 reg106_ref7_virstride;
+};
+
+struct vdpu383_regs_h26x {
+ struct vdpu383_regs_common common; /* 8-30 */
+ struct vdpu383_regs_h26x_params h26x_params; /* 64-74, 80-106 */
+ struct vdpu383_regs_common_addr common_addr; /* 128-134, 140-161 */
+ struct vdpu383_regs_h26x_addr h26x_addr; /* 168-185, 192-210, 216-232 */
+} __packed;
+
+#endif /* __RKVDEC_VDPU838_REGS_H__ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
index b4bf01e839ef..ba51a7c2fe55 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
@@ -163,6 +163,7 @@ struct rkvdec_vp9_ctx {
struct v4l2_vp9_frame_context frame_context[4];
struct rkvdec_vp9_frame_info cur;
struct rkvdec_vp9_frame_info last;
+ struct rkvdec_regs regs;
};
static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane)
@@ -347,38 +348,6 @@ static void init_probs(struct rkvdec_ctx *ctx,
init_inter_probs(ctx, run);
}
-struct rkvdec_vp9_ref_reg {
- u32 reg_frm_size;
- u32 reg_hor_stride;
- u32 reg_y_stride;
- u32 reg_yuv_stride;
- u32 reg_ref_base;
-};
-
-static struct rkvdec_vp9_ref_reg ref_regs[] = {
- {
- .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(0),
- .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(0),
- .reg_y_stride = RKVDEC_VP9_LAST_FRAME_YSTRIDE,
- .reg_yuv_stride = RKVDEC_VP9_LAST_FRAME_YUVSTRIDE,
- .reg_ref_base = RKVDEC_REG_VP9_LAST_FRAME_BASE,
- },
- {
- .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(1),
- .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(1),
- .reg_y_stride = RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE,
- .reg_yuv_stride = 0,
- .reg_ref_base = RKVDEC_REG_VP9_GOLDEN_FRAME_BASE,
- },
- {
- .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(2),
- .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(2),
- .reg_y_stride = RKVDEC_VP9_ALTREF_FRAME_YSTRIDE,
- .reg_yuv_stride = 0,
- .reg_ref_base = RKVDEC_REG_VP9_ALTREF_FRAME_BASE,
- }
-};
-
static struct rkvdec_decoded_buffer *
get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
{
@@ -412,18 +381,17 @@ static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf)
static void config_ref_registers(struct rkvdec_ctx *ctx,
const struct rkvdec_vp9_run *run,
struct rkvdec_decoded_buffer *ref_buf,
- struct rkvdec_vp9_ref_reg *ref_reg)
+ int i)
{
unsigned int aligned_pitch, aligned_height, y_len, yuv_len;
- struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_regs *regs = &vp9_ctx->regs;
aligned_height = round_up(ref_buf->vp9.height, 64);
- writel_relaxed(RKVDEC_VP9_FRAMEWIDTH(ref_buf->vp9.width) |
- RKVDEC_VP9_FRAMEHEIGHT(ref_buf->vp9.height),
- rkvdec->regs + ref_reg->reg_frm_size);
+ regs->vp9.reg17_19[i].frameheight = ref_buf->vp9.height;
+ regs->vp9.reg17_19[i].framewidth = ref_buf->vp9.width;
- writel_relaxed(vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0),
- rkvdec->regs + ref_reg->reg_ref_base);
+ regs->vp9.refer_bases[i] = vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0);
if (&ref_buf->base.vb == run->base.bufs.dst)
return;
@@ -432,59 +400,50 @@ static void config_ref_registers(struct rkvdec_ctx *ctx,
y_len = aligned_height * aligned_pitch;
yuv_len = (y_len * 3) / 2;
- writel_relaxed(RKVDEC_HOR_Y_VIRSTRIDE(aligned_pitch / 16) |
- RKVDEC_HOR_UV_VIRSTRIDE(aligned_pitch / 16),
- rkvdec->regs + ref_reg->reg_hor_stride);
- writel_relaxed(RKVDEC_VP9_REF_YSTRIDE(y_len / 16),
- rkvdec->regs + ref_reg->reg_y_stride);
-
- if (!ref_reg->reg_yuv_stride)
- return;
+ regs->vp9.reg37_39[i].y_hor_virstride = aligned_pitch / 16;
+ regs->vp9.reg37_39[i].uv_hor_virstride = aligned_pitch / 16;
+ regs->vp9.reg48_50[i].virstride = y_len / 16;
- writel_relaxed(RKVDEC_VP9_REF_YUVSTRIDE(yuv_len / 16),
- rkvdec->regs + ref_reg->reg_yuv_stride);
+ if (!i)
+ regs->vp9.reg51.lastref_yuv_virstride = yuv_len / 16;
}
static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid)
{
struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_regs *regs = &vp9_ctx->regs;
const struct v4l2_vp9_segmentation *seg;
- struct rkvdec_dev *rkvdec = ctx->dev;
s16 feature_val;
int feature_id;
- u32 val = 0;
seg = vp9_ctx->last.valid ? &vp9_ctx->last.seg : &vp9_ctx->cur.seg;
feature_id = V4L2_VP9_SEG_LVL_ALT_Q;
if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
feature_val = seg->feature_data[segid][feature_id];
- val |= RKVDEC_SEGID_FRAME_QP_DELTA_EN(1) |
- RKVDEC_SEGID_FRAME_QP_DELTA(feature_val);
+ regs->vp9.reg20_27[segid].segid_frame_qp_delta_en = 1;
+ regs->vp9.reg20_27[segid].segid_frame_qp_delta = feature_val;
}
feature_id = V4L2_VP9_SEG_LVL_ALT_L;
if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
feature_val = seg->feature_data[segid][feature_id];
- val |= RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(1) |
- RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(feature_val);
+ regs->vp9.reg20_27[segid].segid_frame_loopfilter_value_en = 1;
+ regs->vp9.reg20_27[segid].segid_frame_loopfilter_value = feature_val;
}
feature_id = V4L2_VP9_SEG_LVL_REF_FRAME;
if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
feature_val = seg->feature_data[segid][feature_id];
- val |= RKVDEC_SEGID_REFERINFO_EN(1) |
- RKVDEC_SEGID_REFERINFO(feature_val);
+ regs->vp9.reg20_27[segid].segid_referinfo_en = 1;
+ regs->vp9.reg20_27[segid].segid_referinfo = feature_val;
}
feature_id = V4L2_VP9_SEG_LVL_SKIP;
- if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid))
- val |= RKVDEC_SEGID_FRAME_SKIP_EN(1);
-
- if (!segid &&
- (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE))
- val |= RKVDEC_SEGID_ABS_DELTA(1);
+ regs->vp9.reg20_27[segid].segid_frame_skip_en =
+ v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid);
- writel_relaxed(val, rkvdec->regs + RKVDEC_VP9_SEGID_GRP(segid));
+ regs->vp9.reg20_27[segid].segid_abs_delta = !segid &&
+ (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
}
static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf,
@@ -521,7 +480,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_decoded_buffer *ref_bufs[3];
struct rkvdec_decoded_buffer *dst, *last, *mv_ref;
struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
- u32 val, last_frame_info = 0;
+ struct rkvdec_regs *regs = &vp9_ctx->regs;
const struct v4l2_vp9_segmentation *seg;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_addr_t addr;
@@ -547,8 +506,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
(V4L2_VP9_FRAME_FLAG_KEY_FRAME |
V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
- writel_relaxed(RKVDEC_MODE(RKVDEC_MODE_VP9),
- rkvdec->regs + RKVDEC_REG_SYSCTRL);
+ regs->common.reg02.dec_mode = RKVDEC_MODE_VP9;
bit_depth = dec_params->bit_depth;
aligned_height = round_up(ctx->decoded_fmt.fmt.pix_mp.height, 64);
@@ -560,17 +518,14 @@ static void config_registers(struct rkvdec_ctx *ctx,
uv_len = y_len / 2;
yuv_len = y_len + uv_len;
- writel_relaxed(RKVDEC_Y_HOR_VIRSTRIDE(aligned_pitch / 16) |
- RKVDEC_UV_HOR_VIRSTRIDE(aligned_pitch / 16),
- rkvdec->regs + RKVDEC_REG_PICPAR);
- writel_relaxed(RKVDEC_Y_VIRSTRIDE(y_len / 16),
- rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
- writel_relaxed(RKVDEC_YUV_VIRSTRIDE(yuv_len / 16),
- rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+ regs->common.reg03.y_hor_virstride = aligned_pitch / 16;
+ regs->common.reg03.uv_hor_virstride = aligned_pitch / 16;
+ regs->common.reg08.y_virstride = y_len / 16;
+ regs->common.reg09.yuv_virstride = yuv_len / 16;
stream_len = vb2_get_plane_payload(&run->base.bufs.src->vb2_buf, 0);
- writel_relaxed(RKVDEC_STRM_LEN(stream_len),
- rkvdec->regs + RKVDEC_REG_STRM_LEN);
+
+ regs->common.stream_len = stream_len;
/*
* Reset count buffer, because decoder only output intra related syntax
@@ -588,14 +543,13 @@ static void config_registers(struct rkvdec_ctx *ctx,
vp9_ctx->cur.segmapid++;
for (i = 0; i < ARRAY_SIZE(ref_bufs); i++)
- config_ref_registers(ctx, run, ref_bufs[i], &ref_regs[i]);
+ config_ref_registers(ctx, run, ref_bufs[i], i);
for (i = 0; i < 8; i++)
config_seg_registers(ctx, i);
- writel_relaxed(RKVDEC_VP9_TX_MODE(vp9_ctx->cur.tx_mode) |
- RKVDEC_VP9_FRAME_REF_MODE(dec_params->reference_mode),
- rkvdec->regs + RKVDEC_VP9_CPRHEADER_CONFIG);
+ regs->vp9.reg28.tx_mode = vp9_ctx->cur.tx_mode;
+ regs->vp9.reg28.frame_reference_mode = dec_params->reference_mode;
if (!intra_only) {
const struct v4l2_vp9_loop_filter *lf;
@@ -606,46 +560,58 @@ static void config_registers(struct rkvdec_ctx *ctx,
else
lf = &vp9_ctx->cur.lf;
- val = 0;
for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) {
delta = lf->ref_deltas[i];
- val |= RKVDEC_REF_DELTAS_LASTFRAME(i, delta);
+ switch (i) {
+ case 0:
+ regs->vp9.reg32.ref_deltas_lastframe0 = delta;
+ break;
+ case 1:
+ regs->vp9.reg32.ref_deltas_lastframe1 = delta;
+ break;
+ case 2:
+ regs->vp9.reg32.ref_deltas_lastframe2 = delta;
+ break;
+ case 3:
+ regs->vp9.reg32.ref_deltas_lastframe3 = delta;
+ break;
+ }
}
- writel_relaxed(val,
- rkvdec->regs + RKVDEC_VP9_REF_DELTAS_LASTFRAME);
-
for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) {
delta = lf->mode_deltas[i];
- last_frame_info |= RKVDEC_MODE_DELTAS_LASTFRAME(i,
- delta);
+ switch (i) {
+ case 0:
+ regs->vp9.reg33.mode_deltas_lastframe0 = delta;
+ break;
+ case 1:
+ regs->vp9.reg33.mode_deltas_lastframe1 = delta;
+ break;
+ }
}
}
- if (vp9_ctx->last.valid && !intra_only &&
- vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED)
- last_frame_info |= RKVDEC_SEG_EN_LASTFRAME;
+ regs->vp9.reg33.segmentation_enable_lstframe =
+ vp9_ctx->last.valid && !intra_only &&
+ vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED;
- if (vp9_ctx->last.valid &&
- vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME)
- last_frame_info |= RKVDEC_LAST_SHOW_FRAME;
+ regs->vp9.reg33.last_show_frame =
+ vp9_ctx->last.valid &&
+ vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME;
- if (vp9_ctx->last.valid &&
- vp9_ctx->last.flags &
- (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY))
- last_frame_info |= RKVDEC_LAST_INTRA_ONLY;
+ regs->vp9.reg33.last_intra_only =
+ vp9_ctx->last.valid &&
+ vp9_ctx->last.flags &
+ (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY);
- if (vp9_ctx->last.valid &&
- last->vp9.width == dst->vp9.width &&
- last->vp9.height == dst->vp9.height)
- last_frame_info |= RKVDEC_LAST_WIDHHEIGHT_EQCUR;
+ regs->vp9.reg33.last_widthheight_eqcur =
+ vp9_ctx->last.valid &&
+ last->vp9.width == dst->vp9.width &&
+ last->vp9.height == dst->vp9.height;
- writel_relaxed(last_frame_info,
- rkvdec->regs + RKVDEC_VP9_INFO_LASTFRAME);
-
- writel_relaxed(stream_len - dec_params->compressed_header_size -
- dec_params->uncompressed_header_size,
- rkvdec->regs + RKVDEC_VP9_LASTTILE_SIZE);
+ regs->vp9.reg36.lasttile_size =
+ stream_len - dec_params->compressed_header_size -
+ dec_params->uncompressed_header_size;
for (i = 0; !intra_only && i < ARRAY_SIZE(ref_bufs); i++) {
unsigned int refw = ref_bufs[i]->vp9.width;
@@ -654,29 +620,28 @@ static void config_registers(struct rkvdec_ctx *ctx,
hscale = (refw << 14) / dst->vp9.width;
vscale = (refh << 14) / dst->vp9.height;
- writel_relaxed(RKVDEC_VP9_REF_HOR_SCALE(hscale) |
- RKVDEC_VP9_REF_VER_SCALE(vscale),
- rkvdec->regs + RKVDEC_VP9_REF_SCALE(i));
+
+ regs->vp9.reg29_31[i].ref_hor_scale = hscale;
+ regs->vp9.reg29_31[i].ref_ver_scale = vscale;
}
addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 0);
- writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+ regs->common.decout_base = addr;
addr = vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0);
- writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
- writel_relaxed(vp9_ctx->priv_tbl.dma +
- offsetof(struct rkvdec_vp9_priv_tbl, probs),
- rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
- writel_relaxed(vp9_ctx->count_tbl.dma,
- rkvdec->regs + RKVDEC_REG_VP9COUNT_BASE);
-
- writel_relaxed(vp9_ctx->priv_tbl.dma +
- offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
- (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid),
- rkvdec->regs + RKVDEC_REG_VP9_SEGIDCUR_BASE);
- writel_relaxed(vp9_ctx->priv_tbl.dma +
- offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
- (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)),
- rkvdec->regs + RKVDEC_REG_VP9_SEGIDLAST_BASE);
+ regs->common.strm_rlc_base = addr;
+
+ regs->common.cabactbl_base = vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, probs);
+
+ regs->vp9.count_base = vp9_ctx->count_tbl.dma;
+
+ regs->vp9.segidlast_base = vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
+ (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid));
+
+ regs->vp9.segidcur_base = vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
+ (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid);
if (!intra_only &&
!(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
@@ -685,12 +650,15 @@ static void config_registers(struct rkvdec_ctx *ctx,
else
mv_ref = dst;
- writel_relaxed(get_mv_base_addr(mv_ref),
- rkvdec->regs + RKVDEC_VP9_REF_COLMV_BASE);
+ regs->vp9.refcolmv_base = get_mv_base_addr(mv_ref);
- writel_relaxed(ctx->decoded_fmt.fmt.pix_mp.width |
- (ctx->decoded_fmt.fmt.pix_mp.height << 16),
- rkvdec->regs + RKVDEC_REG_PERFORMANCE_CYCLE);
+ regs->vp9.performance_cycle = ctx->decoded_fmt.fmt.pix_mp.width |
+ (ctx->decoded_fmt.fmt.pix_mp.height << 16);
+
+ regs->vp9.reg44.strmd_error_e = 0xe;
+
+ rkvdec_memcpy_toio(rkvdec->regs, regs,
+ MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
}
static int validate_dec_params(struct rkvdec_ctx *ctx,
@@ -823,8 +791,6 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
- writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
-
if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
rkvdec_quirks_disable_qos(ctx);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 5af9aa5ab353..967c452ab61f 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -9,7 +9,9 @@
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*/
+#include <linux/hw_bitfield.h>
#include <linux/clk.h>
+#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/module.h>
@@ -28,6 +30,9 @@
#include "rkvdec.h"
#include "rkvdec-regs.h"
+#include "rkvdec-vdpu381-regs.h"
+#include "rkvdec-vdpu383-regs.h"
+#include "rkvdec-rcb.h"
static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,
enum rkvdec_image_fmt fmt2)
@@ -83,14 +88,26 @@ static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc,
return false;
}
+static u32 rkvdec_colmv_size(u16 width, u16 height)
+{
+ return 128 * DIV_ROUND_UP(width, 16) * DIV_ROUND_UP(height, 16);
+}
+
+static u32 vdpu383_colmv_size(u16 width, u16 height)
+{
+ return ALIGN(width, 64) * ALIGN(height, 16);
+}
+
static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
struct v4l2_pix_format_mplane *pix_mp)
{
- v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
- pix_mp->width, pix_mp->height);
- pix_mp->plane_fmt[0].sizeimage += 128 *
- DIV_ROUND_UP(pix_mp->width, 16) *
- DIV_ROUND_UP(pix_mp->height, 16);
+ const struct rkvdec_variant *variant = ctx->dev->variant;
+
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, pix_mp->width, pix_mp->height);
+
+ ctx->colmv_offset = pix_mp->plane_fmt[0].sizeimage;
+
+ pix_mp->plane_fmt[0].sizeimage += variant->ops->colmv_size(pix_mp->width, pix_mp->height);
}
static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f,
@@ -136,6 +153,16 @@ static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl)
enum rkvdec_image_fmt image_fmt;
struct vb2_queue *vq;
+ if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS) {
+ ctx->has_sps_st_rps |= !!(ctrl->has_changed);
+ return 0;
+ }
+
+ if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS) {
+ ctx->has_sps_lt_rps |= !!(ctrl->has_changed);
+ return 0;
+ }
+
/* Check if this change requires a capture format reset */
if (!desc->ops->get_image_fmt)
return 0;
@@ -209,6 +236,62 @@ static const struct rkvdec_ctrls rkvdec_hevc_ctrls = {
.num_ctrls = ARRAY_SIZE(rkvdec_hevc_ctrl_descs),
};
+static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_SPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_PPS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
+ .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE,
+ .cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ .cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ .cfg.menu_skip_mask =
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ .cfg.dims = { 65 },
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ .cfg.dims = { 65 },
+ },
+};
+
+static const struct rkvdec_ctrls vdpu38x_hevc_ctrls = {
+ .ctrls = vdpu38x_hevc_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(vdpu38x_hevc_ctrl_descs),
+};
+
static const struct rkvdec_decoded_fmt_desc rkvdec_hevc_decoded_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
@@ -267,6 +350,53 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = {
.num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs),
};
+static const struct rkvdec_ctrl_desc vdpu38x_h264_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_SPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_PPS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE,
+ .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_START_CODE,
+ .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA,
+ .cfg.menu_skip_mask =
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE),
+ .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0,
+ },
+};
+
+static const struct rkvdec_ctrls vdpu38x_h264_ctrls = {
+ .ctrls = vdpu38x_h264_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(vdpu38x_h264_ctrl_descs),
+};
+
static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
@@ -328,7 +458,6 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
.ops = &rkvdec_hevc_fmt_ops,
.num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
.decoded_fmts = rkvdec_hevc_decoded_fmts,
- .capability = RKVDEC_CAPABILITY_HEVC,
},
{
.fourcc = V4L2_PIX_FMT_H264_SLICE,
@@ -345,7 +474,6 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
.num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
.decoded_fmts = rkvdec_h264_decoded_fmts,
.subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
- .capability = RKVDEC_CAPABILITY_H264,
},
{
.fourcc = V4L2_PIX_FMT_VP9_FRAME,
@@ -361,27 +489,108 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
.ops = &rkvdec_vp9_fmt_ops,
.num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts),
.decoded_fmts = rkvdec_vp9_decoded_fmts,
- .capability = RKVDEC_CAPABILITY_VP9,
}
};
-static bool rkvdec_is_capable(struct rkvdec_ctx *ctx, unsigned int capability)
-{
- return (ctx->dev->variant->capabilities & capability) == capability;
-}
+static const struct rkvdec_coded_fmt_desc rk3288_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 4096,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 2304,
+ .step_height = 16,
+ },
+ .ctrls = &rkvdec_hevc_ctrls,
+ .ops = &rkvdec_hevc_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
+ .decoded_fmts = rkvdec_hevc_decoded_fmts,
+ }
+};
+
+static const struct rkvdec_coded_fmt_desc vdpu381_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 65472,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 65472,
+ .step_height = 16,
+ },
+ .ctrls = &vdpu38x_hevc_ctrls,
+ .ops = &rkvdec_vdpu381_hevc_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
+ .decoded_fmts = rkvdec_hevc_decoded_fmts,
+ .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 65520,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 65520,
+ .step_height = 16,
+ },
+ .ctrls = &vdpu38x_h264_ctrls,
+ .ops = &rkvdec_vdpu381_h264_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
+ .decoded_fmts = rkvdec_h264_decoded_fmts,
+ .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ },
+};
+
+static const struct rkvdec_coded_fmt_desc vdpu383_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 65472,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 65472,
+ .step_height = 16,
+ },
+ .ctrls = &vdpu38x_hevc_ctrls,
+ .ops = &rkvdec_vdpu383_hevc_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
+ .decoded_fmts = rkvdec_hevc_decoded_fmts,
+ .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 65520,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 65520,
+ .step_height = 16,
+ },
+ .ctrls = &vdpu38x_h264_ctrls,
+ .ops = &rkvdec_vdpu383_h264_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
+ .decoded_fmts = rkvdec_h264_decoded_fmts,
+ .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ },
+};
static const struct rkvdec_coded_fmt_desc *
rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index)
{
+ const struct rkvdec_variant *variant = ctx->dev->variant;
int fmt_idx = -1;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
- if (!rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability))
- continue;
+ for (i = 0; i < variant->num_coded_fmts; i++) {
fmt_idx++;
if (index == fmt_idx)
- return &rkvdec_coded_fmts[i];
+ return &variant->coded_fmts[i];
}
return NULL;
@@ -390,12 +599,12 @@ rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index)
static const struct rkvdec_coded_fmt_desc *
rkvdec_find_coded_fmt_desc(struct rkvdec_ctx *ctx, u32 fourcc)
{
+ const struct rkvdec_variant *variant = ctx->dev->variant;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
- if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability) &&
- rkvdec_coded_fmts[i].fourcc == fourcc)
- return &rkvdec_coded_fmts[i];
+ for (i = 0; i < variant->num_coded_fmts; i++) {
+ if (variant->coded_fmts[i].fourcc == fourcc)
+ return &variant->coded_fmts[i];
}
return NULL;
@@ -769,6 +978,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
const struct rkvdec_coded_fmt_desc *desc;
+ const struct rkvdec_variant *variant = ctx->dev->variant;
int ret;
if (V4L2_TYPE_IS_CAPTURE(q->type))
@@ -778,13 +988,22 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (WARN_ON(!desc))
return -EINVAL;
+ ret = rkvdec_allocate_rcb(ctx, variant->rcb_sizes, variant->num_rcb_sizes);
+ if (ret)
+ return ret;
+
if (desc->ops->start) {
ret = desc->ops->start(ctx);
if (ret)
- return ret;
+ goto err_ops_start;
}
return 0;
+
+err_ops_start:
+ rkvdec_free_rcb(ctx);
+
+ return ret;
}
static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state)
@@ -820,6 +1039,8 @@ static void rkvdec_stop_streaming(struct vb2_queue *q)
if (desc->ops->stop)
desc->ops->stop(ctx);
+
+ rkvdec_free_rcb(ctx);
}
rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
@@ -914,6 +1135,29 @@ void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx)
writel(reg, rkvdec->regs + RKVDEC_REG_QOS_CTRL);
}
+void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len)
+{
+#ifdef CONFIG_ARM64
+ __iowrite32_copy(dst, src, len / 4);
+#else
+ memcpy_toio(dst, src, len);
+#endif
+}
+
+void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold)
+{
+ /* Set watchdog at 2 times the hardware timeout threshold */
+ u32 watchdog_time;
+ unsigned long axi_rate = clk_get_rate(rkvdec->axi_clk);
+
+ if (axi_rate)
+ watchdog_time = 2 * div_u64(1000 * (u64)timeout_threshold, axi_rate);
+ else
+ watchdog_time = 2000;
+
+ schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(watchdog_time));
+}
+
static void rkvdec_device_run(void *priv)
{
struct rkvdec_ctx *ctx = priv;
@@ -1005,21 +1249,19 @@ static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx,
static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx)
{
+ const struct rkvdec_variant *variant = ctx->dev->variant;
unsigned int i, nctrls = 0;
int ret;
- for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++)
- if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability))
- nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls;
+ for (i = 0; i < variant->num_coded_fmts; i++)
+ nctrls += variant->coded_fmts[i].ctrls->num_ctrls;
v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls);
- for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
- if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) {
- ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls);
- if (ret)
- goto err_free_handler;
- }
+ for (i = 0; i < variant->num_coded_fmts; i++) {
+ ret = rkvdec_add_ctrls(ctx, variant->coded_fmts[i].ctrls);
+ if (ret)
+ goto err_free_handler;
}
ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
@@ -1192,10 +1434,9 @@ static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
}
}
-static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
+static irqreturn_t rk3399_irq_handler(struct rkvdec_ctx *ctx)
{
- struct rkvdec_dev *rkvdec = priv;
- struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
+ struct rkvdec_dev *rkvdec = ctx->dev;
enum vb2_buffer_state state;
u32 status;
@@ -1216,6 +1457,145 @@ static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
+static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ enum vb2_buffer_state state;
+ bool need_reset = 0;
+ u32 status;
+
+ status = readl(rkvdec->regs + VDPU381_REG_STA_INT);
+ writel(0, rkvdec->regs + VDPU381_REG_STA_INT);
+
+ if (status & VDPU381_STA_INT_DEC_RDY_STA) {
+ state = VB2_BUF_STATE_DONE;
+ } else {
+ state = VB2_BUF_STATE_ERROR;
+ if (status & (VDPU381_STA_INT_SOFTRESET_RDY |
+ VDPU381_STA_INT_TIMEOUT |
+ VDPU381_STA_INT_ERROR))
+ rkvdec_iommu_restore(rkvdec);
+ }
+
+ if (need_reset)
+ rkvdec_iommu_restore(rkvdec);
+
+ if (cancel_delayed_work(&rkvdec->watchdog_work))
+ rkvdec_job_finish(ctx, state);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ enum vb2_buffer_state state;
+ bool need_reset = 0;
+ u32 status;
+
+ status = readl(rkvdec->link + VDPU383_LINK_STA_INT);
+ writel(FIELD_PREP_WM16(VDPU383_STA_INT_ALL, 0), rkvdec->link + VDPU383_LINK_STA_INT);
+ /* On vdpu383, the interrupts must be disabled */
+ writel(FIELD_PREP_WM16(VDPU383_INT_EN_IRQ | VDPU383_INT_EN_LINE_IRQ, 0),
+ rkvdec->link + VDPU383_LINK_INT_EN);
+
+ if (status & VDPU383_STA_INT_DEC_RDY_STA) {
+ state = VB2_BUF_STATE_DONE;
+ } else {
+ state = VB2_BUF_STATE_ERROR;
+ rkvdec_iommu_restore(rkvdec);
+ }
+
+ if (need_reset)
+ rkvdec_iommu_restore(rkvdec);
+
+ if (cancel_delayed_work(&rkvdec->watchdog_work))
+ rkvdec_job_finish(ctx, state);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
+{
+ struct rkvdec_dev *rkvdec = priv;
+ struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
+ const struct rkvdec_variant *variant = rkvdec->variant;
+
+ return variant->ops->irq_handler(ctx);
+}
+
+/*
+ * Flip one or more matrices along their main diagonal and flatten them
+ * before writing it to the memory.
+ * Convert:
+ * ABCD AEIM
+ * EFGH => BFJN => AEIMBFJNCGKODHLP
+ * IJKL CGKO
+ * MNOP DHLP
+ */
+static void transpose_and_flatten_matrices(u8 *output, const u8 *input,
+ int matrices, int row_length)
+{
+ int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value;
+
+ matrix_size = row_length * row_length;
+ for (i = 0; i < matrices; i++) {
+ row = 0;
+ x_offset = 0;
+ matrix_offset = i * matrix_size;
+ for (j = 0; j < matrix_size; j++) {
+ y_offset = j - (row * row_length);
+ rot_index = y_offset * row_length + x_offset;
+ new_value = *(input + i * matrix_size + j);
+ output[matrix_offset + rot_index] = new_value;
+ if ((j + 1) % row_length == 0) {
+ row += 1;
+ x_offset += 1;
+ }
+ }
+ }
+}
+
+/*
+ * VDPU383 needs a specific order:
+ * The 8x8 flatten matrix is based on 4x4 blocks.
+ * Each 4x4 block is written separately in order.
+ *
+ * Base data => Transposed VDPU383 transposed
+ *
+ * ABCDEFGH AIQYaiqy AIQYBJRZ
+ * IJKLMNOP BJRZbjrz CKS0DLT1
+ * QRSTUVWX CKS0cks6 aiqybjrz
+ * YZ012345 => DLT1dlt7 cks6dlt7
+ * abcdefgh EMU2emu8 EMU2FNV3
+ * ijklmnop FNV3fnv9 GOW4HPX5
+ * qrstuvwx GOW4gow# emu8fnv9
+ * yz6789#$ HPX5hpx$ gow#hpx$
+ *
+ * As the function reads block of 4x4 it can be used for both 4x4 and 8x8 matrices.
+ *
+ */
+static void vdpu383_flatten_matrices(u8 *output, const u8 *input, int matrices, int row_length)
+{
+ u8 block;
+ int i, j, matrix_offset, matrix_size, new_value, input_idx, line_offset, block_offset;
+
+ matrix_size = row_length * row_length;
+ for (i = 0; i < matrices; i++) {
+ matrix_offset = i * matrix_size;
+ for (j = 0; j < matrix_size; j++) {
+ block = j / 16;
+ line_offset = (j % 16) / 4;
+ block_offset = (block & 1) * 32 + (block & 2) * 2;
+ input_idx = ((j % 4) * row_length) + line_offset + block_offset;
+
+ new_value = *(input + i * matrix_size + input_idx);
+
+ output[matrix_offset + j] = new_value;
+ }
+ }
+}
+
static void rkvdec_watchdog_func(struct work_struct *work)
{
struct rkvdec_dev *rkvdec;
@@ -1227,29 +1607,136 @@ static void rkvdec_watchdog_func(struct work_struct *work)
if (ctx) {
dev_err(rkvdec->dev, "Frame processing timed out!\n");
writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT);
- writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL);
rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
}
}
+/*
+ * Some SoCs, like RK3588 have multiple identical VDPU cores, but the
+ * kernel is currently missing support for multi-core handling. Exposing
+ * separate devices for each core to userspace is bad, since that does
+ * not allow scheduling tasks properly (and creates ABI). With this workaround
+ * the driver will only probe for the first core and early exit for the other
+ * cores. Once the driver gains multi-core support, the same technique
+ * for detecting the first core can be used to cluster all cores together.
+ */
+static int rkvdec_disable_multicore(struct rkvdec_dev *rkvdec)
+{
+ struct device_node *node = NULL;
+ const char *compatible;
+ bool is_first_core;
+ int ret;
+
+ /* Intentionally ignores the fallback strings */
+ ret = of_property_read_string(rkvdec->dev->of_node, "compatible", &compatible);
+ if (ret)
+ return ret;
+
+ /* The first compatible and available node found is considered the main core */
+ do {
+ node = of_find_compatible_node(node, NULL, compatible);
+ if (of_device_is_available(node))
+ break;
+ } while (node);
+
+ if (!node)
+ return -EINVAL;
+
+ is_first_core = (rkvdec->dev->of_node == node);
+
+ of_node_put(node);
+
+ if (!is_first_core) {
+ dev_info(rkvdec->dev, "missing multi-core support, ignoring this instance\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct rkvdec_variant_ops rk3399_variant_ops = {
+ .irq_handler = rk3399_irq_handler,
+ .colmv_size = rkvdec_colmv_size,
+ .flatten_matrices = transpose_and_flatten_matrices,
+};
+
static const struct rkvdec_variant rk3288_rkvdec_variant = {
.num_regs = 68,
- .capabilities = RKVDEC_CAPABILITY_HEVC,
+ .coded_fmts = rk3288_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(rk3288_coded_fmts),
+ .ops = &rk3399_variant_ops,
+ .has_single_reg_region = true,
};
static const struct rkvdec_variant rk3328_rkvdec_variant = {
.num_regs = 109,
- .capabilities = RKVDEC_CAPABILITY_HEVC |
- RKVDEC_CAPABILITY_H264 |
- RKVDEC_CAPABILITY_VP9,
+ .coded_fmts = rkvdec_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts),
+ .ops = &rk3399_variant_ops,
+ .has_single_reg_region = true,
.quirks = RKVDEC_QUIRK_DISABLE_QOS,
};
static const struct rkvdec_variant rk3399_rkvdec_variant = {
.num_regs = 78,
- .capabilities = RKVDEC_CAPABILITY_HEVC |
- RKVDEC_CAPABILITY_H264 |
- RKVDEC_CAPABILITY_VP9,
+ .coded_fmts = rkvdec_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts),
+ .ops = &rk3399_variant_ops,
+ .has_single_reg_region = true,
+};
+
+static const struct rcb_size_info vdpu381_rcb_sizes[] = {
+ {6, PIC_WIDTH}, // intrar
+ {1, PIC_WIDTH}, // transdr (Is actually 0.4*pic_width)
+ {1, PIC_HEIGHT}, // transdc (Is actually 0.1*pic_height)
+ {3, PIC_WIDTH}, // streamdr
+ {6, PIC_WIDTH}, // interr
+ {3, PIC_HEIGHT}, // interc
+ {22, PIC_WIDTH}, // dblkr
+ {6, PIC_WIDTH}, // saor
+ {11, PIC_WIDTH}, // fbcr
+ {67, PIC_HEIGHT}, // filtc col
+};
+
+static const struct rkvdec_variant_ops vdpu381_variant_ops = {
+ .irq_handler = vdpu381_irq_handler,
+ .colmv_size = rkvdec_colmv_size,
+ .flatten_matrices = transpose_and_flatten_matrices,
+};
+
+static const struct rkvdec_variant vdpu381_variant = {
+ .coded_fmts = vdpu381_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(vdpu381_coded_fmts),
+ .rcb_sizes = vdpu381_rcb_sizes,
+ .num_rcb_sizes = ARRAY_SIZE(vdpu381_rcb_sizes),
+ .ops = &vdpu381_variant_ops,
+};
+
+static const struct rcb_size_info vdpu383_rcb_sizes[] = {
+ {6, PIC_WIDTH}, // streamd
+ {6, PIC_WIDTH}, // streamd_tile
+ {12, PIC_WIDTH}, // inter
+ {12, PIC_WIDTH}, // inter_tile
+ {16, PIC_WIDTH}, // intra
+ {10, PIC_WIDTH}, // intra_tile
+ {120, PIC_WIDTH}, // filterd
+ {120, PIC_WIDTH}, // filterd_protect
+ {120, PIC_WIDTH}, // filterd_tile_row
+ {180, PIC_HEIGHT}, // filterd_tile_col
+};
+
+static const struct rkvdec_variant_ops vdpu383_variant_ops = {
+ .irq_handler = vdpu383_irq_handler,
+ .colmv_size = vdpu383_colmv_size,
+ .flatten_matrices = vdpu383_flatten_matrices,
+};
+
+static const struct rkvdec_variant vdpu383_variant = {
+ .coded_fmts = vdpu383_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(vdpu383_coded_fmts),
+ .rcb_sizes = vdpu383_rcb_sizes,
+ .num_rcb_sizes = ARRAY_SIZE(vdpu383_rcb_sizes),
+ .ops = &vdpu383_variant_ops,
};
static const struct of_device_id of_rkvdec_match[] = {
@@ -1265,19 +1752,22 @@ static const struct of_device_id of_rkvdec_match[] = {
.compatible = "rockchip,rk3399-vdec",
.data = &rk3399_rkvdec_variant,
},
+ {
+ .compatible = "rockchip,rk3588-vdec",
+ .data = &vdpu381_variant,
+ },
+ {
+ .compatible = "rockchip,rk3576-vdec",
+ .data = &vdpu383_variant,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_rkvdec_match);
-static const char * const rkvdec_clk_names[] = {
- "axi", "ahb", "cabac", "core"
-};
-
static int rkvdec_probe(struct platform_device *pdev)
{
const struct rkvdec_variant *variant;
struct rkvdec_dev *rkvdec;
- unsigned int i;
int ret, irq;
variant = of_device_get_match_data(&pdev->dev);
@@ -1294,22 +1784,30 @@ static int rkvdec_probe(struct platform_device *pdev)
mutex_init(&rkvdec->vdev_lock);
INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func);
- rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names),
- sizeof(*rkvdec->clocks), GFP_KERNEL);
- if (!rkvdec->clocks)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++)
- rkvdec->clocks[i].id = rkvdec_clk_names[i];
-
- ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names),
- rkvdec->clocks);
+ ret = rkvdec_disable_multicore(rkvdec);
if (ret)
return ret;
- rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(rkvdec->regs))
- return PTR_ERR(rkvdec->regs);
+ ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &rkvdec->clocks);
+ if (ret < 0)
+ return ret;
+
+ rkvdec->num_clocks = ret;
+ rkvdec->axi_clk = devm_clk_get(&pdev->dev, "axi");
+
+ if (rkvdec->variant->has_single_reg_region) {
+ rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rkvdec->regs))
+ return PTR_ERR(rkvdec->regs);
+ } else {
+ rkvdec->regs = devm_platform_ioremap_resource_byname(pdev, "function");
+ if (IS_ERR(rkvdec->regs))
+ return PTR_ERR(rkvdec->regs);
+
+ rkvdec->link = devm_platform_ioremap_resource_byname(pdev, "link");
+ if (IS_ERR(rkvdec->link))
+ return PTR_ERR(rkvdec->link);
+ }
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
@@ -1331,6 +1829,10 @@ static int rkvdec_probe(struct platform_device *pdev)
return ret;
}
+ rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (!rkvdec->sram_pool && rkvdec->variant->num_rcb_sizes > 0)
+ dev_info(&pdev->dev, "No sram node, RCB will be stored in RAM\n");
+
pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
@@ -1339,7 +1841,8 @@ static int rkvdec_probe(struct platform_device *pdev)
if (ret)
goto err_disable_runtime_pm;
- if (iommu_get_domain_for_dev(&pdev->dev)) {
+ rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
+ if (rkvdec->iommu_domain) {
rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
if (IS_ERR(rkvdec->empty_domain)) {
@@ -1353,6 +1856,10 @@ static int rkvdec_probe(struct platform_device *pdev)
err_disable_runtime_pm:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+
+ if (rkvdec->sram_pool)
+ gen_pool_destroy(rkvdec->sram_pool);
+
return ret;
}
@@ -1375,16 +1882,14 @@ static int rkvdec_runtime_resume(struct device *dev)
{
struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
- return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names),
- rkvdec->clocks);
+ return clk_bulk_prepare_enable(rkvdec->num_clocks, rkvdec->clocks);
}
static int rkvdec_runtime_suspend(struct device *dev)
{
struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
- clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names),
- rkvdec->clocks);
+ clk_bulk_disable_unprepare(rkvdec->num_clocks, rkvdec->clocks);
return 0;
}
#endif
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 566e06fa2b1e..a24be6638b6b 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -19,16 +19,18 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
-#define RKVDEC_CAPABILITY_HEVC BIT(0)
-#define RKVDEC_CAPABILITY_H264 BIT(1)
-#define RKVDEC_CAPABILITY_VP9 BIT(2)
-
#define RKVDEC_QUIRK_DISABLE_QOS BIT(0)
+#define RKVDEC_1080P_PIXELS (1920 * 1088)
+#define RKVDEC_4K_PIXELS (4096 * 2304)
+#define RKVDEC_8K_PIXELS (7680 * 4320)
+
struct rkvdec_ctx;
+struct rkvdec_rcb_config;
struct rkvdec_ctrl_desc {
struct v4l2_ctrl_config cfg;
@@ -69,9 +71,20 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf)
base.vb.vb2_buf);
}
+struct rkvdec_variant_ops {
+ irqreturn_t (*irq_handler)(struct rkvdec_ctx *ctx);
+ u32 (*colmv_size)(u16 width, u16 height);
+ void (*flatten_matrices)(u8 *output, const u8 *input, int matrices, int row_length);
+};
+
struct rkvdec_variant {
unsigned int num_regs;
- unsigned int capabilities;
+ const struct rkvdec_coded_fmt_desc *coded_fmts;
+ size_t num_coded_fmts;
+ const struct rcb_size_info *rcb_sizes;
+ size_t num_rcb_sizes;
+ const struct rkvdec_variant_ops *ops;
+ bool has_single_reg_region;
unsigned int quirks;
};
@@ -110,7 +123,6 @@ struct rkvdec_coded_fmt_desc {
unsigned int num_decoded_fmts;
const struct rkvdec_decoded_fmt_desc *decoded_fmts;
u32 subsystem_flags;
- unsigned int capability;
};
struct rkvdec_dev {
@@ -120,9 +132,14 @@ struct rkvdec_dev {
struct v4l2_m2m_dev *m2m_dev;
struct device *dev;
struct clk_bulk_data *clocks;
+ unsigned int num_clocks;
+ struct clk *axi_clk;
void __iomem *regs;
+ void __iomem *link;
struct mutex vdev_lock; /* serializes ioctls */
struct delayed_work watchdog_work;
+ struct gen_pool *sram_pool;
+ struct iommu_domain *iommu_domain;
struct iommu_domain *empty_domain;
const struct rkvdec_variant *variant;
};
@@ -135,7 +152,11 @@ struct rkvdec_ctx {
struct v4l2_ctrl_handler ctrl_hdl;
struct rkvdec_dev *dev;
enum rkvdec_image_fmt image_fmt;
+ struct rkvdec_rcb_config *rcb_config;
+ u32 colmv_offset;
void *priv;
+ u8 has_sps_st_rps: 1;
+ u8 has_sps_lt_rps: 1;
};
static inline struct rkvdec_ctx *file_to_rkvdec_ctx(struct file *filp)
@@ -143,19 +164,36 @@ static inline struct rkvdec_ctx *file_to_rkvdec_ctx(struct file *filp)
return container_of(file_to_v4l2_fh(filp), struct rkvdec_ctx, fh);
}
+enum rkvdec_alloc_type {
+ RKVDEC_ALLOC_DMA = 0,
+ RKVDEC_ALLOC_SRAM = 1,
+};
+
struct rkvdec_aux_buf {
void *cpu;
dma_addr_t dma;
size_t size;
+ enum rkvdec_alloc_type type;
};
void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
+void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len);
+void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold);
void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx);
+/* RKVDEC ops */
extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops;
+/* VDPU381 ops */
+extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops;
+extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops;
+
+/* VDPU383 ops */
+extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops;
+extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops;
+
#endif /* RKVDEC_H_ */
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index 1c1b6b48918e..19e6b187be22 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -57,6 +57,7 @@ struct dcmipp_bytecap_pix_map {
static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565),
PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565),
+ PIXMAP_MBUS_PFMT(RGB888_1X24, RGB24),
PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV),
PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV),
PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU),
@@ -66,6 +67,9 @@ static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY),
PIXMAP_MBUS_PFMT(VYUY8_1X16, VYUY),
PIXMAP_MBUS_PFMT(Y8_1X8, GREY),
+ PIXMAP_MBUS_PFMT(Y10_1X10, Y10),
+ PIXMAP_MBUS_PFMT(Y12_1X12, Y12),
+ PIXMAP_MBUS_PFMT(Y14_1X14, Y14),
PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8),
PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8),
PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8),
@@ -143,7 +147,6 @@ struct dcmipp_bytecap_device {
void __iomem *regs;
- u32 cmier;
u32 cmsr2;
struct {
@@ -439,8 +442,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
dcmipp_start_capture(vcap, vcap->next);
/* Enable interruptions */
- vcap->cmier |= DCMIPP_CMIER_P0ALL;
- reg_set(vcap, DCMIPP_CMIER, vcap->cmier);
+ reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
vcap->state = DCMIPP_RUNNING;
@@ -496,7 +498,7 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
media_pipeline_stop(vcap->vdev.entity.pads);
/* Disable interruptions */
- reg_clear(vcap, DCMIPP_CMIER, vcap->cmier);
+ reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
/* Stop capture */
reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
@@ -512,6 +514,9 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
/* Disable pipe */
reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
+ /* Clear any pending interrupts */
+ reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_P0ALL);
+
spin_lock_irq(&vcap->irqlock);
/* Return all queued buffers to vb2 in ERROR state */
@@ -742,23 +747,20 @@ 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) {
+ if (vcap->cmsr2 & DCMIPP_CMSR2_P0OVRF) {
vcap->count.errors++;
vcap->count.overrun++;
}
- if (cmsr2 & DCMIPP_CMSR2_P0FRAMEF) {
+ if (vcap->cmsr2 & DCMIPP_CMSR2_P0FRAMEF) {
vcap->count.frame++;
/* Read captured buffer size */
@@ -766,7 +768,7 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
dcmipp_bytecap_process_frame(vcap, bytesused);
}
- if (cmsr2 & DCMIPP_CMSR2_P0VSYNCF) {
+ if (vcap->cmsr2 & DCMIPP_CMSR2_P0VSYNCF) {
vcap->count.vsync++;
if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) {
vcap->count.underrun++;
@@ -797,7 +799,7 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
container_of(arg, struct dcmipp_bytecap_device, ved);
/* Store interrupt status register */
- vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & vcap->cmier;
+ vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & DCMIPP_CMIER_P0ALL;
vcap->count.it++;
/* Clear interrupt */
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index db76a02a1848..f9e4a3a9ef3f 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -49,6 +49,8 @@ struct dcmipp_byteproc_pix_map {
static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {
PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2),
PIXMAP_MBUS_BPP(RGB565_1X16, 2),
+ PIXMAP_MBUS_BPP(RGB888_3X8, 3),
+ PIXMAP_MBUS_BPP(RGB888_1X24, 3),
PIXMAP_MBUS_BPP(YUYV8_2X8, 2),
PIXMAP_MBUS_BPP(YUYV8_1X16, 2),
PIXMAP_MBUS_BPP(YVYU8_2X8, 2),
@@ -58,6 +60,9 @@ static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {
PIXMAP_MBUS_BPP(VYUY8_2X8, 2),
PIXMAP_MBUS_BPP(VYUY8_1X16, 2),
PIXMAP_MBUS_BPP(Y8_1X8, 1),
+ PIXMAP_MBUS_BPP(Y10_1X10, 2),
+ PIXMAP_MBUS_BPP(Y12_1X12, 2),
+ PIXMAP_MBUS_BPP(Y14_1X14, 2),
PIXMAP_MBUS_BPP(SBGGR8_1X8, 1),
PIXMAP_MBUS_BPP(SGBRG8_1X8, 1),
PIXMAP_MBUS_BPP(SGRBG8_1X8, 1),
@@ -126,15 +131,22 @@ static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r,
static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r,
const struct v4l2_mbus_framefmt *fmt)
{
+ const struct dcmipp_byteproc_pix_map *vpix;
+
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) {
+ if (fmt->code >= MEDIA_BUS_FMT_SBGGR8_1X8 &&
+ fmt->code <= MEDIA_BUS_FMT_JPEG_1X8) {
+ r->width = fmt->width;
+ r->height = fmt->height;
+ return;
+ }
+
+ /* Prevent compose on formats which are not 1 or 2 bytes per pixel */
+ vpix = dcmipp_byteproc_pix_map_by_code(fmt->code);
+ if (vpix->bpp != 1 && vpix->bpp != 2) {
r->width = fmt->width;
r->height = fmt->height;
return;
@@ -147,7 +159,7 @@ static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r,
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))
+ if (vpix->bpp == 1 && r->width <= (fmt->width / 4))
r->width = fmt->width / 4;
else if (r->width <= (fmt->width / 2))
r->width = fmt->width / 2;
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 1b7bae3266c8..49398d077764 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -526,7 +526,12 @@ static int dcmipp_probe(struct platform_device *pdev)
return ret;
}
- kclk = devm_clk_get(&pdev->dev, "kclk");
+ /*
+ * In case of the DCMIPP has only 1 clock (such as on MP13), the
+ * clock might not be named.
+ */
+ kclk = devm_clk_get(&pdev->dev,
+ dcmipp->pipe_cfg->needs_mclk ? "kclk" : NULL);
if (IS_ERR(kclk))
return dev_err_probe(&pdev->dev, PTR_ERR(kclk),
"Unable to get kclk\n");
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
index 7e5311b67d7e..c4bc76909b1c 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -19,11 +19,15 @@
#define DCMIPP_PRCR_FORMAT_SHIFT 16
#define DCMIPP_PRCR_FORMAT_YUV422 0x1e
#define DCMIPP_PRCR_FORMAT_RGB565 0x22
+#define DCMIPP_PRCR_FORMAT_RGB888 0x24
#define DCMIPP_PRCR_FORMAT_RAW8 0x2a
#define DCMIPP_PRCR_FORMAT_RAW10 0x2b
#define DCMIPP_PRCR_FORMAT_RAW12 0x2c
#define DCMIPP_PRCR_FORMAT_RAW14 0x2d
#define DCMIPP_PRCR_FORMAT_G8 0x4a
+#define DCMIPP_PRCR_FORMAT_G10 0x4b
+#define DCMIPP_PRCR_FORMAT_G12 0x4c
+#define DCMIPP_PRCR_FORMAT_G14 0x4d
#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a
#define DCMIPP_PRCR_ESS BIT(4)
#define DCMIPP_PRCR_PCKPOL BIT(5)
@@ -72,6 +76,9 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = {
PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565),
PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565),
PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565),
+ /* RGB888 */
+ PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_3X8, RGB888_3X8, RGB888, 0, MIPI_CSI2_DT_RGB888),
+ PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_1X24, RGB888_1X24, RGB888, 0, MIPI_CSI2_DT_RGB888),
/* YUV422 */
PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
@@ -85,6 +92,9 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = {
PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
/* GREY */
PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8),
+ PIXMAP_SINK_SRC_PRCR_SWAP(Y10_1X10, Y10_1X10, G10, 0, MIPI_CSI2_DT_RAW10),
+ PIXMAP_SINK_SRC_PRCR_SWAP(Y12_1X12, Y12_1X12, G12, 0, MIPI_CSI2_DT_RAW12),
+ PIXMAP_SINK_SRC_PRCR_SWAP(Y14_1X14, Y14_1X14, G14, 0, MIPI_CSI2_DT_RAW14),
/* Raw Bayer */
PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
index 4fd521f78425..e798ec00b189 100644
--- a/drivers/media/platform/synopsys/Kconfig
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -1,3 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/media/platform/synopsys/hdmirx/Kconfig"
+
+config VIDEO_DW_MIPI_CSI2RX
+ tristate "Synopsys DesignWare MIPI CSI-2 Receiver"
+ depends on VIDEO_DEV
+ depends on V4L_PLATFORM_DRIVERS
+ depends on PM && COMMON_CLK
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with
+ one input port and one output port. It receives the data with the
+ help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g.,
+ the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs.
+ This is a driver for this unit.
+
+ To compile this driver as a module, choose M here: the module
+ will be called dw-mipi-csi2rx.
diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
index 3b12c574dd67..e0232ee23304 100644
--- a/drivers/media/platform/synopsys/Makefile
+++ b/drivers/media/platform/synopsys/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += hdmirx/
+
+obj-$(CONFIG_VIDEO_DW_MIPI_CSI2RX) += dw-mipi-csi2rx.o
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
new file mode 100644
index 000000000000..170346ae1a59
--- /dev/null
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synopsys DesignWare MIPI CSI-2 Receiver Driver
+ *
+ * Copyright (C) 2019 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2026 Collabora, Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#define DW_MIPI_CSI2RX_N_LANES 0x04
+#define DW_MIPI_CSI2RX_RESETN 0x10
+#define DW_MIPI_CSI2RX_PHY_STATE 0x14
+#define DW_MIPI_CSI2RX_ERR1 0x20
+#define DW_MIPI_CSI2RX_ERR2 0x24
+#define DW_MIPI_CSI2RX_MSK1 0x28
+#define DW_MIPI_CSI2RX_MSK2 0x2c
+#define DW_MIPI_CSI2RX_CONTROL 0x40
+
+#define SW_CPHY_EN(x) ((x) << 0)
+#define SW_DSI_EN(x) ((x) << 4)
+#define SW_DATATYPE_FS(x) ((x) << 8)
+#define SW_DATATYPE_FE(x) ((x) << 14)
+#define SW_DATATYPE_LS(x) ((x) << 20)
+#define SW_DATATYPE_LE(x) ((x) << 26)
+
+#define DW_MIPI_CSI2RX_CLKS_MAX 1
+
+enum {
+ DW_MIPI_CSI2RX_PAD_SINK,
+ DW_MIPI_CSI2RX_PAD_SRC,
+ DW_MIPI_CSI2RX_PAD_MAX,
+};
+
+struct dw_mipi_csi2rx_format {
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+};
+
+struct dw_mipi_csi2rx_device {
+ struct device *dev;
+
+ void __iomem *base_addr;
+ struct clk_bulk_data *clks;
+ unsigned int clks_num;
+ struct phy *phy;
+ struct reset_control *reset;
+
+ const struct dw_mipi_csi2rx_format *formats;
+ unsigned int formats_num;
+
+ struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev sd;
+
+ enum v4l2_mbus_type bus_type;
+ u32 lanes_num;
+};
+
+static const struct v4l2_mbus_framefmt default_format = {
+ .width = 3840,
+ .height = 2160,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+};
+
+static const struct dw_mipi_csi2rx_format formats[] = {
+ /* YUV formats */
+ {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ /* RGB formats */
+ {
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ },
+ {
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ },
+ /* Bayer formats */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ },
+};
+
+static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct dw_mipi_csi2rx_device, sd);
+}
+
+static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2,
+ unsigned int addr, u32 val)
+{
+ writel(val, csi2->base_addr + addr);
+}
+
+static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2,
+ unsigned int addr)
+{
+ return readl(csi2->base_addr + addr);
+}
+
+static const struct dw_mipi_csi2rx_format *
+dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code)
+{
+ WARN_ON(csi2->formats_num == 0);
+
+ for (unsigned int i = 0; i < csi2->formats_num; i++) {
+ const struct dw_mipi_csi2rx_format *format = &csi2->formats[i];
+
+ if (format->code == mbus_code)
+ return format;
+ }
+
+ return NULL;
+}
+
+static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
+{
+ struct media_pad *source_pad;
+ union phy_configure_opts opts;
+ u32 lanes = csi2->lanes_num;
+ u32 control = 0;
+ s64 link_freq;
+ int ret;
+
+ if (lanes < 1 || lanes > 4)
+ return -EINVAL;
+
+ source_pad = media_pad_remote_pad_unique(
+ &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
+ if (IS_ERR(source_pad))
+ return PTR_ERR(source_pad);
+
+ /* set mult and div to 0, thus completely rely on V4L2_CID_LINK_FREQ */
+ link_freq = v4l2_get_link_freq(source_pad, 0, 0);
+ if (link_freq < 0)
+ return link_freq;
+
+ switch (csi2->bus_type) {
+ case V4L2_MBUS_CSI2_DPHY:
+ ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2,
+ lanes, &opts.mipi_dphy);
+ if (ret)
+ return ret;
+
+ ret = phy_set_mode(csi2->phy, PHY_MODE_MIPI_DPHY);
+ if (ret)
+ return ret;
+
+ ret = phy_configure(csi2->phy, &opts);
+ if (ret)
+ return ret;
+
+ control |= SW_CPHY_EN(0);
+ break;
+
+ case V4L2_MBUS_CSI2_CPHY:
+ /* TODO: implement CPHY configuration */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+
+ control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
+ SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
+
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
+
+ return phy_power_on(csi2->phy);
+}
+
+static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
+{
+ phy_power_off(csi2->phy);
+
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
+ dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
+}
+
+static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int
+dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
+
+ switch (code->pad) {
+ case DW_MIPI_CSI2RX_PAD_SRC:
+ if (code->index)
+ return -EINVAL;
+
+ code->code =
+ v4l2_subdev_state_get_format(sd_state,
+ DW_MIPI_CSI2RX_PAD_SINK)->code;
+
+ return 0;
+ case DW_MIPI_CSI2RX_PAD_SINK:
+ if (code->index > csi2->formats_num)
+ return -EINVAL;
+
+ code->code = csi2->formats[code->index].code;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
+ const struct dw_mipi_csi2rx_format *fmt;
+ struct v4l2_mbus_framefmt *sink, *src;
+
+ /* the format on the source pad always matches the sink pad */
+ if (format->pad == DW_MIPI_CSI2RX_PAD_SRC)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!sink)
+ return -EINVAL;
+
+ fmt = dw_mipi_csi2rx_find_format(csi2, format->format.code);
+ if (!fmt)
+ format->format = default_format;
+
+ *sink = format->format;
+
+ /* propagate the format to the source pad */
+ src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!src)
+ return -EINVAL;
+
+ *src = *sink;
+
+ return 0;
+}
+
+static int dw_mipi_csi2rx_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &default_format);
+}
+
+static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
+ struct v4l2_subdev *remote_sd;
+ struct media_pad *sink_pad, *remote_pad;
+ struct device *dev = csi2->dev;
+ u64 mask;
+ int ret;
+
+ sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
+ remote_pad = media_pad_remote_pad_first(sink_pad);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
+ DW_MIPI_CSI2RX_PAD_SRC,
+ &streams_mask);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto err;
+
+ ret = dw_mipi_csi2rx_start(csi2);
+ if (ret) {
+ dev_err(dev, "failed to enable CSI hardware\n");
+ goto err_pm_runtime_put;
+ }
+
+ ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
+ if (ret)
+ goto err_csi_stop;
+
+ return 0;
+
+err_csi_stop:
+ dw_mipi_csi2rx_stop(csi2);
+err_pm_runtime_put:
+ pm_runtime_put(dev);
+err:
+ return ret;
+}
+
+static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
+ struct v4l2_subdev *remote_sd;
+ struct media_pad *sink_pad, *remote_pad;
+ struct device *dev = csi2->dev;
+ u64 mask;
+ int ret;
+
+ sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
+ remote_pad = media_pad_remote_pad_first(sink_pad);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
+ DW_MIPI_CSI2RX_PAD_SRC,
+ &streams_mask);
+
+ ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
+
+ dw_mipi_csi2rx_stop(csi2);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
+ .enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dw_mipi_csi2rx_set_fmt,
+ .set_routing = dw_mipi_csi2rx_set_routing,
+ .enable_streams = dw_mipi_csi2rx_enable_streams,
+ .disable_streams = dw_mipi_csi2rx_disable_streams,
+};
+
+static const struct v4l2_subdev_ops dw_mipi_csi2rx_ops = {
+ .pad = &dw_mipi_csi2rx_pad_ops,
+};
+
+static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = DW_MIPI_CSI2RX_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = DW_MIPI_CSI2RX_PAD_SRC,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+ struct v4l2_subdev_krouting routing = {
+ .len_routes = ARRAY_SIZE(routes),
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
+ &default_format);
+}
+
+static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = {
+ .init_state = dw_mipi_csi2rx_init_state,
+};
+
+static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asd)
+{
+ struct dw_mipi_csi2rx_device *csi2 =
+ container_of(notifier, struct dw_mipi_csi2rx_device, notifier);
+ struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK];
+ int ret;
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(csi2->dev, "failed to link source pad of %s\n",
+ sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = {
+ .bound = dw_mipi_csi2rx_notifier_bound,
+};
+
+static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2)
+{
+ struct v4l2_async_connection *asd;
+ struct v4l2_async_notifier *ntf = &csi2->notifier;
+ struct v4l2_fwnode_endpoint vep;
+ struct v4l2_subdev *sd = &csi2->sd;
+ struct device *dev = csi2->dev;
+ int ret;
+
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+ if (!ep)
+ return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n");
+
+ vep.bus_type = V4L2_MBUS_UNKNOWN;
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to parse endpoint\n");
+
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
+ vep.bus_type != V4L2_MBUS_CSI2_CPHY)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid bus type of endpoint\n");
+
+ csi2->bus_type = vep.bus_type;
+ csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes;
+
+ v4l2_async_subdev_nf_init(ntf, sd);
+ ntf->ops = &dw_mipi_csi2rx_notifier_ops;
+
+ asd = v4l2_async_nf_add_fwnode_remote(ntf, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto err_nf_cleanup;
+ }
+
+ ret = v4l2_async_nf_register(ntf);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to register notifier\n");
+ goto err_nf_cleanup;
+ }
+
+ return 0;
+
+err_nf_cleanup:
+ v4l2_async_nf_cleanup(ntf);
+
+ return ret;
+}
+
+static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
+{
+ struct media_pad *pads = csi2->pads;
+ struct v4l2_subdev *sd = &csi2->sd;
+ int ret;
+
+ ret = dw_mipi_csi2rx_register_notifier(csi2);
+ if (ret)
+ goto err;
+
+ v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops);
+ sd->dev = csi2->dev;
+ sd->entity.ops = &dw_mipi_csi2rx_media_ops;
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+ sd->internal_ops = &dw_mipi_csi2rx_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s",
+ dev_name(csi2->dev));
+
+ pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads);
+ if (ret)
+ goto err_notifier_unregister;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(sd->dev, "failed to register CSI-2 subdev\n");
+ goto err_subdev_cleanup;
+ }
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+err_notifier_unregister:
+ v4l2_async_nf_unregister(&csi2->notifier);
+ v4l2_async_nf_cleanup(&csi2->notifier);
+err:
+ return ret;
+}
+
+static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
+{
+ struct v4l2_subdev *sd = &csi2->sd;
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_async_nf_unregister(&csi2->notifier);
+ v4l2_async_nf_cleanup(&csi2->notifier);
+}
+
+static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
+ {
+ .compatible = "rockchip,rk3568-mipi-csi2",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_csi2rx_of_match);
+
+static int dw_mipi_csi2rx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_mipi_csi2rx_device *csi2;
+ int ret;
+
+ csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL);
+ if (!csi2)
+ return -ENOMEM;
+ csi2->dev = dev;
+ dev_set_drvdata(dev, csi2);
+
+ csi2->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(csi2->base_addr))
+ return PTR_ERR(csi2->base_addr);
+
+ ret = devm_clk_bulk_get_all(dev, &csi2->clks);
+ if (ret != DW_MIPI_CSI2RX_CLKS_MAX)
+ return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
+ csi2->clks_num = ret;
+
+ csi2->phy = devm_phy_get(dev, NULL);
+ if (IS_ERR(csi2->phy))
+ return dev_err_probe(dev, PTR_ERR(csi2->phy),
+ "failed to get MIPI CSI-2 PHY\n");
+
+ csi2->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(csi2->reset))
+ return dev_err_probe(dev, PTR_ERR(csi2->reset),
+ "failed to get reset\n");
+
+ csi2->formats = formats;
+ csi2->formats_num = ARRAY_SIZE(formats);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable pm runtime\n");
+
+ ret = phy_init(csi2->phy);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to initialize MIPI CSI-2 PHY\n");
+
+ ret = dw_mipi_csi2rx_register(csi2);
+ if (ret)
+ goto err_phy_exit;
+
+ return 0;
+
+err_phy_exit:
+ phy_exit(csi2->phy);
+
+ return ret;
+}
+
+static void dw_mipi_csi2rx_remove(struct platform_device *pdev)
+{
+ struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev);
+
+ dw_mipi_csi2rx_unregister(csi2);
+ phy_exit(csi2->phy);
+}
+
+static int dw_mipi_csi2rx_runtime_suspend(struct device *dev)
+{
+ struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(csi2->clks_num, csi2->clks);
+
+ return 0;
+}
+
+static int dw_mipi_csi2rx_runtime_resume(struct device *dev)
+{
+ struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
+ int ret;
+
+ reset_control_assert(csi2->reset);
+ udelay(5);
+ reset_control_deassert(csi2->reset);
+
+ ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(dw_mipi_csi2rx_pm_ops,
+ dw_mipi_csi2rx_runtime_suspend,
+ dw_mipi_csi2rx_runtime_resume, NULL);
+
+static struct platform_driver dw_mipi_csi2rx_drv = {
+ .driver = {
+ .name = "dw-mipi-csi2rx",
+ .of_match_table = dw_mipi_csi2rx_of_match,
+ .pm = pm_ptr(&dw_mipi_csi2rx_pm_ops),
+ },
+ .probe = dw_mipi_csi2rx_probe,
+ .remove = dw_mipi_csi2rx_remove,
+};
+module_platform_driver(dw_mipi_csi2rx_drv);
+
+MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Receiver platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index c3007e09bc9f..9cceffa4ce25 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -132,12 +132,14 @@ struct snps_hdmirx_dev {
struct delayed_work delayed_work_hotplug;
struct delayed_work delayed_work_res_change;
struct hdmirx_cec *cec;
+ struct mutex phy_rw_lock; /* to protect phy r/w configuration */
struct mutex stream_lock; /* to lock video stream capture */
struct mutex work_lock; /* to lock the critical section of hotplug event */
struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
struct clk_bulk_data *clks;
struct regmap *grf;
struct regmap *vo1_grf;
+ struct completion cr_read_done;
struct completion cr_write_done;
struct completion timer_base_lock;
struct completion avi_pkt_rcv;
@@ -796,11 +798,50 @@ static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
return 0;
}
+static int hdmirx_phy_register_read(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 phy_reg, u32 *val)
+{
+ struct device *dev = hdmirx_dev->dev;
+ u32 status;
+
+ guard(mutex)(&hdmirx_dev->phy_rw_lock);
+
+ reinit_completion(&hdmirx_dev->cr_read_done);
+ /* clear irq status */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_READ_DONE, PHYCREG_CR_READ_DONE);
+ /* write phy reg addr */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
+ /* config read enable */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_READ_P);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->cr_read_done,
+ msecs_to_jiffies(20))) {
+ dev_err(dev, "%s wait cr read done failed\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* read phy reg value */
+ status = hdmirx_readl(hdmirx_dev, PHYCREG_STATUS);
+ if (!(status & PHYCREG_CR_PARA_DATAVALID)) {
+ dev_err(dev, "%s cr read failed\n", __func__);
+ return -EINVAL;
+ }
+
+ *val = status & PHYCREG_CR_PARA_RD_DATA_MASK;
+
+ return 0;
+}
+
static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
u32 phy_reg, u32 val)
{
struct device *dev = hdmirx_dev->dev;
+ guard(mutex)(&hdmirx_dev->phy_rw_lock);
+
reinit_completion(&hdmirx_dev->cr_write_done);
/* clear irq status */
hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
@@ -1814,6 +1855,13 @@ static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
*handled = true;
}
+ if (status & PHYCREG_CR_READ_DONE) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_READ_DONE, 0);
+ complete(&hdmirx_dev->cr_read_done);
+ *handled = true;
+ }
+
if (status & TMDSVALID_STABLE_CHG) {
process_signal_change(hdmirx_dev);
v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
@@ -2313,18 +2361,51 @@ static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
}
-static void hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
+static int hdmirx_detect_broken_interrupt(struct snps_hdmirx_dev *hdmirx_dev)
{
+ int ret;
+ u32 val;
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
+
+ ret = hdmirx_phy_register_read(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG,
+ &val);
+
+ disable_irq(hdmirx_dev->hdmi_irq);
+
+ return ret;
+}
+
+static int hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ int ret;
+
hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
(HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
+
+ /*
+ * RK3588 downstream version of TF-A remaps HDMIRX interrupt and
+ * requires use of a vendor-specific FW API that we don't support
+ * in this driver.
+ */
+ ret = hdmirx_detect_broken_interrupt(hdmirx_dev);
+ if (ret)
+ dev_err_probe(hdmirx_dev->dev, ret,
+ "interrupt not functioning, open-source TF-A is required by this driver\n");
+
/*
* Some interrupts are enabled by default, so we disable
* all interrupts and clear interrupts status first.
*/
hdmirx_disable_all_interrupts(hdmirx_dev);
+
+ return ret;
}
/* hdmi-4k-300mhz EDID produced by v4l2-ctl tool */
@@ -2606,10 +2687,12 @@ static int hdmirx_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
"failed to remap regs resource\n");
+ mutex_init(&hdmirx_dev->phy_rw_lock);
mutex_init(&hdmirx_dev->stream_lock);
mutex_init(&hdmirx_dev->work_lock);
spin_lock_init(&hdmirx_dev->rst_lock);
+ init_completion(&hdmirx_dev->cr_read_done);
init_completion(&hdmirx_dev->cr_write_done);
init_completion(&hdmirx_dev->timer_base_lock);
init_completion(&hdmirx_dev->avi_pkt_rcv);
@@ -2623,7 +2706,10 @@ static int hdmirx_probe(struct platform_device *pdev)
hdmirx_dev->timings = cea640x480;
hdmirx_enable(dev);
- hdmirx_init(hdmirx_dev);
+
+ ret = hdmirx_init(hdmirx_dev);
+ if (ret)
+ goto err_pm;
v4l2_dev = &hdmirx_dev->v4l2_dev;
strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
index b13f58e31944..31b887e94e87 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -116,6 +116,8 @@
#define PHYCREG_CR_PARA_WRITE_P BIT(1)
#define PHYCREG_CR_PARA_READ_P BIT(0)
#define PHYCREG_STATUS 0x00f4
+#define PHYCREG_CR_PARA_DATAVALID BIT(24)
+#define PHYCREG_CR_PARA_RD_DATA_MASK GENMASK(15, 0)
#define MAINUNIT_STATUS 0x0150
#define TMDSVALID_STABLE_ST BIT(1)
diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
index 3bc4aa35887e..da33facf4467 100644
--- a/drivers/media/platform/ti/Kconfig
+++ b/drivers/media/platform/ti/Kconfig
@@ -41,6 +41,19 @@ config VIDEO_TI_CAL_MC
default. Note that this behavior can be overridden via
module parameter 'mc_api'.
+config VIDEO_TI_VIP
+ tristate "TI Video Input Port"
+ depends on VIDEO_DEV
+ depends on SOC_DRA7XX || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_TI_VPDMA
+ select VIDEO_TI_SC
+ select VIDEO_TI_CSC
+ help
+ Driver support for VIP module on certain TI SoC's
+ VIP = Video Input Port.
+
# Mem2mem drivers
config VIDEO_TI_VPE
diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c
index 55ee14e8b449..9dbf06ac058d 100644
--- a/drivers/media/platform/ti/omap3isp/ispccdc.c
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.c
@@ -2675,6 +2675,7 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ccdc_media_ops;
+ me->function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV;
ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c
index 1204ee221c9e..d668111b44f4 100644
--- a/drivers/media/platform/ti/omap3isp/ispccp2.c
+++ b/drivers/media/platform/ti/omap3isp/ispccp2.c
@@ -658,7 +658,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2,
fmt->height = clamp_t(u32, fmt->height,
ISPCCP2_DAT_SIZE_MIN,
ISPCCP2_DAT_SIZE_MAX);
- } else if (ccp2->input == CCP2_INPUT_MEMORY) {
+ } else {
fmt->width = clamp_t(u32, fmt->width,
ISPCCP2_LCM_HSIZE_COUNT_MIN,
ISPCCP2_LCM_HSIZE_COUNT_MAX);
@@ -1086,6 +1086,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ccp2_media_ops;
+ me->function = MEDIA_ENT_F_VID_IF_BRIDGE;
ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c
index ae574e1b6528..f227042b61b6 100644
--- a/drivers/media/platform/ti/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c
@@ -1251,6 +1251,7 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
| MEDIA_PAD_FL_MUST_CONNECT;
me->ops = &csi2_media_ops;
+ me->function = MEDIA_ENT_F_VID_IF_BRIDGE;
ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c
index e383a57654de..3f3b5bd9cdc7 100644
--- a/drivers/media/platform/ti/omap3isp/isppreview.c
+++ b/drivers/media/platform/ti/omap3isp/isppreview.c
@@ -1742,22 +1742,17 @@ static void preview_try_format(struct isp_prev_device *prev,
switch (pad) {
case PREV_PAD_SINK:
- /* When reading data from the CCDC, the input size has already
- * been mangled by the CCDC output pad so it can be accepted
- * as-is.
- *
- * When reading data from memory, clamp the requested width and
- * height. The TRM doesn't specify a minimum input height, make
+ /*
+ * Clamp the requested width and height.
+ * The TRM doesn't specify a minimum input height, make
* sure we got enough lines to enable the noise filter and color
* filter array interpolation.
*/
- if (prev->input == PREVIEW_INPUT_MEMORY) {
- fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
- preview_max_out_width(prev));
- fmt->height = clamp_t(u32, fmt->height,
- PREV_MIN_IN_HEIGHT,
- PREV_MAX_IN_HEIGHT);
- }
+ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+ preview_max_out_width(prev));
+ fmt->height = clamp_t(u32, fmt->height,
+ PREV_MIN_IN_HEIGHT,
+ PREV_MAX_IN_HEIGHT);
fmt->colorspace = V4L2_COLORSPACE_SRGB;
@@ -1796,7 +1791,7 @@ static void preview_try_format(struct isp_prev_device *prev,
fmt->width = crop->width;
fmt->height = crop->height;
- fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
break;
}
@@ -2277,7 +2272,7 @@ static int preview_init_entities(struct isp_prev_device *prev)
strscpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name));
sd->grp_id = 1 << 16; /* group ID for isp subdevs */
v4l2_set_subdevdata(sd, prev);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&prev->ctrls, 2);
v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS,
@@ -2294,6 +2289,7 @@ static int preview_init_entities(struct isp_prev_device *prev)
pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &preview_media_ops;
+ me->function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
ret = media_entity_pads_init(me, PREV_PADS_NUM, pads);
if (ret < 0)
goto error_handler_free;
diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c
index 87d821b02e5c..ad0127f5b5cb 100644
--- a/drivers/media/platform/ti/omap3isp/ispresizer.c
+++ b/drivers/media/platform/ti/omap3isp/ispresizer.c
@@ -1405,7 +1405,7 @@ static void resizer_try_format(struct isp_res_device *res,
break;
}
- fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->field = V4L2_FIELD_NONE;
}
@@ -1738,6 +1738,7 @@ static int resizer_init_entities(struct isp_res_device *res)
pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &resizer_media_ops;
+ me->function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
ret = media_entity_pads_init(me, RESZ_PADS_NUM, pads);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/ti/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c
index 07bd62a93d99..64bc71d830c4 100644
--- a/drivers/media/platform/ti/omap3isp/ispstat.c
+++ b/drivers/media/platform/ti/omap3isp/ispstat.c
@@ -1037,6 +1037,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name,
stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
me->ops = NULL;
+ me->function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
return media_entity_pads_init(me, 1, &stat->pad);
}
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index 0e7f0bf2b346..86cb27b6ca4e 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -35,6 +35,10 @@
/*
* NOTE: When adding new media bus codes, always remember to add
* corresponding in-memory formats to the table below!!!
+ *
+ * If there are multiple entries with the same pixelformat but
+ * different media bus codes, then keep those together. Otherwise
+ * isp_video_enum_format() cannot detect duplicate pixelformats.
*/
static struct isp_format_info formats[] = {
{ MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8,
@@ -97,12 +101,12 @@ static struct isp_format_info formats[] = {
{ MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16,
MEDIA_BUS_FMT_UYVY8_1X16, 0,
V4L2_PIX_FMT_UYVY, 16, 2, },
- { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16,
- MEDIA_BUS_FMT_YUYV8_1X16, 0,
- V4L2_PIX_FMT_YUYV, 16, 2, },
{ MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8,
MEDIA_BUS_FMT_UYVY8_2X8, 0,
V4L2_PIX_FMT_UYVY, 8, 2, },
+ { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, 2, },
{ MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8,
MEDIA_BUS_FMT_YUYV8_2X8, 0,
V4L2_PIX_FMT_YUYV, 8, 2, },
@@ -148,12 +152,12 @@ static unsigned int isp_video_mbus_to_pix(const struct isp_video *video,
pix->width = mbus->width;
pix->height = mbus->height;
- for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
if (formats[i].code == mbus->code)
break;
}
- if (WARN_ON(i == ARRAY_SIZE(formats)))
+ if (WARN_ON(i == ARRAY_SIZE(formats) - 1))
return 0;
min_bpl = pix->width * formats[i].bpp;
@@ -191,7 +195,7 @@ static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix,
/* Skip the last format in the loop so that it will be selected if no
* match is found.
*/
- for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
+ for (i = 0; i < ARRAY_SIZE(formats) - 2; ++i) {
if (formats[i].pixelformat == pix->pixelformat)
break;
}
@@ -325,6 +329,13 @@ static int isp_video_queue_setup(struct vb2_queue *queue,
struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
struct isp_video *video = vfh->video;
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+ if (sizes[0] < vfh->format.fmt.pix.sizeimage)
+ return -EINVAL;
+ return 0;
+ }
*num_planes = 1;
sizes[0] = vfh->format.fmt.pix.sizeimage;
@@ -340,6 +351,7 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf);
struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
+ unsigned int size = vfh->format.fmt.pix.sizeimage;
struct isp_buffer *buffer = to_isp_buffer(vbuf);
struct isp_video *video = vfh->video;
dma_addr_t addr;
@@ -360,8 +372,13 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf)
return -EINVAL;
}
- vb2_set_plane_payload(&buffer->vb.vb2_buf, 0,
- vfh->format.fmt.pix.sizeimage);
+ if (vb2_plane_size(&buffer->vb.vb2_buf, 0) < size) {
+ dev_dbg(video->isp->dev,
+ "data will not fit into plane (%lu < %u)\n",
+ vb2_plane_size(&buffer->vb.vb2_buf, 0), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, size);
buffer->dma = addr;
return 0;
@@ -645,16 +662,41 @@ isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
strscpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
strscpy(cap->card, video->video.name, sizeof(cap->card));
- strscpy(cap->bus_info, "media", sizeof(cap->bus_info));
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
-
+ | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_IO_MC;
return 0;
}
static int
+isp_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct isp_video *video = video_drvdata(file);
+ unsigned int i, j;
+
+ if (f->type != video->type)
+ return -EINVAL;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) {
+ /* Weed out duplicate pixelformats with different mbus codes */
+ if (!f->mbus_code && i &&
+ formats[i - 1].pixelformat == formats[i].pixelformat)
+ continue;
+ if (f->mbus_code && formats[i].code != f->mbus_code)
+ continue;
+
+ if (j == f->index) {
+ f->pixelformat = formats[i].pixelformat;
+ return 0;
+ }
+ j++;
+ }
+
+ return -EINVAL;
+}
+
+static int
isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
{
struct isp_video_fh *vfh = file_to_isp_video_fh(file);
@@ -671,11 +713,15 @@ isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
}
static int
-isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
{
- struct isp_video_fh *vfh = file_to_isp_video_fh(file);
struct isp_video *video = video_drvdata(file);
- struct v4l2_mbus_framefmt fmt;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
if (format->type != video->type)
return -EINVAL;
@@ -715,32 +761,11 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
break;
}
- /* Fill the bytesperline and sizeimage fields by converting to media bus
- * format and back to pixel format.
- */
- isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
- isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
-
- mutex_lock(&video->mutex);
- vfh->format = *format;
- mutex_unlock(&video->mutex);
-
- return 0;
-}
-
-static int
-isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_subdev *subdev;
- u32 pad;
- int ret;
-
- if (format->type != video->type)
- return -EINVAL;
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+ isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+ return 0;
+ }
subdev = isp_video_remote_subdev(video, &pad);
if (subdev == NULL)
@@ -758,6 +783,24 @@ isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
}
static int
+isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video_fh *vfh = file_to_isp_video_fh(file);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ ret = isp_video_try_format(file, fh, format);
+ if (ret)
+ return ret;
+
+ mutex_lock(&video->mutex);
+ vfh->format = *format;
+ mutex_unlock(&video->mutex);
+
+ return 0;
+}
+
+static int
isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel)
{
struct isp_video *video = video_drvdata(file);
@@ -885,7 +928,10 @@ isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
if (a->parm.output.timeperframe.denominator == 0)
a->parm.output.timeperframe.denominator = 1;
+ if (a->parm.output.timeperframe.numerator == 0)
+ a->parm.output.timeperframe.numerator = 1;
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
vfh->timeperframe = a->parm.output.timeperframe;
return 0;
@@ -906,6 +952,20 @@ isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
}
static int
+isp_video_create_bufs(struct file *file, void *fh, struct v4l2_create_buffers *p)
+{
+ struct isp_video_fh *vfh = file_to_isp_video_fh(file);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_create_bufs(&vfh->queue, p);
+ mutex_unlock(&video->queue_lock);
+
+ return ret;
+}
+
+static int
isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct isp_video_fh *vfh = file_to_isp_video_fh(file);
@@ -920,6 +980,20 @@ isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
}
static int
+isp_video_prepare_buf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = file_to_isp_video_fh(file);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_prepare_buf(&vfh->queue, video->video.v4l2_dev->mdev, b);
+ mutex_unlock(&video->queue_lock);
+
+ return ret;
+}
+
+static int
isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct isp_video_fh *vfh = file_to_isp_video_fh(file);
@@ -1260,9 +1334,11 @@ isp_video_s_input(struct file *file, void *fh, unsigned int input)
static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
.vidioc_querycap = isp_video_querycap,
+ .vidioc_enum_fmt_vid_cap = isp_video_enum_format,
.vidioc_g_fmt_vid_cap = isp_video_get_format,
.vidioc_s_fmt_vid_cap = isp_video_set_format,
.vidioc_try_fmt_vid_cap = isp_video_try_format,
+ .vidioc_enum_fmt_vid_out = isp_video_enum_format,
.vidioc_g_fmt_vid_out = isp_video_get_format,
.vidioc_s_fmt_vid_out = isp_video_set_format,
.vidioc_try_fmt_vid_out = isp_video_try_format,
@@ -1271,7 +1347,9 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
.vidioc_g_parm = isp_video_get_param,
.vidioc_s_parm = isp_video_set_param,
.vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_create_bufs = isp_video_create_bufs,
.vidioc_querybuf = isp_video_querybuf,
+ .vidioc_prepare_buf = isp_video_prepare_buf,
.vidioc_qbuf = isp_video_qbuf,
.vidioc_dqbuf = isp_video_dqbuf,
.vidioc_streamon = isp_video_streamon,
@@ -1288,6 +1366,7 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
static int isp_video_open(struct file *file)
{
struct isp_video *video = video_drvdata(file);
+ struct v4l2_mbus_framefmt fmt;
struct isp_video_fh *handle;
struct vb2_queue *queue;
int ret = 0;
@@ -1330,6 +1409,14 @@ static int isp_video_open(struct file *file)
memset(&handle->format, 0, sizeof(handle->format));
handle->format.type = video->type;
+ handle->format.fmt.pix.width = 720;
+ handle->format.fmt.pix.height = 480;
+ handle->format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ handle->format.fmt.pix.field = V4L2_FIELD_NONE;
+ handle->format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ isp_video_pix_to_mbus(&handle->format.fmt.pix, &fmt);
+ isp_video_mbus_to_pix(video, &fmt, &handle->format.fmt.pix);
+ handle->timeperframe.numerator = 1;
handle->timeperframe.denominator = 1;
handle->video = video;
@@ -1449,12 +1536,15 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
video->video.vfl_type = VFL_TYPE_VIDEO;
video->video.release = video_device_release_empty;
video->video.ioctl_ops = &isp_video_ioctl_ops;
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING;
- else
+ | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ v4l2_disable_ioctl(&video->video, VIDIOC_S_PARM);
+ v4l2_disable_ioctl(&video->video, VIDIOC_G_PARM);
+ } else {
video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
+ | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ }
video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;
diff --git a/drivers/media/platform/ti/vpe/Makefile b/drivers/media/platform/ti/vpe/Makefile
index 3fadfe084f87..fbb0dec5a30e 100644
--- a/drivers/media/platform/ti/vpe/Makefile
+++ b/drivers/media/platform/ti/vpe/Makefile
@@ -3,10 +3,12 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
obj-$(CONFIG_VIDEO_TI_VPDMA) += ti-vpdma.o
obj-$(CONFIG_VIDEO_TI_SC) += ti-sc.o
obj-$(CONFIG_VIDEO_TI_CSC) += ti-csc.o
+obj-$(CONFIG_VIDEO_TI_VIP) += ti-vip.o
ti-vpe-y := vpe.o
ti-vpdma-y := vpdma.o
ti-sc-y := sc.o
ti-csc-y := csc.o
+ti-vip-y := vip.o
ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
new file mode 100644
index 000000000000..d4236ac6d867
--- /dev/null
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -0,0 +1,3673 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI VIP capture driver
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com/
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Yemike Abhilash Chandra, <y-abhilashchandra@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include "vip.h"
+
+#define VIP_MODULE_NAME "vip"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-8)");
+
+/*
+ * Minimum and maximum frame sizes
+ */
+#define MIN_W 128
+#define MIN_H 128
+#define MAX_W 2048
+#define MAX_H 1536
+
+/*
+ * Required alignments
+ */
+#define S_ALIGN 0 /* multiple of 1 */
+#define H_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN 1 /* multiple of 2 */
+
+/*
+ * Need a descriptor entry for each of up to 15 outputs,
+ * and up to 2 control transfers.
+ */
+#define VIP_DESC_LIST_SIZE (17 * sizeof(struct vpdma_dtd))
+
+/*
+ * port flag bits
+ */
+#define FLAG_INTERLACED BIT(4)
+#define FLAG_MULT_PORT BIT(6)
+#define FLAG_MULT_ANC BIT(7)
+
+#define VIP_VPDMA_FIFO_SIZE 2
+#define VIP_DROPQ_SIZE 3
+
+/*
+ * Define indices into the srce_info tables
+ */
+
+#define VIP_SRCE_MULT_PORT 0
+#define VIP_SRCE_MULT_ANC 1
+#define VIP_SRCE_LUMA 2
+#define VIP_SRCE_CHROMA 3
+#define VIP_SRCE_RGB 4
+
+#define reg_read(dev, offset) ioread32((dev)->base + (offset))
+#define reg_write(dev, offset, val) iowrite32((val), (dev)->base + (offset))
+
+#define GET_OFFSET_TOP(port, obj, reg) \
+ ((obj)->base - (port)->dev->base + (reg))
+
+#define VIP_SET_MMR_ADB_HDR(port, hdr, regs, offset_a) \
+ VPDMA_SET_MMR_ADB_HDR((port)->mmr_adb, vip_mmr_adb, hdr, regs, offset_a)
+
+/*
+ * These represent the module resets bit for slice 1
+ * Upon detecting slice2 we simply left shift by 1
+ */
+#define VIP_DP_RST BIT(16)
+#define VIP_CSC_RST BIT(20)
+#define VIP_SC_RST BIT(22)
+
+#define VIP_PARSER_PORT(p) (VIP_PARSER_PORTA_0 + ((p) * 0x8U))
+#define VIP_PARSER_CROP_H_PORT(p) \
+ (VIP_PARSER_PORTA_EXTRA4 + ((p) * 0x10U))
+#define VIP_PARSER_CROP_V_PORT(p) \
+ (VIP_PARSER_PORTA_EXTRA5 + ((p) * 0x10U))
+#define VIP_PARSER_STOP_IMM_PORT(p) (VIP_PARSER_PORTA_EXTRA6 + ((p) * 0x4U))
+
+#define PARSER_IRQ_MASK (VIP_PORTA_OUTPUT_FIFO_YUV | \
+ VIP_PORTB_OUTPUT_FIFO_YUV)
+
+/*
+ * The srce_info structure contains per-srce data.
+ */
+struct vip_srce_info {
+ u8 base_channel; /* the VPDMA channel number */
+ u8 vb_index; /* input frame f, f-1, f-2 index */
+ u8 vb_part; /* identifies section of co-planar formats */
+};
+
+static struct vip_srce_info srce_info[5] = {
+ [VIP_SRCE_MULT_PORT] = {
+ .base_channel = VIP1_CHAN_NUM_MULT_PORT_A_SRC0,
+ .vb_index = 0,
+ .vb_part = VIP_CHROMA,
+ },
+ [VIP_SRCE_MULT_ANC] = {
+ .base_channel = VIP1_CHAN_NUM_MULT_ANC_A_SRC0,
+ .vb_index = 0,
+ .vb_part = VIP_LUMA,
+ },
+ [VIP_SRCE_LUMA] = {
+ .base_channel = VIP1_CHAN_NUM_PORT_A_LUMA,
+ .vb_index = 1,
+ .vb_part = VIP_LUMA,
+ },
+ [VIP_SRCE_CHROMA] = {
+ .base_channel = VIP1_CHAN_NUM_PORT_A_CHROMA,
+ .vb_index = 1,
+ .vb_part = VIP_CHROMA,
+ },
+ [VIP_SRCE_RGB] = {
+ .base_channel = VIP1_CHAN_NUM_PORT_A_RGB,
+ .vb_part = VIP_LUMA,
+ },
+};
+
+static struct vip_fmt vip_formats[VIP_MAX_ACTIVE_FMT] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 1,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+ &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+ },
+ },
+ {
+ /* V4L2 currently only defines one 16 bit variant */
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16],
+ },
+ },
+};
+
+/*
+ * DMA address/data block for the shadow registers
+ */
+struct vip_mmr_adb {
+ struct vpdma_adb_hdr sc_hdr0;
+ u32 sc_regs0[7];
+ u32 sc_pad0[1];
+ struct vpdma_adb_hdr sc_hdr8;
+ u32 sc_regs8[6];
+ u32 sc_pad8[2];
+ struct vpdma_adb_hdr sc_hdr17;
+ u32 sc_regs17[9];
+ u32 sc_pad17[3];
+ struct vpdma_adb_hdr csc_hdr;
+ u32 csc_regs[6];
+ u32 csc_pad[2];
+};
+
+/*
+ * Function prototype declarations
+ */
+static int alloc_port(struct vip_dev *, int);
+static void free_port(struct vip_port *);
+static int vip_setup_parser(struct vip_port *port);
+static int vip_setup_scaler(struct vip_stream *stream);
+static void vip_enable_parser(struct vip_port *port, bool on);
+static void vip_reset_parser(struct vip_port *port, bool on);
+static void vip_parser_stop_imm(struct vip_port *port, bool on);
+static void stop_dma(struct vip_stream *stream, bool clear_list);
+static int vip_load_vpdma_list_fifo(struct vip_stream *stream);
+static inline bool is_scaler_available(struct vip_port *port);
+static inline bool allocate_scaler(struct vip_port *port);
+static inline void free_scaler(struct vip_port *port);
+static bool is_csc_available(struct vip_port *port);
+static bool allocate_csc(struct vip_port *port,
+ enum vip_csc_state csc_direction);
+static void free_csc(struct vip_port *port);
+
+/* initialize v4l2_format_info member in vip_formats array */
+static void vip_init_format_info(struct device *dev)
+{
+ struct vip_fmt *fmt;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vip_formats); i++) {
+ fmt = &vip_formats[i];
+ fmt->finfo = v4l2_format_info(fmt->fourcc);
+ }
+}
+
+/* Print Four-character-code (FOURCC) */
+static char *fourcc_to_str(u32 fmt)
+{
+ static char code[5];
+
+ code[0] = (unsigned char)(fmt & 0xff);
+ code[1] = (unsigned char)((fmt >> 8) & 0xff);
+ code[2] = (unsigned char)((fmt >> 16) & 0xff);
+ code[3] = (unsigned char)((fmt >> 24) & 0xff);
+ code[4] = '\0';
+
+ return code;
+}
+
+/*
+ * Find our format description corresponding to the passed v4l2_format
+ */
+static struct vip_fmt *find_port_format_by_pix(struct vip_port *port,
+ u32 pixelformat)
+{
+ struct vip_fmt *fmt;
+ unsigned int index;
+
+ for (index = 0; index < port->num_active_fmt; index++) {
+ fmt = port->active_fmt[index];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static struct vip_fmt *find_port_format_by_code(struct vip_port *port,
+ u32 code)
+{
+ struct vip_fmt *fmt;
+ unsigned int index;
+
+ for (index = 0; index < port->num_active_fmt; index++) {
+ fmt = port->active_fmt[index];
+ if (fmt->code == code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+inline struct vip_port *notifier_to_vip_port(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct vip_port, notifier);
+}
+
+static bool vip_is_mbuscode_yuv(u32 code)
+{
+ return ((code & 0xff00) == 0x2000);
+}
+
+static bool vip_is_mbuscode_rgb(u32 code)
+{
+ return ((code & 0xff00) == 0x1000);
+}
+
+static bool vip_is_mbuscode_raw(u32 code)
+{
+ return ((code & 0xff00) == 0x3000);
+}
+
+/*
+ * This is not an accurate conversion but it is only used to
+ * assess if color conversion is needed.
+ */
+static u32 vip_mbus_code_to_fourcc(u32 code)
+{
+ if (vip_is_mbuscode_rgb(code))
+ return V4L2_PIX_FMT_RGB24;
+
+ if (vip_is_mbuscode_yuv(code))
+ return V4L2_PIX_FMT_UYVY;
+
+ return V4L2_PIX_FMT_SBGGR8;
+}
+
+static enum vip_csc_state
+vip_csc_direction(u32 src_code, const struct v4l2_format_info *dfinfo)
+{
+ if (vip_is_mbuscode_yuv(src_code) && v4l2_is_format_rgb(dfinfo))
+ return VIP_CSC_Y2R;
+ else if (vip_is_mbuscode_rgb(src_code) && v4l2_is_format_yuv(dfinfo))
+ return VIP_CSC_R2Y;
+ else
+ return VIP_CSC_NA;
+}
+
+/*
+ * Insert a masked field into a 32-bit field
+ */
+static void insert_field(u32 *valp, u32 field, u32 mask, int shift)
+{
+ u32 val = *valp;
+
+ val &= ~(mask << shift);
+ val |= (field & mask) << shift;
+ *valp = val;
+}
+
+/*
+ * Set the headers for all of the address/data block structures.
+ */
+static void init_adb_hdrs(struct vip_port *port)
+{
+ VIP_SET_MMR_ADB_HDR(port, sc_hdr0, sc_regs0,
+ GET_OFFSET_TOP(port, port->dev->sc, CFG_SC0));
+ VIP_SET_MMR_ADB_HDR(port, sc_hdr8, sc_regs8,
+ GET_OFFSET_TOP(port, port->dev->sc, CFG_SC8));
+ VIP_SET_MMR_ADB_HDR(port, sc_hdr17, sc_regs17,
+ GET_OFFSET_TOP(port, port->dev->sc, CFG_SC17));
+ VIP_SET_MMR_ADB_HDR(port, csc_hdr, csc_regs,
+ GET_OFFSET_TOP(port, port->dev->csc, CSC_CSC00));
+
+};
+
+static void vip_module_toggle(struct vip_dev *dev, uint32_t module, bool on)
+{
+ u32 val = 0;
+
+ val = reg_read(dev, VIP_CLK_RESET);
+
+ if (dev->slice_id == VIP_SLICE2)
+ module <<= 1;
+
+ if (on)
+ val |= module;
+ else
+ val &= ~module;
+
+ reg_write(dev, VIP_CLK_RESET, val);
+}
+
+/*
+ * Enable or disable the VIP clocks
+ */
+static void vip_set_clock_enable(struct vip_dev *dev, bool on)
+{
+ u32 val = 0;
+
+ val = reg_read(dev, VIP_CLK_ENABLE);
+ if (on) {
+ val |= VIP_VPDMA_CLK_ENABLE;
+ if (dev->slice_id == VIP_SLICE1)
+ val |= VIP_VIP1_DATA_PATH_CLK_ENABLE;
+ else
+ val |= VIP_VIP2_DATA_PATH_CLK_ENABLE;
+ } else {
+ if (dev->slice_id == VIP_SLICE1)
+ val &= ~VIP_VIP1_DATA_PATH_CLK_ENABLE;
+ else
+ val &= ~VIP_VIP2_DATA_PATH_CLK_ENABLE;
+
+ /* Both VIP are disabled then shutdown VPDMA also */
+ if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE |
+ VIP_VIP2_DATA_PATH_CLK_ENABLE)))
+ val = 0;
+ }
+
+ reg_write(dev, VIP_CLK_ENABLE, val);
+}
+
+/* This helper function is used to enable the clock early on to
+ * enable vpdma firmware loading before the slice device are created
+ */
+static void vip_shared_set_clock_enable(struct vip_shared *shared, bool on)
+{
+ u32 val = 0;
+
+ val = VIP_VIP1_DATA_PATH_CLK_ENABLE | VIP_VPDMA_CLK_ENABLE;
+
+ reg_write(shared, VIP_CLK_ENABLE, val);
+}
+
+static void vip_top_reset(struct vip_dev *dev)
+{
+ u32 val = 0;
+
+ val = reg_read(dev, VIP_CLK_RESET);
+
+ if (dev->slice_id == VIP_SLICE1)
+ insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
+ VIP_VIP1_DATA_PATH_RESET_SHIFT);
+ else
+ insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
+ VIP_VIP2_DATA_PATH_RESET_SHIFT);
+
+ reg_write(dev, VIP_CLK_RESET, val);
+
+ usleep_range(200, 250);
+
+ val = reg_read(dev, VIP_CLK_RESET);
+
+ if (dev->slice_id == VIP_SLICE1)
+ insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
+ VIP_VIP1_DATA_PATH_RESET_SHIFT);
+ else
+ insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
+ VIP_VIP2_DATA_PATH_RESET_SHIFT);
+ reg_write(dev, VIP_CLK_RESET, val);
+}
+
+static void vip_top_vpdma_reset(struct vip_shared *shared)
+{
+ u32 val;
+
+ val = reg_read(shared, VIP_CLK_RESET);
+ insert_field(&val, 1, VIP_VPDMA_CLK_RESET_MASK,
+ VIP_VPDMA_CLK_RESET_SHIFT);
+ reg_write(shared, VIP_CLK_RESET, val);
+
+ usleep_range(200, 250);
+
+ val = reg_read(shared, VIP_CLK_RESET);
+ insert_field(&val, 0, VIP_VPDMA_CLK_RESET_MASK,
+ VIP_VPDMA_CLK_RESET_SHIFT);
+ reg_write(shared, VIP_CLK_RESET, val);
+}
+
+static void vip_set_pclk_invert(struct vip_port *port)
+{
+ struct vip_ctrl_module *ctrl = port->dev->syscon;
+ struct vip_dev *dev = port->dev;
+ u32 index;
+ /*
+ * When the VIP parser is configured to so that the pixel clock
+ * is to be sampled at falling edge, the pixel clock needs to be
+ * inverted before it is given to the VIP module. This is done
+ * by setting a bit in the CTRL_CORE_SMA_SW1 register.
+ */
+
+ index = 2 * port->dev->slice_id + port->port_id;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: slice%d:port%d -> index: %d\n", __func__,
+ port->dev->slice_id, port->port_id, index);
+
+ if (ctrl->syscon_pol)
+ regmap_update_bits(ctrl->syscon_pol,
+ ctrl->syscon_offset,
+ ctrl->syscon_bit_field[index],
+ ctrl->syscon_bit_field[index]);
+}
+
+static void vip_set_data_interface(struct vip_port *port,
+ enum data_interface_modes mode)
+{
+ u32 val = 0;
+
+ insert_field(&val, mode, VIP_DATA_INTERFACE_MODE_MASK,
+ VIP_DATA_INTERFACE_MODE_SHFT);
+
+ reg_write(port->dev->parser, VIP_PARSER_MAIN_CFG, val);
+}
+
+static void vip_set_slice_path(struct vip_dev *dev,
+ enum data_path_select data_path, u32 path_val)
+{
+ u32 val = 0;
+ int data_path_reg;
+
+ data_path_reg = VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id;
+
+ switch (data_path) {
+ case ALL_FIELDS_DATA_SELECT:
+ val |= path_val;
+ break;
+ case VIP_CSC_SRC_DATA_SELECT:
+ insert_field(&val, path_val, VIP_CSC_SRC_SELECT_MASK,
+ VIP_CSC_SRC_SELECT_SHFT);
+ break;
+ case VIP_SC_SRC_DATA_SELECT:
+ insert_field(&val, path_val, VIP_SC_SRC_SELECT_MASK,
+ VIP_SC_SRC_SELECT_SHFT);
+ break;
+ case VIP_RGB_SRC_DATA_SELECT:
+ val |= (path_val) ? VIP_RGB_SRC_SELECT : 0;
+ break;
+ case VIP_RGB_OUT_LO_DATA_SELECT:
+ val |= (path_val) ? VIP_RGB_OUT_LO_SRC_SELECT : 0;
+ break;
+ case VIP_RGB_OUT_HI_DATA_SELECT:
+ val |= (path_val) ? VIP_RGB_OUT_HI_SRC_SELECT : 0;
+ break;
+ case VIP_CHR_DS_1_SRC_DATA_SELECT:
+ insert_field(&val, path_val, VIP_DS1_SRC_SELECT_MASK,
+ VIP_DS1_SRC_SELECT_SHFT);
+ break;
+ case VIP_CHR_DS_2_SRC_DATA_SELECT:
+ insert_field(&val, path_val, VIP_DS2_SRC_SELECT_MASK,
+ VIP_DS2_SRC_SELECT_SHFT);
+ break;
+ case VIP_MULTI_CHANNEL_DATA_SELECT:
+ val |= (path_val) ? VIP_MULTI_CHANNEL_SELECT : 0;
+ break;
+ case VIP_CHR_DS_1_DATA_BYPASS:
+ val |= (path_val) ? VIP_DS1_BYPASS : 0;
+ break;
+ case VIP_CHR_DS_2_DATA_BYPASS:
+ val |= (path_val) ? VIP_DS2_BYPASS : 0;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "%s: data_path 0x%x is not valid\n",
+ __func__, data_path);
+ return;
+ }
+ insert_field(&val, data_path, VIP_DATAPATH_SELECT_MASK,
+ VIP_DATAPATH_SELECT_SHFT);
+ reg_write(dev, data_path_reg, val);
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: DATA_PATH_SELECT(%08X): %08X\n", __func__,
+ data_path_reg, reg_read(dev, data_path_reg));
+}
+
+/*
+ * Return the vip_stream structure for a given struct file
+ */
+static inline struct vip_stream *file2stream(struct file *file)
+{
+ return video_drvdata(file);
+}
+
+/*
+ * Append a destination descriptor to the current descriptor list,
+ * setting up dma to the given srce.
+ */
+static int add_out_dtd(struct vip_stream *stream, int srce_type)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vip_srce_info *sinfo = &srce_info[srce_type];
+ struct v4l2_rect *c_rect = &port->c_rect;
+ struct vip_fmt *fmt = port->fmt;
+ int channel, plane = 0;
+ int max_width, max_height;
+ dma_addr_t dma_addr = 0;
+ u32 flags;
+ u32 width = stream->width;
+
+ channel = sinfo->base_channel;
+
+ switch (srce_type) {
+ case VIP_SRCE_MULT_PORT:
+ case VIP_SRCE_MULT_ANC:
+ if (port->port_id == VIP_PORTB)
+ channel += VIP_CHAN_MULT_PORTB_OFFSET;
+ channel += stream->stream_id;
+ flags = 0;
+ break;
+ case VIP_SRCE_CHROMA:
+ plane = 1;
+ fallthrough;
+ case VIP_SRCE_LUMA:
+ if (port->port_id == VIP_PORTB) {
+ if (port->scaler && !port->fmt->coplanar)
+ /*
+ * In this case Port A Chroma channel
+ * is used to carry Port B scaled YUV422
+ */
+ channel += 1;
+ else
+ channel += VIP_CHAN_YUV_PORTB_OFFSET;
+ }
+ flags = port->flags;
+ break;
+ case VIP_SRCE_RGB:
+ if (port->port_id == VIP_PORTB ||
+ (port->port_id == VIP_PORTA &&
+ port->csc == VIP_CSC_NA &&
+ v4l2_is_format_rgb(port->fmt->finfo)))
+ /*
+ * RGB sensor only connect to Y_LO
+ * channel i.e. port B channel.
+ */
+ channel += VIP_CHAN_RGB_PORTB_OFFSET;
+ flags = port->flags;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "%s: srce_type 0x%x is not valid\n",
+ __func__, srce_type);
+ return -1;
+ }
+
+ if (dev->slice_id == VIP_SLICE2)
+ channel += VIP_CHAN_VIP2_OFFSET;
+
+ if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
+ /*
+ * Special case since we are faking a YUV422 16bit format
+ * to have the vpdma perform the needed byte swap
+ * we need to adjust the pixel width accordingly
+ * otherwise the parser will attempt to collect more pixels
+ * then available and the vpdma transfer will exceed the
+ * allocated frame buffer.
+ */
+ width >>= 1;
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting width to %d\n",
+ __func__, width);
+ }
+
+ /*
+ * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1
+ */
+
+ if (dev->slice_id == VIP_SLICE1) {
+ vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1,
+ width, stream->height);
+
+ max_width = MAX_OUT_WIDTH_REG1;
+ max_height = MAX_OUT_HEIGHT_REG1;
+ } else {
+ vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2,
+ width, stream->height);
+
+ max_width = MAX_OUT_WIDTH_REG2;
+ max_height = MAX_OUT_HEIGHT_REG2;
+ }
+
+ /*
+ * Mark this channel to be cleared while cleaning up resources
+ * This will make sure that an abort descriptor for this channel
+ * would be submitted to VPDMA causing any ongoing transaction to be
+ * aborted and cleanup the VPDMA FSM for this channel
+ */
+ stream->vpdma_channels[channel] = 1;
+
+ vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width,
+ stream->bytesperline, c_rect,
+ fmt->vpdma_fmt[plane], dma_addr,
+ max_width, max_height, channel, flags);
+ return 0;
+}
+
+/*
+ * add_stream_dtds - prepares and starts DMA for pending transfers
+ */
+static void add_stream_dtds(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ int srce_type;
+
+ if (port->flags & FLAG_MULT_PORT)
+ srce_type = VIP_SRCE_MULT_PORT;
+ else if (port->flags & FLAG_MULT_ANC)
+ srce_type = VIP_SRCE_MULT_ANC;
+ else if (v4l2_is_format_rgb(port->fmt->finfo))
+ srce_type = VIP_SRCE_RGB;
+ else
+ srce_type = VIP_SRCE_LUMA;
+
+ add_out_dtd(stream, srce_type);
+
+ if (srce_type == VIP_SRCE_LUMA && port->fmt->coplanar)
+ add_out_dtd(stream, VIP_SRCE_CHROMA);
+}
+
+static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+ struct vip_parser_data *parser = dev->parser;
+ u32 reg_addr = VIP_INT0_ENABLE0_SET +
+ VIP_INTC_INTX_OFFSET * irq_num;
+ u32 irq_val = (1 << (list_num * 2)) |
+ (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+ /* Enable Parser Interrupt */
+ reg_write(parser, VIP_PARSER_FIQ_MASK, (u32)~PARSER_IRQ_MASK);
+
+ reg_write(dev->shared, reg_addr, irq_val);
+
+ vpdma_enable_list_complete_irq(dev->shared->vpdma,
+ irq_num, list_num, true);
+}
+
+static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+ struct vip_parser_data *parser = dev->parser;
+ u32 reg_addr = VIP_INT0_ENABLE0_CLR +
+ VIP_INTC_INTX_OFFSET * irq_num;
+ u32 irq_val = (1 << (list_num * 2)) |
+ (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+ /* Disable all Parser Interrupt */
+ reg_write(parser, VIP_PARSER_FIQ_MASK, 0xffffffff);
+
+ reg_write(dev->shared, reg_addr, irq_val);
+
+ vpdma_enable_list_complete_irq(dev->shared->vpdma,
+ irq_num, list_num, false);
+}
+
+static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+ struct vip_parser_data *parser = dev->parser;
+ u32 reg_addr = VIP_INT0_STATUS0_CLR +
+ VIP_INTC_INTX_OFFSET * irq_num;
+ u32 irq_val = (1 << (list_num * 2)) |
+ (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+ /* Clear all Parser Interrupt */
+ reg_write(parser, VIP_PARSER_FIQ_CLR, 0xffffffff);
+ reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
+
+ reg_write(dev->shared, reg_addr, irq_val);
+
+ vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
+}
+
+static void populate_desc_list(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+
+ stream->desc_next = stream->desc_list.buf.addr;
+ add_stream_dtds(stream);
+
+ vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+}
+
+/*
+ * start_dma - adds descriptors to the dma list and submits them.
+ * Should be called after a new vb is queued and on a vpdma list
+ * completion interrupt.
+ */
+static void start_dma(struct vip_stream *stream, struct vip_buffer *buf)
+{
+ struct vip_dev *dev = stream->port->dev;
+ struct vpdma_data *vpdma = dev->shared->vpdma;
+ int list_num = stream->list_num;
+ dma_addr_t dma_addr;
+ int drop_data;
+
+ if (vpdma_list_busy(vpdma, list_num)) {
+ v4l2_err(&dev->v4l2_dev, "vpdma list busy, cannot post\n");
+ return; /* nothing to do */
+ }
+
+ if (buf) {
+ dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ drop_data = 0;
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: vb2 buf idx:%d, dma_addr:%pad\n",
+ __func__, buf->vb.vb2_buf.index, &dma_addr);
+ } else {
+ dma_addr = 0;
+ drop_data = 1;
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: dropped\n", __func__);
+ }
+
+ vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+ dma_addr, stream->write_desc, drop_data, 0);
+
+ if (stream->port->fmt->coplanar) {
+ dma_addr += stream->bytesperline * stream->height;
+ vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+ dma_addr, stream->write_desc + 1,
+ drop_data, 1);
+ }
+
+ vpdma_submit_descs(dev->shared->vpdma,
+ &stream->desc_list, stream->list_num);
+}
+
+static void vip_schedule_next_buffer(struct vip_stream *stream)
+{
+ struct vip_dev *dev = stream->port->dev;
+ struct vip_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&stream->vidq)) {
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "Dropping frame\n");
+ if (list_empty(&stream->dropq)) {
+ v4l2_err(&dev->v4l2_dev, "No dropq buffer left!");
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return;
+ }
+ buf = list_entry(stream->dropq.next,
+ struct vip_buffer, list);
+
+ buf->drop = true;
+ list_move_tail(&buf->list, &stream->post_bufs);
+ buf = NULL;
+ } else {
+ buf = list_entry(stream->vidq.next,
+ struct vip_buffer, list);
+ buf->drop = false;
+ list_move_tail(&buf->list, &stream->post_bufs);
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "added next buffer\n");
+ }
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+ start_dma(stream, buf);
+}
+
+static void vip_process_buffer_complete(struct vip_stream *stream)
+{
+ struct vip_dev *dev = stream->port->dev;
+ struct vip_buffer *buf;
+ struct vb2_v4l2_buffer *vb = NULL;
+ unsigned long flags, fld;
+
+ buf = list_first_entry_or_null(&stream->post_bufs, struct vip_buffer, list);
+
+ if (stream->port->flags & FLAG_INTERLACED) {
+ vpdma_unmap_desc_buf(dev->shared->vpdma,
+ &stream->desc_list.buf);
+
+ fld = dtd_get_field(stream->write_desc);
+ stream->field = fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+
+ vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+ }
+
+ if (buf) {
+ vb = &buf->vb;
+ vb->sequence = stream->sequence;
+ vb->vb2_buf.timestamp = ktime_get_ns();
+ vb->field = V4L2_FIELD_NONE;
+
+ if (buf->drop) {
+ spin_lock_irqsave(&dev->slock, flags);
+ list_move_tail(&buf->list, &stream->dropq);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else {
+ spin_lock_irqsave(&dev->slock, flags);
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
+ }
+ } else {
+ v4l2_err(&dev->v4l2_dev, "%s: buf is null!!!\n", __func__);
+ return;
+ }
+
+ stream->sequence++;
+}
+
+static int vip_reset_vpdma(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vip_buffer *buf;
+ unsigned long flags;
+
+ stop_dma(stream, false);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ /* requeue all active buffers in the opposite order */
+ while (!list_empty(&stream->post_bufs)) {
+ buf = list_last_entry(&stream->post_bufs,
+ struct vip_buffer, list);
+ list_del(&buf->list);
+ if (buf->drop == 1) {
+ list_add_tail(&buf->list, &stream->dropq);
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing drop buffer on dropq\n");
+ } else {
+ list_add(&buf->list, &stream->vidq);
+ v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing vb2 buf idx:%d on vidq\n",
+ buf->vb.vb2_buf.index);
+ }
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ /* Make sure the desc_list is unmapped */
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+
+ return 0;
+}
+
+static void vip_overflow_recovery_work(struct work_struct *work)
+{
+ struct vip_stream *stream = container_of(work, struct vip_stream,
+ recovery_work);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+
+ v4l2_err(&dev->v4l2_dev, "%s: Port %c\n", __func__,
+ port->port_id == VIP_PORTA ? 'A' : 'B');
+
+ disable_irqs(dev, dev->slice_id, stream->list_num);
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+
+ /* 1. Set VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMMEDIATELY */
+ /* 2. Set VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMMEDIATELY */
+ vip_parser_stop_imm(port, 1);
+
+ /* 3. Clear VIP_PORT_A[8] ENABLE */
+ /*
+ * 4. Set VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
+ * Set VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
+ */
+ vip_enable_parser(port, false);
+
+ /* 5. Set VIP_PORT_A[23] SW_RESET */
+ vip_reset_parser(port, 1);
+
+ /*
+ * 6. Reset other VIP modules
+ * For each module used downstream of VIP_PARSER, write 1 to the
+ * bit location of the VIP_CLKC_RST register which is connected
+ * to VIP_PARSER
+ */
+ vip_module_toggle(dev, VIP_DP_RST, true);
+
+ usleep_range(200, 250);
+
+ /*
+ * 7. Abort VPDMA channels
+ * Write to list attribute to stop list 0
+ * Write to list address register location of abort list
+ * Write to list attribute register list 0 and size of abort list
+ */
+ vip_reset_vpdma(stream);
+
+ /* 8. Clear VIP_PORT_A[23] SW_RESET */
+ vip_reset_parser(port, 0);
+
+ /*
+ * 9. Un-reset other VIP modules
+ * For each module used downstream of VIP_PARSER, write 0 to
+ * the bit location of the VIP_CLKC_RST register which is
+ * connected to VIP_PARSER
+ */
+ vip_module_toggle(dev, VIP_DP_RST, false);
+
+ /* 10. (Delay) */
+ /* 11. SC coeff downloaded (if VIP_SCALER is being used) */
+ vip_setup_scaler(stream);
+
+ /* 12. (Delay) */
+ /* the above are not needed here yet */
+
+ populate_desc_list(stream);
+ stream->num_recovery++;
+ if (stream->num_recovery < 5) {
+ /* Reload the vpdma */
+ vip_load_vpdma_list_fifo(stream);
+
+ enable_irqs(dev, dev->slice_id, stream->list_num);
+ vip_schedule_next_buffer(stream);
+
+ /* 13. Clear VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMM */
+ /* 14. Clear VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMM */
+
+ vip_parser_stop_imm(port, 0);
+
+ /* 15. Set VIP_PORT_A[8] ENABLE */
+ /*
+ * 16. Clear VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
+ * Clear VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
+ */
+ vip_enable_parser(port, true);
+ } else {
+ v4l2_err(&dev->v4l2_dev, "%s: num_recovery limit exceeded leaving disabled\n",
+ __func__);
+ }
+}
+
+static void handle_parser_irqs(struct vip_dev *dev)
+{
+ struct vip_parser_data *parser = dev->parser;
+ struct vip_port *porta = dev->ports[VIP_PORTA];
+ struct vip_port *portb = dev->ports[VIP_PORTB];
+ struct vip_stream *stream = NULL;
+ u32 vip_irq_stat = reg_read(parser, VIP_PARSER_FIQ_STATUS);
+ int i;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: FIQ_STATUS: 0x%08x\n", __func__, vip_irq_stat);
+
+ /* Clear all Parser Interrupt */
+ reg_write(parser, VIP_PARSER_FIQ_CLR, vip_irq_stat);
+ reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
+
+ #ifdef DEBUG
+ if (vip_irq_stat & VIP_PORTA_VDET)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_VDET\n");
+ if (vip_irq_stat & VIP_PORTB_VDET)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_VDET\n");
+ if (vip_irq_stat & VIP_PORTA_ASYNC_FIFO_OF)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTA_ASYNC_FIFO_OF\n");
+ if (vip_irq_stat & VIP_PORTB_ASYNC_FIFO_OF)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTB_ASYNC_FIFO_OF\n");
+ if (vip_irq_stat & VIP_PORTA_OUTPUT_FIFO_YUV)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_YUV\n");
+ if (vip_irq_stat & VIP_PORTA_OUTPUT_FIFO_ANC)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_ANC\n");
+ if (vip_irq_stat & VIP_PORTB_OUTPUT_FIFO_YUV)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_YUV\n");
+ if (vip_irq_stat & VIP_PORTB_OUTPUT_FIFO_ANC)
+ v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_ANC\n");
+ if (vip_irq_stat & VIP_PORTA_CONN)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CONN\n");
+ if (vip_irq_stat & VIP_PORTA_DISCONN)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_DISCONN\n");
+ if (vip_irq_stat & VIP_PORTB_CONN)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CONN\n");
+ if (vip_irq_stat & VIP_PORTB_DISCONN)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_DISCONN\n");
+ if (vip_irq_stat & VIP_PORTA_SRC0_SIZE)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_SRC0_SIZE\n");
+ if (vip_irq_stat & VIP_PORTB_SRC0_SIZE)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_SRC0_SIZE\n");
+ if (vip_irq_stat & VIP_PORTA_YUV_PROTO_VIOLATION)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_YUV_PROTO_VIOLATION\n");
+ if (vip_irq_stat & VIP_PORTA_ANC_PROTO_VIOLATION)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_ANC_PROTO_VIOLATION\n");
+ if (vip_irq_stat & VIP_PORTB_YUV_PROTO_VIOLATION)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_YUV_PROTO_VIOLATION\n");
+ if (vip_irq_stat & VIP_PORTB_ANC_PROTO_VIOLATION)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_ANC_PROTO_VIOLATION\n");
+ if (vip_irq_stat & VIP_PORTA_CFG_DISABLE_COMPLETE)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CFG_DISABLE_COMPLETE\n");
+ if (vip_irq_stat & VIP_PORTB_CFG_DISABLE_COMPLETE)
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CFG_DISABLE_COMPLETE\n");
+ #endif
+
+ if (vip_irq_stat & (VIP_PORTA_ASYNC_FIFO_OF |
+ VIP_PORTA_OUTPUT_FIFO_YUV |
+ VIP_PORTA_OUTPUT_FIFO_ANC)) {
+ for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
+ if (porta->cap_streams[i] &&
+ porta->cap_streams[i]->port->port_id ==
+ porta->port_id) {
+ stream = porta->cap_streams[i];
+ break;
+ }
+ }
+ if (stream) {
+ disable_irqs(dev, dev->slice_id,
+ stream->list_num);
+ schedule_work(&stream->recovery_work);
+ return;
+ }
+ }
+ if (vip_irq_stat & (VIP_PORTB_ASYNC_FIFO_OF |
+ VIP_PORTB_OUTPUT_FIFO_YUV |
+ VIP_PORTB_OUTPUT_FIFO_ANC)) {
+ for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
+ if (portb->cap_streams[i] &&
+ portb->cap_streams[i]->port->port_id ==
+ portb->port_id) {
+ stream = portb->cap_streams[i];
+ break;
+ }
+ }
+ if (stream) {
+ disable_irqs(dev, dev->slice_id,
+ stream->list_num);
+ schedule_work(&stream->recovery_work);
+ return;
+ }
+ }
+}
+
+static irqreturn_t vip_irq(int irq_vip, void *data)
+{
+ struct vip_dev *dev = (struct vip_dev *)data;
+ struct vpdma_data *vpdma;
+ struct vip_stream *stream;
+ int list_num;
+ int irq_num = dev->slice_id;
+ u32 irqst, irqst_saved, reg_addr;
+
+ if (!dev->shared)
+ return IRQ_HANDLED;
+
+ vpdma = dev->shared->vpdma;
+ reg_addr = VIP_INT0_STATUS0 +
+ VIP_INTC_INTX_OFFSET * irq_num;
+ irqst_saved = reg_read(dev->shared, reg_addr);
+ irqst = irqst_saved;
+
+ v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n",
+ irq_vip, irq_num, irqst);
+ if (irqst) {
+ if (irqst & (VIP_VIP1_PARSER_INT << (irq_num * 1))) {
+ irqst &= ~(VIP_VIP1_PARSER_INT << (irq_num * 1));
+ handle_parser_irqs(dev);
+ }
+
+ for (list_num = 0; irqst && (list_num < 8); list_num++) {
+ /* Check for LIST_COMPLETE IRQ */
+ if (!(irqst & (1 << list_num * 2)))
+ continue;
+
+ v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d: handling LIST%d_COMPLETE\n",
+ irq_num, list_num);
+
+ stream = vpdma_hwlist_get_priv(vpdma, list_num);
+ if (!stream || stream->list_num != list_num) {
+ v4l2_err(&dev->v4l2_dev, "IRQ occurred for unused list");
+ continue;
+ }
+
+ vpdma_clear_list_stat(vpdma, irq_num, list_num);
+
+ vip_process_buffer_complete(stream);
+
+ vip_schedule_next_buffer(stream);
+
+ irqst &= ~((1 << list_num * 2));
+ }
+ }
+
+ /* Acknowledge that we are done with all interrupts */
+ reg_write(dev->shared, VIP_INTC_E0I, 1 << irq_num);
+
+ /* Clear handled events from status register */
+ reg_addr = VIP_INT0_STATUS0_CLR +
+ VIP_INTC_INTX_OFFSET * irq_num;
+ reg_write(dev->shared, reg_addr, irqst_saved);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vip_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, VIP_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card));
+ return 0;
+}
+
+static int vip_enuminput(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct vip_stream *stream = file2stream(file);
+
+ if (inp->index)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = stream->vfd->tvnorms;
+ sprintf(inp->name, "Camera %u", stream->vfd->num);
+
+ return 0;
+}
+
+static int vip_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vip_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+
+ *std = stream->vfd->tvnorms;
+ v4l2_subdev_call(port->subdev, video, querystd, std);
+ return 0;
+}
+
+static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+
+ *std = stream->vfd->tvnorms;
+ v4l2_subdev_call(port->subdev, video, g_std, std);
+
+ return 0;
+}
+
+static int vip_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+
+ if (stream->vfd->tvnorms == std)
+ return 0;
+
+ if (!(std & stream->vfd->tvnorms))
+ return -EINVAL;
+
+ if (vb2_is_busy(&stream->vb_vidq))
+ return -EBUSY;
+
+ v4l2_subdev_call(port->subdev, video, s_std, std);
+ return 0;
+}
+
+static int vip_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_fmt *fmt;
+
+ if (f->index >= port->num_active_fmt)
+ return -EINVAL;
+
+ fmt = port->active_fmt[f->index];
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int vip_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vip_fmt *fmt;
+ int ret;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = 0,
+ };
+
+ fmt = find_port_format_by_pix(port, f->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fse.index = f->index;
+ fse.code = fmt->code;
+ ret = v4l2_subdev_call(port->subdev, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return -EINVAL;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+ fse.min_height, fse.max_height);
+
+ f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ f->discrete.width = fse.max_width;
+ f->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int vip_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_fmt *fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = f->index,
+ .width = f->width,
+ .height = f->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ fmt = find_port_format_by_pix(port, f->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fie.code = fmt->code;
+ ret = v4l2_subdev_call(port->subdev, pad, enum_frame_interval,
+ NULL, &fie);
+ if (ret)
+ return ret;
+ f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ f->discrete = fie.interval;
+
+ return 0;
+}
+
+static int vip_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct vip_stream *stream = video_drvdata(file);
+ struct vip_port *port = stream->port;
+
+ return v4l2_g_parm_cap(video_devdata(file), port->subdev, a);
+}
+
+static int vip_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct vip_stream *stream = video_drvdata(file);
+ struct vip_port *port = stream->port;
+
+ return v4l2_s_parm_cap(video_devdata(file), port->subdev, a);
+}
+
+static int vip_calc_format_size(struct vip_port *port,
+ struct vip_fmt *fmt,
+ struct v4l2_format *f)
+{
+ enum v4l2_field *field;
+ unsigned int stride;
+ struct vip_dev *dev = port->dev;
+
+ if (!fmt) {
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "no vip_fmt format provided!\n");
+ return -EINVAL;
+ }
+
+ field = &f->fmt.pix.field;
+ if (*field == V4L2_FIELD_ANY)
+ *field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != *field && V4L2_FIELD_ALTERNATE != *field)
+ return -EINVAL;
+
+ 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);
+
+ stride = f->fmt.pix.width * (fmt->vpdma_fmt[0]->depth >> 3);
+ f->fmt.pix.bytesperline = ALIGN(stride, VPDMA_STRIDE_ALIGN);
+
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (fmt->coplanar) {
+ f->fmt.pix.sizeimage += f->fmt.pix.height *
+ f->fmt.pix.bytesperline *
+ fmt->vpdma_fmt[VIP_CHROMA]->depth >> 3;
+ }
+
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "calc_format_size: fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+ fourcc_to_str(f->fmt.pix.pixelformat),
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static inline bool vip_is_size_dma_aligned(u32 bpp, u32 width)
+{
+ return ((width * bpp) == ALIGN(width * bpp, VPDMA_STRIDE_ALIGN));
+}
+
+static int vip_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vip_fmt *fmt;
+ u32 best_width, best_height, largest_width, largest_height;
+ int ret, found;
+ enum vip_csc_state csc_direction;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = 0,
+ };
+
+ fmt = find_port_format_by_pix(port, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ /* Just get the first one enumerated */
+ fmt = port->active_fmt[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ csc_direction = vip_csc_direction(fmt->code, fmt->finfo);
+ if (csc_direction != VIP_CSC_NA) {
+ if (!is_csc_available(port)) {
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "CSC not available for Fourcc format (0x%08x).\n",
+ f->fmt.pix.pixelformat);
+
+ /* Just get the first one enumerated */
+ fmt = port->active_fmt[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ /* re-evaluate the csc_direction here */
+ csc_direction = vip_csc_direction(fmt->code,
+ fmt->finfo);
+ } else {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "CSC active on Port %c: going %s\n",
+ port->port_id == VIP_PORTA ? 'A' : 'B',
+ (csc_direction == VIP_CSC_Y2R) ? "Y2R" : "R2Y");
+ }
+ }
+
+ /*
+ * Given that sensors might support multiple mbus code we need
+ * to use the one that matches the requested pixel format
+ */
+ port->try_mbus_framefmt = port->mbus_framefmt;
+ port->try_mbus_framefmt.code = fmt->code;
+
+ /* check for/find a valid width/height */
+ ret = 0;
+ found = false;
+ best_width = 0;
+ best_height = 0;
+ largest_width = 0;
+ largest_height = 0;
+
+ fse.code = fmt->code;
+ for (fse.index = 0; ; fse.index++) {
+ u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
+
+ ret = v4l2_subdev_call(port->subdev, pad,
+ enum_frame_size, NULL, &fse);
+ if (ret)
+ break;
+
+ if (!vip_is_size_dma_aligned(bpp, fse.max_width))
+ continue;
+
+ if (fse.max_width >= largest_width &&
+ fse.max_height >= largest_height) {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new larger: %dx%d\n",
+ fse.index, fse.max_width, fse.max_height);
+ largest_width = fse.max_width;
+ largest_height = fse.max_height;
+ }
+
+ if (fse.max_width >= f->fmt.pix.width &&
+ fse.max_height >= f->fmt.pix.height) {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found at least larger: %dx%d\n",
+ fse.index, fse.max_width, fse.max_height);
+
+ if (!best_width ||
+ ((abs(best_width - f->fmt.pix.width) >=
+ abs(fse.max_width - f->fmt.pix.width)) &&
+ (abs(best_height - f->fmt.pix.height) >=
+ abs(fse.max_height - f->fmt.pix.height)))) {
+ best_width = fse.max_width;
+ best_height = fse.max_height;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new best: %dx%d\n",
+ fse.index, fse.max_width,
+ fse.max_height);
+ }
+ }
+
+ if (f->fmt.pix.width == fse.max_width &&
+ f->fmt.pix.height == fse.max_height) {
+ found = true;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct match: %dx%d\n",
+ fse.index, fse.max_width,
+ fse.max_height);
+ break;
+ }
+
+ if (f->fmt.pix.width >= fse.min_width &&
+ f->fmt.pix.width <= fse.max_width &&
+ f->fmt.pix.height >= fse.min_height &&
+ f->fmt.pix.height <= fse.max_height) {
+ found = true;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct range match: %dx%d\n",
+ fse.index, fse.max_width,
+ fse.max_height);
+ break;
+ }
+ }
+
+ if (found) {
+ port->try_mbus_framefmt.width = f->fmt.pix.width;
+ port->try_mbus_framefmt.height = f->fmt.pix.height;
+ /* No need to check for scaling */
+ goto calc_size;
+ } else if (f->fmt.pix.width > largest_width) {
+ port->try_mbus_framefmt.width = largest_width;
+ port->try_mbus_framefmt.height = largest_height;
+ } else if (best_width) {
+ port->try_mbus_framefmt.width = best_width;
+ port->try_mbus_framefmt.height = best_height;
+ } else {
+ /* use existing values as default */
+ }
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt best subdev size: %dx%d\n",
+ port->try_mbus_framefmt.width,
+ port->try_mbus_framefmt.height);
+
+ if (is_scaler_available(port) &&
+ csc_direction != VIP_CSC_Y2R &&
+ !vip_is_mbuscode_raw(fmt->code) &&
+ f->fmt.pix.height <= port->try_mbus_framefmt.height &&
+ port->try_mbus_framefmt.height <= SC_MAX_PIXEL_HEIGHT &&
+ port->try_mbus_framefmt.width <= SC_MAX_PIXEL_WIDTH) {
+ /*
+ * Scaler is only accessible if the dst colorspace is YUV.
+ * As the input to the scaler must be in YUV mode only.
+ *
+ * Scaling up is allowed only horizontally.
+ */
+ unsigned int hratio, vratio, width_align, height_align;
+ u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler active on Port %c: requesting %dx%d\n",
+ port->port_id == VIP_PORTA ? 'A' : 'B',
+ f->fmt.pix.width, f->fmt.pix.height);
+
+ /* Just make sure everything is properly aligned */
+ width_align = ALIGN(f->fmt.pix.width * bpp, VPDMA_STRIDE_ALIGN);
+ width_align /= bpp;
+ height_align = ALIGN(f->fmt.pix.height, 2);
+
+ f->fmt.pix.width = width_align;
+ f->fmt.pix.height = height_align;
+
+ hratio = f->fmt.pix.width * 1000 /
+ port->try_mbus_framefmt.width;
+ vratio = f->fmt.pix.height * 1000 /
+ port->try_mbus_framefmt.height;
+ if (hratio < 125) {
+ f->fmt.pix.width = port->try_mbus_framefmt.width / 8;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Horizontal scaling ratio out of range adjusting -> %d\n",
+ f->fmt.pix.width);
+ }
+
+ if (vratio < 188) {
+ f->fmt.pix.height = port->try_mbus_framefmt.height / 4;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Vertical scaling ratio out of range adjusting -> %d\n",
+ f->fmt.pix.height);
+ }
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler: got %dx%d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ } else {
+ /* use existing values as default */
+ f->fmt.pix.width = port->try_mbus_framefmt.width;
+ f->fmt.pix.height = port->try_mbus_framefmt.height;
+ }
+
+calc_size:
+ /* That we have a fmt calculate imagesize and bytesperline */
+ return vip_calc_format_size(port, fmt, f);
+}
+
+static int vip_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+
+ /* Use last known values or defaults */
+ f->fmt.pix.width = stream->width;
+ f->fmt.pix.height = stream->height;
+ f->fmt.pix.pixelformat = port->fmt->fourcc;
+ f->fmt.pix.field = stream->sup_field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ f->fmt.pix.bytesperline = stream->bytesperline;
+ f->fmt.pix.sizeimage = stream->sizeimage;
+
+ return 0;
+}
+
+static int vip_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct v4l2_subdev_format sfmt;
+ struct v4l2_mbus_framefmt *mf;
+ enum vip_csc_state csc_direction;
+ int ret;
+
+ ret = vip_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ if (vb2_is_busy(&stream->vb_vidq)) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ /*
+ * Check if we need the scaler or not
+ *
+ * Since on previous S_FMT call the scaler might have been
+ * allocated if it is not needed in this instance we will
+ * attempt to free it just in case.
+ *
+ * free_scaler() is harmless unless the current port
+ * allocated it.
+ */
+ if (f->fmt.pix.width == port->try_mbus_framefmt.width &&
+ f->fmt.pix.height == port->try_mbus_framefmt.height)
+ free_scaler(port);
+ else
+ allocate_scaler(port);
+
+ port->fmt = find_port_format_by_pix(port,
+ f->fmt.pix.pixelformat);
+ stream->width = f->fmt.pix.width;
+ stream->height = f->fmt.pix.height;
+ stream->bytesperline = f->fmt.pix.bytesperline;
+ stream->sizeimage = f->fmt.pix.sizeimage;
+ stream->sup_field = f->fmt.pix.field;
+ stream->field = f->fmt.pix.field;
+
+ port->c_rect.left = 0;
+ port->c_rect.top = 0;
+ port->c_rect.width = stream->width;
+ port->c_rect.height = stream->height;
+
+ /*
+ * Check if we need the csc unit or not
+ *
+ * Since on previous S_FMT call, the csc might have been
+ * allocated if it is not needed in this instance we will
+ * attempt to free it just in case.
+ *
+ * free_csc() is harmless unless the current port
+ * allocated it.
+ */
+ csc_direction = vip_csc_direction(port->fmt->code, port->fmt->finfo);
+ if (csc_direction == VIP_CSC_NA)
+ free_csc(port);
+ else
+ allocate_csc(port, csc_direction);
+
+ if (stream->sup_field == V4L2_FIELD_ALTERNATE)
+ port->flags |= FLAG_INTERLACED;
+ else
+ port->flags &= ~FLAG_INTERLACED;
+
+ memset(&sfmt, 0, sizeof(sfmt));
+ mf = &sfmt.format;
+ v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code);
+ /* Make sure to use the subdev size found in the try_fmt */
+ mf->width = port->try_mbus_framefmt.width;
+ mf->height = port->try_mbus_framefmt.height;
+
+ sfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sfmt.pad = 0;
+ ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt);
+ if (ret) {
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "set_fmt failed in subdev\n");
+ return ret;
+ }
+
+ /* Save it */
+ port->mbus_framefmt = *mf;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "s_fmt subdev fmt mbus_code: %04X size: %dx%d\n",
+ port->mbus_framefmt.code,
+ port->mbus_framefmt.width, port->mbus_framefmt.height);
+
+ return 0;
+}
+
+static void vip_unset_csc_y2r(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ }
+}
+
+static void vip_unset_csc_r2y(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->port_id != VIP_PORTA) {
+ v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n");
+ return;
+ }
+
+ if (port->scaler && port->fmt->coplanar) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else if (port->scaler) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else if (port->fmt->coplanar) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ }
+}
+
+static void vip_unset_rgb(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->port_id != VIP_PORTA) {
+ v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n");
+ return;
+ }
+
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+}
+
+static void vip_unset_yuv(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->scaler && port->fmt->coplanar) {
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ }
+ } else if (port->scaler) {
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ }
+ } else if (port->fmt->coplanar) {
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ }
+ } else {
+ /*
+ * We undo all data path setting except for the multi
+ * stream case.
+ * Because we cannot disrupt other on-going capture if only
+ * one stream is terminated the other might still be going
+ */
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ }
+}
+
+/*
+ * Does the exact opposite of set_fmt_params
+ * It makes sure the DataPath register is sane after tear down
+ */
+static void unset_fmt_params(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+
+ stream->sequence = 0;
+ stream->field = V4L2_FIELD_TOP;
+
+ /* Undo CSC Y2R routing */
+ if (port->csc == VIP_CSC_Y2R) {
+ vip_unset_csc_y2r(dev, port);
+ /* Undo CSC R2Y routing */
+ } else if (port->csc == VIP_CSC_R2Y) {
+ vip_unset_csc_r2y(dev, port);
+ /* Undo RGB output routing (no CSC) */
+ } else if (v4l2_is_format_rgb(port->fmt->finfo)) {
+ vip_unset_rgb(dev, port);
+ /* Undo YUV routing with no CSC */
+ } else {
+ vip_unset_yuv(dev, port);
+ }
+}
+
+static void vip_config_csc_y2r(struct vip_dev *dev, struct vip_port *port)
+{
+ port->flags &= ~FLAG_MULT_PORT;
+
+ /* Set alpha component in background color */
+ vpdma_set_bg_color(dev->shared->vpdma,
+ (struct vpdma_data_format *)
+ port->fmt->vpdma_fmt[0],
+ 0xff);
+
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 1);
+ } else {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 2);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
+ }
+}
+
+static void vip_config_csc_r2y(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->port_id != VIP_PORTA) {
+ v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n");
+ return;
+ }
+
+ port->flags &= ~FLAG_MULT_PORT;
+
+ if (port->scaler && port->fmt->coplanar) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4);
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else if (port->scaler) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4);
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else if (port->fmt->coplanar) {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 2);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 2);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ }
+}
+
+static void vip_config_rgb(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->port_id != VIP_PORTA) {
+ v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n");
+ return;
+ }
+
+ port->flags &= ~FLAG_MULT_PORT;
+
+ /* Set alpha component in background color */
+ vpdma_set_bg_color(dev->shared->vpdma,
+ (struct vpdma_data_format *)
+ port->fmt->vpdma_fmt[0],
+ 0xff);
+
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
+}
+
+static void vip_config_yuv(struct vip_dev *dev, struct vip_port *port)
+{
+ if (port->scaler && port->fmt->coplanar) {
+ port->flags &= ~FLAG_MULT_PORT;
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ }
+ } else if (port->scaler) {
+ port->flags &= ~FLAG_MULT_PORT;
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ }
+ } else if (port->fmt->coplanar) {
+ port->flags &= ~FLAG_MULT_PORT;
+ if (port->port_id == VIP_PORTA) {
+ vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 3);
+ vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+ } else {
+ vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 4);
+ vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ }
+ } else {
+ port->flags |= FLAG_MULT_PORT;
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+ vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+ }
+}
+
+/*
+ * Set the registers that are modified when the video format changes.
+ */
+static void set_fmt_params(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+
+ stream->sequence = 0;
+ stream->field = V4L2_FIELD_TOP;
+
+ /* YUV input, RGB output using CSC (Y2R) */
+ if (port->csc == VIP_CSC_Y2R) {
+ vip_config_csc_y2r(dev, port);
+ /* RGB input, YUV output using CSC (R2Y) */
+ } else if (port->csc == VIP_CSC_R2Y) {
+ vip_config_csc_r2y(dev, port);
+ /* RGB output without CSC */
+ } else if (v4l2_is_format_rgb(port->fmt->finfo)) {
+ vip_config_rgb(dev, port);
+ /* YUV output without CSC */
+ } else {
+ vip_config_yuv(dev, port);
+ }
+}
+
+static int vip_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ struct vip_stream *stream = file2stream(file);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = stream->width;
+ s->r.height = stream->height;
+ return 0;
+
+ case V4L2_SEL_TGT_CROP:
+ s->r = stream->port->c_rect;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int vip_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct v4l2_rect r = s->r;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ if (vb2_is_busy(&stream->vb_vidq))
+ return -EBUSY;
+
+ v4l_bound_align_image(&r.width, 0, stream->width, 0,
+ &r.height, 0, stream->height, 0, 0);
+
+ r.left = clamp_t(unsigned int, r.left, 0, stream->width - r.width);
+ r.top = clamp_t(unsigned int, r.top, 0, stream->height - r.height);
+
+ if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
+ return -ERANGE;
+
+ s->r = r;
+ stream->port->c_rect = r;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "cropped (%d,%d)/%dx%d of %dx%d\n",
+ r.left, r.top, r.width, r.height,
+ stream->width, stream->height);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops vip_ioctl_ops = {
+ .vidioc_querycap = vip_querycap,
+ .vidioc_enum_input = vip_enuminput,
+ .vidioc_g_input = vip_g_input,
+ .vidioc_s_input = vip_s_input,
+
+ .vidioc_querystd = vip_querystd,
+ .vidioc_g_std = vip_g_std,
+ .vidioc_s_std = vip_s_std,
+
+ .vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vip_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vip_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vip_s_fmt_vid_cap,
+
+ .vidioc_enum_frameintervals = vip_enum_frameintervals,
+ .vidioc_enum_framesizes = vip_enum_framesizes,
+ .vidioc_s_parm = vip_s_parm,
+ .vidioc_g_parm = vip_g_parm,
+ .vidioc_g_selection = vip_g_selection,
+ .vidioc_s_selection = vip_s_selection,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * Videobuf operations
+ */
+static int vip_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vq);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ unsigned int size = stream->sizeimage;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "get %d buffer(s) of size %d each.\n",
+ *nbuffers, sizes[0]);
+
+ return 0;
+}
+
+static int vip_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+
+ if (vb2_plane_size(vb, 0) < stream->sizeimage) {
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)stream->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, stream->sizeimage);
+
+ return 0;
+}
+
+static void vip_buf_queue(struct vb2_buffer *vb)
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+ struct vip_dev *dev = stream->port->dev;
+ struct vip_buffer *buf = container_of(vb, struct vip_buffer,
+ vb.vb2_buf);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ list_add_tail(&buf->list, &stream->vidq);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static void return_buffers(struct vb2_queue *vq, int state)
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vq);
+ struct vip_dev *dev = stream->port->dev;
+ struct vip_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ /* release all active buffers */
+ while (!list_empty(&stream->post_bufs)) {
+ buf = list_entry(stream->post_bufs.next,
+ struct vip_buffer, list);
+ list_del(&buf->list);
+ if (buf->drop == 1)
+ list_add_tail(&buf->list, &stream->dropq);
+ else
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ while (!list_empty(&stream->vidq)) {
+ buf = list_entry(stream->vidq.next, struct vip_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ INIT_LIST_HEAD(&stream->post_bufs);
+ INIT_LIST_HEAD(&stream->vidq);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int vip_setup_scaler(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct sc_data *sc = dev->sc;
+ struct csc_data *csc = dev->csc;
+ struct vpdma_data *vpdma = dev->shared->vpdma;
+ struct vip_mmr_adb *mmr_adb = port->mmr_adb.addr;
+ int list_num = stream->list_num;
+ int timeout = 500;
+ struct v4l2_format dst_f;
+ struct v4l2_format src_f;
+
+ memset(&src_f, 0, sizeof(src_f));
+ src_f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_fill_pix_format(&src_f.fmt.pix, &port->mbus_framefmt);
+ src_f.fmt.pix.pixelformat = vip_mbus_code_to_fourcc(port->fmt->code);
+
+ dst_f = src_f;
+ dst_f.fmt.pix.pixelformat = port->fmt->fourcc;
+ dst_f.fmt.pix.width = stream->width;
+ dst_f.fmt.pix.height = stream->height;
+
+ /* if scaler not associated with this port then skip */
+ if (port->scaler) {
+ sc_set_hs_coeffs(sc, port->sc_coeff_h.addr,
+ port->mbus_framefmt.width,
+ port->c_rect.width);
+ sc_set_vs_coeffs(sc, port->sc_coeff_v.addr,
+ port->mbus_framefmt.height,
+ port->c_rect.height);
+ sc_config_scaler(sc, &mmr_adb->sc_regs0[0],
+ &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0],
+ port->mbus_framefmt.width,
+ port->mbus_framefmt.height,
+ port->c_rect.width,
+ port->c_rect.height);
+ port->load_mmrs = true;
+ }
+
+ /* if csc not associated with this port then skip */
+ if (port->csc) {
+ csc_set_coeff(csc, &mmr_adb->csc_regs[0],
+ &src_f, &dst_f);
+
+ port->load_mmrs = true;
+ }
+
+ /* If coeff are already loaded then skip */
+ if (!sc->load_coeff_v && !sc->load_coeff_h && !port->load_mmrs)
+ return 0;
+
+ if (vpdma_list_busy(vpdma, list_num)) {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: List %d is busy\n",
+ __func__, list_num);
+ }
+
+ /* Make sure we start with a clean list */
+ vpdma_reset_desc_list(&stream->desc_list);
+
+ /* config descriptors */
+ if (port->load_mmrs) {
+ vpdma_map_desc_buf(vpdma, &port->mmr_adb);
+ vpdma_add_cfd_adb(&stream->desc_list, CFD_MMR_CLIENT,
+ &port->mmr_adb);
+
+ port->load_mmrs = false;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Added mmr_adb config desc\n");
+ }
+
+ if (sc->loaded_coeff_h != port->sc_coeff_h.dma_addr ||
+ sc->load_coeff_h) {
+ vpdma_map_desc_buf(vpdma, &port->sc_coeff_h);
+ vpdma_add_cfd_block(&stream->desc_list,
+ VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
+ &port->sc_coeff_h, 0);
+
+ sc->loaded_coeff_h = port->sc_coeff_h.dma_addr;
+ sc->load_coeff_h = false;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_h config desc\n");
+ }
+
+ if (sc->loaded_coeff_v != port->sc_coeff_v.dma_addr ||
+ sc->load_coeff_v) {
+ vpdma_map_desc_buf(vpdma, &port->sc_coeff_v);
+ vpdma_add_cfd_block(&stream->desc_list,
+ VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
+ &port->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4);
+
+ sc->loaded_coeff_v = port->sc_coeff_v.dma_addr;
+ sc->load_coeff_v = false;
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_v config desc\n");
+ }
+ v4l2_dbg(3, debug, stream, "CFD_SC_CLIENT %d slice_id: %d\n",
+ VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, dev->slice_id);
+
+ vpdma_map_desc_buf(vpdma, &stream->desc_list.buf);
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Submitting desc on list# %d\n", list_num);
+ vpdma_submit_descs(vpdma, &stream->desc_list, list_num);
+
+ while (vpdma_list_busy(vpdma, list_num) && timeout--)
+ usleep_range(1000, 1100);
+
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &port->mmr_adb);
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_h);
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_v);
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+
+ vpdma_reset_desc_list(&stream->desc_list);
+
+ if (timeout <= 0) {
+ v4l2_err(&dev->v4l2_dev, "Timed out setting up scaler through VPDMA list\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int vip_load_vpdma_list_fifo(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vpdma_data *vpdma = dev->shared->vpdma;
+ int list_num = stream->list_num;
+ struct vip_buffer *buf;
+ unsigned long flags;
+ int timeout, i;
+
+ if (vpdma_list_busy(dev->shared->vpdma, stream->list_num))
+ return -EBUSY;
+
+ for (i = 0; i < VIP_VPDMA_FIFO_SIZE; i++) {
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&stream->vidq)) {
+ v4l2_err(&dev->v4l2_dev, "No buffer left!");
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return -EINVAL;
+ }
+
+ buf = list_entry(stream->vidq.next,
+ struct vip_buffer, list);
+ buf->drop = false;
+
+ list_move_tail(&buf->list, &stream->post_bufs);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: start_dma vb2 buf idx:%d\n",
+ __func__, buf->vb.vb2_buf.index);
+ start_dma(stream, buf);
+
+ timeout = 500;
+ while (vpdma_list_busy(vpdma, list_num) && timeout--)
+ usleep_range(1000, 1100);
+
+ if (timeout <= 0) {
+ v4l2_err(&dev->v4l2_dev, "Timed out loading VPDMA list fifo\n");
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vq);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ int ret;
+
+ ret = vip_setup_scaler(stream);
+ if (ret)
+ goto err;
+
+ /*
+ * Make sure the scaler is configured before the datapath is
+ * enabled. The scaler can only load the coefficient
+ * parameters when it is idle. If the scaler path is enabled
+ * and video data is being received then the VPDMA transfer will
+ * stall indefinitely.
+ */
+ set_fmt_params(stream);
+ ret = vip_setup_parser(port);
+ if (ret)
+ goto err;
+
+ if (port->subdev) {
+ ret = v4l2_subdev_call(port->subdev, video, s_stream, 1);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "stream on failed in subdev\n");
+ goto err;
+ }
+ }
+
+ stream->sequence = 0;
+ stream->field = V4L2_FIELD_TOP;
+ populate_desc_list(stream);
+
+ ret = vip_load_vpdma_list_fifo(stream);
+ if (ret)
+ goto err;
+
+ stream->num_recovery = 0;
+
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+ enable_irqs(dev, dev->slice_id, stream->list_num);
+ vip_schedule_next_buffer(stream);
+ vip_parser_stop_imm(port, false);
+ vip_enable_parser(port, true);
+
+ return 0;
+
+err:
+ return_buffers(vq, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+/*
+ * Abort streaming and wait for last buffer
+ */
+static void vip_stop_streaming(struct vb2_queue *vq)
+{
+ struct vip_stream *stream = vb2_get_drv_priv(vq);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ int ret;
+
+ vip_parser_stop_imm(port, true);
+ vip_enable_parser(port, false);
+ unset_fmt_params(stream);
+
+ disable_irqs(dev, dev->slice_id, stream->list_num);
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+
+ if (port->subdev) {
+ ret = v4l2_subdev_call(port->subdev, video, s_stream, 0);
+ if (ret)
+ v4l2_err(&dev->v4l2_dev, "Failed to stop subdev stream");
+ }
+
+ stop_dma(stream, true);
+
+ return_buffers(vq, VB2_BUF_STATE_ERROR);
+
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+ vpdma_reset_desc_list(&stream->desc_list);
+}
+
+static const struct vb2_ops vip_video_qops = {
+ .queue_setup = vip_queue_setup,
+ .buf_prepare = vip_buf_prepare,
+ .buf_queue = vip_buf_queue,
+ .start_streaming = vip_start_streaming,
+ .stop_streaming = vip_stop_streaming,
+};
+
+static int vip_init_dev(struct vip_dev *dev)
+{
+ if (dev->num_ports != 0)
+ goto done;
+
+ vip_set_clock_enable(dev, 1);
+ vip_module_toggle(dev, VIP_SC_RST, false);
+ vip_module_toggle(dev, VIP_CSC_RST, false);
+done:
+ dev->num_ports++;
+
+ return 0;
+}
+
+static inline bool is_scaler_available(struct vip_port *port)
+{
+ if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL)
+ if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
+ port->dev->sc_assigned == port->port_id)
+ return true;
+ return false;
+}
+
+static inline bool allocate_scaler(struct vip_port *port)
+{
+ if (is_scaler_available(port)) {
+ if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
+ port->dev->sc_assigned == port->port_id) {
+ port->dev->sc_assigned = port->port_id;
+ port->scaler = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline void free_scaler(struct vip_port *port)
+{
+ if (port->dev->sc_assigned == port->port_id) {
+ port->dev->sc_assigned = VIP_NOT_ASSIGNED;
+ port->scaler = false;
+ }
+}
+
+static bool is_csc_available(struct vip_port *port)
+{
+ if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL)
+ if (port->dev->csc_assigned == VIP_NOT_ASSIGNED ||
+ port->dev->csc_assigned == port->port_id)
+ return true;
+ return false;
+}
+
+static bool allocate_csc(struct vip_port *port,
+ enum vip_csc_state csc_direction)
+{
+ struct vip_dev *dev = port->dev;
+ /* Is CSC needed? */
+ if (csc_direction != VIP_CSC_NA) {
+ if (is_csc_available(port)) {
+ port->dev->csc_assigned = port->port_id;
+ port->csc = csc_direction;
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc allocated: dir: %d\n",
+ __func__, csc_direction);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void free_csc(struct vip_port *port)
+{
+ struct vip_dev *dev = port->dev;
+
+ if (port->dev->csc_assigned == port->port_id) {
+ port->dev->csc_assigned = VIP_NOT_ASSIGNED;
+ port->csc = VIP_CSC_NA;
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc freed\n",
+ __func__);
+ }
+}
+
+static int vip_init_port(struct vip_port *port)
+{
+ struct vip_dev *dev = port->dev;
+ int ret;
+ struct vip_fmt *fmt;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = 0,
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+
+ if (port->num_streams != 0)
+ goto done;
+
+ ret = vip_init_dev(port->dev);
+ if (ret)
+ goto done;
+
+ /* Get subdevice current frame format */
+ ret = v4l2_subdev_call(port->subdev, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port get_fmt failed in subdev: (%d)\n",
+ ret);
+
+ /* try to find one that matches */
+ fmt = find_port_format_by_code(port, mbus_fmt->code);
+ if (!fmt) {
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "subdev default mbus_fmt %04x is not matched.\n",
+ mbus_fmt->code);
+ /* if all else fails just pick the first one */
+ fmt = port->active_fmt[0];
+
+ mbus_fmt->code = fmt->code;
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+ ret = v4l2_subdev_call(port->subdev, pad, set_fmt,
+ NULL, &sd_fmt);
+ if (ret)
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port set_fmt failed in subdev: (%d)\n",
+ ret);
+ }
+
+ /* Assign current format */
+ port->fmt = fmt;
+ port->mbus_framefmt = *mbus_fmt;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: g_mbus_fmt subdev mbus_code: %04X fourcc:%s size: %dx%d\n",
+ __func__, fmt->code,
+ fourcc_to_str(fmt->fourcc),
+ mbus_fmt->width, mbus_fmt->height);
+
+ if (mbus_fmt->field == V4L2_FIELD_ALTERNATE)
+ port->flags |= FLAG_INTERLACED;
+ else
+ port->flags &= ~FLAG_INTERLACED;
+
+ port->c_rect.left = 0;
+ port->c_rect.top = 0;
+ port->c_rect.width = mbus_fmt->width;
+ port->c_rect.height = mbus_fmt->height;
+
+ ret = vpdma_alloc_desc_buf(&port->sc_coeff_h, SC_COEF_SRAM_SIZE);
+ if (ret != 0)
+ return ret;
+
+ ret = vpdma_alloc_desc_buf(&port->sc_coeff_v, SC_COEF_SRAM_SIZE);
+ if (ret != 0)
+ goto free_sc_h;
+
+ ret = vpdma_alloc_desc_buf(&port->mmr_adb, sizeof(struct vip_mmr_adb));
+ if (ret != 0)
+ goto free_sc_v;
+
+ init_adb_hdrs(port);
+
+ vip_enable_parser(port, false);
+done:
+ port->num_streams++;
+ return 0;
+
+free_sc_v:
+ vpdma_free_desc_buf(&port->sc_coeff_v);
+free_sc_h:
+ vpdma_free_desc_buf(&port->sc_coeff_h);
+ return ret;
+}
+
+static int vip_init_stream(struct vip_stream *stream)
+{
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ struct vip_fmt *fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ struct v4l2_format f;
+ int ret;
+
+ ret = vip_init_port(port);
+ if (ret != 0)
+ return ret;
+
+ fmt = port->fmt;
+ mbus_fmt = &port->mbus_framefmt;
+
+ memset(&f, 0, sizeof(f));
+
+ /* Properly calculate the sizeimage and bytesperline values. */
+ v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt);
+ f.fmt.pix.pixelformat = fmt->fourcc;
+ ret = vip_calc_format_size(port, fmt, &f);
+ if (ret)
+ return ret;
+
+ stream->width = f.fmt.pix.width;
+ stream->height = f.fmt.pix.height;
+ stream->sup_field = f.fmt.pix.field;
+ stream->bytesperline = f.fmt.pix.bytesperline;
+ stream->sizeimage = f.fmt.pix.sizeimage;
+
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+ fourcc_to_str(f.fmt.pix.pixelformat),
+ f.fmt.pix.width, f.fmt.pix.height,
+ f.fmt.pix.bytesperline, f.fmt.pix.sizeimage);
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream vpdma data type: 0x%02X\n",
+ port->fmt->vpdma_fmt[0]->data_type);
+
+ ret = vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE,
+ VPDMA_LIST_TYPE_NORMAL);
+
+ if (ret != 0)
+ return ret;
+
+ stream->write_desc = (struct vpdma_dtd *)stream->desc_list.buf.addr
+ + 15;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n",
+ __func__, &stream);
+
+ return 0;
+}
+
+static void vip_release_dev(struct vip_dev *dev)
+{
+ /*
+ * On last close, disable clocks to conserve power
+ */
+
+ if (--dev->num_ports == 0) {
+ /* reset the scaler module */
+ vip_module_toggle(dev, VIP_SC_RST, true);
+ vip_module_toggle(dev, VIP_CSC_RST, true);
+ vip_set_clock_enable(dev, 0);
+ }
+}
+
+static int vip_set_crop_parser(struct vip_port *port)
+{
+ struct vip_dev *dev = port->dev;
+ struct vip_parser_data *parser = dev->parser;
+ u32 hcrop = 0, vcrop = 0;
+ u32 width = port->mbus_framefmt.width;
+
+ if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
+ /*
+ * Special case since we are faking a YUV422 16bit format
+ * to have the vpdma perform the needed byte swap
+ * we need to adjust the pixel width accordingly
+ * otherwise the parser will attempt to collect more pixels
+ * then available and the vpdma transfer will exceed the
+ * allocated frame buffer.
+ */
+ width >>= 1;
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting width to %d\n",
+ __func__, width);
+ }
+
+ /*
+ * Set Parser Crop parameters to source size otherwise
+ * scaler and colorspace converter will yield garbage.
+ */
+ hcrop = VIP_ACT_BYPASS;
+ insert_field(&hcrop, 0, VIP_ACT_SKIP_NUMPIX_MASK,
+ VIP_ACT_SKIP_NUMPIX_SHFT);
+ insert_field(&hcrop, width,
+ VIP_ACT_USE_NUMPIX_MASK, VIP_ACT_USE_NUMPIX_SHFT);
+ reg_write(parser, VIP_PARSER_CROP_H_PORT(port->port_id), hcrop);
+
+ insert_field(&vcrop, 0, VIP_ACT_SKIP_NUMLINES_MASK,
+ VIP_ACT_SKIP_NUMLINES_SHFT);
+ insert_field(&vcrop, port->mbus_framefmt.height,
+ VIP_ACT_USE_NUMLINES_MASK, VIP_ACT_USE_NUMLINES_SHFT);
+ reg_write(parser, VIP_PARSER_CROP_V_PORT(port->port_id), vcrop);
+
+ return 0;
+}
+
+static int vip_setup_parser(struct vip_port *port)
+{
+ struct vip_dev *dev = port->dev;
+ struct vip_parser_data *parser = dev->parser;
+ struct v4l2_fwnode_endpoint *endpoint = &port->endpoint;
+ int iface, sync_type;
+ u32 flags = 0, config0;
+
+ /* Reset the port */
+ vip_reset_parser(port, true);
+ usleep_range(200, 250);
+ vip_reset_parser(port, false);
+
+ config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+ if (endpoint->bus_type == V4L2_MBUS_BT656) {
+ flags = endpoint->bus.parallel.flags;
+ iface = DUAL_8B_INTERFACE;
+
+ /*
+ * Ideally, this should come from subdev
+ * port->fmt can be anything once CSC is enabled
+ */
+ if (vip_is_mbuscode_rgb(port->fmt->code))
+ sync_type = EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444;
+ else
+ sync_type = EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
+
+ } else if (endpoint->bus_type == V4L2_MBUS_PARALLEL) {
+ flags = endpoint->bus.parallel.flags;
+
+ switch (endpoint->bus.parallel.bus_width) {
+ case 24:
+ iface = SINGLE_24B_INTERFACE;
+ break;
+ case 16:
+ iface = SINGLE_16B_INTERFACE;
+ break;
+ case 8:
+ default:
+ iface = DUAL_8B_INTERFACE;
+ }
+
+ if (vip_is_mbuscode_rgb(port->fmt->code))
+ sync_type = DISCRETE_SYNC_SINGLE_RGB_24B;
+ else
+ sync_type = DISCRETE_SYNC_SINGLE_YUV422;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ config0 |= VIP_HSYNC_POLARITY;
+ else if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ config0 &= ~VIP_HSYNC_POLARITY;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ config0 |= VIP_VSYNC_POLARITY;
+ else if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ config0 &= ~VIP_VSYNC_POLARITY;
+
+ config0 &= ~VIP_USE_ACTVID_HSYNC_ONLY;
+ config0 |= VIP_ACTVID_POLARITY;
+ config0 |= VIP_DISCRETE_BASIC_MODE;
+
+ } else {
+ v4l2_err(&dev->v4l2_dev, "Device doesn't support CSI2");
+ return -EINVAL;
+ }
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) {
+ vip_set_pclk_invert(port);
+ config0 |= VIP_PIXCLK_EDGE_POLARITY;
+ } else {
+ config0 &= ~VIP_PIXCLK_EDGE_POLARITY;
+ }
+
+ config0 |= ((sync_type & VIP_SYNC_TYPE_MASK) << VIP_SYNC_TYPE_SHFT);
+
+ reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+
+ vip_set_data_interface(port, iface);
+ vip_set_crop_parser(port);
+
+ return 0;
+}
+
+static void vip_enable_parser(struct vip_port *port, bool on)
+{
+ u32 config0;
+ struct vip_dev *dev = port->dev;
+ struct vip_parser_data *parser = dev->parser;
+
+ config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+ if (on) {
+ config0 |= VIP_PORT_ENABLE;
+ config0 &= ~(VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
+ } else {
+ config0 &= ~VIP_PORT_ENABLE;
+ config0 |= (VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
+ }
+ reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+}
+
+static void vip_reset_parser(struct vip_port *port, bool on)
+{
+ u32 config0;
+ struct vip_dev *dev = port->dev;
+ struct vip_parser_data *parser = dev->parser;
+
+ config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+ if (on)
+ config0 |= VIP_SW_RESET;
+ else
+ config0 &= ~VIP_SW_RESET;
+
+ reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+}
+
+static void vip_parser_stop_imm(struct vip_port *port, bool on)
+{
+ u32 config0;
+ struct vip_dev *dev = port->dev;
+ struct vip_parser_data *parser = dev->parser;
+
+ config0 = reg_read(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id));
+
+ if (on)
+ config0 = 0xffffffff;
+ else
+ config0 = 0;
+
+ reg_write(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id), config0);
+}
+
+static void vip_release_stream(struct vip_stream *stream)
+{
+ struct vip_dev *dev = stream->port->dev;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n",
+ __func__, &stream);
+
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+ vpdma_free_desc_buf(&stream->desc_list.buf);
+ vpdma_free_desc_list(&stream->desc_list);
+}
+
+static void vip_release_port(struct vip_port *port)
+{
+ struct vip_dev *dev = port->dev;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: port instance %pa\n",
+ __func__, &port);
+
+ vpdma_free_desc_buf(&port->mmr_adb);
+ vpdma_free_desc_buf(&port->sc_coeff_h);
+ vpdma_free_desc_buf(&port->sc_coeff_v);
+}
+
+static void stop_dma(struct vip_stream *stream, bool clear_list)
+{
+ struct vip_dev *dev = stream->port->dev;
+ int ch, size = 0;
+
+ /* Create a list of channels to be cleared */
+ for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) {
+ if (stream->vpdma_channels[ch] == 1) {
+ stream->vpdma_channels_to_abort[size++] = ch;
+ v4l2_dbg(2, debug, &dev->v4l2_dev, "Clear channel no: %d\n", ch);
+ }
+ }
+
+ /* Clear all the used channels for the list */
+ vpdma_list_cleanup(dev->shared->vpdma, stream->list_num,
+ stream->vpdma_channels_to_abort, size);
+
+ if (clear_list)
+ for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++)
+ stream->vpdma_channels[ch] = 0;
+}
+
+static int vip_open(struct file *file)
+{
+ struct vip_stream *stream = video_drvdata(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ int ret = 0;
+
+ mutex_lock(&dev->mutex);
+
+ ret = v4l2_fh_open(file);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "v4l2_fh_open failed\n");
+ goto unlock;
+ }
+
+ /*
+ * If this is the first open file.
+ * Then initialize hw module.
+ */
+ if (!v4l2_fh_is_singular_file(file))
+ goto unlock;
+
+ if (vip_init_stream(stream))
+ ret = -ENODEV;
+unlock:
+ mutex_unlock(&dev->mutex);
+ return ret;
+}
+
+static int vip_release(struct file *file)
+{
+ struct vip_stream *stream = video_drvdata(file);
+ struct vip_port *port = stream->port;
+ struct vip_dev *dev = port->dev;
+ bool fh_singular;
+ int ret;
+
+ mutex_lock(&dev->mutex);
+
+ /* Save the singular status before we call the clean-up helper */
+ fh_singular = v4l2_fh_is_singular_file(file);
+
+ /* the release helper will cleanup any on-going streaming */
+ ret = _vb2_fop_release(file, NULL);
+
+ free_csc(port);
+ free_scaler(port);
+
+ /*
+ * If this is the last open file.
+ * Then de-initialize hw module.
+ */
+ if (fh_singular) {
+ vip_release_stream(stream);
+
+ if (--port->num_streams == 0) {
+ vip_release_port(port);
+ vip_release_dev(port->dev);
+ }
+ }
+
+ mutex_unlock(&dev->mutex);
+
+ return ret;
+}
+
+/*
+ * File operations
+ */
+static const struct v4l2_file_operations vip_fops = {
+ .owner = THIS_MODULE,
+ .open = vip_open,
+ .release = vip_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static struct video_device vip_videodev = {
+ .name = VIP_MODULE_NAME,
+ .fops = &vip_fops,
+ .ioctl_ops = &vip_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE,
+};
+
+static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
+{
+ struct vip_stream *stream;
+ struct vip_dev *dev = port->dev;
+ struct vb2_queue *q;
+ struct video_device *vfd;
+ struct vip_buffer *buf;
+ struct list_head *pos, *tmp;
+ int ret, i;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->port = port;
+ stream->stream_id = stream_id;
+ stream->vfl_type = vfl_type;
+ port->cap_streams[stream_id] = stream;
+
+ stream->list_num = vpdma_hwlist_alloc(dev->shared->vpdma, stream);
+ if (stream->list_num < 0) {
+ v4l2_err(&dev->v4l2_dev, "Could not get VPDMA hwlist");
+ ret = -ENODEV;
+ goto do_free_stream;
+ }
+
+ INIT_LIST_HEAD(&stream->post_bufs);
+
+ /*
+ * Initialize queue
+ */
+ q = &stream->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->buf_struct_size = sizeof(struct vip_buffer);
+ q->ops = &vip_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &dev->mutex;
+ q->min_queued_buffers = 2;
+ q->dev = dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto do_free_hwlist;
+
+ INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work);
+
+ INIT_LIST_HEAD(&stream->vidq);
+
+ /* Allocate/populate Drop queue entries */
+ INIT_LIST_HEAD(&stream->dropq);
+ for (i = 0; i < VIP_DROPQ_SIZE; i++) {
+ buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto do_free_dropq;
+ }
+ buf->drop = true;
+ list_add(&buf->list, &stream->dropq);
+ }
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ ret = -ENOMEM;
+ goto do_free_dropq;
+ }
+ *vfd = vip_videodev;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->queue = q;
+
+ vfd->lock = &dev->mutex;
+ video_set_drvdata(vfd, stream);
+ stream->vfd = vfd;
+
+ ret = video_register_device(vfd, vfl_type, -1);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto do_free_vfd;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "device registered as %s\n",
+ video_device_node_name(vfd));
+ return 0;
+
+do_free_vfd:
+ video_device_release(vfd);
+do_free_dropq:
+ list_for_each_safe(pos, tmp, &stream->dropq) {
+ buf = list_entry(pos,
+ struct vip_buffer, list);
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n");
+ list_del(pos);
+ kfree(buf);
+ }
+do_free_hwlist:
+ vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
+do_free_stream:
+ kfree(stream);
+ return ret;
+}
+
+static void free_stream(struct vip_stream *stream)
+{
+ struct vip_dev *dev;
+ struct vip_buffer *buf;
+ struct list_head *pos, *q;
+
+ if (!stream)
+ return;
+
+ dev = stream->port->dev;
+ /* Free up the Drop queue */
+ list_for_each_safe(pos, q, &stream->dropq) {
+ buf = list_entry(pos,
+ struct vip_buffer, list);
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n");
+ list_del(pos);
+ kfree(buf);
+ }
+
+ video_unregister_device(stream->vfd);
+ vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
+ stream->port->cap_streams[stream->stream_id] = NULL;
+ kfree(stream);
+}
+
+static int get_subdev_active_format(struct vip_port *port,
+ struct v4l2_subdev *subdev)
+{
+ struct vip_fmt *fmt;
+ struct vip_dev *dev = port->dev;
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ int ret = 0;
+ unsigned int k, i, j;
+ enum vip_csc_state csc;
+
+ /* Enumerate sub device formats and enable all matching local formats */
+ port->num_active_fmt = 0;
+ for (k = 0, i = 0; (ret != -EINVAL); k++) {
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = k;
+ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code);
+ if (ret)
+ continue;
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "subdev %s: code: %04x idx: %d\n",
+ subdev->name, mbus_code.code, k);
+
+ for (j = 0; j < ARRAY_SIZE(vip_formats); j++) {
+ fmt = &vip_formats[j];
+ if (mbus_code.code != fmt->code)
+ continue;
+
+ /*
+ * When the port is configured for BT656
+ * then none of the downstream unit can be used.
+ * So here we need to skip all format requiring
+ * either CSC or CHR_DS
+ */
+ csc = vip_csc_direction(fmt->code, fmt->finfo);
+ if (port->endpoint.bus_type == V4L2_MBUS_BT656 &&
+ (csc != VIP_CSC_NA || fmt->coplanar))
+ continue;
+
+ port->active_fmt[i] = fmt;
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "matched fourcc: %s: code: %04x idx: %d\n",
+ fourcc_to_str(fmt->fourcc), fmt->code, i);
+ port->num_active_fmt = ++i;
+ }
+ }
+
+ if (i == 0) {
+ v4l2_err(&dev->v4l2_dev, "No suitable format reported by subdev %s\n",
+ subdev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int alloc_port(struct vip_dev *dev, int id)
+{
+ struct vip_port *port;
+
+ if (dev->ports[id])
+ return -EINVAL;
+
+ port = devm_kzalloc(&dev->pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ dev->ports[id] = port;
+ port->dev = dev;
+ port->port_id = id;
+ port->num_streams = 0;
+ return 0;
+}
+
+static void free_port(struct vip_port *port)
+{
+ if (!port)
+ return;
+
+ v4l2_async_nf_unregister(&port->notifier);
+ v4l2_async_nf_cleanup(&port->notifier);
+ free_stream(port->cap_streams[0]);
+}
+
+static int get_field(u32 value, u32 mask, int shift)
+{
+ return (value & (mask << shift)) >> shift;
+}
+
+static int vip_probe_complete(struct platform_device *pdev);
+static void vip_vpdma_fw_cb(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "VPDMA firmware loaded\n");
+
+ if (pdev->dev.of_node)
+ vip_probe_complete(pdev);
+}
+
+static int vip_create_streams(struct vip_port *port,
+ struct v4l2_subdev *subdev)
+{
+ int i;
+
+ for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++)
+ free_stream(port->cap_streams[i]);
+
+ if (get_subdev_active_format(port, subdev))
+ return -ENODEV;
+
+ port->subdev = subdev;
+
+ if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL) {
+ port->flags |= FLAG_MULT_PORT;
+ port->num_streams_configured = 1;
+ alloc_stream(port, 0, VFL_TYPE_VIDEO);
+ } else if (port->endpoint.bus_type == V4L2_MBUS_BT656) {
+ port->flags |= FLAG_MULT_PORT;
+ port->num_streams_configured = 1;
+ alloc_stream(port, 0, VFL_TYPE_VIDEO);
+ }
+ return 0;
+}
+
+static int vip_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asd)
+{
+ struct vip_port *port = notifier_to_vip_port(notifier);
+ int ret;
+
+ if (port->subdev) {
+ v4l2_info(&port->dev->v4l2_dev, "Rejecting subdev %s (Already set!!)",
+ subdev->name);
+ return 0;
+ }
+
+ v4l2_info(&port->dev->v4l2_dev, "Port %c: Using subdev %s for capture\n",
+ port->port_id == VIP_PORTA ? 'A' : 'B', subdev->name);
+
+ ret = vip_create_streams(port, subdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vip_async_complete(struct v4l2_async_notifier *notifier)
+{
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations vip_async_ops = {
+ .bound = vip_async_bound,
+ .complete = vip_async_complete,
+};
+
+static struct fwnode_handle *
+fwnode_graph_get_next_endpoint_by_regs(const struct fwnode_handle *fwnode,
+ int port_reg, int reg)
+{
+ return of_fwnode_handle(of_graph_get_endpoint_by_regs(to_of_node(fwnode),
+ port_reg, reg));
+}
+
+static int vip_register_subdev_notify(struct vip_port *port,
+ struct fwnode_handle *ep)
+{
+ struct v4l2_async_notifier *notifier = &port->notifier;
+ struct fwnode_handle *subdev;
+ struct v4l2_fwnode_endpoint *vep;
+ struct v4l2_async_connection *asd;
+ int ret;
+ struct vip_dev *dev = port->dev;
+
+ vep = &port->endpoint;
+
+ subdev = fwnode_graph_get_remote_port_parent(ep);
+ if (!subdev) {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "can't get remote parent\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep, vep);
+ if (ret) {
+ v4l2_dbg(3, debug, &dev->v4l2_dev, "Failed to parse endpoint:\n");
+ fwnode_handle_put(subdev);
+ return -EINVAL;
+ }
+
+ v4l2_async_nf_init(notifier, &port->dev->shared->v4l2_dev);
+
+ asd = v4l2_async_nf_add_fwnode(notifier, subdev, struct v4l2_async_connection);
+ if (IS_ERR(asd)) {
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "Error adding asd\n");
+ fwnode_handle_put(subdev);
+ v4l2_async_nf_cleanup(notifier);
+ return -EINVAL;
+ }
+
+ notifier->ops = &vip_async_ops;
+ ret = v4l2_async_nf_register(notifier);
+ if (ret) {
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "Error registering async notifier\n");
+ v4l2_async_nf_cleanup(notifier);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vip_endpoint_scan(struct platform_device *pdev)
+{
+ struct device_node *parent = pdev->dev.of_node;
+ struct device_node *ep = NULL;
+ int count = 0, p;
+
+ for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) {
+ ep = of_graph_get_endpoint_by_regs(parent, p, 0);
+ if (!ep)
+ continue;
+
+ count++;
+ of_node_put(ep);
+ }
+
+ return count;
+}
+
+static int vip_probe_complete(struct platform_device *pdev)
+{
+ struct vip_shared *shared = platform_get_drvdata(pdev);
+ struct vip_ctrl_module *ctrl = NULL;
+ struct vip_port *port;
+ struct vip_dev *dev;
+ struct device_node *parent = pdev->dev.of_node;
+ struct fwnode_handle *ep = NULL;
+ unsigned int syscon_args[5];
+ int ret, i, slice_id, port_id, p;
+
+ /* Allocate ctrl before using it */
+ ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->syscon_pol = syscon_regmap_lookup_by_phandle_args(parent, "ti,ctrl-module",
+ 5, syscon_args);
+
+ if (IS_ERR(ctrl->syscon_pol))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ctrl->syscon_pol),
+ "Failed to get ti,ctrl-module\n");
+
+ ctrl->syscon_offset = syscon_args[0];
+
+ for (i = 0; i < ARRAY_SIZE(ctrl->syscon_bit_field); i++)
+ ctrl->syscon_bit_field[i] = syscon_args[i + 1];
+
+ for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) {
+ ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent),
+ p, 0);
+ if (!ep)
+ continue;
+
+ switch (p) {
+ case 0:
+ slice_id = VIP_SLICE1;
+ port_id = VIP_PORTA;
+ break;
+ case 1:
+ slice_id = VIP_SLICE2;
+ port_id = VIP_PORTA;
+ break;
+ case 2:
+ slice_id = VIP_SLICE1;
+ port_id = VIP_PORTB;
+ break;
+ case 3:
+ slice_id = VIP_SLICE2;
+ port_id = VIP_PORTB;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown port reg=<%d>\n", p);
+ continue;
+ }
+
+ ret = alloc_port(shared->devs[slice_id], port_id);
+ if (ret < 0)
+ continue;
+
+ dev = shared->devs[slice_id];
+ dev->syscon = ctrl;
+ port = dev->ports[port_id];
+
+ vip_register_subdev_notify(port, ep);
+ fwnode_handle_put(ep);
+ }
+ return 0;
+}
+
+static int vip_probe_slice(struct platform_device *pdev, int slice)
+{
+ struct vip_shared *shared = platform_get_drvdata(pdev);
+ struct vip_dev *dev;
+ struct vip_parser_data *parser;
+ struct sc_data *sc;
+ struct csc_data *csc;
+ int ret;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->irq = platform_get_irq(pdev, slice);
+ if (dev->irq < 0)
+ return dev->irq;
+
+ ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq,
+ 0, VIP_MODULE_NAME, dev);
+ if (ret < 0)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->slock);
+ mutex_init(&dev->mutex);
+
+ dev->slice_id = slice;
+ dev->pdev = pdev;
+ dev->base = shared->base;
+ dev->v4l2_dev = shared->v4l2_dev;
+
+ dev->shared = shared;
+ shared->devs[slice] = dev;
+
+ vip_top_reset(dev);
+ vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+
+ parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL);
+ if (!parser)
+ return PTR_ERR_OR_ZERO(parser);
+
+ parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER);
+ if (IS_ERR(parser->base))
+ return PTR_ERR(parser->base);
+
+ parser->pdev = pdev;
+ dev->parser = parser;
+
+ dev->sc_assigned = VIP_NOT_ASSIGNED;
+ sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL);
+ if (!sc)
+ return PTR_ERR_OR_ZERO(sc);
+
+ sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC);
+ if (IS_ERR(sc->base))
+ return PTR_ERR(sc->base);
+
+ sc->pdev = pdev;
+ dev->sc = sc;
+
+ dev->csc_assigned = VIP_NOT_ASSIGNED;
+ csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL);
+ if (!csc)
+ return PTR_ERR_OR_ZERO(csc);
+
+ csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC);
+ if (IS_ERR(csc->base))
+ return PTR_ERR(csc->base);
+
+ csc->pdev = pdev;
+ dev->csc = csc;
+
+ return 0;
+}
+
+static int vip_probe(struct platform_device *pdev)
+{
+ struct vip_shared *shared;
+ int ret, slice = VIP_SLICE1;
+ u32 tmp, pid;
+
+ /* If there are no endpoint defined there is nothing to do */
+ if (!vip_endpoint_scan(pdev)) {
+ dev_err(&pdev->dev, "%s: No sensor", __func__);
+ return -ENODEV;
+ }
+
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return ret;
+ }
+
+ shared = devm_kzalloc(&pdev->dev, sizeof(*shared), GFP_KERNEL);
+ if (!shared)
+ return -ENOMEM;
+
+ shared->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(shared->base))
+ return PTR_ERR(shared->base);
+
+ vip_init_format_info(&pdev->dev);
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_runtime_disable;
+
+ /* Make sure H/W module has the right functionality */
+ pid = reg_read(shared, VIP_PID);
+ tmp = get_field(pid, VIP_PID_FUNC_MASK, VIP_PID_FUNC_SHIFT);
+
+ if (tmp != VIP_PID_FUNC) {
+ dev_info(&pdev->dev, "vip: unexpected PID function: 0x%x\n",
+ tmp);
+ ret = -ENODEV;
+ goto err_runtime_put;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &shared->v4l2_dev);
+ if (ret)
+ goto err_runtime_put;
+
+ /* enable clocks, so the firmware will load properly */
+ vip_shared_set_clock_enable(shared, 1);
+ vip_top_vpdma_reset(shared);
+
+ platform_set_drvdata(pdev, shared);
+
+ v4l2_ctrl_handler_init(&shared->ctrl_handler, 11);
+ shared->v4l2_dev.ctrl_handler = &shared->ctrl_handler;
+
+ for (slice = VIP_SLICE1; slice < VIP_NUM_SLICES; slice++) {
+ ret = vip_probe_slice(pdev, slice);
+ if (ret) {
+ dev_err(&pdev->dev, "Creating slice failed");
+ goto err_dev_unreg;
+ }
+ }
+
+ shared->vpdma = &shared->vpdma_data;
+
+ shared->vpdma->pdev = pdev;
+ shared->vpdma->cb = vip_vpdma_fw_cb;
+ spin_lock_init(&shared->vpdma->lock);
+
+ shared->vpdma->base = shared->base + VIP_VPDMA_BASE;
+ if (!shared->vpdma->base) {
+ dev_err(&pdev->dev, "failed to ioremap\n");
+ ret = -ENOMEM;
+ goto err_dev_unreg;
+ }
+
+ ret = vpdma_load_firmware(shared->vpdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Creating VPDMA failed");
+ goto err_dev_unreg;
+ }
+
+ return 0;
+
+err_dev_unreg:
+ v4l2_ctrl_handler_free(&shared->ctrl_handler);
+ v4l2_device_unregister(&shared->v4l2_dev);
+err_runtime_put:
+ pm_runtime_put_sync(&pdev->dev);
+err_runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void vip_remove(struct platform_device *pdev)
+{
+ struct vip_shared *shared = platform_get_drvdata(pdev);
+ struct vip_dev *dev;
+ int slice;
+
+ for (slice = 0; slice < VIP_NUM_SLICES; slice++) {
+ dev = shared->devs[slice];
+ if (!dev)
+ continue;
+
+ free_port(dev->ports[VIP_PORTA]);
+ free_port(dev->ports[VIP_PORTB]);
+ }
+
+ v4l2_ctrl_handler_free(&shared->ctrl_handler);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id vip_of_match[] = {
+ {
+ .compatible = "ti,dra7-vip",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, vip_of_match);
+#endif
+
+static struct platform_driver vip_pdrv = {
+ .probe = vip_probe,
+ .remove = vip_remove,
+ .driver = {
+ .name = VIP_MODULE_NAME,
+ .of_match_table = of_match_ptr(vip_of_match),
+ },
+};
+
+module_platform_driver(vip_pdrv);
+
+MODULE_DESCRIPTION("TI VIP driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/ti/vpe/vip.h b/drivers/media/platform/ti/vpe/vip.h
new file mode 100644
index 000000000000..20525369955d
--- /dev/null
+++ b/drivers/media/platform/ti/vpe/vip.h
@@ -0,0 +1,717 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TI VIP capture driver
+ *
+ * Copyright (C) 2025 Texas Instruments Incorpated - http://www.ti.com/
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Yemike Abhilash Chandra, <y-abhilashchandra@ti.com>
+ */
+
+#ifndef __TI_VIP_H
+#define __TI_VIP_H
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-memops.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-async.h>
+
+#include "vpdma.h"
+#include "vpdma_priv.h"
+#include "sc.h"
+#include "csc.h"
+
+#define VIP_INSTANCE1 1
+#define VIP_INSTANCE2 2
+#define VIP_INSTANCE3 3
+
+#define VIP_SLICE1 0
+#define VIP_SLICE2 1
+#define VIP_NUM_SLICES 2
+
+/*
+ * Additional client identifiers used for VPDMA configuration descriptors
+ */
+#define VIP_SLICE1_CFD_SC_CLIENT 7
+#define VIP_SLICE2_CFD_SC_CLIENT 8
+
+#define VIP_PORTA 0
+#define VIP_PORTB 1
+#define VIP_NUM_PORTS 2
+
+#define VIP_MAX_PLANES 2
+#define VIP_LUMA 0
+#define VIP_CHROMA 1
+
+#define VIP_CAP_STREAMS_PER_PORT 16
+#define VIP_VBI_STREAMS_PER_PORT 16
+
+#define VIP_MAX_SUBDEV 5
+
+#define VPDMA_FIRMWARE "vpdma-1b8.bin"
+
+/*
+ * This value needs to be at least as large as the number of entry in
+ * vip_formats[].
+ * When vip_formats[] is modified make sure to adjust this value also.
+ */
+#define VIP_MAX_ACTIVE_FMT 16
+/*
+ * Colorspace conversion unit can be in one of 3 modes:
+ * NA - Not Available on this port
+ * Y2R - Needed for YUV to RGB on this port
+ * R2Y - Needed for RGB to YUV on this port
+ */
+enum vip_csc_state {
+ VIP_CSC_NA = 0,
+ VIP_CSC_Y2R,
+ VIP_CSC_R2Y,
+};
+
+/* buffer for one video frame */
+struct vip_buffer {
+ /* common v4l buffer stuff */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ bool drop;
+};
+
+/*
+ * struct vip_fmt - VIP media bus format information
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @code: V4L2 media bus format code
+ * @colorspace: V4L2 colorspace identifier
+ * @coplanar: 1 if unpacked Luma and Chroma, 0 otherwise (packed/interleaved)
+ * @vpdma_fmt: VPDMA data format per plane.
+ * @finfo: Cache v4l2_format_info for associated fourcc
+ */
+struct vip_fmt {
+ u32 fourcc;
+ u32 code;
+ u32 colorspace;
+ u8 coplanar;
+ const struct vpdma_data_format *vpdma_fmt[VIP_MAX_PLANES];
+ const struct v4l2_format_info *finfo;
+};
+
+/*
+ * The vip_parser_data structures contains the memory mapped
+ * info to access the parser registers.
+ */
+struct vip_parser_data {
+ void __iomem *base;
+
+ struct platform_device *pdev;
+};
+
+/*
+ * The vip_shared structure contains data that is shared by both
+ * the VIP1 and VIP2 slices.
+ */
+struct vip_shared {
+ struct list_head list;
+ void __iomem *base;
+ struct vpdma_data vpdma_data;
+ struct vpdma_data *vpdma;
+ struct v4l2_device v4l2_dev;
+ struct vip_dev *devs[VIP_NUM_SLICES];
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+struct vip_ctrl_module {
+ struct regmap *syscon_pol;
+ u32 syscon_offset;
+ u32 syscon_bit_field[4];
+};
+
+/*
+ * There are two vip_dev structure, one for each vip slice: VIP1 & VIP2.
+ */
+struct vip_dev {
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ struct vip_shared *shared;
+ struct vip_ctrl_module *syscon;
+ int instance_id;
+ int slice_id;
+ int num_ports; /* count of open ports */
+ struct mutex mutex;
+ /* protects access to stream buffer queues */
+ spinlock_t slock;
+
+ int irq;
+ void __iomem *base;
+
+ struct vip_port *ports[VIP_NUM_PORTS];
+
+ char name[16];
+ /* parser data handle */
+ struct vip_parser_data *parser;
+ /* scaler data handle */
+ struct sc_data *sc;
+ /* scaler port assignation */
+ int sc_assigned;
+ /* csc data handle */
+ struct csc_data *csc;
+ /* csc port assignation */
+ int csc_assigned;
+};
+
+/*
+ * There are two vip_port structures for each vip_dev, one for port A
+ * and one for port B.
+ */
+struct vip_port {
+ struct vip_dev *dev;
+ int port_id;
+
+ unsigned int flags;
+ struct v4l2_rect c_rect; /* crop rectangle */
+ struct v4l2_mbus_framefmt mbus_framefmt;
+ struct v4l2_mbus_framefmt try_mbus_framefmt;
+
+ char name[16];
+ struct vip_fmt *fmt; /* current format info */
+ /* Number of channels/streams configured */
+ int num_streams_configured;
+ int num_streams; /* count of open streams */
+ struct vip_stream *cap_streams[VIP_CAP_STREAMS_PER_PORT];
+
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *subdev;
+ struct v4l2_fwnode_endpoint endpoint;
+ struct vip_fmt *active_fmt[VIP_MAX_ACTIVE_FMT];
+ int num_active_fmt;
+ /* have new shadow reg values */
+ bool load_mmrs;
+ /* shadow reg addr/data block */
+ struct vpdma_buf mmr_adb;
+ /* h coeff buffer */
+ struct vpdma_buf sc_coeff_h;
+ /* v coeff buffer */
+ struct vpdma_buf sc_coeff_v;
+ /* Show if scaler resource is available on this port */
+ bool scaler;
+ /* Show the csc resource state on this port */
+ enum vip_csc_state csc;
+};
+
+/*
+ * When handling multiplexed video, there can be multiple streams for each
+ * port. The vip_stream structure holds per-stream data.
+ */
+struct vip_stream {
+ struct video_device *vfd;
+ struct vip_port *port;
+ int stream_id;
+ int list_num;
+ int vfl_type;
+ char name[16];
+ struct work_struct recovery_work;
+ int num_recovery;
+ enum v4l2_field field; /* current field */
+ unsigned int sequence; /* current frame/field seq */
+ enum v4l2_field sup_field; /* supported field value */
+ unsigned int width; /* frame width */
+ unsigned int height; /* frame height */
+ unsigned int bytesperline; /* bytes per line in memory */
+ unsigned int sizeimage; /* image size in memory */
+ struct list_head vidq; /* incoming vip_bufs queue */
+ struct list_head dropq; /* drop vip_bufs queue */
+ struct list_head post_bufs; /* vip_bufs to be DMAed */
+ /* Maintain a list of used channels - Needed for VPDMA cleanup */
+ int vpdma_channels[VPDMA_MAX_CHANNELS];
+ int vpdma_channels_to_abort[VPDMA_MAX_CHANNELS];
+ struct vpdma_desc_list desc_list; /* DMA descriptor list */
+ struct vpdma_dtd *write_desc;
+ /* next unused desc_list addr */
+ void *desc_next;
+ struct vb2_queue vb_vidq;
+};
+
+/*
+ * VIP Enumerations
+ */
+enum data_path_select {
+ ALL_FIELDS_DATA_SELECT = 0,
+ VIP_CSC_SRC_DATA_SELECT,
+ VIP_SC_SRC_DATA_SELECT,
+ VIP_RGB_SRC_DATA_SELECT,
+ VIP_RGB_OUT_LO_DATA_SELECT,
+ VIP_RGB_OUT_HI_DATA_SELECT,
+ VIP_CHR_DS_1_SRC_DATA_SELECT,
+ VIP_CHR_DS_2_SRC_DATA_SELECT,
+ VIP_MULTI_CHANNEL_DATA_SELECT,
+ VIP_CHR_DS_1_DATA_BYPASS,
+ VIP_CHR_DS_2_DATA_BYPASS,
+};
+
+enum data_interface_modes {
+ SINGLE_24B_INTERFACE = 0,
+ SINGLE_16B_INTERFACE = 1,
+ DUAL_8B_INTERFACE = 2,
+};
+
+enum sync_types {
+ EMBEDDED_SYNC_SINGLE_YUV422 = 0,
+ EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422 = 1,
+ EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422 = 2,
+ EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422 = 3,
+ DISCRETE_SYNC_SINGLE_YUV422 = 4,
+ EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444 = 5,
+ DISCRETE_SYNC_SINGLE_RGB_24B = 10,
+};
+
+#define VIP_NOT_ASSIGNED -1
+
+/*
+ * Register offsets and field selectors
+ */
+#define VIP_PID_FUNC 0xf02
+
+#define VIP_PID 0x0000
+#define VIP_PID_MINOR_MASK 0x3f
+#define VIP_PID_MINOR_SHIFT 0
+#define VIP_PID_CUSTOM_MASK 0x03
+#define VIP_PID_CUSTOM_SHIFT 6
+#define VIP_PID_MAJOR_MASK 0x07
+#define VIP_PID_MAJOR_SHIFT 8
+#define VIP_PID_RTL_MASK 0x1f
+#define VIP_PID_RTL_SHIFT 11
+#define VIP_PID_FUNC_MASK 0xfff
+#define VIP_PID_FUNC_SHIFT 16
+#define VIP_PID_SCHEME_MASK 0x03
+#define VIP_PID_SCHEME_SHIFT 30
+
+#define VIP_SYSCONFIG 0x0010
+#define VIP_SYSCONFIG_IDLE_MASK 0x03
+#define VIP_SYSCONFIG_IDLE_SHIFT 2
+#define VIP_SYSCONFIG_STANDBY_MASK 0x03
+#define VIP_SYSCONFIG_STANDBY_SHIFT 4
+#define VIP_FORCE_IDLE_MODE 0
+#define VIP_NO_IDLE_MODE 1
+#define VIP_SMART_IDLE_MODE 2
+#define VIP_SMART_IDLE_WAKEUP_MODE 3
+#define VIP_FORCE_STANDBY_MODE 0
+#define VIP_NO_STANDBY_MODE 1
+#define VIP_SMART_STANDBY_MODE 2
+#define VIP_SMART_STANDBY_WAKEUP_MODE 3
+
+#define VIP_INTC_INTX_OFFSET 0x0020
+
+#define VIP_INT0_STATUS0_RAW_SET 0x0020
+#define VIP_INT0_STATUS0_RAW VIP_INT0_STATUS0_RAW_SET
+#define VIP_INT0_STATUS0_CLR 0x0028
+#define VIP_INT0_STATUS0 VIP_INT0_STATUS0_CLR
+#define VIP_INT0_ENABLE0_SET 0x0030
+#define VIP_INT0_ENABLE0 VIP_INT0_ENABLE0_SET
+#define VIP_INT0_ENABLE0_CLR 0x0038
+#define VIP_INT0_LIST0_COMPLETE BIT(0)
+#define VIP_INT0_LIST0_NOTIFY BIT(1)
+#define VIP_INT0_LIST1_COMPLETE BIT(2)
+#define VIP_INT0_LIST1_NOTIFY BIT(3)
+#define VIP_INT0_LIST2_COMPLETE BIT(4)
+#define VIP_INT0_LIST2_NOTIFY BIT(5)
+#define VIP_INT0_LIST3_COMPLETE BIT(6)
+#define VIP_INT0_LIST3_NOTIFY BIT(7)
+#define VIP_INT0_LIST4_COMPLETE BIT(8)
+#define VIP_INT0_LIST4_NOTIFY BIT(9)
+#define VIP_INT0_LIST5_COMPLETE BIT(10)
+#define VIP_INT0_LIST5_NOTIFY BIT(11)
+#define VIP_INT0_LIST6_COMPLETE BIT(12)
+#define VIP_INT0_LIST6_NOTIFY BIT(13)
+#define VIP_INT0_LIST7_COMPLETE BIT(14)
+#define VIP_INT0_LIST7_NOTIFY BIT(15)
+#define VIP_INT0_DESCRIPTOR BIT(16)
+#define VIP_VIP1_PARSER_INT BIT(20)
+#define VIP_VIP2_PARSER_INT BIT(21)
+
+#define VIP_INT0_STATUS1_RAW_SET 0x0024
+#define VIP_INT0_STATUS1_RAW VIP_INT0_STATUS0_RAW_SET
+#define VIP_INT0_STATUS1_CLR 0x002c
+#define VIP_INT0_STATUS1 VIP_INT0_STATUS0_CLR
+#define VIP_INT0_ENABLE1_SET 0x0034
+#define VIP_INT0_ENABLE1 VIP_INT0_ENABLE0_SET
+#define VIP_INT0_ENABLE1_CLR 0x003c
+#define VIP_INT0_ENABLE1_STAT 0x004c
+#define VIP_INT0_CHANNEL_GROUP0 BIT(0)
+#define VIP_INT0_CHANNEL_GROUP1 BIT(1)
+#define VIP_INT0_CHANNEL_GROUP2 BIT(2)
+#define VIP_INT0_CHANNEL_GROUP3 BIT(3)
+#define VIP_INT0_CHANNEL_GROUP4 BIT(4)
+#define VIP_INT0_CHANNEL_GROUP5 BIT(5)
+#define VIP_INT0_CLIENT BIT(7)
+#define VIP_VIP1_DS1_UV_ERROR_INT BIT(22)
+#define VIP_VIP1_DS2_UV_ERROR_INT BIT(23)
+#define VIP_VIP2_DS1_UV_ERROR_INT BIT(24)
+#define VIP_VIP2_DS2_UV_ERROR_INT BIT(25)
+
+#define VIP_INTC_E0I 0x00a0
+
+#define VIP_CLK_ENABLE 0x0100
+#define VIP_VPDMA_CLK_ENABLE BIT(0)
+#define VIP_VIP1_DATA_PATH_CLK_ENABLE BIT(16)
+#define VIP_VIP2_DATA_PATH_CLK_ENABLE BIT(17)
+
+#define VIP_CLK_RESET 0x0104
+#define VIP_VPDMA_RESET BIT(0)
+#define VIP_VPDMA_CLK_RESET_MASK 0x1
+#define VIP_VPDMA_CLK_RESET_SHIFT 0
+#define VIP_DATA_PATH_CLK_RESET_MASK 0x1
+#define VIP_VIP1_DATA_PATH_RESET_SHIFT 16
+#define VIP_VIP2_DATA_PATH_RESET_SHIFT 17
+#define VIP_VIP1_DATA_PATH_RESET BIT(16)
+#define VIP_VIP2_DATA_PATH_RESET BIT(17)
+#define VIP_VIP1_PARSER_RESET BIT(18)
+#define VIP_VIP2_PARSER_RESET BIT(19)
+#define VIP_VIP1_CSC_RESET BIT(20)
+#define VIP_VIP2_CSC_RESET BIT(21)
+#define VIP_VIP1_SC_RESET BIT(22)
+#define VIP_VIP2_SC_RESET BIT(23)
+#define VIP_VIP1_DS1_RESET BIT(25)
+#define VIP_VIP2_DS1_RESET BIT(26)
+#define VIP_VIP1_DS2_RESET BIT(27)
+#define VIP_VIP2_DS2_RESET BIT(28)
+#define VIP_MAIN_RESET BIT(31)
+
+#define VIP_VIP1_DATA_PATH_SELECT 0x010c
+#define VIP_VIP2_DATA_PATH_SELECT 0x0110
+#define VIP_CSC_SRC_SELECT_MASK 0x07
+#define VIP_CSC_SRC_SELECT_SHFT 0
+#define VIP_SC_SRC_SELECT_MASK 0x07
+#define VIP_SC_SRC_SELECT_SHFT 3
+#define VIP_RGB_SRC_SELECT BIT(6)
+#define VIP_RGB_OUT_LO_SRC_SELECT BIT(7)
+#define VIP_RGB_OUT_HI_SRC_SELECT BIT(8)
+#define VIP_DS1_SRC_SELECT_MASK 0x07
+#define VIP_DS1_SRC_SELECT_SHFT 9
+#define VIP_DS2_SRC_SELECT_MASK 0x07
+#define VIP_DS2_SRC_SELECT_SHFT 12
+#define VIP_MULTI_CHANNEL_SELECT BIT(15)
+#define VIP_DS1_BYPASS BIT(16)
+#define VIP_DS2_BYPASS BIT(17)
+#define VIP_TESTPORT_B_SELECT BIT(26)
+#define VIP_TESTPORT_A_SELECT BIT(27)
+#define VIP_DATAPATH_SELECT_MASK 0x0f
+#define VIP_DATAPATH_SELECT_SHFT 28
+
+#define VIP_PARSER_MAIN_CFG 0x0000
+#define VIP_DATA_INTERFACE_MODE_MASK 0x03
+#define VIP_DATA_INTERFACE_MODE_SHFT 0
+#define VIP_CLIP_BLANK BIT(4)
+#define VIP_CLIP_ACTIVE BIT(5)
+
+#define VIP_SLICE0_PARSER 0x5500
+#define VIP_SLICE1_PARSER 0x5a00
+#define VIP_PARSER_PORTA_0 0x0004
+#define VIP_PARSER_PORTB_0 0x000c
+#define VIP_SYNC_TYPE_MASK 0x0f
+#define VIP_SYNC_TYPE_SHFT 0
+#define VIP_CTRL_CHANNEL_SEL_MASK 0x03
+#define VIP_CTRL_CHANNEL_SEL_SHFT 4
+#define VIP_ASYNC_FIFO_WR BIT(6)
+#define VIP_ASYNC_FIFO_RD BIT(7)
+#define VIP_PORT_ENABLE BIT(8)
+#define VIP_FID_POLARITY BIT(9)
+#define VIP_PIXCLK_EDGE_POLARITY BIT(10)
+#define VIP_HSYNC_POLARITY BIT(11)
+#define VIP_VSYNC_POLARITY BIT(12)
+#define VIP_ACTVID_POLARITY BIT(13)
+#define VIP_FID_DETECT_MODE BIT(14)
+#define VIP_USE_ACTVID_HSYNC_ONLY BIT(15)
+#define VIP_FID_SKEW_PRECOUNT_MASK 0x3f
+#define VIP_FID_SKEW_PRECOUNT_SHFT 16
+#define VIP_DISCRETE_BASIC_MODE BIT(22)
+#define VIP_SW_RESET BIT(23)
+#define VIP_FID_SKEW_POSTCOUNT_MASK 0x3f
+#define VIP_FID_SKEW_POSTCOUNT_SHFT 24
+#define VIP_ANALYZER_2X4X_SRCNUM_POS BIT(30)
+#define VIP_ANALYZER_FVH_ERR_COR_EN BIT(31)
+
+#define VIP_PARSER_PORTA_1 0x0008
+#define VIP_PARSER_PORTB_1 0x0010
+#define VIP_SRC0_NUMLINES_MASK 0x0fff
+#define VIP_SRC0_NUMLINES_SHFT 0
+#define VIP_ANC_CHAN_SEL_8B_MASK 0x03
+#define VIP_ANC_CHAN_SEL_8B_SHFT 13
+#define VIP_SRC0_NUMPIX_MASK 0x0fff
+#define VIP_SRC0_NUMPIX_SHFT 16
+#define VIP_REPACK_SEL_MASK 0x07
+#define VIP_REPACK_SEL_SHFT 28
+
+#define VIP_PARSER_FIQ_MASK 0x0014
+#define VIP_PARSER_FIQ_CLR 0x0018
+#define VIP_PARSER_FIQ_STATUS 0x001c
+#define VIP_PORTA_VDET BIT(0)
+#define VIP_PORTB_VDET BIT(1)
+#define VIP_PORTA_ASYNC_FIFO_OF BIT(2)
+#define VIP_PORTB_ASYNC_FIFO_OF BIT(3)
+#define VIP_PORTA_OUTPUT_FIFO_YUV BIT(4)
+#define VIP_PORTA_OUTPUT_FIFO_ANC BIT(6)
+#define VIP_PORTB_OUTPUT_FIFO_YUV BIT(7)
+#define VIP_PORTB_OUTPUT_FIFO_ANC BIT(9)
+#define VIP_PORTA_CONN BIT(10)
+#define VIP_PORTA_DISCONN BIT(11)
+#define VIP_PORTB_CONN BIT(12)
+#define VIP_PORTB_DISCONN BIT(13)
+#define VIP_PORTA_SRC0_SIZE BIT(14)
+#define VIP_PORTB_SRC0_SIZE BIT(15)
+#define VIP_PORTA_YUV_PROTO_VIOLATION BIT(16)
+#define VIP_PORTA_ANC_PROTO_VIOLATION BIT(17)
+#define VIP_PORTB_YUV_PROTO_VIOLATION BIT(18)
+#define VIP_PORTB_ANC_PROTO_VIOLATION BIT(19)
+#define VIP_PORTA_CFG_DISABLE_COMPLETE BIT(20)
+#define VIP_PORTB_CFG_DISABLE_COMPLETE BIT(21)
+
+#define VIP_PARSER_PORTA_SOURCE_FID 0x0020
+#define VIP_PARSER_PORTA_ENCODER_FID 0x0024
+#define VIP_PARSER_PORTB_SOURCE_FID 0x0028
+#define VIP_PARSER_PORTB_ENCODER_FID 0x002c
+
+#define VIP_PARSER_PORTA_SRC0_SIZE 0x0030
+#define VIP_PARSER_PORTB_SRC0_SIZE 0x0070
+#define VIP_SOURCE_HEIGHT_MASK 0x0fff
+#define VIP_SOURCE_HEIGHT_SHFT 0
+#define VIP_SOURCE_WIDTH_MASK 0x0fff
+#define VIP_SOURCE_WIDTH_SHFT 16
+
+#define VIP_PARSER_PORTA_VDET_VEC 0x00b0
+#define VIP_PARSER_PORTB_VDET_VEC 0x00b4
+
+#define VIP_PARSER_PORTA_EXTRA2 0x00b8
+#define VIP_PARSER_PORTB_EXTRA2 0x00c8
+#define VIP_ANC_SKIP_NUMPIX_MASK 0x0fff
+#define VIP_ANC_SKIP_NUMPIX_SHFT 0
+#define VIP_ANC_BYPASS BIT(15)
+#define VIP_ANC_USE_NUMPIX_MASK 0x0fff
+#define VIP_ANC_USE_NUMPIX_SHFT 16
+#define VIP_ANC_TARGET_SRCNUM_MASK 0x0f
+#define VIP_ANC_TARGET_SRCNUM_SHFT 28
+
+#define VIP_PARSER_PORTA_EXTRA3 0x00bc
+#define VIP_PARSER_PORTB_EXTRA3 0x00cc
+#define VIP_ANC_SKIP_NUMLINES_MASK 0x0fff
+#define VIP_ANC_SKIP_NUMLINES_SHFT 0
+#define VIP_ANC_USE_NUMLINES_MASK 0x0fff
+#define VIP_ANC_USE_NUMLINES_SHFT 16
+
+#define VIP_PARSER_PORTA_EXTRA4 0x00c0
+#define VIP_PARSER_PORTB_EXTRA4 0x00d0
+#define VIP_ACT_SKIP_NUMPIX_MASK 0x0fff
+#define VIP_ACT_SKIP_NUMPIX_SHFT 0
+#define VIP_ACT_BYPASS BIT(15)
+#define VIP_ACT_USE_NUMPIX_MASK 0x0fff
+#define VIP_ACT_USE_NUMPIX_SHFT 16
+#define VIP_ACT_TARGET_SRCNUM_MASK 0x0f
+#define VIP_ACT_TARGET_SRCNUM_SHFT 28
+
+#define VIP_PARSER_PORTA_EXTRA5 0x00c4
+#define VIP_PARSER_PORTB_EXTRA5 0x00d4
+#define VIP_ACT_SKIP_NUMLINES_MASK 0x0fff
+#define VIP_ACT_SKIP_NUMLINES_SHFT 0
+#define VIP_ACT_USE_NUMLINES_MASK 0x0fff
+#define VIP_ACT_USE_NUMLINES_SHFT 16
+
+#define VIP_PARSER_PORTA_EXTRA6 0x00d8
+#define VIP_PARSER_PORTB_EXTRA6 0x00dc
+#define VIP_ANC_SRCNUM_STOP_IMM_SHFT 0
+#define VIP_YUV_SRCNUM_STOP_IMM_SHFT 16
+
+#define VIP_SLICE0_CSC 0x5700
+#define VIP_SLICE1_CSC 0x5c00
+#define VIP_CSC_CSC00 0x0200
+#define VIP_CSC_A0_MASK 0x1fff
+#define VIP_CSC_A0_SHFT 0
+#define VIP_CSC_B0_MASK 0x1fff
+#define VIP_CSC_B0_SHFT 16
+
+#define VIP_CSC_CSC01 0x0204
+#define VIP_CSC_C0_MASK 0x1fff
+#define VIP_CSC_C0_SHFT 0
+#define VIP_CSC_A1_MASK 0x1fff
+#define VIP_CSC_A1_SHFT 16
+
+#define VIP_CSC_CSC02 0x0208
+#define VIP_CSC_B1_MASK 0x1fff
+#define VIP_CSC_B1_SHFT 0
+#define VIP_CSC_C1_MASK 0x1fff
+#define VIP_CSC_C1_SHFT 16
+
+#define VIP_CSC_CSC03 0x020c
+#define VIP_CSC_A2_MASK 0x1fff
+#define VIP_CSC_A2_SHFT 0
+#define VIP_CSC_B2_MASK 0x1fff
+#define VIP_CSC_B2_SHFT 16
+
+#define VIP_CSC_CSC04 0x0210
+#define VIP_CSC_C2_MASK 0x1fff
+#define VIP_CSC_C2_SHFT 0
+#define VIP_CSC_D0_MASK 0x0fff
+#define VIP_CSC_D0_SHFT 16
+
+#define VIP_CSC_CSC05 0x0214
+#define VIP_CSC_D1_MASK 0x0fff
+#define VIP_CSC_D1_SHFT 0
+#define VIP_CSC_D2_MASK 0x0fff
+#define VIP_CSC_D2_SHFT 16
+#define VIP_CSC_BYPASS BIT(28)
+
+#define VIP_SLICE0_SC 0x5800
+#define VIP_SLICE1_SC 0x5d00
+#define VIP_SC_MP_SC0 0x0300
+#define VIP_INTERLACE_O BIT(0)
+#define VIP_LINEAR BIT(1)
+#define VIP_SC_BYPASS BIT(2)
+#define VIP_INVT_FID BIT(3)
+#define VIP_USE_RAV BIT(4)
+#define VIP_ENABLE_EV BIT(5)
+#define VIP_AUTH_HS BIT(6)
+#define VIP_DCM_2X BIT(7)
+#define VIP_DCM_4X BIT(8)
+#define VIP_HP_BYPASS BIT(9)
+#define VIP_INTERLACE_I BIT(10)
+#define VIP_ENABLE_SIN2_VER_INTP BIT(11)
+#define VIP_Y_PK_EN BIT(14)
+#define VIP_TRIM BIT(15)
+#define VIP_SELFGEN_FID BIT(16)
+
+#define VIP_SC_MP_SC1 0x0304
+#define VIP_ROW_ACC_INC_MASK 0x07ffffff
+#define VIP_ROW_ACC_INC_SHFT 0
+
+#define VIP_SC_MP_SC2 0x0308
+#define VIP_ROW_ACC_OFFSET_MASK 0x0fffffff
+#define VIP_ROW_ACC_OFFSET_SHFT 0
+
+#define VIP_SC_MP_SC3 0x030c
+#define VIP_ROW_ACC_OFFSET_B_MASK 0x0fffffff
+#define VIP_ROW_ACC_OFFSET_B_SHFT 0
+
+#define VIP_SC_MP_SC4 0x0310
+#define VIP_TAR_H_MASK 0x07ff
+#define VIP_TAR_H_SHFT 0
+#define VIP_TAR_W_MASK 0x07ff
+#define VIP_TAR_W_SHFT 12
+#define VIP_LIN_ACC_INC_U_MASK 0x07
+#define VIP_LIN_ACC_INC_U_SHFT 24
+#define VIP_NLIN_ACC_INIT_U_MASK 0x07
+#define VIP_NLIN_ACC_INIT_U_SHFT 28
+
+#define VIP_SC_MP_SC5 0x0314
+#define VIP_SRC_H_MASK 0x03ff
+#define VIP_SRC_H_SHFT 0
+#define VIP_SRC_W_MASK 0x07ff
+#define VIP_SRC_W_SHFT 12
+#define VIP_NLIN_ACC_INC_U_MASK 0x07
+#define VIP_NLIN_ACC_INC_U_SHFT 24
+
+#define VIP_SC_MP_SC6 0x0318
+#define VIP_ROW_ACC_INIT_RAV_MASK 0x03ff
+#define VIP_ROW_ACC_INIT_RAV_SHFT 0
+#define VIP_ROW_ACC_INIT_RAV_B_MASK 0x03ff
+#define VIP_ROW_ACC_INIT_RAV_B_SHFT 10
+
+#define VIP_SC_MP_SC8 0x0320
+#define VIP_NLIN_LEFT_MASK 0x07ff
+#define VIP_NLIN_LEFT_SHFT 0
+#define VIP_NLIN_RIGHT_MASK 0x07ff
+#define VIP_NLIN_RIGHT_SHFT 12
+
+#define VIP_SC_MP_SC9 0x0324
+#define VIP_LIN_ACC_INC VIP_SC_MP_SC9
+
+#define VIP_SC_MP_SC10 0x0328
+#define VIP_NLIN_ACC_INIT VIP_SC_MP_SC10
+
+#define VIP_SC_MP_SC11 0x032c
+#define VIP_NLIN_ACC_INC VIP_SC_MP_SC11
+
+#define VIP_SC_MP_SC12 0x0330
+#define VIP_COL_ACC_OFFSET_MASK 0x01ffffff
+#define VIP_COL_ACC_OFFSET_SHFT 0
+
+#define VIP_SC_MP_SC13 0x0334
+#define VIP_SC_FACTOR_RAV_MASK 0x03ff
+#define VIP_SC_FACTOR_RAV_SHFT 0
+#define VIP_CHROMA_INTP_THR_MASK 0x03ff
+#define VIP_CHROMA_INTP_THR_SHFT 12
+#define VIP_DELTA_CHROMA_THR_MASK 0x0f
+#define VIP_DELTA_CHROMA_THR_SHFT 24
+
+#define VIP_SC_MP_SC17 0x0344
+#define VIP_EV_THR_MASK 0x03ff
+#define VIP_EV_THR_SHFT 12
+#define VIP_DELTA_LUMA_THR_MASK 0x0f
+#define VIP_DELTA_LUMA_THR_SHFT 24
+#define VIP_DELTA_EV_THR_MASK 0x0f
+#define VIP_DELTA_EV_THR_SHFT 28
+
+#define VIP_SC_MP_SC18 0x0348
+#define VIP_HS_FACTOR_MASK 0x03ff
+#define VIP_HS_FACTOR_SHFT 0
+#define VIP_CONF_DEFAULT_MASK 0x01ff
+#define VIP_CONF_DEFAULT_SHFT 16
+
+#define VIP_SC_MP_SC19 0x034c
+#define VIP_HPF_COEFF0_MASK 0xff
+#define VIP_HPF_COEFF0_SHFT 0
+#define VIP_HPF_COEFF1_MASK 0xff
+#define VIP_HPF_COEFF1_SHFT 8
+#define VIP_HPF_COEFF2_MASK 0xff
+#define VIP_HPF_COEFF2_SHFT 16
+#define VIP_HPF_COEFF3_MASK 0xff
+#define VIP_HPF_COEFF3_SHFT 23
+
+#define VIP_SC_MP_SC20 0x0350
+#define VIP_HPF_COEFF4_MASK 0xff
+#define VIP_HPF_COEFF4_SHFT 0
+#define VIP_HPF_COEFF5_MASK 0xff
+#define VIP_HPF_COEFF5_SHFT 8
+#define VIP_HPF_NORM_SHFT_MASK 0x07
+#define VIP_HPF_NORM_SHFT_SHFT 16
+#define VIP_NL_LIMIT_MASK 0x1ff
+#define VIP_NL_LIMIT_SHFT 20
+
+#define VIP_SC_MP_SC21 0x0354
+#define VIP_NL_LO_THR_MASK 0x01ff
+#define VIP_NL_LO_THR_SHFT 0
+#define VIP_NL_LO_SLOPE_MASK 0xff
+#define VIP_NL_LO_SLOPE_SHFT 16
+
+#define VIP_SC_MP_SC22 0x0358
+#define VIP_NL_HI_THR_MASK 0x01ff
+#define VIP_NL_HI_THR_SHFT 0
+#define VIP_NL_HI_SLOPE_SH_MASK 0x07
+#define VIP_NL_HI_SLOPE_SH_SHFT 16
+
+#define VIP_SC_MP_SC23 0x035c
+#define VIP_GRADIENT_THR_MASK 0x07ff
+#define VIP_GRADIENT_THR_SHFT 0
+#define VIP_GRADIENT_THR_RANGE_MASK 0x0f
+#define VIP_GRADIENT_THR_RANGE_SHFT 12
+#define VIP_MIN_GY_THR_MASK 0xff
+#define VIP_MIN_GY_THR_SHFT 16
+#define VIP_MIN_GY_THR_RANGE_MASK 0x0f
+#define VIP_MIN_GY_THR_RANGE_SHFT 28
+
+#define VIP_SC_MP_SC24 0x0360
+#define VIP_ORG_H_MASK 0x07ff
+#define VIP_ORG_H_SHFT 0
+#define VIP_ORG_W_MASK 0x07ff
+#define VIP_ORG_W_SHFT 16
+
+#define VIP_SC_MP_SC25 0x0364
+#define VIP_OFF_H_MASK 0x07ff
+#define VIP_OFF_H_SHFT 0
+#define VIP_OFF_W_MASK 0x07ff
+#define VIP_OFF_W_SHFT 16
+
+#define VIP_VPDMA_BASE 0xd000
+
+#endif
diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index bb8a8bd7980c..573aa83f62eb 100644
--- a/drivers/media/platform/ti/vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
@@ -552,6 +552,54 @@ EXPORT_SYMBOL(vpdma_submit_descs);
static void dump_dtd(struct vpdma_dtd *dtd);
+/**
+ * vpdma_update_dma_addr() - update DMA address in a descriptor
+ * @vpdma: VPDMA device context
+ * @list: vpdma desc list to which we add this descriptor
+ * @dma_addr: new DMA address to program into the descriptor
+ * @write_dtd: descriptor pointer used to compute write-back address
+ * @drop: if true, set the drop bit in the write descriptor
+ * @idx: index of the descriptor in the list to update
+ *
+ * Updates dma addresses of the descriptor at @idx in @list.
+ * This allows reusing an existing descriptor list with a new buffer
+ * address, instead of rebuilding the list, which is needed when
+ * multiple clients share the same VPDMA engine. The list buffer is
+ * unmapped before the update and remapped after.
+ */
+void vpdma_update_dma_addr(struct vpdma_data *vpdma,
+ struct vpdma_desc_list *list,
+ dma_addr_t dma_addr,
+ void *write_dtd, int drop, int idx)
+{
+ struct vpdma_dtd *dtd = list->buf.addr;
+ dma_addr_t write_desc_addr;
+ int offset;
+
+ dtd += idx;
+ vpdma_unmap_desc_buf(vpdma, &list->buf);
+
+ dtd->start_addr = dma_addr;
+
+ /* Calculate write address from the offset of write_dtd from start
+ * of the list->buf
+ */
+ offset = (void *)write_dtd - list->buf.addr;
+ write_desc_addr = list->buf.dma_addr + offset;
+
+ if (drop)
+ dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
+ 1, 1, 0);
+ else
+ dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
+ 1, 0, 0);
+
+ vpdma_map_desc_buf(vpdma, &list->buf);
+
+ dump_dtd(dtd);
+}
+EXPORT_SYMBOL_GPL(vpdma_update_dma_addr);
+
void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
u32 width, u32 height)
{
@@ -1087,7 +1135,7 @@ rel_fw:
release_firmware(f);
}
-static int vpdma_load_firmware(struct vpdma_data *vpdma)
+int vpdma_load_firmware(struct vpdma_data *vpdma)
{
int r;
struct device *dev = &vpdma->pdev->dev;
@@ -1104,6 +1152,7 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma)
return 0;
}
+EXPORT_SYMBOL_GPL(vpdma_load_firmware);
int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma,
void (*cb)(struct platform_device *pdev))
diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h
index e4d7941c6207..1fc53fb33497 100644
--- a/drivers/media/platform/ti/vpe/vpdma.h
+++ b/drivers/media/platform/ti/vpe/vpdma.h
@@ -222,6 +222,9 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list);
int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list,
int list_num);
bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num);
+void vpdma_update_dma_addr(struct vpdma_data *vpdma,
+ struct vpdma_desc_list *list, dma_addr_t dma_addr,
+ void *write_dtd, int drop, int idx);
/* VPDMA hardware list funcs */
int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv);
@@ -278,4 +281,7 @@ void vpdma_dump_regs(struct vpdma_data *vpdma);
int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma,
void (*cb)(struct platform_device *pdev));
+/* load vpdma firmware*/
+int vpdma_load_firmware(struct vpdma_data *vpdma);
+
#endif
diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
index e0fdc4535b2d..0353de154a1e 100644
--- a/drivers/media/platform/verisilicon/hantro.h
+++ b/drivers/media/platform/verisilicon/hantro.h
@@ -77,6 +77,7 @@ struct hantro_irq {
* @double_buffer: core needs double buffering
* @legacy_regs: core uses legacy register set
* @late_postproc: postproc must be set up at the end of the job
+ * @shared_devices: an array of device ids that cannot run concurrently
*/
struct hantro_variant {
unsigned int enc_offset;
@@ -101,6 +102,7 @@ struct hantro_variant {
unsigned int double_buffer : 1;
unsigned int legacy_regs : 1;
unsigned int late_postproc : 1;
+ const struct of_device_id *shared_devices;
};
/**
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 60b95b5d8565..94f58f4e4a4e 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -1035,6 +1036,41 @@ static int hantro_disable_multicore(struct hantro_dev *vpu)
return 0;
}
+static struct v4l2_m2m_dev *hantro_get_v4l2_m2m_dev(struct hantro_dev *vpu)
+{
+ struct device_node *node;
+ struct hantro_dev *shared_vpu;
+
+ if (!vpu->variant || !vpu->variant->shared_devices)
+ goto init_new_m2m_dev;
+
+ for_each_matching_node(node, vpu->variant->shared_devices) {
+ struct platform_device *pdev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev)
+ continue;
+
+ shared_vpu = platform_get_drvdata(pdev);
+ if (IS_ERR_OR_NULL(shared_vpu) || shared_vpu == vpu) {
+ platform_device_put(pdev);
+ continue;
+ }
+
+ v4l2_m2m_get(shared_vpu->m2m_dev);
+ m2m_dev = shared_vpu->m2m_dev;
+ platform_device_put(pdev);
+
+ of_node_put(node);
+
+ return m2m_dev;
+ }
+
+init_new_m2m_dev:
+ return v4l2_m2m_init(&vpu_m2m_ops);
+}
+
static int hantro_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -1186,7 +1222,7 @@ static int hantro_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, vpu);
- vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops);
+ vpu->m2m_dev = hantro_get_v4l2_m2m_dev(vpu);
if (IS_ERR(vpu->m2m_dev)) {
v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(vpu->m2m_dev);
@@ -1225,7 +1261,7 @@ err_rm_enc_func:
hantro_remove_enc_func(vpu);
err_m2m_rel:
media_device_cleanup(&vpu->mdev);
- v4l2_m2m_release(vpu->m2m_dev);
+ v4l2_m2m_put(vpu->m2m_dev);
err_v4l2_unreg:
v4l2_device_unregister(&vpu->v4l2_dev);
err_clk_unprepare:
@@ -1248,7 +1284,7 @@ static void hantro_remove(struct platform_device *pdev)
hantro_remove_dec_func(vpu);
hantro_remove_enc_func(vpu);
media_device_cleanup(&vpu->mdev);
- v4l2_m2m_release(vpu->m2m_dev);
+ v4l2_m2m_put(vpu->m2m_dev);
v4l2_device_unregister(&vpu->v4l2_dev);
clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
reset_control_assert(vpu->resets);
diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
index 5be0e2e76882..6f8e43b7f157 100644
--- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
@@ -343,6 +343,12 @@ const struct hantro_variant imx8mq_vpu_variant = {
.num_regs = ARRAY_SIZE(imx8mq_reg_names)
};
+static const struct of_device_id imx8mq_vpu_shared_resources[] __initconst = {
+ { .compatible = "nxp,imx8mq-vpu-g1", },
+ { .compatible = "nxp,imx8mq-vpu-g2", },
+ { /* sentinel */ }
+};
+
const struct hantro_variant imx8mq_vpu_g1_variant = {
.dec_fmts = imx8m_vpu_dec_fmts,
.num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts),
@@ -356,6 +362,7 @@ const struct hantro_variant imx8mq_vpu_g1_variant = {
.num_irqs = ARRAY_SIZE(imx8mq_irqs),
.clk_names = imx8mq_g1_clk_names,
.num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names),
+ .shared_devices = imx8mq_vpu_shared_resources,
};
const struct hantro_variant imx8mq_vpu_g2_variant = {
@@ -371,6 +378,7 @@ const struct hantro_variant imx8mq_vpu_g2_variant = {
.num_irqs = ARRAY_SIZE(imx8mq_g2_irqs),
.clk_names = imx8mq_g2_clk_names,
.num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names),
+ .shared_devices = imx8mq_vpu_shared_resources,
};
const struct hantro_variant imx8mm_vpu_g1_variant = {
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index e4703bb6be7c..e4e21ad37323 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -72,6 +72,14 @@
: AV1_DIV_ROUND_UP_POW2((_value_), (_n_))); \
})
+enum rockchip_av1_tx_mode {
+ ROCKCHIP_AV1_TX_MODE_ONLY_4X4 = 0,
+ ROCKCHIP_AV1_TX_MODE_8X8 = 1,
+ ROCKCHIP_AV1_TX_MODE_16x16 = 2,
+ ROCKCHIP_AV1_TX_MODE_32x32 = 3,
+ ROCKCHIP_AV1_TX_MODE_SELECT = 4,
+};
+
struct rockchip_av1_film_grain {
u8 scaling_lut_y[256];
u8 scaling_lut_cb[256];
@@ -373,12 +381,12 @@ int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx)
return -ENOMEM;
av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
- av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_MAX_TILES,
+ av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
&av1_dec->tile_info.dma,
GFP_KERNEL);
if (!av1_dec->tile_info.cpu)
return -ENOMEM;
- av1_dec->tile_info.size = AV1_MAX_TILES;
+ av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
ALIGN(sizeof(struct rockchip_av1_film_grain), 2048),
@@ -1396,8 +1404,16 @@ static void rockchip_vpu981_av1_dec_set_cdef(struct hantro_ctx *ctx)
u16 luma_sec_strength = 0;
u32 chroma_pri_strength = 0;
u16 chroma_sec_strength = 0;
+ bool enable_cdef;
int i;
+ enable_cdef = !(cdef->bits == 0 &&
+ cdef->damping_minus_3 == 0 &&
+ cdef->y_pri_strength[0] == 0 &&
+ cdef->y_sec_strength[0] == 0 &&
+ cdef->uv_pri_strength[0] == 0 &&
+ cdef->uv_sec_strength[0] == 0);
+ hantro_reg_write(vpu, &av1_enable_cdef, enable_cdef);
hantro_reg_write(vpu, &av1_cdef_bits, cdef->bits);
hantro_reg_write(vpu, &av1_cdef_damping, cdef->damping_minus_3);
@@ -1927,11 +1943,26 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
rockchip_vpu981_av1_dec_set_other_frames(ctx);
}
+static int rockchip_vpu981_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode)
+{
+ switch (tx_mode) {
+ case V4L2_AV1_TX_MODE_ONLY_4X4:
+ return ROCKCHIP_AV1_TX_MODE_ONLY_4X4;
+ case V4L2_AV1_TX_MODE_LARGEST:
+ return ROCKCHIP_AV1_TX_MODE_32x32;
+ case V4L2_AV1_TX_MODE_SELECT:
+ return ROCKCHIP_AV1_TX_MODE_SELECT;
+ }
+
+ return ROCKCHIP_AV1_TX_MODE_32x32;
+}
+
static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
{
struct hantro_dev *vpu = ctx->dev;
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+ int tx_mode;
hantro_reg_write(vpu, &av1_skip_mode,
!!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT));
@@ -1953,8 +1984,6 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
!!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_SHOW_FRAME));
hantro_reg_write(vpu, &av1_switchable_motion_mode,
!!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE));
- hantro_reg_write(vpu, &av1_enable_cdef,
- !!(ctrls->sequence->flags & V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF));
hantro_reg_write(vpu, &av1_allow_masked_compound,
!!(ctrls->sequence->flags
& V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND));
@@ -1989,7 +2018,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
!!(ctrls->frame->quantization.flags
& V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT));
- hantro_reg_write(vpu, &av1_idr_pic_e, !ctrls->frame->frame_type);
+ hantro_reg_write(vpu, &av1_idr_pic_e, IS_INTRA(ctrls->frame->frame_type));
hantro_reg_write(vpu, &av1_quant_base_qindex, ctrls->frame->quantization.base_q_idx);
hantro_reg_write(vpu, &av1_bit_depth_y_minus8, ctx->bit_depth - 8);
hantro_reg_write(vpu, &av1_bit_depth_c_minus8, ctx->bit_depth - 8);
@@ -1999,7 +2028,9 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
!!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV));
hantro_reg_write(vpu, &av1_comp_pred_mode,
(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT) ? 2 : 0);
- hantro_reg_write(vpu, &av1_transform_mode, (ctrls->frame->tx_mode == 1) ? 3 : 4);
+
+ tx_mode = rockchip_vpu981_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
+ hantro_reg_write(vpu, &av1_transform_mode, tx_mode);
hantro_reg_write(vpu, &av1_max_cb_size,
(ctrls->sequence->flags
& V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK) ? 7 : 6);
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
index f3b57f0cb1ec..c133305fd019 100644
--- a/drivers/media/radio/radio-keene.c
+++ b/drivers/media/radio/radio-keene.c
@@ -338,7 +338,6 @@ static int usb_keene_probe(struct usb_interface *intf,
if (hdl->error) {
retval = hdl->error;
- v4l2_ctrl_handler_free(hdl);
goto err_v4l2;
}
retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
@@ -384,6 +383,7 @@ static int usb_keene_probe(struct usb_interface *intf,
err_vdev:
v4l2_device_unregister(&radio->v4l2_dev);
err_v4l2:
+ v4l2_ctrl_handler_free(&radio->hdl);
kfree(radio->buffer);
kfree(radio);
err:
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index a7ab668ce70b..be846f711969 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -448,8 +448,10 @@ static void device_run(void *priv)
ctx->comp_magic_cnt = 0;
ctx->comp_has_frame = false;
spin_unlock(ctx->lock);
- if (ctx->is_stateless && src_req)
+ if (ctx->is_stateless && src_req) {
v4l2_ctrl_request_complete(src_req, &ctx->hdl);
+ media_request_manual_complete(src_req);
+ }
if (ctx->is_enc)
v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx);
@@ -1518,8 +1520,12 @@ static void vicodec_return_bufs(struct vb2_queue *q, u32 state)
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (vbuf == NULL)
return;
- v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
- &ctx->hdl);
+ if (ctx->is_stateless && V4L2_TYPE_IS_OUTPUT(q->type)) {
+ struct media_request *req = vbuf->vb2_buf.req_obj.req;
+
+ v4l2_ctrl_request_complete(req, &ctx->hdl);
+ media_request_manual_complete(req);
+ }
spin_lock(ctx->lock);
v4l2_m2m_buf_done(vbuf, state);
spin_unlock(ctx->lock);
@@ -1672,6 +1678,7 @@ static void vicodec_buf_request_complete(struct vb2_buffer *vb)
struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+ media_request_manual_complete(vb->req_obj.req);
}
@@ -2002,6 +2009,12 @@ static int vicodec_request_validate(struct media_request *req)
return vb2_request_validate(req);
}
+static void vicodec_request_queue(struct media_request *req)
+{
+ media_request_mark_manual_completion(req);
+ v4l2_m2m_request_queue(req);
+}
+
static const struct v4l2_file_operations vicodec_fops = {
.owner = THIS_MODULE,
.open = vicodec_open,
@@ -2022,7 +2035,7 @@ static const struct video_device vicodec_videodev = {
static const struct media_device_ops vicodec_m2m_media_ops = {
.req_validate = vicodec_request_validate,
- .req_queue = v4l2_m2m_request_queue,
+ .req_queue = vicodec_request_queue,
};
static const struct v4l2_m2m_ops m2m_ops = {
diff --git a/drivers/media/test-drivers/visl/visl-dec.c b/drivers/media/test-drivers/visl/visl-dec.c
index d90b79de8384..6bbf93757047 100644
--- a/drivers/media/test-drivers/visl/visl-dec.c
+++ b/drivers/media/test-drivers/visl/visl-dec.c
@@ -547,6 +547,9 @@ 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);
+ trace_v4l2_ctrl_hevc_ext_sps_lt_rps(run->hevc.rps_lt);
+ trace_v4l2_ctrl_hevc_ext_sps_st_rps(run->hevc.rps_st);
+
break;
case VISL_CODEC_AV1:
trace_v4l2_ctrl_av1_sequence(run->av1.seq);
@@ -611,6 +614,10 @@ void visl_device_run(void *priv)
run.hevc.spram = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SLICE_PARAMS);
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);
+ run.hevc.rps_lt = visl_find_control_data(ctx,
+ V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS);
+ run.hevc.rps_st = visl_find_control_data(ctx,
+ V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS);
break;
case VISL_CODEC_AV1:
run.av1.seq = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
diff --git a/drivers/media/test-drivers/visl/visl-dec.h b/drivers/media/test-drivers/visl/visl-dec.h
index c2c2ef3a8798..6e7562e555bb 100644
--- a/drivers/media/test-drivers/visl/visl-dec.h
+++ b/drivers/media/test-drivers/visl/visl-dec.h
@@ -7,6 +7,7 @@
#ifndef _VISL_DEC_H_
#define _VISL_DEC_H_
+#include "linux/v4l2-controls.h"
#include "visl.h"
struct visl_fwht_run {
@@ -43,6 +44,8 @@ struct visl_hevc_run {
const struct v4l2_ctrl_hevc_slice_params *spram;
const struct v4l2_ctrl_hevc_scaling_matrix *sm;
const struct v4l2_ctrl_hevc_decode_params *dpram;
+ const struct v4l2_ctrl_hevc_ext_sps_lt_rps *rps_lt;
+ const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_st;
};
struct visl_av1_run {
diff --git a/drivers/media/test-drivers/visl/visl-trace-hevc.h b/drivers/media/test-drivers/visl/visl-trace-hevc.h
index 837b8ec12e97..963914c463db 100644
--- a/drivers/media/test-drivers/visl/visl-trace-hevc.h
+++ b/drivers/media/test-drivers/visl/visl-trace-hevc.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0+ */
+#include "linux/v4l2-controls.h"
#if !defined(_VISL_TRACE_HEVC_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _VISL_TRACE_HEVC_H_
@@ -343,6 +344,54 @@ DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_decode_params_tmpl,
))
);
+DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_ext_sps_lt_rps_tmpl,
+ TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_lt_rps *lt),
+ TP_ARGS(lt),
+ TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_hevc_ext_sps_lt_rps, lt)),
+ TP_fast_assign(__entry->lt = *lt),
+ TP_printk("\nflags %s\n"
+ "lt_ref_pic_poc_lsb_sps %x\n",
+ __print_flags(__entry->lt.flags, "|",
+ {V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT, "USED_LT"}
+ ),
+ __entry->lt.lt_ref_pic_poc_lsb_sps
+ )
+)
+
+DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_ext_sps_st_rps_tmpl,
+ TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_st_rps *st),
+ TP_ARGS(st),
+ TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_hevc_ext_sps_st_rps, st)),
+ TP_fast_assign(__entry->st = *st),
+ TP_printk("\nflags %s\n"
+ "delta_idx_minus1: %u\n"
+ "delta_rps_sign: %u\n"
+ "abs_delta_rps_minus1: %u\n"
+ "num_negative_pics: %u\n"
+ "num_positive_pics: %u\n"
+ "used_by_curr_pic: %08x\n"
+ "use_delta_flag: %08x\n"
+ "delta_poc_s0_minus1: %s\n"
+ "delta_poc_s1_minus1: %s\n",
+ __print_flags(__entry->st.flags, "|",
+ {V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED, "INTER_REF_PIC_SET_PRED"}
+ ),
+ __entry->st.delta_idx_minus1,
+ __entry->st.delta_rps_sign,
+ __entry->st.abs_delta_rps_minus1,
+ __entry->st.num_negative_pics,
+ __entry->st.num_positive_pics,
+ __entry->st.used_by_curr_pic,
+ __entry->st.use_delta_flag,
+ __print_array(__entry->st.delta_poc_s0_minus1,
+ ARRAY_SIZE(__entry->st.delta_poc_s0_minus1),
+ sizeof(__entry->st.delta_poc_s0_minus1[0])),
+ __print_array(__entry->st.delta_poc_s1_minus1,
+ ARRAY_SIZE(__entry->st.delta_poc_s1_minus1),
+ sizeof(__entry->st.delta_poc_s1_minus1[0]))
+ )
+)
+
DECLARE_EVENT_CLASS(v4l2_hevc_dpb_entry_tmpl,
TP_PROTO(const struct v4l2_hevc_dpb_entry *e),
@@ -391,6 +440,16 @@ DEFINE_EVENT(v4l2_ctrl_hevc_decode_params_tmpl, v4l2_ctrl_hevc_decode_params,
TP_ARGS(d)
);
+DEFINE_EVENT(v4l2_ctrl_hevc_ext_sps_lt_rps_tmpl, v4l2_ctrl_hevc_ext_sps_lt_rps,
+ TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_lt_rps *lt),
+ TP_ARGS(lt)
+);
+
+DEFINE_EVENT(v4l2_ctrl_hevc_ext_sps_st_rps_tmpl, v4l2_ctrl_hevc_ext_sps_st_rps,
+ TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_st_rps *st),
+ TP_ARGS(st)
+);
+
DEFINE_EVENT(v4l2_hevc_dpb_entry_tmpl, v4l2_hevc_dpb_entry,
TP_PROTO(const struct v4l2_hevc_dpb_entry *e),
TP_ARGS(e)
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index b32bb906a9de..5807734ae26c 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -3709,6 +3709,11 @@ status);
"Failed to submit read-control URB status=%d",
status);
hdw->ctl_read_pend_flag = 0;
+ if (hdw->ctl_write_pend_flag) {
+ usb_unlink_urb(hdw->ctl_write_urb);
+ while (hdw->ctl_write_pend_flag)
+ wait_for_completion(&hdw->ctl_done);
+ }
goto done;
}
}
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 2905505c240c..f0f6f8454d9c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -483,6 +483,7 @@ static int uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, u8 query,
return 0;
case UVC_GET_MIN:
+ /* Not used, we use -UVC_GET_MAX */
case UVC_GET_MAX:
case UVC_GET_RES:
case UVC_GET_DEF:
@@ -526,8 +527,7 @@ static int uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
*out = (sign == 0) ? 0 : (sign > 0 ? value : -value);
return 0;
case UVC_GET_MIN:
- *out = -value;
- return 0;
+ /* Not used, we use -UVC_GET_MAX */
case UVC_GET_MAX:
case UVC_GET_RES:
case UVC_GET_DEF:
@@ -1432,7 +1432,7 @@ static bool uvc_ctrl_is_readable(u32 which, struct uvc_control *ctrl,
* auto_exposure=1, exposure_time_absolute=251.
*/
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
- const struct v4l2_ext_controls *ctrls,
+ const struct v4l2_ext_controls *ctrls, u32 which,
unsigned long ioctl)
{
struct uvc_control_mapping *master_map = NULL;
@@ -1442,14 +1442,24 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
s32 val;
int ret;
int i;
+ /*
+ * There is no need to check the ioctl, all the ioctls except
+ * VIDIOC_G_EXT_CTRLS use which=V4L2_CTRL_WHICH_CUR_VAL.
+ */
+ bool is_which_min_max = which == V4L2_CTRL_WHICH_MIN_VAL ||
+ which == V4L2_CTRL_WHICH_MAX_VAL;
if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
- return -EACCES;
+ return is_which_min_max ? -EINVAL : -EACCES;
ctrl = uvc_find_control(chain, v4l2_id, &mapping);
if (!ctrl)
return -EINVAL;
+ if ((!(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) ||
+ !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)) && is_which_min_max)
+ return -EINVAL;
+
if (ioctl == VIDIOC_G_EXT_CTRLS)
return uvc_ctrl_is_readable(ctrls->which, ctrl, mapping);
@@ -1517,6 +1527,17 @@ static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
return ~0;
}
+static bool uvc_ctrl_is_relative_ptz(__u32 ctrl_id)
+{
+ switch (ctrl_id) {
+ case V4L2_CID_ZOOM_CONTINUOUS:
+ case V4L2_CID_PAN_SPEED:
+ case V4L2_CID_TILT_SPEED:
+ return true;
+ }
+ return false;
+}
+
/*
* Maximum retry count to avoid spurious errors with controls. Increasing this
* value does no seem to produce better results in the tested hardware.
@@ -1576,18 +1597,32 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
break;
}
- if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
- v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
- else
- v4l2_ctrl->minimum = 0;
-
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
else
v4l2_ctrl->maximum = 0;
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
+ /*
+ * For relative PTZ controls, UVC_GET_MIN for
+ * b(Pan|Tilt|Zoom)Speed returns the minimum speed of the
+ * movement in direction specified in the sign field.
+ * See in USB Device Class Definition for Video Devices:
+ * 4.2.2.1.13 Zoom (Relative) Control
+ * 4.2.2.1.15 PanTilt (Relative) Control
+ *
+ * For minimum value, use maximum speed but in negative direction.
+ */
+ if (uvc_ctrl_is_relative_ptz(v4l2_ctrl->id))
+ v4l2_ctrl->minimum = -v4l2_ctrl->maximum;
+ else
+ v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping,
+ UVC_GET_MIN, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+ } else {
+ v4l2_ctrl->minimum = 0;
+ }
+
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
@@ -2449,6 +2484,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, u32 which,
static int uvc_ctrl_clamp(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
+ u32 v4l2_id,
struct uvc_control_mapping *mapping,
s32 *value_in_out)
{
@@ -2466,10 +2502,24 @@ static int uvc_ctrl_clamp(struct uvc_video_chain *chain,
return ret;
}
- min = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
max = uvc_mapping_get_s32(mapping, UVC_GET_MAX,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+ /*
+ * For relative PTZ controls, UVC_GET_MIN for
+ * b(Pan|Tilt|Zoom)Speed returns the minimum speed of the
+ * movement in direction specified in the sign field.
+ * See in USB Device Class Definition for Video Devices:
+ * 4.2.2.1.13 Zoom (Relative) Control
+ * 4.2.2.1.15 PanTilt (Relative) Control
+ *
+ * For minimum value, use maximum speed but in negative direction.
+ */
+ if (uvc_ctrl_is_relative_ptz(v4l2_id))
+ min = -max;
+ else
+ min = uvc_mapping_get_s32(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+
step = uvc_mapping_get_s32(mapping, UVC_GET_RES,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
if (step == 0)
@@ -2583,7 +2633,7 @@ int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl)
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
return -EACCES;
- ret = uvc_ctrl_clamp(chain, ctrl, mapping, &xctrl->value);
+ ret = uvc_ctrl_clamp(chain, ctrl, xctrl->id, mapping, &xctrl->value);
if (ret)
return ret;
/*
@@ -2929,8 +2979,7 @@ int uvc_ctrl_restore_values(struct uvc_device *dev)
if (!ctrl->initialized || !ctrl->modified ||
(ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0)
continue;
- dev_dbg(&dev->intf->dev,
- "restoring control %pUl/%u/%u\n",
+ uvc_dbg(dev, CONTROL, "restoring control %pUl/%u/%u\n",
ctrl->info.entity, ctrl->info.index,
ctrl->info.selector);
ctrl->dirty = 1;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index ee4f54d68349..aa3e8d295e0f 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -165,28 +165,17 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
return NULL;
}
-static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+static struct uvc_streaming *uvc_stream_for_terminal(struct uvc_device *dev,
+ struct uvc_entity *term)
{
- struct uvc_streaming *stream, *last_stream;
- unsigned int count = 0;
+ u16 id = UVC_HARDWARE_ENTITY_ID(term->id);
+ struct uvc_streaming *stream;
list_for_each_entry(stream, &dev->streams, list) {
- count += 1;
- last_stream = stream;
if (stream->header.bTerminalLink == id)
return stream;
}
- /*
- * If the streaming entity is referenced by an invalid ID, notify the
- * user and use heuristics to guess the correct entity.
- */
- if (count == 1 && id == UVC_INVALID_ENTITY_ID) {
- dev_warn(&dev->intf->dev,
- "UVC non compliance: Invalid USB header. The streaming entity has an invalid ID, guessing the correct one.");
- return last_stream;
- }
-
return NULL;
}
@@ -823,10 +812,12 @@ static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
}
/* Per UVC 1.1+ spec 3.7.2, the ID is unique. */
- if (uvc_entity_by_id(dev, id)) {
- dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", id);
+ if (uvc_entity_by_id(dev, UVC_HARDWARE_ENTITY_ID(id)))
+ dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n",
+ UVC_HARDWARE_ENTITY_ID(id));
+
+ if (uvc_entity_by_id(dev, id))
id = UVC_INVALID_ENTITY_ID;
- }
extra_size = roundup(extra_size, sizeof(*entity->pads));
if (num_pads)
@@ -982,6 +973,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
struct usb_host_interface *alts = dev->intf->cur_altsetting;
unsigned int i, n, p, len;
const char *type_name;
+ unsigned int id;
u16 type;
switch (buffer[2]) {
@@ -1120,8 +1112,28 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return 0;
}
+ id = buffer[3];
+
+ /*
+ * Some devices, such as the Grandstream GUV3100, exhibit entity
+ * ID collisions between units and streaming output terminals.
+ * Move streaming output terminals to their own ID namespace by
+ * setting bit UVC_TERM_OUTPUT (15), above the ID's 8-bit value.
+ * The bit is ignored in uvc_stream_for_terminal() when looking
+ * up the streaming interface for the terminal.
+ *
+ * This hack is safe to enable unconditionally, as the ID is not
+ * used for any other purpose (streaming output terminals have
+ * no controls and are never referenced as sources in UVC
+ * descriptors). Other types output terminals can have controls,
+ * so limit usage of this separate namespace to streaming output
+ * terminals.
+ */
+ if (type & UVC_TT_STREAMING)
+ id |= UVC_TERM_OUTPUT;
+
term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT,
- buffer[3], 1, 0);
+ id, 1, 0);
if (IS_ERR(term))
return PTR_ERR(term);
@@ -2118,8 +2130,8 @@ static int uvc_register_terms(struct uvc_device *dev,
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;
- stream = uvc_stream_by_id(dev, term->id);
- if (stream == NULL) {
+ stream = uvc_stream_for_terminal(dev, term);
+ if (!stream) {
dev_info(&dev->intf->dev,
"No streaming interface found for terminal %u.",
term->id);
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 790184c9843d..8b8f44b4a045 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -177,18 +177,20 @@ static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count)
ret = uvc_pm_get(stream->dev);
if (ret)
- return ret;
+ goto err_buffers;
queue->buf_used = 0;
ret = uvc_video_start_streaming(stream);
- if (ret == 0)
- return 0;
+ if (ret)
+ goto err_pm;
- uvc_pm_put(stream->dev);
+ return 0;
+err_pm:
+ uvc_pm_put(stream->dev);
+err_buffers:
uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
-
return ret;
}
@@ -339,7 +341,7 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
* the device has been disconnected.
*/
static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue,
- struct uvc_buffer *buf)
+ struct uvc_buffer *buf)
{
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 9e4a251eca88..30c160daed8c 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -765,14 +765,15 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *priv,
static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
struct v4l2_ext_controls *ctrls,
- unsigned long ioctl)
+ u32 which, unsigned long ioctl)
{
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
int ret = 0;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl);
+ ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, which,
+ ioctl);
if (ret)
break;
}
@@ -806,7 +807,7 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *priv,
which = V4L2_CTRL_WHICH_CUR_VAL;
}
- ret = uvc_ctrl_check_access(chain, ctrls, VIDIOC_G_EXT_CTRLS);
+ ret = uvc_ctrl_check_access(chain, ctrls, which, VIDIOC_G_EXT_CTRLS);
if (ret < 0)
return ret;
@@ -840,7 +841,8 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
if (!ctrls->count)
return 0;
- ret = uvc_ctrl_check_access(chain, ctrls, ioctl);
+ ret = uvc_ctrl_check_access(chain, ctrls, V4L2_CTRL_WHICH_CUR_VAL,
+ ioctl);
if (ret < 0)
return ret;
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 2094e059d7d3..59eb95a4b70c 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1771,12 +1771,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
}
static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream,
- struct uvc_urb *uvc_urb, gfp_t gfp_flags)
+ struct uvc_urb *uvc_urb, unsigned int size,
+ gfp_t gfp_flags)
{
struct usb_device *udev = stream->dev->udev;
- uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size,
- gfp_flags, &uvc_urb->dma,
+ uvc_urb->buffer = usb_alloc_noncoherent(udev, size, gfp_flags,
+ &uvc_urb->dma,
uvc_stream_dir(stream),
&uvc_urb->sgt);
return !!uvc_urb->buffer;
@@ -1812,13 +1813,14 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
npackets = UVC_MAX_PACKETS;
/* Retry allocations until one succeed. */
- for (; npackets > 1; npackets /= 2) {
- stream->urb_size = psize * npackets;
+ for (; npackets > 0; npackets /= 2) {
+ unsigned int urb_size = psize * npackets;
for (i = 0; i < UVC_URBS; ++i) {
struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
- if (!uvc_alloc_urb_buffer(stream, uvc_urb, gfp_flags)) {
+ if (!uvc_alloc_urb_buffer(stream, uvc_urb, urb_size,
+ gfp_flags)) {
uvc_free_urb_buffers(stream);
break;
}
@@ -1830,6 +1832,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
uvc_dbg(stream->dev, VIDEO,
"Allocated %u URB buffers of %ux%u bytes each\n",
UVC_URBS, npackets, psize);
+ stream->urb_size = urb_size;
return npackets;
}
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index ed7bad31f75c..8480d65ecb85 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -41,7 +41,8 @@
#define UVC_EXT_GPIO_UNIT 0x7ffe
#define UVC_EXT_GPIO_UNIT_ID 0x100
-#define UVC_INVALID_ENTITY_ID 0xffff
+#define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff)
+#define UVC_INVALID_ENTITY_ID 0xffff
/* ------------------------------------------------------------------------
* Driver specific constants.
@@ -786,7 +787,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, u32 which,
struct v4l2_ext_control *xctrl);
int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
- const struct v4l2_ext_controls *ctrls,
+ const struct v4l2_ext_controls *ctrls, u32 which,
unsigned long ioctl);
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index ee884a8221fb..1c08bba9ecb9 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -343,7 +343,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_connection *asc)
{
- struct v4l2_async_notifier *subdev_notifier;
bool registered = false;
int ret;
@@ -389,6 +388,25 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n",
dev_name(sd->dev), ret);
+ return 0;
+
+err_call_unbind:
+ v4l2_async_nf_call_unbind(notifier, sd, asc);
+ list_del(&asc->asc_subdev_entry);
+
+err_unregister_subdev:
+ if (registered)
+ v4l2_device_unregister_subdev(sd);
+
+ return ret;
+}
+
+static int
+v4l2_async_nf_try_subdev_notifier(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_async_notifier *subdev_notifier;
+
/*
* See if the sub-device has a notifier. If not, return here.
*/
@@ -404,16 +422,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
subdev_notifier->parent = notifier;
return v4l2_async_nf_try_all_subdevs(subdev_notifier);
-
-err_call_unbind:
- v4l2_async_nf_call_unbind(notifier, sd, asc);
- list_del(&asc->asc_subdev_entry);
-
-err_unregister_subdev:
- if (registered)
- v4l2_device_unregister_subdev(sd);
-
- return ret;
}
/* Test all async sub-devices in a notifier for a match. */
@@ -445,6 +453,10 @@ again:
if (ret < 0)
return ret;
+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd);
+ if (ret < 0)
+ return ret;
+
/*
* v4l2_async_match_notify() may lead to registering a
* new notifier and thus changing the async subdevs
@@ -829,7 +841,11 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd,
asc);
if (ret)
- goto err_unbind;
+ goto err_unlock;
+
+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd);
+ if (ret)
+ goto err_unbind_one;
ret = v4l2_async_nf_try_complete(notifier);
if (ret)
@@ -853,9 +869,10 @@ err_unbind:
if (subdev_notifier)
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
- if (asc)
- v4l2_async_unbind_subdev_one(notifier, asc);
+err_unbind_one:
+ v4l2_async_unbind_subdev_one(notifier, asc);
+err_unlock:
mutex_unlock(&list_lock);
sd->owner = NULL;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 209bc05883bb..79a157975f70 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -424,6 +424,12 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
pr_cont("HEVC_SLICE_PARAMS");
break;
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS:
+ pr_cont("HEVC_EXT_SPS_ST_RPS");
+ break;
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS:
+ pr_cont("HEVC_EXT_SPS_LT_RPS");
+ break;
case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
pr_cont("HEVC_SCALING_MATRIX");
break;
@@ -961,6 +967,8 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_h264_pred_weights *p_h264_pred_weights;
struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
struct v4l2_ctrl_h264_decode_params *p_h264_dec_params;
+ struct v4l2_ctrl_hevc_ext_sps_lt_rps *p_hevc_lt_rps;
+ struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps;
struct v4l2_ctrl_hevc_sps *p_hevc_sps;
struct v4l2_ctrl_hevc_pps *p_hevc_pps;
struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
@@ -1254,6 +1262,20 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
break;
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS:
+ p_hevc_st_rps = p;
+
+ if (p_hevc_st_rps->flags & ~V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED)
+ return -EINVAL;
+ break;
+
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS:
+ p_hevc_lt_rps = p;
+
+ if (p_hevc_lt_rps->flags & ~V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT)
+ return -EINVAL;
+ break;
+
case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
break;
@@ -2006,6 +2028,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
break;
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS:
+ elem_size = sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps);
+ break;
+ case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS:
+ elem_size = sizeof(struct v4l2_ctrl_hevc_ext_sps_lt_rps);
+ break;
case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix);
break;
@@ -2780,7 +2808,8 @@ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl,
orientation_ctrl = V4L2_CAMERA_ORIENTATION_EXTERNAL;
break;
default:
- return -EINVAL;
+ hdl->error = -EINVAL;
+ return hdl->error;
}
if (!v4l2_ctrl_new_std_menu(hdl, ctrl_ops,
V4L2_CID_CAMERA_ORIENTATION,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index ad41f65374e2..551426c4cd01 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -1135,6 +1135,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_FLASH_FAULT: return "Faults";
case V4L2_CID_FLASH_CHARGE: return "Charge";
case V4L2_CID_FLASH_READY: return "Ready to Strobe";
+ case V4L2_CID_FLASH_DURATION: return "Strobe Duration";
+ case V4L2_CID_FLASH_STROBE_OE: return "Strobe Output Enable";
/* JPEG encoder controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1233,6 +1235,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return "HEVC Decode Mode";
case V4L2_CID_STATELESS_HEVC_START_CODE: return "HEVC Start Code";
case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: return "HEVC Entry Point Offsets";
+ case V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS: return "HEVC Short Term Ref Sets";
+ case V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS: return "HEVC Long Term Ref Sets";
case V4L2_CID_STATELESS_AV1_SEQUENCE: return "AV1 Sequence Parameters";
case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY: return "AV1 Tile Group Entry";
case V4L2_CID_STATELESS_AV1_FRAME: return "AV1 Frame Parameters";
@@ -1281,6 +1285,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_FLASH_STROBE_STATUS:
case V4L2_CID_FLASH_CHARGE:
case V4L2_CID_FLASH_READY:
+ case V4L2_CID_FLASH_STROBE_OE:
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE:
@@ -1578,6 +1583,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
*type = V4L2_CTRL_TYPE_U32;
*flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
break;
+ case V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS:
+ *type = V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
+ break;
+ case V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS:
+ *type = V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
+ break;
case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR:
*type = V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR;
break;
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index cb153ce42c45..22ec702a4567 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -465,6 +465,9 @@ static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
enum v4l2_mbus_type mbus_type;
int rval;
+ if (!fwnode)
+ return -EINVAL;
+
pr_debug("===== begin parsing endpoint %pfw\n", fwnode);
fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 98512ea4cc5b..37d33d4a363d 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1544,6 +1544,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break;
case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
+ case V4L2_PIX_FMT_AV1: descr = "AV1 OBU Stream"; break;
case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break;
case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break;
case V4L2_PIX_FMT_HEXTILE: descr = "Hextile Compressed Format"; break;
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index fec93c1a9231..b661f483dad3 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -90,6 +90,7 @@ static const char * const m2m_entity_name[] = {
* @job_work: worker to run queued jobs.
* @job_queue_flags: flags of the queue status, %QUEUE_PAUSED.
* @m2m_ops: driver callbacks
+ * @kref: device reference count
*/
struct v4l2_m2m_dev {
struct v4l2_m2m_ctx *curr_ctx;
@@ -109,6 +110,8 @@ struct v4l2_m2m_dev {
unsigned long job_queue_flags;
const struct v4l2_m2m_ops *m2m_ops;
+
+ struct kref kref;
};
static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
@@ -1034,8 +1037,6 @@ static int v4l2_m2m_register_entity(struct media_device *mdev,
{
struct media_entity *entity;
struct media_pad *pads;
- char *name;
- unsigned int len;
int num_pads;
int ret;
@@ -1068,12 +1069,10 @@ static int v4l2_m2m_register_entity(struct media_device *mdev,
entity->info.dev.major = VIDEO_MAJOR;
entity->info.dev.minor = vdev->minor;
}
- len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]);
- name = kmalloc(len, GFP_KERNEL);
- if (!name)
+ entity->name = kasprintf(GFP_KERNEL, "%s-%s", vdev->name,
+ m2m_entity_name[type]);
+ if (!entity->name)
return -ENOMEM;
- snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]);
- entity->name = name;
entity->function = function;
ret = media_entity_pads_init(entity, num_pads, pads);
@@ -1200,6 +1199,7 @@ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops)
INIT_LIST_HEAD(&m2m_dev->job_queue);
spin_lock_init(&m2m_dev->job_spinlock);
INIT_WORK(&m2m_dev->job_work, v4l2_m2m_device_run_work);
+ kref_init(&m2m_dev->kref);
return m2m_dev;
}
@@ -1211,6 +1211,25 @@ void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
}
EXPORT_SYMBOL_GPL(v4l2_m2m_release);
+void v4l2_m2m_get(struct v4l2_m2m_dev *m2m_dev)
+{
+ kref_get(&m2m_dev->kref);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_get);
+
+static void v4l2_m2m_release_from_kref(struct kref *kref)
+{
+ struct v4l2_m2m_dev *m2m_dev = container_of(kref, struct v4l2_m2m_dev, kref);
+
+ v4l2_m2m_release(m2m_dev);
+}
+
+void v4l2_m2m_put(struct v4l2_m2m_dev *m2m_dev)
+{
+ kref_put(&m2m_dev->kref, v4l2_m2m_release_from_kref);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_put);
+
struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
void *drv_priv,
int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 25e66bf18f5f..66842b975f91 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -2112,7 +2112,7 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
{
u32 *remote_pads = NULL;
unsigned int i, j;
- int ret = -EINVAL;
+ int ret = -ENXIO;
if (disallow & (V4L2_SUBDEV_ROUTING_NO_STREAM_MIX |
V4L2_SUBDEV_ROUTING_NO_MULTIPLEXING)) {
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 3a4eb4f6d3be..a3cd9d3e9ce7 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -2961,11 +2961,8 @@ int atomisp_set_parameters(struct video_device *vdev,
* per-frame setting only works for the main output frame.
*/
param = kvzalloc(sizeof(*param), GFP_KERNEL);
- if (!param) {
- dev_err(asd->isp->dev, "%s: failed to alloc params buffer\n",
- __func__);
+ if (!param)
return -ENOMEM;
- }
css_param = &param->params;
}
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c
index 6a10d3545278..e422cca55022 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c
@@ -35,7 +35,8 @@ ia_css_formats_dump(
const struct sh_css_isp_formats_params *formats,
unsigned int level)
{
- if (!formats) return;
+ if (!formats)
+ return;
ia_css_debug_dtrace(level, "\t%-32s = %d\n",
"video_full_range_flag", formats->video_full_range_flag);
}
diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c
index bf8e6dca40e5..49ce295771e4 100644
--- a/drivers/staging/media/av7110/av7110_hw.c
+++ b/drivers/staging/media/av7110/av7110_hw.c
@@ -312,7 +312,7 @@ int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
pr_err("%s(): timeout waiting for MSGSTATE %04x\n", __func__, stat & flags);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
return 0;
}
@@ -343,7 +343,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length)
av7110->arm_errors++;
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
if (FW_VERSION(av7110->arm_app) <= 0x261f)
@@ -359,7 +359,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length)
pr_err("%s(): timeout waiting for HANDSHAKE_REG\n", __func__);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
#endif
@@ -405,7 +405,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length)
av7110->arm_errors++;
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
}
@@ -433,7 +433,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length)
__func__, (buf[0] >> 8) & 0xff);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
@@ -559,7 +559,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
return -ETIMEDOUT;
}
#ifdef _NOHANDSHAKE
- msleep(1);
+ usleep_range(1000, 2000);
#endif
}
@@ -574,7 +574,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
mutex_unlock(&av7110->dcomlock);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
#endif
@@ -719,7 +719,7 @@ static int FlushText(struct av7110 *av7110)
mutex_unlock(&av7110->dcomlock);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
mutex_unlock(&av7110->dcomlock);
return 0;
@@ -745,7 +745,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)
mutex_unlock(&av7110->dcomlock);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
#ifndef _NOHANDSHAKE
start = jiffies;
@@ -758,7 +758,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)
mutex_unlock(&av7110->dcomlock);
return -ETIMEDOUT;
}
- msleep(1);
+ usleep_range(1000, 2000);
}
#endif
for (i = 0; i < length / 2; i++)
diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c
index 0c813860f5b2..93bf47a62e38 100644
--- a/drivers/staging/media/av7110/sp8870.c
+++ b/drivers/staging/media/av7110/sp8870.c
@@ -496,7 +496,7 @@ static int sp8870_set_frontend(struct dvb_frontend *fe)
dprintk("delay = %i usec\n", check_count * 10);
break;
}
- udelay(10);
+ usleep_range(10, 20);
}
if (valid)
break;
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index dd8c7b3233bc..211f67fb92b5 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -23,65 +23,65 @@
* there must be 5 pads: 1 input pad from sensor, and
* the 4 virtual channel output pads
*/
-#define CSI2_SINK_PAD 0
-#define CSI2_NUM_SINK_PADS 1
-#define CSI2_NUM_SRC_PADS 4
-#define CSI2_NUM_PADS 5
+#define CSI2_SINK_PAD 0
+#define CSI2_NUM_SINK_PADS 1
+#define CSI2_NUM_SRC_PADS 4
+#define CSI2_NUM_PADS 5
/*
* The default maximum bit-rate per lane in Mbps, if the
* source subdev does not provide V4L2_CID_LINK_FREQ.
*/
-#define CSI2_DEFAULT_MAX_MBPS 849
+#define CSI2_DEFAULT_MAX_MBPS 849
struct csi2_dev {
- struct device *dev;
- struct v4l2_subdev sd;
+ struct device *dev;
+ struct v4l2_subdev sd;
struct v4l2_async_notifier notifier;
- struct media_pad pad[CSI2_NUM_PADS];
- struct clk *dphy_clk;
- struct clk *pllref_clk;
- struct clk *pix_clk; /* what is this? */
- void __iomem *base;
+ struct media_pad pad[CSI2_NUM_PADS];
+ struct clk *dphy_clk;
+ struct clk *pllref_clk;
+ struct clk *pix_clk; /* what is this? */
+ void __iomem *base;
- struct v4l2_subdev *remote;
- unsigned int remote_pad;
- unsigned short data_lanes;
+ struct v4l2_subdev *remote;
+ unsigned int remote_pad;
+ unsigned short data_lanes;
/* lock to protect all members below */
struct mutex lock;
struct v4l2_mbus_framefmt format_mbus;
- int stream_count;
- struct v4l2_subdev *src_sd;
- bool sink_linked[CSI2_NUM_SRC_PADS];
+ int stream_count;
+ struct v4l2_subdev *src_sd;
+ bool sink_linked[CSI2_NUM_SRC_PADS];
};
#define DEVICE_NAME "imx6-mipi-csi2"
/* Register offsets */
-#define CSI2_VERSION 0x000
-#define CSI2_N_LANES 0x004
-#define CSI2_PHY_SHUTDOWNZ 0x008
-#define CSI2_DPHY_RSTZ 0x00c
-#define CSI2_RESETN 0x010
-#define CSI2_PHY_STATE 0x014
-#define PHY_STOPSTATEDATA_BIT 4
-#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n))
-#define PHY_RXCLKACTIVEHS BIT(8)
-#define PHY_RXULPSCLKNOT BIT(9)
-#define PHY_STOPSTATECLK BIT(10)
-#define CSI2_DATA_IDS_1 0x018
-#define CSI2_DATA_IDS_2 0x01c
-#define CSI2_ERR1 0x020
-#define CSI2_ERR2 0x024
-#define CSI2_MSK1 0x028
-#define CSI2_MSK2 0x02c
-#define CSI2_PHY_TST_CTRL0 0x030
+#define CSI2_VERSION 0x000
+#define CSI2_N_LANES 0x004
+#define CSI2_PHY_SHUTDOWNZ 0x008
+#define CSI2_DPHY_RSTZ 0x00c
+#define CSI2_RESETN 0x010
+#define CSI2_PHY_STATE 0x014
+#define PHY_STOPSTATEDATA_BIT 4
+#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n))
+#define PHY_RXCLKACTIVEHS BIT(8)
+#define PHY_RXULPSCLKNOT BIT(9)
+#define PHY_STOPSTATECLK BIT(10)
+#define CSI2_DATA_IDS_1 0x018
+#define CSI2_DATA_IDS_2 0x01c
+#define CSI2_ERR1 0x020
+#define CSI2_ERR2 0x024
+#define CSI2_MSK1 0x028
+#define CSI2_MSK2 0x02c
+#define CSI2_PHY_TST_CTRL0 0x030
#define PHY_TESTCLR BIT(0)
#define PHY_TESTCLK BIT(1)
-#define CSI2_PHY_TST_CTRL1 0x034
+#define CSI2_PHY_TST_CTRL1 0x034
#define PHY_TESTEN BIT(16)
/*
* i.MX CSI2IPU Gasket registers follow. The CSI2IPU gasket is
@@ -106,13 +106,13 @@ static inline struct csi2_dev *notifier_to_dev(struct v4l2_async_notifier *n)
* reference manual is as follows:
*
* 1. Deassert presetn signal (global reset).
- * It's not clear what this "global reset" signal is (maybe APB
- * global reset), but in any case this step would be probably
- * be carried out during driver load in csi2_probe().
+ * It's not clear what this "global reset" signal is (maybe APB
+ * global reset), but in any case this step would be probably
+ * be carried out during driver load in csi2_probe().
*
* 2. Configure MIPI Camera Sensor to put all Tx lanes in LP-11 state.
- * This must be carried out by the MIPI sensor's s_power(ON) subdev
- * op.
+ * This must be carried out by the MIPI sensor's s_power(ON) subdev
+ * op.
*
* 3. D-PHY initialization.
* 4. CSI2 Controller programming (Set N_LANES, deassert PHY_SHUTDOWNZ,
@@ -719,7 +719,6 @@ err_parse:
static int csi2_probe(struct platform_device *pdev)
{
struct csi2_dev *csi2;
- struct resource *res;
int i, ret;
csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
@@ -767,22 +766,18 @@ static int csi2_probe(struct platform_device *pdev)
return PTR_ERR(csi2->pix_clk);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- v4l2_err(&csi2->sd, "failed to get platform resources\n");
- return -ENODEV;
- }
-
- csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
- if (!csi2->base)
- return -ENOMEM;
+ csi2->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(csi2->base))
+ return PTR_ERR(csi2->base);
- mutex_init(&csi2->lock);
+ ret = devm_mutex_init(&pdev->dev, &csi2->lock);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(csi2->pllref_clk);
if (ret) {
v4l2_err(&csi2->sd, "failed to enable pllref_clk\n");
- goto rmmutex;
+ return ret;
}
ret = clk_prepare_enable(csi2->dphy_clk);
@@ -805,8 +800,6 @@ clean_notifier:
clk_disable_unprepare(csi2->dphy_clk);
pllref_off:
clk_disable_unprepare(csi2->pllref_clk);
-rmmutex:
- mutex_destroy(&csi2->lock);
return ret;
}
@@ -820,7 +813,6 @@ static void csi2_remove(struct platform_device *pdev)
v4l2_async_unregister_subdev(sd);
clk_disable_unprepare(csi2->dphy_clk);
clk_disable_unprepare(csi2->pllref_clk);
- mutex_destroy(&csi2->lock);
media_entity_cleanup(&sd->entity);
}
diff --git a/drivers/staging/media/ipu7/ipu7-buttress.c b/drivers/staging/media/ipu7/ipu7-buttress.c
index e5707f5e300b..40c6c8473357 100644
--- a/drivers/staging/media/ipu7/ipu7-buttress.c
+++ b/drivers/staging/media/ipu7/ipu7-buttress.c
@@ -342,14 +342,23 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr)
u32 disable_irqs = 0;
u32 irq_status;
unsigned int i;
+ int active;
- pm_runtime_get_noresume(dev);
+ active = pm_runtime_get_if_active(dev);
+ if (active <= 0)
+ return IRQ_NONE;
pb_irq = readl(isp->pb_base + INTERRUPT_STATUS);
writel(pb_irq, isp->pb_base + INTERRUPT_STATUS);
/* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */
pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK);
+ if (pb_local_irq == 0xffffffff) {
+ dev_warn_once(dev, "invalid PB irq status\n");
+ pm_runtime_put_noidle(dev);
+ return IRQ_NONE;
+ }
+
if (pb_local_irq & ~BIT(0)) {
dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq,
pb_local_irq);
@@ -370,6 +379,12 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr)
return IRQ_NONE;
}
+ if (irq_status == 0xffffffff) {
+ dev_warn_once(dev, "invalid irq status 0x%08x\n", irq_status);
+ pm_runtime_put_noidle(dev);
+ return IRQ_NONE;
+ }
+
do {
writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR);
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
index 2d5717883518..3f15af3b4c79 100644
--- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
@@ -124,6 +124,7 @@ static const struct cdr_fbk_cap_prog_params table7[] = {
{ 1350, 1589, 4 },
{ 1590, 1949, 5 },
{ 1950, 2499, 6 },
+ { 2500, 3500, 7 },
{ }
};
@@ -838,9 +839,10 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
dwc_phy_write_mask(isys, id, reg + 0x400 * i,
reset_thresh, 9, 11);
+ /* Tuning ITMINRX to 2 for CPHY */
reg = CORE_DIG_CLANE_0_RW_LP_0;
for (i = 0; i < trios; i++)
- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15);
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15);
reg = CORE_DIG_CLANE_0_RW_LP_2;
for (i = 0; i < trios; i++)
@@ -860,7 +862,11 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
for (i = 0; i < (lanes + 1); i++) {
reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i;
dwc_phy_write_mask(isys, id, reg, 4U, 0, 2);
- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4);
+ /* Set GMODE to 2 when CPHY >= 1.5Gsps */
+ if (mbps >= 1500)
+ dwc_phy_write_mask(isys, id, reg, 2U, 3, 4);
+ else
+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4);
reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i;
dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12);
@@ -930,8 +936,9 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
7, 12, 14);
dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
0, 8, 10);
+ /* resistance tuning: 1 for 45ohm, 0 for 50ohm */
dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5,
- 0, 8, 8);
+ 1, 8, 8);
if (aggregation)
phy_mode = isys->csi2[0].phy_mode;
diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c
index ded1986eb8ba..ea35cce4830a 100644
--- a/drivers/staging/media/ipu7/ipu7-mmu.c
+++ b/drivers/staging/media/ipu7/ipu7-mmu.c
@@ -231,7 +231,7 @@ static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info)
dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
- for (i = 0; i < ISP_L1PT_PTES; i++)
+ for (i = 0; i < ISP_L2PT_PTES; i++)
pt[i] = mmu_info->dummy_page_pteval;
return pt;
diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c
index 5cddc09c72bf..fa5a1867626f 100644
--- a/drivers/staging/media/ipu7/ipu7.c
+++ b/drivers/staging/media/ipu7/ipu7.c
@@ -2620,7 +2620,7 @@ out_ipu_bus_del_devices:
if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu))
ipu7_mmu_cleanup(isp->isys->mmu);
if (!IS_ERR_OR_NULL(isp->psys))
- pm_runtime_put(&isp->psys->auxdev.dev);
+ pm_runtime_put_sync(&isp->psys->auxdev.dev);
ipu7_bus_del_devices(pdev);
release_firmware(isp->cpd_fw);
buttress_exit:
@@ -2684,6 +2684,10 @@ static void ipu7_pci_reset_done(struct pci_dev *pdev)
*/
static int ipu7_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ synchronize_irq(pdev->irq);
+
return 0;
}
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 604185c00a1a..3c3f6e3fd1ec 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -835,10 +835,6 @@ static void tegra_csi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#if defined(CONFIG_ARCH_TEGRA_210_SOC)
-extern const struct tegra_csi_soc tegra210_csi_soc;
-#endif
-
static const struct of_device_id tegra_csi_of_id_table[] = {
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 3e6e5ee1bb1e..609c5952e050 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -130,6 +130,10 @@ struct tegra_csi_soc {
unsigned int tpg_frmrate_table_size;
};
+#if defined(CONFIG_ARCH_TEGRA_210_SOC)
+extern const struct tegra_csi_soc tegra210_csi_soc;
+#endif
+
/**
* struct tegra_csi - NVIDIA Tegra CSI device structure
*
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index c9276ff76157..14b327afe045 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -438,7 +438,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
.target = V4L2_SEL_TGT_CROP_BOUNDS,
};
struct v4l2_rect *try_crop;
- int ret;
+ int ret = 0;
subdev = tegra_channel_get_remote_source_subdev(chan);
if (!subdev)
@@ -482,8 +482,10 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
} else {
ret = v4l2_subdev_call(subdev, pad, get_selection,
NULL, &sdsel);
- if (ret)
- return -EINVAL;
+ if (ret) {
+ ret = -EINVAL;
+ goto out_free;
+ }
try_crop->width = sdsel.r.width;
try_crop->height = sdsel.r.height;
@@ -495,14 +497,15 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt);
if (ret < 0)
- return ret;
+ goto out_free;
v4l2_fill_pix_format(pix, &fmt.format);
chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp);
+out_free:
__v4l2_subdev_state_free(sd_state);
- return 0;
+ return ret;
}
static int tegra_channel_try_format(struct file *file, void *fh,
diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c
index 5ec717f3afd5..80cd3b113125 100644
--- a/drivers/staging/media/tegra-video/vip.c
+++ b/drivers/staging/media/tegra-video/vip.c
@@ -263,10 +263,6 @@ static void tegra_vip_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
-extern const struct tegra_vip_soc tegra20_vip_soc;
-#endif
-
static const struct of_device_id tegra_vip_of_id_table[] = {
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
{ .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc },
diff --git a/drivers/staging/media/tegra-video/vip.h b/drivers/staging/media/tegra-video/vip.h
index 32ceaaccbba2..fdded00447e4 100644
--- a/drivers/staging/media/tegra-video/vip.h
+++ b/drivers/staging/media/tegra-video/vip.h
@@ -50,6 +50,10 @@ struct tegra_vip_soc {
const struct tegra_vip_ops *ops;
};
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern const struct tegra_vip_soc tegra20_vip_soc;
+#endif
+
/**
* struct tegra_vip - NVIDIA Tegra VIP device structure
*