summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig18
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/allegro-dvt/Makefile6
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c3773
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.c552
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.h295
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.c679
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.h208
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.c824
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h350
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.c310
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.h61
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c2
-rw-r--r--drivers/media/platform/aspeed-video.c6
-rw-r--r--drivers/media/platform/atmel/atmel-isc.h1
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c46
-rw-r--r--drivers/media/platform/atmel/atmel-sama5d2-isc.c44
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c17
-rw-r--r--drivers/media/platform/davinci/vpbe.c2
-rw-r--r--drivers/media/platform/davinci/vpif.c3
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c2
-rw-r--r--drivers/media/platform/davinci/vpif_display.c86
-rw-r--r--drivers/media/platform/davinci/vpif_display.h1
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c25
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h2
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c14
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c12
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h1
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c11
-rw-r--r--drivers/media/platform/meson/ge2d/ge2d.c1
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c3
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c6
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c10
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c4
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c12
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c3
-rw-r--r--drivers/media/platform/omap3isp/isp.c74
-rw-r--r--drivers/media/platform/pxa_camera.c86
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss.c11
-rw-r--r--drivers/media/platform/qcom/venus/Makefile4
-rw-r--r--drivers/media/platform/qcom/venus/core.c47
-rw-r--r--drivers/media/platform/qcom/venus/core.h78
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c3
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c154
-rw-r--r--drivers/media/platform/qcom/venus/helpers.h4
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c18
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c12
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h22
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c59
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.h7
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs.h38
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c1317
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.c65
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.h67
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v4.c319
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v6.c326
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c80
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c48
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c129
-rw-r--r--drivers/media/platform/qcom/venus/venc.c202
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c138
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c5
-rw-r--r--drivers/media/platform/rcar-vin/rcar-csi2.c2
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c5
-rw-r--r--drivers/media/platform/rcar-vin/rcar-v4l2.c42
-rw-r--r--drivers/media/platform/rcar_drif.c2
-rw-r--r--drivers/media/platform/rcar_fdp1.c4
-rw-r--r--drivers/media/platform/rcar_jpu.c6
-rw-r--r--drivers/media/platform/renesas-ceu.c56
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c15
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c34
-rw-r--r--drivers/media/platform/sh_vou.c2
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c1
-rw-r--r--drivers/media/platform/sti/hva/hva-hw.c1
-rw-r--r--drivers/media/platform/stm32/stm32-dcmi.c87
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c9
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h1
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c4
-rw-r--r--drivers/media/platform/ti-vpe/cal-camerarx.c373
-rw-r--r--drivers/media/platform/ti-vpe/cal-video.c394
-rw-r--r--drivers/media/platform/ti-vpe/cal.c399
-rw-r--r--drivers/media/platform/ti-vpe/cal.h105
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c2
-rw-r--r--drivers/media/platform/video-mux.c14
-rw-r--r--drivers/media/platform/vsp1/vsp1.h20
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c4
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c10
88 files changed, 11068 insertions, 1231 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 35a18d388f3f..fd1831e97b22 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -199,6 +199,21 @@ menuconfig V4L_MEM2MEM_DRIVERS
if V4L_MEM2MEM_DRIVERS
+config VIDEO_ALLEGRO_DVT
+ tristate "Allegro DVT Video IP Core"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ select V4L2_MEM2MEM_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ select REGMAP_MMIO
+ help
+ Support for the encoder video IP core by Allegro DVT. This core is
+ found for example on the Xilinx ZynqMP SoC in the EV family and is
+ called VCU in the reference manual.
+
+ To compile this driver as a module, choose M here: the module
+ will be called allegro.
+
config VIDEO_CODA
tristate "Chips&Media Coda multi-standard codec IP"
depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST)
@@ -530,10 +545,9 @@ config VIDEO_QCOM_VENUS
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
- depends on INTERCONNECT || !INTERCONNECT
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM if ARCH_QCOM
- select VIDEOBUF2_DMA_SG
+ select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This is a V4L2 driver for Qualcomm Venus video accelerator
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 1d63aa956bcd..9d4d6370908d 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,6 +3,7 @@
# Makefile for the video capture/playback device drivers.
#
+obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/
obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o
obj-$(CONFIG_VIDEO_CADENCE) += cadence/
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile
new file mode 100644
index 000000000000..66108a303774
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+allegro-objs := allegro-core.o allegro-mail.o
+allegro-objs += nal-rbsp.o nal-h264.o nal-hevc.o
+
+obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c
new file mode 100644
index 000000000000..887b492e4ad1
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-core.c
@@ -0,0 +1,3773 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Allegro DVT video encoder driver
+ */
+
+#include <linux/bits.h>
+#include <linux/firmware.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sizes.h>
+#include <linux/slab.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/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "allegro-mail.h"
+#include "nal-h264.h"
+#include "nal-hevc.h"
+
+/*
+ * Support up to 4k video streams. The hardware actually supports higher
+ * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video
+ * Codec Unit v1.1) Chapter 3.
+ */
+#define ALLEGRO_WIDTH_MIN 128
+#define ALLEGRO_WIDTH_DEFAULT 1920
+#define ALLEGRO_WIDTH_MAX 3840
+#define ALLEGRO_HEIGHT_MIN 64
+#define ALLEGRO_HEIGHT_DEFAULT 1080
+#define ALLEGRO_HEIGHT_MAX 2160
+
+#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 })
+
+#define ALLEGRO_GOP_SIZE_DEFAULT 25
+#define ALLEGRO_GOP_SIZE_MAX 1000
+
+/*
+ * MCU Control Registers
+ *
+ * The Zynq UltraScale+ Devices Register Reference documents the registers
+ * with an offset of 0x9000, which equals the size of the SRAM and one page
+ * gap. The driver handles SRAM and registers separately and, therefore, is
+ * oblivious of the offset.
+ */
+#define AL5_MCU_RESET 0x0000
+#define AL5_MCU_RESET_SOFT BIT(0)
+#define AL5_MCU_RESET_REGS BIT(1)
+#define AL5_MCU_RESET_MODE 0x0004
+#define AL5_MCU_RESET_MODE_SLEEP BIT(0)
+#define AL5_MCU_RESET_MODE_HALT BIT(1)
+#define AL5_MCU_STA 0x0008
+#define AL5_MCU_STA_SLEEP BIT(0)
+#define AL5_MCU_WAKEUP 0x000c
+
+#define AL5_ICACHE_ADDR_OFFSET_MSB 0x0010
+#define AL5_ICACHE_ADDR_OFFSET_LSB 0x0014
+#define AL5_DCACHE_ADDR_OFFSET_MSB 0x0018
+#define AL5_DCACHE_ADDR_OFFSET_LSB 0x001c
+
+#define AL5_MCU_INTERRUPT 0x0100
+#define AL5_ITC_CPU_IRQ_MSK 0x0104
+#define AL5_ITC_CPU_IRQ_CLR 0x0108
+#define AL5_ITC_CPU_IRQ_STA 0x010C
+#define AL5_ITC_CPU_IRQ_STA_TRIGGERED BIT(0)
+
+#define AXI_ADDR_OFFSET_IP 0x0208
+
+/*
+ * The MCU accesses the system memory with a 2G offset compared to CPU
+ * physical addresses.
+ */
+#define MCU_CACHE_OFFSET SZ_2G
+
+/*
+ * The driver needs to reserve some space at the beginning of capture buffers,
+ * because it needs to write SPS/PPS NAL units. The encoder writes the actual
+ * frame data after the offset.
+ */
+#define ENCODER_STREAM_OFFSET SZ_128
+
+#define SIZE_MACROBLOCK 16
+
+/* Encoding options */
+#define LOG2_MAX_FRAME_NUM 4
+#define LOG2_MAX_PIC_ORDER_CNT 10
+#define BETA_OFFSET_DIV_2 -1
+#define TC_OFFSET_DIV_2 -1
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+struct allegro_buffer {
+ void *vaddr;
+ dma_addr_t paddr;
+ size_t size;
+ struct list_head head;
+};
+
+struct allegro_dev;
+struct allegro_channel;
+
+struct allegro_mbox {
+ struct allegro_dev *dev;
+ unsigned int head;
+ unsigned int tail;
+ unsigned int data;
+ size_t size;
+ /* protect mailbox from simultaneous accesses */
+ struct mutex lock;
+};
+
+struct allegro_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device video_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct platform_device *plat_dev;
+
+ /* mutex protecting vb2_queue structure */
+ struct mutex lock;
+
+ struct regmap *regmap;
+ struct regmap *sram;
+
+ const struct fw_info *fw_info;
+ struct allegro_buffer firmware;
+ struct allegro_buffer suballocator;
+
+ struct completion init_complete;
+
+ /* The mailbox interface */
+ struct allegro_mbox *mbox_command;
+ struct allegro_mbox *mbox_status;
+
+ /*
+ * The downstream driver limits the users to 64 users, thus I can use
+ * a bitfield for the user_ids that are in use. See also user_id in
+ * struct allegro_channel.
+ */
+ unsigned long channel_user_ids;
+ struct list_head channels;
+};
+
+static struct regmap_config allegro_regmap_config = {
+ .name = "regmap",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0xfff,
+ .cache_type = REGCACHE_NONE,
+};
+
+static struct regmap_config allegro_sram_config = {
+ .name = "sram",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x7fff,
+ .cache_type = REGCACHE_NONE,
+};
+
+#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh)
+
+struct allegro_channel {
+ struct allegro_dev *dev;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ unsigned int width;
+ unsigned int height;
+ unsigned int stride;
+ struct v4l2_fract framerate;
+
+ enum v4l2_colorspace colorspace;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
+ enum v4l2_xfer_func xfer_func;
+
+ u32 pixelformat;
+ unsigned int sizeimage_raw;
+ unsigned int osequence;
+
+ u32 codec;
+ unsigned int sizeimage_encoded;
+ unsigned int csequence;
+
+ bool frame_rc_enable;
+ unsigned int bitrate;
+ unsigned int bitrate_peak;
+
+ struct allegro_buffer config_blob;
+
+ unsigned int log2_max_frame_num;
+ bool temporal_mvp_enable;
+
+ bool enable_loop_filter_across_tiles;
+ bool enable_loop_filter_across_slices;
+ bool enable_deblocking_filter_override;
+ bool enable_reordering;
+ bool dbf_ovr_en;
+
+ unsigned int num_ref_idx_l0;
+ unsigned int num_ref_idx_l1;
+
+ /* Maximum range for motion estimation */
+ int b_hrz_me_range;
+ int b_vrt_me_range;
+ int p_hrz_me_range;
+ int p_vrt_me_range;
+ /* Size limits of coding unit */
+ int min_cu_size;
+ int max_cu_size;
+ /* Size limits of transform unit */
+ int min_tu_size;
+ int max_tu_size;
+ int max_transfo_depth_intra;
+ int max_transfo_depth_inter;
+
+ struct v4l2_ctrl *mpeg_video_h264_profile;
+ struct v4l2_ctrl *mpeg_video_h264_level;
+ struct v4l2_ctrl *mpeg_video_h264_i_frame_qp;
+ struct v4l2_ctrl *mpeg_video_h264_max_qp;
+ struct v4l2_ctrl *mpeg_video_h264_min_qp;
+ struct v4l2_ctrl *mpeg_video_h264_p_frame_qp;
+ struct v4l2_ctrl *mpeg_video_h264_b_frame_qp;
+
+ struct v4l2_ctrl *mpeg_video_hevc_profile;
+ struct v4l2_ctrl *mpeg_video_hevc_level;
+ struct v4l2_ctrl *mpeg_video_hevc_tier;
+ struct v4l2_ctrl *mpeg_video_hevc_i_frame_qp;
+ struct v4l2_ctrl *mpeg_video_hevc_max_qp;
+ struct v4l2_ctrl *mpeg_video_hevc_min_qp;
+ struct v4l2_ctrl *mpeg_video_hevc_p_frame_qp;
+ struct v4l2_ctrl *mpeg_video_hevc_b_frame_qp;
+
+ struct v4l2_ctrl *mpeg_video_frame_rc_enable;
+ struct { /* video bitrate mode control cluster */
+ struct v4l2_ctrl *mpeg_video_bitrate_mode;
+ struct v4l2_ctrl *mpeg_video_bitrate;
+ struct v4l2_ctrl *mpeg_video_bitrate_peak;
+ };
+ struct v4l2_ctrl *mpeg_video_cpb_size;
+ struct v4l2_ctrl *mpeg_video_gop_size;
+
+ /* user_id is used to identify the channel during CREATE_CHANNEL */
+ /* not sure, what to set here and if this is actually required */
+ int user_id;
+ /* channel_id is set by the mcu and used by all later commands */
+ int mcu_channel_id;
+
+ struct list_head buffers_reference;
+ struct list_head buffers_intermediate;
+
+ struct list_head source_shadow_list;
+ struct list_head stream_shadow_list;
+ /* protect shadow lists of buffers passed to firmware */
+ struct mutex shadow_list_lock;
+
+ struct list_head list;
+ struct completion completion;
+
+ unsigned int error;
+};
+
+static inline int
+allegro_channel_get_i_frame_qp(struct allegro_channel *channel)
+{
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_i_frame_qp);
+ else
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp);
+}
+
+static inline int
+allegro_channel_get_p_frame_qp(struct allegro_channel *channel)
+{
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_p_frame_qp);
+ else
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp);
+}
+
+static inline int
+allegro_channel_get_b_frame_qp(struct allegro_channel *channel)
+{
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_b_frame_qp);
+ else
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp);
+}
+
+static inline int
+allegro_channel_get_min_qp(struct allegro_channel *channel)
+{
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_min_qp);
+ else
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp);
+}
+
+static inline int
+allegro_channel_get_max_qp(struct allegro_channel *channel)
+{
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_max_qp);
+ else
+ return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp);
+}
+
+struct allegro_m2m_buffer {
+ struct v4l2_m2m_buffer buf;
+ struct list_head head;
+};
+
+#define to_allegro_m2m_buffer(__buf) \
+ container_of(__buf, struct allegro_m2m_buffer, buf)
+
+struct fw_info {
+ unsigned int id;
+ unsigned int id_codec;
+ char *version;
+ unsigned int mailbox_cmd;
+ unsigned int mailbox_status;
+ size_t mailbox_size;
+ enum mcu_msg_version mailbox_version;
+ size_t suballocator_size;
+};
+
+static const struct fw_info supported_firmware[] = {
+ {
+ .id = 18296,
+ .id_codec = 96272,
+ .version = "v2018.2",
+ .mailbox_cmd = 0x7800,
+ .mailbox_status = 0x7c00,
+ .mailbox_size = 0x400 - 0x8,
+ .mailbox_version = MCU_MSG_VERSION_2018_2,
+ .suballocator_size = SZ_16M,
+ }, {
+ .id = 14680,
+ .id_codec = 126572,
+ .version = "v2019.2",
+ .mailbox_cmd = 0x7000,
+ .mailbox_status = 0x7800,
+ .mailbox_size = 0x800 - 0x8,
+ .mailbox_version = MCU_MSG_VERSION_2019_2,
+ .suballocator_size = SZ_32M,
+ },
+};
+
+static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys)
+{
+ if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET))
+ v4l2_warn(&dev->v4l2_dev,
+ "address %pad is outside mcu window\n", &phys);
+
+ return lower_32_bits(phys) | MCU_CACHE_OFFSET;
+}
+
+static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size)
+{
+ return lower_32_bits(size);
+}
+
+static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys)
+{
+ if (upper_32_bits(phys))
+ v4l2_warn(&dev->v4l2_dev,
+ "address %pad cannot be used by codec\n", &phys);
+
+ return lower_32_bits(phys);
+}
+
+static inline u64 ptr_to_u64(const void *ptr)
+{
+ return (uintptr_t)ptr;
+}
+
+/* Helper functions for channel and user operations */
+
+static unsigned long allegro_next_user_id(struct allegro_dev *dev)
+{
+ if (dev->channel_user_ids == ~0UL)
+ return -EBUSY;
+
+ return ffz(dev->channel_user_ids);
+}
+
+static struct allegro_channel *
+allegro_find_channel_by_user_id(struct allegro_dev *dev,
+ unsigned int user_id)
+{
+ struct allegro_channel *channel;
+
+ list_for_each_entry(channel, &dev->channels, list) {
+ if (channel->user_id == user_id)
+ return channel;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static struct allegro_channel *
+allegro_find_channel_by_channel_id(struct allegro_dev *dev,
+ unsigned int channel_id)
+{
+ struct allegro_channel *channel;
+
+ list_for_each_entry(channel, &dev->channels, list) {
+ if (channel->mcu_channel_id == channel_id)
+ return channel;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static inline bool channel_exists(struct allegro_channel *channel)
+{
+ return channel->mcu_channel_id != -1;
+}
+
+#define AL_ERROR 0x80
+#define AL_ERR_INIT_FAILED 0x81
+#define AL_ERR_NO_FRAME_DECODED 0x82
+#define AL_ERR_RESOLUTION_CHANGE 0x85
+#define AL_ERR_NO_MEMORY 0x87
+#define AL_ERR_STREAM_OVERFLOW 0x88
+#define AL_ERR_TOO_MANY_SLICES 0x89
+#define AL_ERR_BUF_NOT_READY 0x8c
+#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d
+#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e
+#define AL_ERR_NOT_ENOUGH_CORES 0x8f
+#define AL_ERR_REQUEST_MALFORMED 0x90
+#define AL_ERR_CMD_NOT_ALLOWED 0x91
+#define AL_ERR_INVALID_CMD_VALUE 0x92
+
+static inline const char *allegro_err_to_string(unsigned int err)
+{
+ switch (err) {
+ case AL_ERR_INIT_FAILED:
+ return "initialization failed";
+ case AL_ERR_NO_FRAME_DECODED:
+ return "no frame decoded";
+ case AL_ERR_RESOLUTION_CHANGE:
+ return "resolution change";
+ case AL_ERR_NO_MEMORY:
+ return "out of memory";
+ case AL_ERR_STREAM_OVERFLOW:
+ return "stream buffer overflow";
+ case AL_ERR_TOO_MANY_SLICES:
+ return "too many slices";
+ case AL_ERR_BUF_NOT_READY:
+ return "buffer not ready";
+ case AL_ERR_NO_CHANNEL_AVAILABLE:
+ return "no channel available";
+ case AL_ERR_RESOURCE_UNAVAILABLE:
+ return "resource unavailable";
+ case AL_ERR_NOT_ENOUGH_CORES:
+ return "not enough cores";
+ case AL_ERR_REQUEST_MALFORMED:
+ return "request malformed";
+ case AL_ERR_CMD_NOT_ALLOWED:
+ return "command not allowed";
+ case AL_ERR_INVALID_CMD_VALUE:
+ return "invalid command value";
+ case AL_ERROR:
+ default:
+ return "unknown error";
+ }
+}
+
+static unsigned int estimate_stream_size(unsigned int width,
+ unsigned int height)
+{
+ unsigned int offset = ENCODER_STREAM_OFFSET;
+ unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) *
+ DIV_ROUND_UP(height, SIZE_MACROBLOCK);
+ unsigned int pcm_size = SZ_256;
+ unsigned int partition_table = SZ_256;
+
+ return round_up(offset + num_blocks * pcm_size + partition_table, 32);
+}
+
+static enum v4l2_mpeg_video_h264_level
+select_minimum_h264_level(unsigned int width, unsigned int height)
+{
+ unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK);
+ unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK);
+ unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb;
+ enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
+
+ /*
+ * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and
+ * also specify limits regarding bit rate and CBP size. Only approximate
+ * the levels using the frame size.
+ *
+ * Level 5.1 allows up to 4k video resolution.
+ */
+ if (frame_size_in_mb <= 99)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
+ else if (frame_size_in_mb <= 396)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
+ else if (frame_size_in_mb <= 792)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
+ else if (frame_size_in_mb <= 1620)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
+ else if (frame_size_in_mb <= 3600)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
+ else if (frame_size_in_mb <= 5120)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
+ else if (frame_size_in_mb <= 8192)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
+ else if (frame_size_in_mb <= 8704)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
+ else if (frame_size_in_mb <= 22080)
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
+ else
+ level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
+
+ return level;
+}
+
+static unsigned int h264_maximum_bitrate(enum v4l2_mpeg_video_h264_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ return 64000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+ return 128000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ return 192000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ return 384000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ return 768000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ return 2000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ return 4000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ return 4000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ return 10000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ return 14000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ return 20000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ return 20000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ return 50000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ return 50000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ return 135000000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ default:
+ return 240000000;
+ }
+}
+
+static unsigned int h264_maximum_cpb_size(enum v4l2_mpeg_video_h264_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ return 175;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+ return 350;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ return 500;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ return 1000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ return 2000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ return 2000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ return 4000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ return 4000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ return 10000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ return 14000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ return 20000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ return 25000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ return 62500;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ return 62500;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ return 135000;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ default:
+ return 240000;
+ }
+}
+
+static enum v4l2_mpeg_video_hevc_level
+select_minimum_hevc_level(unsigned int width, unsigned int height)
+{
+ unsigned int luma_picture_size = width * height;
+ enum v4l2_mpeg_video_hevc_level level;
+
+ if (luma_picture_size <= 36864)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_1;
+ else if (luma_picture_size <= 122880)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2;
+ else if (luma_picture_size <= 245760)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1;
+ else if (luma_picture_size <= 552960)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3;
+ else if (luma_picture_size <= 983040)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1;
+ else if (luma_picture_size <= 2228224)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_4;
+ else if (luma_picture_size <= 8912896)
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_5;
+ else
+ level = V4L2_MPEG_VIDEO_HEVC_LEVEL_6;
+
+ return level;
+}
+
+static unsigned int hevc_maximum_bitrate(enum v4l2_mpeg_video_hevc_level level)
+{
+ /*
+ * See Rec. ITU-T H.265 v5 (02/2018), A.4.2 Profile-specific level
+ * limits for the video profiles.
+ */
+ switch (level) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ return 128;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ return 1500;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ return 3000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ return 6000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ return 10000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ return 12000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ return 20000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ return 25000;
+ default:
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ return 40000;
+ }
+}
+
+static unsigned int hevc_maximum_cpb_size(enum v4l2_mpeg_video_hevc_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ return 350;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ return 1500;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ return 3000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ return 6000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ return 10000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ return 12000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ return 20000;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ return 25000;
+ default:
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ return 40000;
+ }
+}
+
+static const struct fw_info *
+allegro_get_firmware_info(struct allegro_dev *dev,
+ const struct firmware *fw,
+ const struct firmware *fw_codec)
+{
+ int i;
+ unsigned int id = fw->size;
+ unsigned int id_codec = fw_codec->size;
+
+ for (i = 0; i < ARRAY_SIZE(supported_firmware); i++)
+ if (supported_firmware[i].id == id &&
+ supported_firmware[i].id_codec == id_codec)
+ return &supported_firmware[i];
+
+ return NULL;
+}
+
+/*
+ * Buffers that are used internally by the MCU.
+ */
+
+static int allegro_alloc_buffer(struct allegro_dev *dev,
+ struct allegro_buffer *buffer, size_t size)
+{
+ buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size,
+ &buffer->paddr, GFP_KERNEL);
+ if (!buffer->vaddr)
+ return -ENOMEM;
+ buffer->size = size;
+
+ return 0;
+}
+
+static void allegro_free_buffer(struct allegro_dev *dev,
+ struct allegro_buffer *buffer)
+{
+ if (buffer->vaddr) {
+ dma_free_coherent(&dev->plat_dev->dev, buffer->size,
+ buffer->vaddr, buffer->paddr);
+ buffer->vaddr = NULL;
+ buffer->size = 0;
+ }
+}
+
+/*
+ * Mailbox interface to send messages to the MCU.
+ */
+
+static void allegro_mcu_interrupt(struct allegro_dev *dev);
+static void allegro_handle_message(struct allegro_dev *dev,
+ union mcu_msg_response *msg);
+
+static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev,
+ unsigned int base, size_t size)
+{
+ struct allegro_mbox *mbox;
+
+ mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return ERR_PTR(-ENOMEM);
+
+ mbox->dev = dev;
+
+ mbox->head = base;
+ mbox->tail = base + 0x4;
+ mbox->data = base + 0x8;
+ mbox->size = size;
+ mutex_init(&mbox->lock);
+
+ regmap_write(dev->sram, mbox->head, 0);
+ regmap_write(dev->sram, mbox->tail, 0);
+
+ return mbox;
+}
+
+static int allegro_mbox_write(struct allegro_mbox *mbox,
+ const u32 *src, size_t size)
+{
+ struct regmap *sram = mbox->dev->sram;
+ unsigned int tail;
+ size_t size_no_wrap;
+ int err = 0;
+ int stride = regmap_get_reg_stride(sram);
+
+ if (!src)
+ return -EINVAL;
+
+ if (size > mbox->size)
+ return -EINVAL;
+
+ mutex_lock(&mbox->lock);
+ regmap_read(sram, mbox->tail, &tail);
+ if (tail > mbox->size) {
+ err = -EIO;
+ goto out;
+ }
+ size_no_wrap = min(size, mbox->size - (size_t)tail);
+ regmap_bulk_write(sram, mbox->data + tail,
+ src, size_no_wrap / stride);
+ regmap_bulk_write(sram, mbox->data,
+ src + (size_no_wrap / sizeof(*src)),
+ (size - size_no_wrap) / stride);
+ regmap_write(sram, mbox->tail, (tail + size) % mbox->size);
+
+out:
+ mutex_unlock(&mbox->lock);
+
+ return err;
+}
+
+static ssize_t allegro_mbox_read(struct allegro_mbox *mbox,
+ u32 *dst, size_t nbyte)
+{
+ struct {
+ u16 length;
+ u16 type;
+ } __attribute__ ((__packed__)) *header;
+ struct regmap *sram = mbox->dev->sram;
+ unsigned int head;
+ ssize_t size;
+ size_t body_no_wrap;
+ int stride = regmap_get_reg_stride(sram);
+
+ regmap_read(sram, mbox->head, &head);
+ if (head > mbox->size)
+ return -EIO;
+
+ /* Assume that the header does not wrap. */
+ regmap_bulk_read(sram, mbox->data + head,
+ dst, sizeof(*header) / stride);
+ header = (void *)dst;
+ size = header->length + sizeof(*header);
+ if (size > mbox->size || size & 0x3)
+ return -EIO;
+ if (size > nbyte)
+ return -EINVAL;
+
+ /*
+ * The message might wrap within the mailbox. If the message does not
+ * wrap, the first read will read the entire message, otherwise the
+ * first read will read message until the end of the mailbox and the
+ * second read will read the remaining bytes from the beginning of the
+ * mailbox.
+ *
+ * Skip the header, as was already read to get the size of the body.
+ */
+ body_no_wrap = min((size_t)header->length,
+ (size_t)(mbox->size - (head + sizeof(*header))));
+ regmap_bulk_read(sram, mbox->data + head + sizeof(*header),
+ dst + (sizeof(*header) / sizeof(*dst)),
+ body_no_wrap / stride);
+ regmap_bulk_read(sram, mbox->data,
+ dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst),
+ (header->length - body_no_wrap) / stride);
+
+ regmap_write(sram, mbox->head, (head + size) % mbox->size);
+
+ return size;
+}
+
+/**
+ * allegro_mbox_send() - Send a message via the mailbox
+ * @mbox: the mailbox which is used to send the message
+ * @msg: the message to send
+ */
+static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg)
+{
+ struct allegro_dev *dev = mbox->dev;
+ ssize_t size;
+ int err;
+ u32 *tmp;
+
+ tmp = kzalloc(mbox->size, GFP_KERNEL);
+ if (!tmp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ size = allegro_encode_mail(tmp, msg);
+
+ err = allegro_mbox_write(mbox, tmp, size);
+ kfree(tmp);
+ if (err)
+ goto out;
+
+ allegro_mcu_interrupt(dev);
+
+out:
+ return err;
+}
+
+/**
+ * allegro_mbox_notify() - Notify the mailbox about a new message
+ * @mbox: The allegro_mbox to notify
+ */
+static void allegro_mbox_notify(struct allegro_mbox *mbox)
+{
+ struct allegro_dev *dev = mbox->dev;
+ union mcu_msg_response *msg;
+ ssize_t size;
+ u32 *tmp;
+ int err;
+
+ msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return;
+
+ msg->header.version = dev->fw_info->mailbox_version;
+
+ tmp = kmalloc(mbox->size, GFP_KERNEL);
+ if (!tmp)
+ goto out;
+
+ size = allegro_mbox_read(mbox, tmp, mbox->size);
+ if (size < 0)
+ goto out;
+
+ err = allegro_decode_mail(msg, tmp);
+ if (err)
+ goto out;
+
+ allegro_handle_message(dev, msg);
+
+out:
+ kfree(tmp);
+ kfree(msg);
+}
+
+static void allegro_mcu_send_init(struct allegro_dev *dev,
+ dma_addr_t suballoc_dma, size_t suballoc_size)
+{
+ struct mcu_msg_init_request msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.header.type = MCU_MSG_TYPE_INIT;
+ msg.header.version = dev->fw_info->mailbox_version;
+
+ msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma);
+ msg.suballoc_size = to_mcu_size(dev, suballoc_size);
+
+ /* disable L2 cache */
+ msg.l2_cache[0] = -1;
+ msg.l2_cache[1] = -1;
+ msg.l2_cache[2] = -1;
+
+ allegro_mbox_send(dev->mbox_command, &msg);
+}
+
+static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */
+ return 0x100 | 0x88;
+ default:
+ return -EINVAL;
+ }
+}
+
+static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace)
+{
+ switch (colorspace) {
+ case V4L2_COLORSPACE_REC709:
+ return 2;
+ case V4L2_COLORSPACE_SMPTE170M:
+ return 3;
+ case V4L2_COLORSPACE_SMPTE240M:
+ return 4;
+ case V4L2_COLORSPACE_SRGB:
+ return 7;
+ default:
+ /* UNKNOWN */
+ return 0;
+ }
+}
+
+static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile)
+{
+ switch (profile) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ default:
+ return 66;
+ }
+}
+
+static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ return 10;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ return 11;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ return 12;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ return 13;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ return 20;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ return 21;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ return 22;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ return 30;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ return 31;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ return 32;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ return 40;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ return 41;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ return 42;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ return 50;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ default:
+ return 51;
+ }
+}
+
+static u8 hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile)
+{
+ switch (profile) {
+ default:
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
+ return 1;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
+ return 2;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
+ return 3;
+ }
+}
+
+static u16 hevc_level_to_mcu_level(enum v4l2_mpeg_video_hevc_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ return 10;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ return 20;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ return 21;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ return 30;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ return 31;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ return 40;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ return 41;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ return 50;
+ default:
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ return 51;
+ }
+}
+
+static u8 hevc_tier_to_mcu_tier(enum v4l2_mpeg_video_hevc_tier tier)
+{
+ switch (tier) {
+ default:
+ case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN:
+ return 0;
+ case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH:
+ return 1;
+ }
+}
+
+static u32
+v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode)
+{
+ switch (mode) {
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
+ return 2;
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
+ default:
+ return 1;
+ }
+}
+
+static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate)
+{
+ unsigned int cpb_size_kbit;
+ unsigned int bitrate_kbps;
+
+ /*
+ * The mcu expects the CPB size in units of a 90 kHz clock, but the
+ * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores
+ * the CPB size in kilobytes.
+ */
+ cpb_size_kbit = cpb_size * BITS_PER_BYTE;
+ bitrate_kbps = bitrate / 1000;
+
+ return (cpb_size_kbit * 90000) / bitrate_kbps;
+}
+
+static s16 get_qp_delta(int minuend, int subtrahend)
+{
+ if (minuend == subtrahend)
+ return -1;
+ else
+ return minuend - subtrahend;
+}
+
+static u32 allegro_channel_get_entropy_mode(struct allegro_channel *channel)
+{
+#define ALLEGRO_ENTROPY_MODE_CAVLC 0
+#define ALLEGRO_ENTROPY_MODE_CABAC 1
+
+ /* HEVC always uses CABAC, but this has to be explicitly set */
+ if (channel->codec == V4L2_PIX_FMT_HEVC)
+ return ALLEGRO_ENTROPY_MODE_CABAC;
+
+ return ALLEGRO_ENTROPY_MODE_CAVLC;
+}
+
+static int fill_create_channel_param(struct allegro_channel *channel,
+ struct create_channel_param *param)
+{
+ int i_frame_qp = allegro_channel_get_i_frame_qp(channel);
+ int p_frame_qp = allegro_channel_get_p_frame_qp(channel);
+ int b_frame_qp = allegro_channel_get_b_frame_qp(channel);
+ int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode);
+ unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size);
+
+ param->width = channel->width;
+ param->height = channel->height;
+ param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat);
+ param->colorspace =
+ v4l2_colorspace_to_mcu_colorspace(channel->colorspace);
+ param->src_mode = 0x0;
+
+ param->codec = channel->codec;
+ if (channel->codec == V4L2_PIX_FMT_H264) {
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_level level;
+
+ profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile);
+ level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level);
+
+ param->profile = v4l2_profile_to_mcu_profile(profile);
+ param->constraint_set_flags = BIT(1);
+ param->level = v4l2_level_to_mcu_level(level);
+ } else {
+ enum v4l2_mpeg_video_hevc_profile profile;
+ enum v4l2_mpeg_video_hevc_level level;
+ enum v4l2_mpeg_video_hevc_tier tier;
+
+ profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
+ level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
+ tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);
+
+ param->profile = hevc_profile_to_mcu_profile(profile);
+ param->level = hevc_level_to_mcu_level(level);
+ param->tier = hevc_tier_to_mcu_tier(tier);
+ }
+
+ param->log2_max_poc = LOG2_MAX_PIC_ORDER_CNT;
+ param->log2_max_frame_num = channel->log2_max_frame_num;
+ param->temporal_mvp_enable = channel->temporal_mvp_enable;
+
+ param->dbf_ovr_en = channel->dbf_ovr_en;
+ param->override_lf = channel->enable_deblocking_filter_override;
+ param->enable_reordering = channel->enable_reordering;
+ param->entropy_mode = allegro_channel_get_entropy_mode(channel);
+ param->rdo_cost_mode = 1;
+ param->custom_lda = 1;
+ param->lf = 1;
+ param->lf_x_tile = channel->enable_loop_filter_across_tiles;
+ param->lf_x_slice = channel->enable_loop_filter_across_slices;
+
+ param->src_bit_depth = 8;
+
+ param->beta_offset = BETA_OFFSET_DIV_2;
+ param->tc_offset = TC_OFFSET_DIV_2;
+ param->num_slices = 1;
+ param->me_range[0] = channel->b_hrz_me_range;
+ param->me_range[1] = channel->b_vrt_me_range;
+ param->me_range[2] = channel->p_hrz_me_range;
+ param->me_range[3] = channel->p_vrt_me_range;
+ param->max_cu_size = channel->max_cu_size;
+ param->min_cu_size = channel->min_cu_size;
+ param->max_tu_size = channel->max_tu_size;
+ param->min_tu_size = channel->min_tu_size;
+ param->max_transfo_depth_intra = channel->max_transfo_depth_intra;
+ param->max_transfo_depth_inter = channel->max_transfo_depth_inter;
+
+ param->prefetch_auto = 0;
+ param->prefetch_mem_offset = 0;
+ param->prefetch_mem_size = 0;
+
+ param->rate_control_mode = channel->frame_rc_enable ?
+ v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0;
+
+ param->cpb_size = v4l2_cpb_size_to_mcu(cpb_size, channel->bitrate_peak);
+ /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */
+ param->initial_rem_delay = param->cpb_size;
+ param->framerate = DIV_ROUND_UP(channel->framerate.numerator,
+ channel->framerate.denominator);
+ param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000;
+ param->target_bitrate = channel->bitrate;
+ param->max_bitrate = channel->bitrate_peak;
+ param->initial_qp = i_frame_qp;
+ param->min_qp = allegro_channel_get_min_qp(channel);
+ param->max_qp = allegro_channel_get_max_qp(channel);
+ param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp);
+ param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp);
+ param->golden_ref = 0;
+ param->golden_delta = 2;
+ param->golden_ref_frequency = 10;
+ param->rate_control_option = 0x00000000;
+
+ param->num_pixel = channel->width + channel->height;
+ param->max_psnr = 4200;
+ param->max_pixel_value = 255;
+
+ param->gop_ctrl_mode = 0x00000002;
+ param->freq_idr = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size);
+ param->freq_lt = 0;
+ param->gdr_mode = 0x00000000;
+ param->gop_length = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size);
+ param->subframe_latency = 0x00000000;
+
+ param->lda_factors[0] = 51;
+ param->lda_factors[1] = 90;
+ param->lda_factors[2] = 151;
+ param->lda_factors[3] = 151;
+ param->lda_factors[4] = 151;
+ param->lda_factors[5] = 151;
+
+ param->max_num_merge_cand = 5;
+
+ return 0;
+}
+
+static int allegro_mcu_send_create_channel(struct allegro_dev *dev,
+ struct allegro_channel *channel)
+{
+ struct mcu_msg_create_channel msg;
+ struct allegro_buffer *blob = &channel->config_blob;
+ struct create_channel_param param;
+ size_t size;
+
+ memset(&param, 0, sizeof(param));
+ fill_create_channel_param(channel, &param);
+ allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param));
+ param.version = dev->fw_info->mailbox_version;
+ size = allegro_encode_config_blob(blob->vaddr, &param);
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL;
+ msg.header.version = dev->fw_info->mailbox_version;
+
+ msg.user_id = channel->user_id;
+
+ msg.blob = blob->vaddr;
+ msg.blob_size = size;
+ msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr);
+
+ allegro_mbox_send(dev->mbox_command, &msg);
+
+ return 0;
+}
+
+static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev,
+ struct allegro_channel *channel)
+{
+ struct mcu_msg_destroy_channel msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL;
+ msg.header.version = dev->fw_info->mailbox_version;
+
+ msg.channel_id = channel->mcu_channel_id;
+
+ allegro_mbox_send(dev->mbox_command, &msg);
+
+ return 0;
+}
+
+static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev,
+ struct allegro_channel *channel,
+ dma_addr_t paddr,
+ unsigned long size,
+ u64 dst_handle)
+{
+ struct mcu_msg_put_stream_buffer msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER;
+ msg.header.version = dev->fw_info->mailbox_version;
+
+ msg.channel_id = channel->mcu_channel_id;
+ msg.dma_addr = to_codec_addr(dev, paddr);
+ msg.mcu_addr = to_mcu_addr(dev, paddr);
+ msg.size = size;
+ msg.offset = ENCODER_STREAM_OFFSET;
+ /* copied to mcu_msg_encode_frame_response */
+ msg.dst_handle = dst_handle;
+
+ allegro_mbox_send(dev->mbox_command, &msg);
+
+ return 0;
+}
+
+static int allegro_mcu_send_encode_frame(struct allegro_dev *dev,
+ struct allegro_channel *channel,
+ dma_addr_t src_y, dma_addr_t src_uv,
+ u64 src_handle)
+{
+ struct mcu_msg_encode_frame msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME;
+ msg.header.version = dev->fw_info->mailbox_version;
+
+ msg.channel_id = channel->mcu_channel_id;
+ msg.encoding_options = AL_OPT_FORCE_LOAD;
+ msg.pps_qp = 26; /* qp are relative to 26 */
+ msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */
+ /* src_handle is copied to mcu_msg_encode_frame_response */
+ msg.src_handle = src_handle;
+ msg.src_y = to_codec_addr(dev, src_y);
+ msg.src_uv = to_codec_addr(dev, src_uv);
+ msg.stride = channel->stride;
+ msg.ep2 = 0x0;
+ msg.ep2_v = to_mcu_addr(dev, msg.ep2);
+
+ allegro_mbox_send(dev->mbox_command, &msg);
+
+ return 0;
+}
+
+static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev,
+ unsigned long timeout_ms)
+{
+ unsigned long tmo;
+
+ tmo = wait_for_completion_timeout(&dev->init_complete,
+ msecs_to_jiffies(timeout_ms));
+ if (tmo == 0)
+ return -ETIMEDOUT;
+
+ reinit_completion(&dev->init_complete);
+ return 0;
+}
+
+static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel,
+ enum mcu_msg_type type)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct mcu_msg_push_buffers_internal *msg;
+ struct mcu_msg_push_buffers_internal_buffer *buffer;
+ unsigned int num_buffers = 0;
+ size_t size;
+ struct allegro_buffer *al_buffer;
+ struct list_head *list;
+ int err;
+
+ switch (type) {
+ case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE:
+ list = &channel->buffers_reference;
+ break;
+ case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE:
+ list = &channel->buffers_intermediate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ list_for_each_entry(al_buffer, list, head)
+ num_buffers++;
+ size = struct_size(msg, buffer, num_buffers);
+
+ msg = kmalloc(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->header.type = type;
+ msg->header.version = dev->fw_info->mailbox_version;
+
+ msg->channel_id = channel->mcu_channel_id;
+ msg->num_buffers = num_buffers;
+
+ buffer = msg->buffer;
+ list_for_each_entry(al_buffer, list, head) {
+ buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr);
+ buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr);
+ buffer->size = to_mcu_size(dev, al_buffer->size);
+ buffer++;
+ }
+
+ err = allegro_mbox_send(dev->mbox_command, msg);
+
+ kfree(msg);
+ return err;
+}
+
+static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel)
+{
+ enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE;
+
+ return allegro_mcu_push_buffer_internal(channel, type);
+}
+
+static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel)
+{
+ enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE;
+
+ return allegro_mcu_push_buffer_internal(channel, type);
+}
+
+static int allocate_buffers_internal(struct allegro_channel *channel,
+ struct list_head *list,
+ size_t n, size_t size)
+{
+ struct allegro_dev *dev = channel->dev;
+ unsigned int i;
+ int err;
+ struct allegro_buffer *buffer, *tmp;
+
+ for (i = 0; i < n; i++) {
+ buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer) {
+ err = -ENOMEM;
+ goto err;
+ }
+ INIT_LIST_HEAD(&buffer->head);
+
+ err = allegro_alloc_buffer(dev, buffer, size);
+ if (err)
+ goto err;
+ list_add(&buffer->head, list);
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry_safe(buffer, tmp, list, head) {
+ list_del(&buffer->head);
+ allegro_free_buffer(dev, buffer);
+ kfree(buffer);
+ }
+ return err;
+}
+
+static void destroy_buffers_internal(struct allegro_channel *channel,
+ struct list_head *list)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct allegro_buffer *buffer, *tmp;
+
+ list_for_each_entry_safe(buffer, tmp, list, head) {
+ list_del(&buffer->head);
+ allegro_free_buffer(dev, buffer);
+ kfree(buffer);
+ }
+}
+
+static void destroy_reference_buffers(struct allegro_channel *channel)
+{
+ return destroy_buffers_internal(channel, &channel->buffers_reference);
+}
+
+static void destroy_intermediate_buffers(struct allegro_channel *channel)
+{
+ return destroy_buffers_internal(channel,
+ &channel->buffers_intermediate);
+}
+
+static int allocate_intermediate_buffers(struct allegro_channel *channel,
+ size_t n, size_t size)
+{
+ return allocate_buffers_internal(channel,
+ &channel->buffers_intermediate,
+ n, size);
+}
+
+static int allocate_reference_buffers(struct allegro_channel *channel,
+ size_t n, size_t size)
+{
+ return allocate_buffers_internal(channel,
+ &channel->buffers_reference,
+ n, PAGE_ALIGN(size));
+}
+
+static ssize_t allegro_h264_write_sps(struct allegro_channel *channel,
+ void *dest, size_t n)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct nal_h264_sps *sps;
+ ssize_t size;
+ unsigned int size_mb = SIZE_MACROBLOCK;
+ /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */
+ unsigned int crop_unit_x = 2;
+ unsigned int crop_unit_y = 2;
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_level level;
+ unsigned int cpb_size;
+ unsigned int cpb_size_scale;
+
+ sps = kzalloc(sizeof(*sps), GFP_KERNEL);
+ if (!sps)
+ return -ENOMEM;
+
+ profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile);
+ level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level);
+
+ sps->profile_idc = nal_h264_profile_from_v4l2(profile);
+ sps->constraint_set0_flag = 0;
+ sps->constraint_set1_flag = 1;
+ sps->constraint_set2_flag = 0;
+ sps->constraint_set3_flag = 0;
+ sps->constraint_set4_flag = 0;
+ sps->constraint_set5_flag = 0;
+ sps->level_idc = nal_h264_level_from_v4l2(level);
+ sps->seq_parameter_set_id = 0;
+ sps->log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4;
+ sps->pic_order_cnt_type = 0;
+ sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4;
+ sps->max_num_ref_frames = 3;
+ sps->gaps_in_frame_num_value_allowed_flag = 0;
+ sps->pic_width_in_mbs_minus1 =
+ DIV_ROUND_UP(channel->width, size_mb) - 1;
+ sps->pic_height_in_map_units_minus1 =
+ DIV_ROUND_UP(channel->height, size_mb) - 1;
+ sps->frame_mbs_only_flag = 1;
+ sps->mb_adaptive_frame_field_flag = 0;
+ sps->direct_8x8_inference_flag = 1;
+ sps->frame_cropping_flag =
+ (channel->width % size_mb) || (channel->height % size_mb);
+ if (sps->frame_cropping_flag) {
+ sps->crop_left = 0;
+ sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x;
+ sps->crop_top = 0;
+ sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y;
+ }
+ sps->vui_parameters_present_flag = 1;
+ sps->vui.aspect_ratio_info_present_flag = 0;
+ sps->vui.overscan_info_present_flag = 0;
+ sps->vui.video_signal_type_present_flag = 1;
+ sps->vui.video_format = 1;
+ sps->vui.video_full_range_flag = 0;
+ sps->vui.colour_description_present_flag = 1;
+ sps->vui.colour_primaries = 5;
+ sps->vui.transfer_characteristics = 5;
+ sps->vui.matrix_coefficients = 5;
+ sps->vui.chroma_loc_info_present_flag = 1;
+ sps->vui.chroma_sample_loc_type_top_field = 0;
+ sps->vui.chroma_sample_loc_type_bottom_field = 0;
+
+ sps->vui.timing_info_present_flag = 1;
+ sps->vui.num_units_in_tick = channel->framerate.denominator;
+ sps->vui.time_scale = 2 * channel->framerate.numerator;
+
+ sps->vui.fixed_frame_rate_flag = 1;
+ sps->vui.nal_hrd_parameters_present_flag = 0;
+ sps->vui.vcl_hrd_parameters_present_flag = 1;
+ sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0;
+ sps->vui.vcl_hrd_parameters.bit_rate_scale = 0;
+ /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */
+ sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] =
+ channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1;
+ /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */
+ cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size);
+ cpb_size_scale = ffs(cpb_size) - 4;
+ sps->vui.vcl_hrd_parameters.cpb_size_scale = cpb_size_scale;
+ sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] =
+ (cpb_size * 1000) / (1 << (4 + cpb_size_scale)) - 1;
+ sps->vui.vcl_hrd_parameters.cbr_flag[0] =
+ !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable);
+ sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31;
+ sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31;
+ sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31;
+ sps->vui.vcl_hrd_parameters.time_offset_length = 0;
+ sps->vui.low_delay_hrd_flag = 0;
+ sps->vui.pic_struct_present_flag = 1;
+ sps->vui.bitstream_restriction_flag = 0;
+
+ size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps);
+
+ kfree(sps);
+
+ return size;
+}
+
+static ssize_t allegro_h264_write_pps(struct allegro_channel *channel,
+ void *dest, size_t n)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct nal_h264_pps *pps;
+ ssize_t size;
+
+ pps = kzalloc(sizeof(*pps), GFP_KERNEL);
+ if (!pps)
+ return -ENOMEM;
+
+ pps->pic_parameter_set_id = 0;
+ pps->seq_parameter_set_id = 0;
+ pps->entropy_coding_mode_flag = 0;
+ pps->bottom_field_pic_order_in_frame_present_flag = 0;
+ pps->num_slice_groups_minus1 = 0;
+ pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1;
+ pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1;
+ pps->weighted_pred_flag = 0;
+ pps->weighted_bipred_idc = 0;
+ pps->pic_init_qp_minus26 = 0;
+ pps->pic_init_qs_minus26 = 0;
+ pps->chroma_qp_index_offset = 0;
+ pps->deblocking_filter_control_present_flag = 1;
+ pps->constrained_intra_pred_flag = 0;
+ pps->redundant_pic_cnt_present_flag = 0;
+ pps->transform_8x8_mode_flag = 0;
+ pps->pic_scaling_matrix_present_flag = 0;
+ pps->second_chroma_qp_index_offset = 0;
+
+ size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps);
+
+ kfree(pps);
+
+ return size;
+}
+
+static void allegro_channel_eos_event(struct allegro_channel *channel)
+{
+ const struct v4l2_event eos_event = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ v4l2_event_queue_fh(&channel->fh, &eos_event);
+}
+
+static ssize_t allegro_hevc_write_vps(struct allegro_channel *channel,
+ void *dest, size_t n)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct nal_hevc_vps *vps;
+ struct nal_hevc_profile_tier_level *ptl;
+ ssize_t size;
+ unsigned int num_ref_frames = channel->num_ref_idx_l0;
+ s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
+ s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
+ s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);
+
+ vps = kzalloc(sizeof(*vps), GFP_KERNEL);
+ if (!vps)
+ return -ENOMEM;
+
+ vps->base_layer_internal_flag = 1;
+ vps->base_layer_available_flag = 1;
+ vps->temporal_id_nesting_flag = 1;
+
+ ptl = &vps->profile_tier_level;
+ ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile);
+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
+ ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier);
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+ ptl->general_level_idc = nal_hevc_level_from_v4l2(level);
+
+ vps->sub_layer_ordering_info_present_flag = 0;
+ vps->max_dec_pic_buffering_minus1[0] = num_ref_frames;
+ vps->max_num_reorder_pics[0] = num_ref_frames;
+
+ size = nal_hevc_write_vps(&dev->plat_dev->dev, dest, n, vps);
+
+ kfree(vps);
+
+ return size;
+}
+
+static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel,
+ void *dest, size_t n)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct nal_hevc_sps *sps;
+ struct nal_hevc_profile_tier_level *ptl;
+ ssize_t size;
+ unsigned int num_ref_frames = channel->num_ref_idx_l0;
+ s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
+ s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
+ s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);
+
+ sps = kzalloc(sizeof(*sps), GFP_KERNEL);
+ if (!sps)
+ return -ENOMEM;
+
+ sps->temporal_id_nesting_flag = 1;
+
+ ptl = &sps->profile_tier_level;
+ ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile);
+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
+ ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier);
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+ ptl->general_level_idc = nal_hevc_level_from_v4l2(level);
+
+ sps->seq_parameter_set_id = 0;
+ sps->chroma_format_idc = 1; /* Only 4:2:0 sampling supported */
+ sps->pic_width_in_luma_samples = round_up(channel->width, 8);
+ sps->pic_height_in_luma_samples = round_up(channel->height, 8);
+ sps->conf_win_right_offset =
+ sps->pic_width_in_luma_samples - channel->width;
+ sps->conf_win_bottom_offset =
+ sps->pic_height_in_luma_samples - channel->height;
+ sps->conformance_window_flag =
+ sps->conf_win_right_offset || sps->conf_win_bottom_offset;
+
+ sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4;
+
+ sps->sub_layer_ordering_info_present_flag = 1;
+ sps->max_dec_pic_buffering_minus1[0] = num_ref_frames;
+ sps->max_num_reorder_pics[0] = num_ref_frames;
+
+ sps->log2_min_luma_coding_block_size_minus3 =
+ channel->min_cu_size - 3;
+ sps->log2_diff_max_min_luma_coding_block_size =
+ channel->max_cu_size - channel->min_cu_size;
+ sps->log2_min_luma_transform_block_size_minus2 =
+ channel->min_tu_size - 2;
+ sps->log2_diff_max_min_luma_transform_block_size =
+ channel->max_tu_size - channel->min_tu_size;
+ sps->max_transform_hierarchy_depth_intra =
+ channel->max_transfo_depth_intra;
+ sps->max_transform_hierarchy_depth_inter =
+ channel->max_transfo_depth_inter;
+
+ sps->sps_temporal_mvp_enabled_flag = channel->temporal_mvp_enable;
+ sps->strong_intra_smoothing_enabled_flag = channel->max_cu_size > 4;
+
+ size = nal_hevc_write_sps(&dev->plat_dev->dev, dest, n, sps);
+
+ kfree(sps);
+
+ return size;
+}
+
+static ssize_t allegro_hevc_write_pps(struct allegro_channel *channel,
+ struct mcu_msg_encode_frame_response *msg,
+ void *dest, size_t n)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct nal_hevc_pps *pps;
+ ssize_t size;
+ int i;
+
+ pps = kzalloc(sizeof(*pps), GFP_KERNEL);
+ if (!pps)
+ return -ENOMEM;
+
+ pps->pps_pic_parameter_set_id = 0;
+ pps->pps_seq_parameter_set_id = 0;
+
+ if (msg->num_column > 1 || msg->num_row > 1) {
+ pps->tiles_enabled_flag = 1;
+ pps->num_tile_columns_minus1 = msg->num_column - 1;
+ pps->num_tile_rows_minus1 = msg->num_row - 1;
+
+ for (i = 0; i < msg->num_column; i++)
+ pps->column_width_minus1[i] = msg->tile_width[i] - 1;
+
+ for (i = 0; i < msg->num_row; i++)
+ pps->row_height_minus1[i] = msg->tile_height[i] - 1;
+ }
+
+ pps->loop_filter_across_tiles_enabled_flag =
+ channel->enable_loop_filter_across_tiles;
+ pps->pps_loop_filter_across_slices_enabled_flag =
+ channel->enable_loop_filter_across_slices;
+ pps->deblocking_filter_control_present_flag = 1;
+ pps->deblocking_filter_override_enabled_flag =
+ channel->enable_deblocking_filter_override;
+ pps->pps_beta_offset_div2 = BETA_OFFSET_DIV_2;
+ pps->pps_tc_offset_div2 = TC_OFFSET_DIV_2;
+
+ pps->lists_modification_present_flag = channel->enable_reordering;
+
+ size = nal_hevc_write_pps(&dev->plat_dev->dev, dest, n, pps);
+
+ kfree(pps);
+
+ return size;
+}
+
+static u64 allegro_put_buffer(struct allegro_channel *channel,
+ struct list_head *list,
+ struct vb2_v4l2_buffer *buffer)
+{
+ struct v4l2_m2m_buffer *b = container_of(buffer,
+ struct v4l2_m2m_buffer, vb);
+ struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b);
+
+ mutex_lock(&channel->shadow_list_lock);
+ list_add_tail(&shadow->head, list);
+ mutex_unlock(&channel->shadow_list_lock);
+
+ return ptr_to_u64(buffer);
+}
+
+static struct vb2_v4l2_buffer *
+allegro_get_buffer(struct allegro_channel *channel,
+ struct list_head *list, u64 handle)
+{
+ struct allegro_m2m_buffer *shadow, *tmp;
+ struct vb2_v4l2_buffer *buffer = NULL;
+
+ mutex_lock(&channel->shadow_list_lock);
+ list_for_each_entry_safe(shadow, tmp, list, head) {
+ if (handle == ptr_to_u64(&shadow->buf.vb)) {
+ buffer = &shadow->buf.vb;
+ list_del_init(&shadow->head);
+ break;
+ }
+ }
+ mutex_unlock(&channel->shadow_list_lock);
+
+ return buffer;
+}
+
+static void allegro_channel_finish_frame(struct allegro_channel *channel,
+ struct mcu_msg_encode_frame_response *msg)
+{
+ struct allegro_dev *dev = channel->dev;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct {
+ u32 offset;
+ u32 size;
+ } *partition;
+ enum vb2_buffer_state state = VB2_BUF_STATE_ERROR;
+ char *curr;
+ ssize_t len;
+ ssize_t free;
+
+ src_buf = allegro_get_buffer(channel, &channel->source_shadow_list,
+ msg->src_handle);
+ if (!src_buf)
+ v4l2_warn(&dev->v4l2_dev,
+ "channel %d: invalid source buffer\n",
+ channel->mcu_channel_id);
+
+ dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list,
+ msg->dst_handle);
+ if (!dst_buf)
+ v4l2_warn(&dev->v4l2_dev,
+ "channel %d: invalid stream buffer\n",
+ channel->mcu_channel_id);
+
+ if (!src_buf || !dst_buf)
+ goto err;
+
+ if (v4l2_m2m_is_last_draining_src_buf(channel->fh.m2m_ctx, src_buf)) {
+ dst_buf->flags |= V4L2_BUF_FLAG_LAST;
+ allegro_channel_eos_event(channel);
+ v4l2_m2m_mark_stopped(channel->fh.m2m_ctx);
+ }
+
+ dst_buf->sequence = channel->csequence++;
+
+ if (msg->error_code & AL_ERROR) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: failed to encode frame: %s (%x)\n",
+ channel->mcu_channel_id,
+ allegro_err_to_string(msg->error_code),
+ msg->error_code);
+ goto err;
+ }
+
+ if (msg->partition_table_size != 1) {
+ v4l2_warn(&dev->v4l2_dev,
+ "channel %d: only handling first partition table entry (%d entries)\n",
+ channel->mcu_channel_id, msg->partition_table_size);
+ }
+
+ if (msg->partition_table_offset +
+ msg->partition_table_size * sizeof(*partition) >
+ vb2_plane_size(&dst_buf->vb2_buf, 0)) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: partition table outside of dst_buf\n",
+ channel->mcu_channel_id);
+ goto err;
+ }
+
+ partition =
+ vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset;
+ if (partition->offset + partition->size >
+ vb2_plane_size(&dst_buf->vb2_buf, 0)) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n",
+ channel->mcu_channel_id, partition->offset,
+ partition->size);
+ goto err;
+ }
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "channel %d: encoded frame of size %d is at offset 0x%x\n",
+ channel->mcu_channel_id, partition->size, partition->offset);
+
+ /*
+ * The payload must include the data before the partition offset,
+ * because we will put the sps and pps data there.
+ */
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
+ partition->offset + partition->size);
+
+ curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+ free = partition->offset;
+
+ if (channel->codec == V4L2_PIX_FMT_HEVC && msg->is_idr) {
+ len = allegro_hevc_write_vps(channel, curr, free);
+ if (len < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough space for video parameter set: %zd left\n",
+ free);
+ goto err;
+ }
+ curr += len;
+ free -= len;
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: wrote %zd byte VPS nal unit\n",
+ channel->mcu_channel_id, len);
+ }
+
+ if (msg->is_idr) {
+ if (channel->codec == V4L2_PIX_FMT_H264)
+ len = allegro_h264_write_sps(channel, curr, free);
+ else
+ len = allegro_hevc_write_sps(channel, curr, free);
+ if (len < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough space for sequence parameter set: %zd left\n",
+ free);
+ goto err;
+ }
+ curr += len;
+ free -= len;
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: wrote %zd byte SPS nal unit\n",
+ channel->mcu_channel_id, len);
+ }
+
+ if (msg->slice_type == AL_ENC_SLICE_TYPE_I) {
+ if (channel->codec == V4L2_PIX_FMT_H264)
+ len = allegro_h264_write_pps(channel, curr, free);
+ else
+ len = allegro_hevc_write_pps(channel, msg, curr, free);
+ if (len < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough space for picture parameter set: %zd left\n",
+ free);
+ goto err;
+ }
+ curr += len;
+ free -= len;
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: wrote %zd byte PPS nal unit\n",
+ channel->mcu_channel_id, len);
+ }
+
+ if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) {
+ dst_buf->vb2_buf.planes[0].data_offset = free;
+ free = 0;
+ } else {
+ if (channel->codec == V4L2_PIX_FMT_H264)
+ len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free);
+ else
+ len = nal_hevc_write_filler(&dev->plat_dev->dev, curr, free);
+ if (len < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to write %zd filler data\n", free);
+ goto err;
+ }
+ curr += len;
+ free -= len;
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "channel %d: wrote %zd bytes filler nal unit\n",
+ channel->mcu_channel_id, len);
+ }
+
+ if (free != 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n",
+ free);
+ goto err;
+ }
+
+ state = VB2_BUF_STATE_DONE;
+
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ if (msg->is_idr)
+ dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else
+ dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n",
+ channel->mcu_channel_id,
+ dst_buf->sequence,
+ msg->is_idr ? "IDR, " : "",
+ msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" :
+ msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown",
+ msg->qp, partition->size);
+
+err:
+ if (src_buf)
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+
+ if (dst_buf)
+ v4l2_m2m_buf_done(dst_buf, state);
+}
+
+static int allegro_handle_init(struct allegro_dev *dev,
+ struct mcu_msg_init_response *msg)
+{
+ complete(&dev->init_complete);
+
+ return 0;
+}
+
+static int
+allegro_handle_create_channel(struct allegro_dev *dev,
+ struct mcu_msg_create_channel_response *msg)
+{
+ struct allegro_channel *channel;
+ int err = 0;
+ struct create_channel_param param;
+
+ channel = allegro_find_channel_by_user_id(dev, msg->user_id);
+ if (IS_ERR(channel)) {
+ v4l2_warn(&dev->v4l2_dev,
+ "received %s for unknown user %d\n",
+ msg_type_name(msg->header.type),
+ msg->user_id);
+ return -EINVAL;
+ }
+
+ if (msg->error_code) {
+ v4l2_err(&dev->v4l2_dev,
+ "user %d: mcu failed to create channel: %s (%x)\n",
+ channel->user_id,
+ allegro_err_to_string(msg->error_code),
+ msg->error_code);
+ err = -EIO;
+ goto out;
+ }
+
+ channel->mcu_channel_id = msg->channel_id;
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "user %d: channel has channel id %d\n",
+ channel->user_id, channel->mcu_channel_id);
+
+ err = allegro_decode_config_blob(&param, msg, channel->config_blob.vaddr);
+ allegro_free_buffer(channel->dev, &channel->config_blob);
+ if (err)
+ goto out;
+
+ channel->num_ref_idx_l0 = param.num_ref_idx_l0;
+ channel->num_ref_idx_l1 = param.num_ref_idx_l1;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: intermediate buffers: %d x %d bytes\n",
+ channel->mcu_channel_id,
+ msg->int_buffers_count, msg->int_buffers_size);
+ err = allocate_intermediate_buffers(channel, msg->int_buffers_count,
+ msg->int_buffers_size);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: failed to allocate intermediate buffers\n",
+ channel->mcu_channel_id);
+ goto out;
+ }
+ err = allegro_mcu_push_buffer_intermediate(channel);
+ if (err)
+ goto out;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: reference buffers: %d x %d bytes\n",
+ channel->mcu_channel_id,
+ msg->rec_buffers_count, msg->rec_buffers_size);
+ err = allocate_reference_buffers(channel, msg->rec_buffers_count,
+ msg->rec_buffers_size);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: failed to allocate reference buffers\n",
+ channel->mcu_channel_id);
+ goto out;
+ }
+ err = allegro_mcu_push_buffer_reference(channel);
+ if (err)
+ goto out;
+
+out:
+ channel->error = err;
+ complete(&channel->completion);
+
+ /* Handled successfully, error is passed via channel->error */
+ return 0;
+}
+
+static int
+allegro_handle_destroy_channel(struct allegro_dev *dev,
+ struct mcu_msg_destroy_channel_response *msg)
+{
+ struct allegro_channel *channel;
+
+ channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
+ if (IS_ERR(channel)) {
+ v4l2_err(&dev->v4l2_dev,
+ "received %s for unknown channel %d\n",
+ msg_type_name(msg->header.type),
+ msg->channel_id);
+ return -EINVAL;
+ }
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "user %d: vcu destroyed channel %d\n",
+ channel->user_id, channel->mcu_channel_id);
+ complete(&channel->completion);
+
+ return 0;
+}
+
+static int
+allegro_handle_encode_frame(struct allegro_dev *dev,
+ struct mcu_msg_encode_frame_response *msg)
+{
+ struct allegro_channel *channel;
+
+ channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
+ if (IS_ERR(channel)) {
+ v4l2_err(&dev->v4l2_dev,
+ "received %s for unknown channel %d\n",
+ msg_type_name(msg->header.type),
+ msg->channel_id);
+ return -EINVAL;
+ }
+
+ allegro_channel_finish_frame(channel, msg);
+
+ return 0;
+}
+
+static void allegro_handle_message(struct allegro_dev *dev,
+ union mcu_msg_response *msg)
+{
+ switch (msg->header.type) {
+ case MCU_MSG_TYPE_INIT:
+ allegro_handle_init(dev, &msg->init);
+ break;
+ case MCU_MSG_TYPE_CREATE_CHANNEL:
+ allegro_handle_create_channel(dev, &msg->create_channel);
+ break;
+ case MCU_MSG_TYPE_DESTROY_CHANNEL:
+ allegro_handle_destroy_channel(dev, &msg->destroy_channel);
+ break;
+ case MCU_MSG_TYPE_ENCODE_FRAME:
+ allegro_handle_encode_frame(dev, &msg->encode_frame);
+ break;
+ default:
+ v4l2_warn(&dev->v4l2_dev,
+ "%s: unknown message %s\n",
+ __func__, msg_type_name(msg->header.type));
+ break;
+ }
+}
+
+static irqreturn_t allegro_hardirq(int irq, void *data)
+{
+ struct allegro_dev *dev = data;
+ unsigned int status;
+
+ regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status);
+ if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED))
+ return IRQ_NONE;
+
+ regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t allegro_irq_thread(int irq, void *data)
+{
+ struct allegro_dev *dev = data;
+
+ allegro_mbox_notify(dev->mbox_status);
+
+ return IRQ_HANDLED;
+}
+
+static void allegro_copy_firmware(struct allegro_dev *dev,
+ const u8 * const buf, size_t size)
+{
+ int err = 0;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "copy mcu firmware (%zu B) to SRAM\n", size);
+ err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4);
+ if (err)
+ v4l2_err(&dev->v4l2_dev,
+ "failed to copy firmware: %d\n", err);
+}
+
+static void allegro_copy_fw_codec(struct allegro_dev *dev,
+ const u8 * const buf, size_t size)
+{
+ int err;
+ dma_addr_t icache_offset, dcache_offset;
+
+ /*
+ * The downstream allocates 600 KB for the codec firmware to have some
+ * extra space for "possible extensions." My tests were fine with
+ * allocating just enough memory for the actual firmware, but I am not
+ * sure that the firmware really does not use the remaining space.
+ */
+ err = allegro_alloc_buffer(dev, &dev->firmware, size);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate %zu bytes for firmware\n", size);
+ return;
+ }
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "copy codec firmware (%zd B) to phys %pad\n",
+ size, &dev->firmware.paddr);
+ memcpy(dev->firmware.vaddr, buf, size);
+
+ regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP,
+ upper_32_bits(dev->firmware.paddr));
+
+ icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET;
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "icache_offset: msb = 0x%x, lsb = 0x%x\n",
+ upper_32_bits(icache_offset), lower_32_bits(icache_offset));
+ regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB,
+ upper_32_bits(icache_offset));
+ regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB,
+ lower_32_bits(icache_offset));
+
+ dcache_offset =
+ (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET;
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "dcache_offset: msb = 0x%x, lsb = 0x%x\n",
+ upper_32_bits(dcache_offset), lower_32_bits(dcache_offset));
+ regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB,
+ upper_32_bits(dcache_offset));
+ regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB,
+ lower_32_bits(dcache_offset));
+}
+
+static void allegro_free_fw_codec(struct allegro_dev *dev)
+{
+ allegro_free_buffer(dev, &dev->firmware);
+}
+
+/*
+ * Control functions for the MCU
+ */
+
+static int allegro_mcu_enable_interrupts(struct allegro_dev *dev)
+{
+ return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0));
+}
+
+static int allegro_mcu_disable_interrupts(struct allegro_dev *dev)
+{
+ return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0);
+}
+
+static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev)
+{
+ unsigned long timeout;
+ unsigned int status;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 &&
+ status != AL5_MCU_STA_SLEEP) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static int allegro_mcu_start(struct allegro_dev *dev)
+{
+ unsigned long timeout;
+ unsigned int status;
+ int err;
+
+ err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0));
+ if (err)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 &&
+ status == AL5_MCU_STA_SLEEP) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int allegro_mcu_reset(struct allegro_dev *dev)
+{
+ int err;
+
+ /*
+ * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu
+ * does not go to sleep after the reset.
+ */
+ err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0);
+ if (err)
+ return err;
+
+ err = regmap_write(dev->regmap,
+ AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT);
+ if (err < 0)
+ return err;
+
+ return allegro_mcu_wait_for_sleep(dev);
+}
+
+static void allegro_mcu_interrupt(struct allegro_dev *dev)
+{
+ regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0));
+}
+
+static void allegro_destroy_channel(struct allegro_channel *channel)
+{
+ struct allegro_dev *dev = channel->dev;
+ unsigned long timeout;
+
+ if (channel_exists(channel)) {
+ reinit_completion(&channel->completion);
+ allegro_mcu_send_destroy_channel(dev, channel);
+ timeout = wait_for_completion_timeout(&channel->completion,
+ msecs_to_jiffies(5000));
+ if (timeout == 0)
+ v4l2_warn(&dev->v4l2_dev,
+ "channel %d: timeout while destroying\n",
+ channel->mcu_channel_id);
+
+ channel->mcu_channel_id = -1;
+ }
+
+ destroy_intermediate_buffers(channel);
+ destroy_reference_buffers(channel);
+
+ v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_level, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false);
+
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_level, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, false);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, false);
+
+ v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate, false);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false);
+ v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false);
+ v4l2_ctrl_grab(channel->mpeg_video_gop_size, false);
+
+ if (channel->user_id != -1) {
+ clear_bit(channel->user_id, &dev->channel_user_ids);
+ channel->user_id = -1;
+ }
+}
+
+/*
+ * Create the MCU channel
+ *
+ * After the channel has been created, the picture size, format, colorspace
+ * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be
+ * changed anymore.
+ *
+ * The channel can be created only once. The MCU will accept source buffers
+ * and stream buffers only after a channel has been created.
+ */
+static int allegro_create_channel(struct allegro_channel *channel)
+{
+ struct allegro_dev *dev = channel->dev;
+ unsigned long timeout;
+
+ if (channel_exists(channel)) {
+ v4l2_warn(&dev->v4l2_dev,
+ "channel already exists\n");
+ return 0;
+ }
+
+ channel->user_id = allegro_next_user_id(dev);
+ if (channel->user_id < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "no free channels available\n");
+ return -EBUSY;
+ }
+ set_bit(channel->user_id, &dev->channel_user_ids);
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "user %d: creating channel (%4.4s, %dx%d@%d)\n",
+ channel->user_id,
+ (char *)&channel->codec, channel->width, channel->height,
+ DIV_ROUND_UP(channel->framerate.numerator,
+ channel->framerate.denominator));
+
+ v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_level, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true);
+
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_level, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, true);
+ v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, true);
+
+ v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate, true);
+ v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true);
+ v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true);
+ v4l2_ctrl_grab(channel->mpeg_video_gop_size, true);
+
+ reinit_completion(&channel->completion);
+ allegro_mcu_send_create_channel(dev, channel);
+ timeout = wait_for_completion_timeout(&channel->completion,
+ msecs_to_jiffies(5000));
+ if (timeout == 0)
+ channel->error = -ETIMEDOUT;
+ if (channel->error)
+ goto err;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "channel %d: accepting buffers\n",
+ channel->mcu_channel_id);
+
+ return 0;
+
+err:
+ allegro_destroy_channel(channel);
+
+ return channel->error;
+}
+
+/**
+ * allegro_channel_adjust() - Adjust channel parameters to current format
+ * @channel: the channel to adjust
+ *
+ * Various parameters of a channel and their limits depend on the currently
+ * set format. Adjust the parameters after a format change in one go.
+ */
+static void allegro_channel_adjust(struct allegro_channel *channel)
+{
+ struct allegro_dev *dev = channel->dev;
+ u32 codec = channel->codec;
+ struct v4l2_ctrl *ctrl;
+ s64 min;
+ s64 max;
+
+ channel->sizeimage_encoded =
+ estimate_stream_size(channel->width, channel->height);
+
+ if (codec == V4L2_PIX_FMT_H264) {
+ ctrl = channel->mpeg_video_h264_level;
+ min = select_minimum_h264_level(channel->width, channel->height);
+ } else {
+ ctrl = channel->mpeg_video_hevc_level;
+ min = select_minimum_hevc_level(channel->width, channel->height);
+ }
+ if (ctrl->minimum > min)
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "%s.minimum: %lld -> %lld\n",
+ v4l2_ctrl_get_name(ctrl->id), ctrl->minimum, min);
+ v4l2_ctrl_lock(ctrl);
+ __v4l2_ctrl_modify_range(ctrl, min, ctrl->maximum,
+ ctrl->step, ctrl->default_value);
+ v4l2_ctrl_unlock(ctrl);
+
+ ctrl = channel->mpeg_video_bitrate;
+ if (codec == V4L2_PIX_FMT_H264)
+ max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level));
+ else
+ max = hevc_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level));
+ if (ctrl->maximum < max)
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "%s: maximum: %lld -> %lld\n",
+ v4l2_ctrl_get_name(ctrl->id), ctrl->maximum, max);
+ v4l2_ctrl_lock(ctrl);
+ __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max,
+ ctrl->step, ctrl->default_value);
+ v4l2_ctrl_unlock(ctrl);
+
+ ctrl = channel->mpeg_video_bitrate_peak;
+ v4l2_ctrl_lock(ctrl);
+ __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max,
+ ctrl->step, ctrl->default_value);
+ v4l2_ctrl_unlock(ctrl);
+
+ v4l2_ctrl_activate(channel->mpeg_video_h264_profile,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_level,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_i_frame_qp,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_max_qp,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_min_qp,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_p_frame_qp,
+ codec == V4L2_PIX_FMT_H264);
+ v4l2_ctrl_activate(channel->mpeg_video_h264_b_frame_qp,
+ codec == V4L2_PIX_FMT_H264);
+
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_profile,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_level,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_tier,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_i_frame_qp,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_max_qp,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_min_qp,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_p_frame_qp,
+ codec == V4L2_PIX_FMT_HEVC);
+ v4l2_ctrl_activate(channel->mpeg_video_hevc_b_frame_qp,
+ codec == V4L2_PIX_FMT_HEVC);
+
+ if (codec == V4L2_PIX_FMT_H264)
+ channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM;
+ channel->temporal_mvp_enable = true;
+ channel->dbf_ovr_en = (codec == V4L2_PIX_FMT_H264);
+ channel->enable_deblocking_filter_override = (codec == V4L2_PIX_FMT_HEVC);
+ channel->enable_reordering = (codec == V4L2_PIX_FMT_HEVC);
+ channel->enable_loop_filter_across_tiles = true;
+ channel->enable_loop_filter_across_slices = true;
+
+ if (codec == V4L2_PIX_FMT_H264) {
+ channel->b_hrz_me_range = 8;
+ channel->b_vrt_me_range = 8;
+ channel->p_hrz_me_range = 16;
+ channel->p_vrt_me_range = 16;
+ channel->max_cu_size = ilog2(16);
+ channel->min_cu_size = ilog2(8);
+ channel->max_tu_size = ilog2(4);
+ channel->min_tu_size = ilog2(4);
+ } else {
+ channel->b_hrz_me_range = 16;
+ channel->b_vrt_me_range = 16;
+ channel->p_hrz_me_range = 32;
+ channel->p_vrt_me_range = 32;
+ channel->max_cu_size = ilog2(32);
+ channel->min_cu_size = ilog2(8);
+ channel->max_tu_size = ilog2(32);
+ channel->min_tu_size = ilog2(4);
+ }
+ channel->max_transfo_depth_intra = 1;
+ channel->max_transfo_depth_inter = 1;
+}
+
+static void allegro_set_default_params(struct allegro_channel *channel)
+{
+ channel->width = ALLEGRO_WIDTH_DEFAULT;
+ channel->height = ALLEGRO_HEIGHT_DEFAULT;
+ channel->stride = round_up(channel->width, 32);
+ channel->framerate = ALLEGRO_FRAMERATE_DEFAULT;
+
+ channel->colorspace = V4L2_COLORSPACE_REC709;
+ channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ channel->quantization = V4L2_QUANTIZATION_DEFAULT;
+ channel->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ channel->pixelformat = V4L2_PIX_FMT_NV12;
+ channel->sizeimage_raw = channel->stride * channel->height * 3 / 2;
+
+ channel->codec = V4L2_PIX_FMT_H264;
+}
+
+static int allegro_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct allegro_channel *channel = vb2_get_drv_priv(vq);
+ struct allegro_dev *dev = channel->dev;
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "%s: queue setup[%s]: nplanes = %d\n",
+ V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture",
+ *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes);
+
+ if (*nplanes != 0) {
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ if (sizes[0] < channel->sizeimage_raw)
+ return -EINVAL;
+ } else {
+ if (sizes[0] < channel->sizeimage_encoded)
+ return -EINVAL;
+ }
+ } else {
+ *nplanes = 1;
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ sizes[0] = channel->sizeimage_raw;
+ else
+ sizes[0] = channel->sizeimage_encoded;
+ }
+
+ return 0;
+}
+
+static int allegro_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
+ struct allegro_dev *dev = channel->dev;
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE) {
+ v4l2_err(&dev->v4l2_dev,
+ "channel %d: unsupported field\n",
+ channel->mcu_channel_id);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void allegro_buf_queue(struct vb2_buffer *vb)
+{
+ struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type) &&
+ vb2_is_streaming(q) &&
+ v4l2_m2m_dst_buf_is_last(channel->fh.m2m_ctx)) {
+ unsigned int i;
+
+ for (i = 0; i < vb->num_planes; i++)
+ vb->planes[i].bytesused = 0;
+
+ vbuf->field = V4L2_FIELD_NONE;
+ vbuf->sequence = channel->csequence++;
+
+ v4l2_m2m_last_buffer_done(channel->fh.m2m_ctx, vbuf);
+ allegro_channel_eos_event(channel);
+ return;
+ }
+
+ v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf);
+}
+
+static int allegro_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct allegro_channel *channel = vb2_get_drv_priv(q);
+ struct allegro_dev *dev = channel->dev;
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "%s: start streaming\n",
+ V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");
+
+ v4l2_m2m_update_start_streaming_state(channel->fh.m2m_ctx, q);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ channel->osequence = 0;
+ else
+ channel->csequence = 0;
+
+ return 0;
+}
+
+static void allegro_stop_streaming(struct vb2_queue *q)
+{
+ struct allegro_channel *channel = vb2_get_drv_priv(q);
+ struct allegro_dev *dev = channel->dev;
+ struct vb2_v4l2_buffer *buffer;
+ struct allegro_m2m_buffer *shadow, *tmp;
+
+ v4l2_dbg(2, debug, &dev->v4l2_dev,
+ "%s: stop streaming\n",
+ V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ mutex_lock(&channel->shadow_list_lock);
+ list_for_each_entry_safe(shadow, tmp,
+ &channel->source_shadow_list, head) {
+ list_del(&shadow->head);
+ v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
+ }
+ mutex_unlock(&channel->shadow_list_lock);
+
+ while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
+ } else {
+ mutex_lock(&channel->shadow_list_lock);
+ list_for_each_entry_safe(shadow, tmp,
+ &channel->stream_shadow_list, head) {
+ list_del(&shadow->head);
+ v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
+ }
+ mutex_unlock(&channel->shadow_list_lock);
+
+ allegro_destroy_channel(channel);
+ while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
+ }
+
+ v4l2_m2m_update_stop_streaming_state(channel->fh.m2m_ctx, q);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type) &&
+ v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
+ allegro_channel_eos_event(channel);
+}
+
+static const struct vb2_ops allegro_queue_ops = {
+ .queue_setup = allegro_queue_setup,
+ .buf_prepare = allegro_buf_prepare,
+ .buf_queue = allegro_buf_queue,
+ .start_streaming = allegro_start_streaming,
+ .stop_streaming = allegro_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int allegro_queue_init(void *priv,
+ struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int err;
+ struct allegro_channel *channel = priv;
+
+ src_vq->dev = &channel->dev->plat_dev->dev;
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->drv_priv = channel;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = &allegro_queue_ops;
+ src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
+ src_vq->lock = &channel->dev->lock;
+ err = vb2_queue_init(src_vq);
+ if (err)
+ return err;
+
+ dst_vq->dev = &channel->dev->plat_dev->dev;
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->drv_priv = channel;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = &allegro_queue_ops;
+ dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
+ dst_vq->lock = &channel->dev->lock;
+ err = vb2_queue_init(dst_vq);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int allegro_clamp_qp(struct allegro_channel *channel,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl *next_ctrl;
+
+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP)
+ next_ctrl = channel->mpeg_video_h264_p_frame_qp;
+ else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP)
+ next_ctrl = channel->mpeg_video_h264_b_frame_qp;
+ else
+ return 0;
+
+ /* Modify range automatically updates the value */
+ __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val);
+
+ return allegro_clamp_qp(channel, next_ctrl);
+}
+
+static int allegro_clamp_bitrate(struct allegro_channel *channel,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate;
+ struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak;
+
+ if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ ctrl_bitrate_peak->val < ctrl_bitrate->val)
+ ctrl_bitrate_peak->val = ctrl_bitrate->val;
+
+ return 0;
+}
+
+static int allegro_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct allegro_channel *channel = container_of(ctrl->handler,
+ struct allegro_channel,
+ ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ allegro_clamp_bitrate(channel, ctrl);
+ break;
+ }
+
+ return 0;
+}
+
+static int allegro_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct allegro_channel *channel = container_of(ctrl->handler,
+ struct allegro_channel,
+ ctrl_handler);
+ struct allegro_dev *dev = channel->dev;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ channel->frame_rc_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ channel->bitrate = channel->mpeg_video_bitrate->val;
+ channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val;
+ v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak,
+ ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ allegro_clamp_qp(channel, ctrl);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops allegro_ctrl_ops = {
+ .try_ctrl = allegro_try_ctrl,
+ .s_ctrl = allegro_s_ctrl,
+};
+
+static int allegro_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct allegro_dev *dev = video_get_drvdata(vdev);
+ struct allegro_channel *channel = NULL;
+ struct v4l2_ctrl_handler *handler;
+ u64 mask;
+ int ret;
+ unsigned int bitrate_max;
+ unsigned int bitrate_def;
+ unsigned int cpb_size_max;
+ unsigned int cpb_size_def;
+
+ channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ if (!channel)
+ return -ENOMEM;
+
+ v4l2_fh_init(&channel->fh, vdev);
+
+ init_completion(&channel->completion);
+ INIT_LIST_HEAD(&channel->source_shadow_list);
+ INIT_LIST_HEAD(&channel->stream_shadow_list);
+ mutex_init(&channel->shadow_list_lock);
+
+ channel->dev = dev;
+
+ allegro_set_default_params(channel);
+
+ handler = &channel->ctrl_handler;
+ v4l2_ctrl_handler_init(handler, 0);
+ channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0,
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
+ mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B;
+ channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ channel->mpeg_video_h264_i_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ 0, 51, 1, 30);
+ channel->mpeg_video_h264_max_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ 0, 51, 1, 51);
+ channel->mpeg_video_h264_min_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ 0, 51, 1, 0);
+ channel->mpeg_video_h264_p_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+ 0, 51, 1, 30);
+ channel->mpeg_video_h264_b_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+ 0, 51, 1, 30);
+
+ channel->mpeg_video_hevc_profile =
+ v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0x0,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
+ channel->mpeg_video_hevc_level =
+ v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0x0,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
+ channel->mpeg_video_hevc_tier =
+ v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_TIER,
+ V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, 0x0,
+ V4L2_MPEG_VIDEO_HEVC_TIER_MAIN);
+ channel->mpeg_video_hevc_i_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+ 0, 51, 1, 30);
+ channel->mpeg_video_hevc_max_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
+ 0, 51, 1, 51);
+ channel->mpeg_video_hevc_min_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+ 0, 51, 1, 0);
+ channel->mpeg_video_hevc_p_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
+ 0, 51, 1, 30);
+ channel->mpeg_video_hevc_b_frame_qp =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP,
+ 0, 51, 1, 30);
+
+ channel->mpeg_video_frame_rc_enable =
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ false, 0x1,
+ true, false);
+ channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+ if (channel->codec == V4L2_PIX_FMT_H264) {
+ bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ } else {
+ bitrate_max = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
+ bitrate_def = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
+ cpb_size_max = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
+ cpb_size_def = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
+ }
+ channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, bitrate_max, 1, bitrate_def);
+ channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0, bitrate_max, 1, bitrate_def);
+ channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+ 0, cpb_size_max, 1, cpb_size_def);
+ channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 0, ALLEGRO_GOP_SIZE_MAX,
+ 1, ALLEGRO_GOP_SIZE_DEFAULT);
+ v4l2_ctrl_new_std(handler,
+ &allegro_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ 1, 32,
+ 1, 1);
+ if (handler->error != 0) {
+ ret = handler->error;
+ goto error;
+ }
+
+ channel->fh.ctrl_handler = handler;
+
+ v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode);
+
+ v4l2_ctrl_handler_setup(handler);
+
+ channel->mcu_channel_id = -1;
+ channel->user_id = -1;
+
+ INIT_LIST_HEAD(&channel->buffers_reference);
+ INIT_LIST_HEAD(&channel->buffers_intermediate);
+
+ channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel,
+ allegro_queue_init);
+
+ if (IS_ERR(channel->fh.m2m_ctx)) {
+ ret = PTR_ERR(channel->fh.m2m_ctx);
+ goto error;
+ }
+
+ list_add(&channel->list, &dev->channels);
+ file->private_data = &channel->fh;
+ v4l2_fh_add(&channel->fh);
+
+ allegro_channel_adjust(channel);
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(handler);
+ kfree(channel);
+ return ret;
+}
+
+static int allegro_release(struct file *file)
+{
+ struct allegro_channel *channel = fh_to_channel(file->private_data);
+
+ v4l2_m2m_ctx_release(channel->fh.m2m_ctx);
+
+ list_del(&channel->list);
+
+ v4l2_ctrl_handler_free(&channel->ctrl_handler);
+
+ v4l2_fh_del(&channel->fh);
+ v4l2_fh_exit(&channel->fh);
+
+ kfree(channel);
+
+ return 0;
+}
+
+static int allegro_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct allegro_dev *dev = video_get_drvdata(vdev);
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(&dev->plat_dev->dev));
+
+ return 0;
+}
+
+static int allegro_enum_fmt_vid(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (f->index >= 1)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_NV12;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (f->index >= 2)
+ return -EINVAL;
+ if (f->index == 0)
+ f->pixelformat = V4L2_PIX_FMT_H264;
+ if (f->index == 1)
+ f->pixelformat = V4L2_PIX_FMT_HEVC;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int allegro_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = channel->width;
+ f->fmt.pix.height = channel->height;
+
+ f->fmt.pix.colorspace = channel->colorspace;
+ f->fmt.pix.ycbcr_enc = channel->ycbcr_enc;
+ f->fmt.pix.quantization = channel->quantization;
+ f->fmt.pix.xfer_func = channel->xfer_func;
+
+ f->fmt.pix.pixelformat = channel->codec;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = channel->sizeimage_encoded;
+
+ return 0;
+}
+
+static int allegro_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width,
+ ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX);
+ f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height,
+ ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX);
+
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_HEVC &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_H264)
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
+
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height);
+
+ return 0;
+}
+
+static int allegro_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+ struct vb2_queue *vq;
+ int err;
+
+ err = allegro_try_fmt_vid_cap(file, fh, f);
+ if (err)
+ return err;
+
+ vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ channel->codec = f->fmt.pix.pixelformat;
+
+ allegro_channel_adjust(channel);
+
+ return 0;
+}
+
+static int allegro_g_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ f->fmt.pix.width = channel->width;
+ f->fmt.pix.height = channel->height;
+
+ f->fmt.pix.colorspace = channel->colorspace;
+ f->fmt.pix.ycbcr_enc = channel->ycbcr_enc;
+ f->fmt.pix.quantization = channel->quantization;
+ f->fmt.pix.xfer_func = channel->xfer_func;
+
+ f->fmt.pix.pixelformat = channel->pixelformat;
+ f->fmt.pix.bytesperline = channel->stride;
+ f->fmt.pix.sizeimage = channel->sizeimage_raw;
+
+ return 0;
+}
+
+static int allegro_try_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ /*
+ * The firmware of the Allegro codec handles the padding internally
+ * and expects the visual frame size when configuring a channel.
+ * Therefore, unlike other encoder drivers, this driver does not round
+ * up the width and height to macroblock alignment and does not
+ * implement the selection api.
+ */
+ f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width,
+ ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX);
+ f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height,
+ ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX);
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32);
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2;
+
+ return 0;
+}
+
+static int allegro_s_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+ int err;
+
+ err = allegro_try_fmt_vid_out(file, fh, f);
+ if (err)
+ return err;
+
+ channel->width = f->fmt.pix.width;
+ channel->height = f->fmt.pix.height;
+ channel->stride = f->fmt.pix.bytesperline;
+ channel->sizeimage_raw = f->fmt.pix.sizeimage;
+
+ channel->colorspace = f->fmt.pix.colorspace;
+ channel->ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ channel->quantization = f->fmt.pix.quantization;
+ channel->xfer_func = f->fmt.pix.xfer_func;
+
+ allegro_channel_adjust(channel);
+
+ return 0;
+}
+
+static int allegro_channel_cmd_stop(struct allegro_channel *channel)
+{
+ if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
+ allegro_channel_eos_event(channel);
+
+ return 0;
+}
+
+static int allegro_channel_cmd_start(struct allegro_channel *channel)
+{
+ if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
+ vb2_clear_last_buffer_dequeued(&channel->fh.m2m_ctx->cap_q_ctx.q);
+
+ return 0;
+}
+
+static int allegro_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *cmd)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+ int err;
+
+ err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
+ if (err)
+ return err;
+
+ err = v4l2_m2m_ioctl_encoder_cmd(file, fh, cmd);
+ if (err)
+ return err;
+
+ if (cmd->cmd == V4L2_ENC_CMD_STOP)
+ err = allegro_channel_cmd_stop(channel);
+
+ if (cmd->cmd == V4L2_ENC_CMD_START)
+ err = allegro_channel_cmd_start(channel);
+
+ return err;
+}
+
+static int allegro_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ switch (fsize->pixel_format) {
+ case V4L2_PIX_FMT_HEVC:
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_NV12:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN;
+ fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN;
+ fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int allegro_ioctl_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct allegro_channel *channel = fh_to_channel(fh);
+ int err;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ err = allegro_create_channel(channel);
+ if (err)
+ return err;
+ }
+
+ return v4l2_m2m_streamon(file, fh->m2m_ctx, type);
+}
+
+static int allegro_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+ struct v4l2_fract *timeperframe;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ timeperframe = &a->parm.output.timeperframe;
+ timeperframe->numerator = channel->framerate.denominator;
+ timeperframe->denominator = channel->framerate.numerator;
+
+ return 0;
+}
+
+static int allegro_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct allegro_channel *channel = fh_to_channel(fh);
+ struct v4l2_fract *timeperframe;
+ int div;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ timeperframe = &a->parm.output.timeperframe;
+
+ if (timeperframe->numerator == 0 || timeperframe->denominator == 0)
+ return allegro_g_parm(file, fh, a);
+
+ div = gcd(timeperframe->denominator, timeperframe->numerator);
+ channel->framerate.numerator = timeperframe->denominator / div;
+ channel->framerate.denominator = timeperframe->numerator / div;
+
+ return 0;
+}
+
+static int allegro_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
+}
+
+static const struct v4l2_ioctl_ops allegro_ioctl_ops = {
+ .vidioc_querycap = allegro_querycap,
+ .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid,
+ .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid,
+ .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = allegro_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out,
+
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+
+ .vidioc_streamon = allegro_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = allegro_encoder_cmd,
+ .vidioc_enum_framesizes = allegro_enum_framesizes,
+
+ .vidioc_g_parm = allegro_g_parm,
+ .vidioc_s_parm = allegro_s_parm,
+
+ .vidioc_subscribe_event = allegro_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations allegro_fops = {
+ .owner = THIS_MODULE,
+ .open = allegro_open,
+ .release = allegro_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static int allegro_register_device(struct allegro_dev *dev)
+{
+ struct video_device *video_dev = &dev->video_dev;
+
+ strscpy(video_dev->name, "allegro", sizeof(video_dev->name));
+ video_dev->fops = &allegro_fops;
+ video_dev->ioctl_ops = &allegro_ioctl_ops;
+ video_dev->release = video_device_release_empty;
+ video_dev->lock = &dev->lock;
+ video_dev->v4l2_dev = &dev->v4l2_dev;
+ video_dev->vfl_dir = VFL_DIR_M2M;
+ video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ video_set_drvdata(video_dev, dev);
+
+ return video_register_device(video_dev, VFL_TYPE_VIDEO, 0);
+}
+
+static void allegro_device_run(void *priv)
+{
+ struct allegro_channel *channel = priv;
+ struct allegro_dev *dev = channel->dev;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
+ dma_addr_t src_y;
+ dma_addr_t src_uv;
+ dma_addr_t dst_addr;
+ unsigned long dst_size;
+ u64 src_handle;
+ u64 dst_handle;
+
+ dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx);
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0);
+ dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list,
+ dst_buf);
+ allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size,
+ dst_handle);
+
+ src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx);
+ src_buf->sequence = channel->osequence++;
+ src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ src_uv = src_y + (channel->stride * channel->height);
+ src_handle = allegro_put_buffer(channel, &channel->source_shadow_list,
+ src_buf);
+ allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle);
+
+ v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx);
+}
+
+static const struct v4l2_m2m_ops allegro_m2m_ops = {
+ .device_run = allegro_device_run,
+};
+
+static int allegro_mcu_hw_init(struct allegro_dev *dev,
+ const struct fw_info *info)
+{
+ int err;
+
+ dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd,
+ info->mailbox_size);
+ dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status,
+ info->mailbox_size);
+ if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to initialize mailboxes\n");
+ return -EIO;
+ }
+
+ allegro_mcu_enable_interrupts(dev);
+
+ /* The mcu sends INIT after reset. */
+ allegro_mcu_start(dev);
+ err = allegro_mcu_wait_for_init_timeout(dev, 5000);
+ if (err < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "mcu did not send INIT after reset\n");
+ err = -EIO;
+ goto err_disable_interrupts;
+ }
+
+ err = allegro_alloc_buffer(dev, &dev->suballocator,
+ info->suballocator_size);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to allocate %zu bytes for suballocator\n",
+ info->suballocator_size);
+ goto err_reset_mcu;
+ }
+
+ allegro_mcu_send_init(dev, dev->suballocator.paddr,
+ dev->suballocator.size);
+ err = allegro_mcu_wait_for_init_timeout(dev, 5000);
+ if (err < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "mcu failed to configure sub-allocator\n");
+ err = -EIO;
+ goto err_free_suballocator;
+ }
+
+ return 0;
+
+err_free_suballocator:
+ allegro_free_buffer(dev, &dev->suballocator);
+err_reset_mcu:
+ allegro_mcu_reset(dev);
+err_disable_interrupts:
+ allegro_mcu_disable_interrupts(dev);
+
+ return err;
+}
+
+static int allegro_mcu_hw_deinit(struct allegro_dev *dev)
+{
+ int err;
+
+ err = allegro_mcu_reset(dev);
+ if (err)
+ v4l2_warn(&dev->v4l2_dev,
+ "mcu failed to enter sleep state\n");
+
+ err = allegro_mcu_disable_interrupts(dev);
+ if (err)
+ v4l2_warn(&dev->v4l2_dev,
+ "failed to disable interrupts\n");
+
+ allegro_free_buffer(dev, &dev->suballocator);
+
+ return 0;
+}
+
+static void allegro_fw_callback(const struct firmware *fw, void *context)
+{
+ struct allegro_dev *dev = context;
+ const char *fw_codec_name = "al5e.fw";
+ const struct firmware *fw_codec;
+ int err;
+
+ if (!fw)
+ return;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "requesting codec firmware '%s'\n", fw_codec_name);
+ err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev);
+ if (err)
+ goto err_release_firmware;
+
+ dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec);
+ if (!dev->fw_info) {
+ v4l2_err(&dev->v4l2_dev, "firmware is not supported\n");
+ goto err_release_firmware_codec;
+ }
+
+ v4l2_info(&dev->v4l2_dev,
+ "using mcu firmware version '%s'\n", dev->fw_info->version);
+
+ /* Ensure that the mcu is sleeping at the reset vector */
+ err = allegro_mcu_reset(dev);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n");
+ goto err_release_firmware_codec;
+ }
+
+ allegro_copy_firmware(dev, fw->data, fw->size);
+ allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size);
+
+ err = allegro_mcu_hw_init(dev, dev->fw_info);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n");
+ goto err_free_fw_codec;
+ }
+
+ dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n");
+ goto err_mcu_hw_deinit;
+ }
+
+ err = allegro_register_device(dev);
+ if (err) {
+ v4l2_err(&dev->v4l2_dev, "failed to register video device\n");
+ goto err_m2m_release;
+ }
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "allegro codec registered as /dev/video%d\n",
+ dev->video_dev.num);
+
+ release_firmware(fw_codec);
+ release_firmware(fw);
+
+ return;
+
+err_m2m_release:
+ v4l2_m2m_release(dev->m2m_dev);
+ dev->m2m_dev = NULL;
+err_mcu_hw_deinit:
+ allegro_mcu_hw_deinit(dev);
+err_free_fw_codec:
+ allegro_free_fw_codec(dev);
+err_release_firmware_codec:
+ release_firmware(fw_codec);
+err_release_firmware:
+ release_firmware(fw);
+}
+
+static int allegro_firmware_request_nowait(struct allegro_dev *dev)
+{
+ const char *fw = "al5e_b.fw";
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "requesting firmware '%s'\n", fw);
+ return request_firmware_nowait(THIS_MODULE, true, fw,
+ &dev->plat_dev->dev, GFP_KERNEL, dev,
+ allegro_fw_callback);
+}
+
+static int allegro_probe(struct platform_device *pdev)
+{
+ struct allegro_dev *dev;
+ struct resource *res, *sram_res;
+ int ret;
+ int irq;
+ void __iomem *regs, *sram_regs;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->plat_dev = pdev;
+ init_completion(&dev->init_complete);
+ INIT_LIST_HEAD(&dev->channels);
+
+ mutex_init(&dev->lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!res) {
+ dev_err(&pdev->dev,
+ "regs resource missing from device tree\n");
+ return -EINVAL;
+ }
+ regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "failed to map registers\n");
+ return -ENOMEM;
+ }
+ dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &allegro_regmap_config);
+ if (IS_ERR(dev->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap\n");
+ return PTR_ERR(dev->regmap);
+ }
+
+ sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+ if (!sram_res) {
+ dev_err(&pdev->dev,
+ "sram resource missing from device tree\n");
+ return -EINVAL;
+ }
+ sram_regs = devm_ioremap(&pdev->dev,
+ sram_res->start,
+ resource_size(sram_res));
+ if (!sram_regs) {
+ dev_err(&pdev->dev, "failed to map sram\n");
+ return -ENOMEM;
+ }
+ dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs,
+ &allegro_sram_config);
+ if (IS_ERR(dev->sram)) {
+ dev_err(&pdev->dev, "failed to init sram\n");
+ return PTR_ERR(dev->sram);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ allegro_hardirq,
+ allegro_irq_thread,
+ IRQF_SHARED, dev_name(&pdev->dev), dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = allegro_firmware_request_nowait(dev);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "failed to request firmware: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int allegro_remove(struct platform_device *pdev)
+{
+ struct allegro_dev *dev = platform_get_drvdata(pdev);
+
+ video_unregister_device(&dev->video_dev);
+ if (dev->m2m_dev)
+ v4l2_m2m_release(dev->m2m_dev);
+ allegro_mcu_hw_deinit(dev);
+ allegro_free_fw_codec(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return 0;
+}
+
+static const struct of_device_id allegro_dt_ids[] = {
+ { .compatible = "allegro,al5e-1.1" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, allegro_dt_ids);
+
+static struct platform_driver allegro_driver = {
+ .probe = allegro_probe,
+ .remove = allegro_remove,
+ .driver = {
+ .name = "allegro",
+ .of_match_table = of_match_ptr(allegro_dt_ids),
+ },
+};
+
+module_platform_driver(allegro_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Tretter <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("Allegro DVT encoder driver");
diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c
new file mode 100644
index 000000000000..7e08c5050f2e
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-mail.c
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Helper functions for handling messages that are send via mailbox to the
+ * Allegro VCU firmware.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include "allegro-mail.h"
+
+const char *msg_type_name(enum mcu_msg_type type)
+{
+ static char buf[9];
+
+ switch (type) {
+ case MCU_MSG_TYPE_INIT:
+ return "INIT";
+ case MCU_MSG_TYPE_CREATE_CHANNEL:
+ return "CREATE_CHANNEL";
+ case MCU_MSG_TYPE_DESTROY_CHANNEL:
+ return "DESTROY_CHANNEL";
+ case MCU_MSG_TYPE_ENCODE_FRAME:
+ return "ENCODE_FRAME";
+ case MCU_MSG_TYPE_PUT_STREAM_BUFFER:
+ return "PUT_STREAM_BUFFER";
+ case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE:
+ return "PUSH_BUFFER_INTERMEDIATE";
+ case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE:
+ return "PUSH_BUFFER_REFERENCE";
+ default:
+ snprintf(buf, sizeof(buf), "(0x%04x)", type);
+ return buf;
+ }
+}
+EXPORT_SYMBOL(msg_type_name);
+
+static ssize_t
+allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg)
+{
+ unsigned int i = 0;
+ enum mcu_msg_version version = msg->header.version;
+
+ dst[i++] = msg->reserved0;
+ dst[i++] = msg->suballoc_dma;
+ dst[i++] = msg->suballoc_size;
+ dst[i++] = msg->l2_cache[0];
+ dst[i++] = msg->l2_cache[1];
+ dst[i++] = msg->l2_cache[2];
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ dst[i++] = -1;
+ dst[i++] = 0;
+ }
+
+ return i * sizeof(*dst);
+}
+
+static inline u32 settings_get_mcu_codec(struct create_channel_param *param)
+{
+ enum mcu_msg_version version = param->version;
+ u32 pixelformat = param->codec;
+
+ if (version < MCU_MSG_VERSION_2019_2) {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_HEVC:
+ return 2;
+ case V4L2_PIX_FMT_H264:
+ default:
+ return 1;
+ }
+ } else {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_HEVC:
+ return 1;
+ case V4L2_PIX_FMT_H264:
+ default:
+ return 0;
+ }
+ }
+}
+
+ssize_t
+allegro_encode_config_blob(u32 *dst, struct create_channel_param *param)
+{
+ enum mcu_msg_version version = param->version;
+ unsigned int i = 0;
+ unsigned int j = 0;
+ u32 val;
+ unsigned int codec = settings_get_mcu_codec(param);
+
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->layer_id;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) |
+ FIELD_PREP(GENMASK(15, 0), param->width);
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->videomode;
+ dst[i++] = param->format;
+ if (version < MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->colorspace;
+ dst[i++] = param->src_mode;
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->src_bit_depth;
+ dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) |
+ FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) |
+ FIELD_PREP(GENMASK(7, 0), param->profile);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) |
+ FIELD_PREP(GENMASK(15, 0), param->level);
+
+ val = 0;
+ val |= param->temporal_mvp_enable ? BIT(20) : 0;
+ val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num);
+ if (version >= MCU_MSG_VERSION_2019_2)
+ val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc - 1);
+ else
+ val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc);
+ dst[i++] = val;
+
+ val = 0;
+ val |= param->enable_reordering ? BIT(0) : 0;
+ val |= param->dbf_ovr_en ? BIT(2) : 0;
+ val |= param->override_lf ? BIT(12) : 0;
+ dst[i++] = val;
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ val = 0;
+ val |= param->custom_lda ? BIT(2) : 0;
+ val |= param->rdo_cost_mode ? BIT(20) : 0;
+ dst[i++] = val;
+
+ val = 0;
+ val |= param->lf ? BIT(2) : 0;
+ val |= param->lf_x_tile ? BIT(3) : 0;
+ val |= param->lf_x_slice ? BIT(4) : 0;
+ dst[i++] = val;
+ } else {
+ val = 0;
+ dst[i++] = val;
+ }
+
+ dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) |
+ FIELD_PREP(GENMASK(7, 0), param->tc_offset);
+ dst[i++] = param->unknown11;
+ dst[i++] = param->unknown12;
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->num_slices;
+ else
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) |
+ FIELD_PREP(GENMASK(15, 0), param->num_slices);
+ dst[i++] = param->prefetch_mem_offset;
+ dst[i++] = param->prefetch_mem_size;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) |
+ FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) |
+ FIELD_PREP(GENMASK(15, 0), param->me_range[0]);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) |
+ FIELD_PREP(GENMASK(15, 0), param->me_range[2]);
+ dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) |
+ FIELD_PREP(GENMASK(23, 16), param->max_tu_size) |
+ FIELD_PREP(GENMASK(15, 8), param->min_cu_size) |
+ FIELD_PREP(GENMASK(8, 0), param->max_cu_size);
+ dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) |
+ FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter);
+ dst[i++] = param->entropy_mode;
+ dst[i++] = param->wp_mode;
+
+ dst[i++] = param->rate_control_mode;
+ dst[i++] = param->initial_rem_delay;
+ dst[i++] = param->cpb_size;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) |
+ FIELD_PREP(GENMASK(15, 0), param->framerate);
+ dst[i++] = param->target_bitrate;
+ dst[i++] = param->max_bitrate;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) |
+ FIELD_PREP(GENMASK(15, 0), param->initial_qp);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) |
+ FIELD_PREP(GENMASK(15, 0), param->max_qp);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) |
+ FIELD_PREP(GENMASK(15, 0), param->pb_delta);
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) |
+ FIELD_PREP(GENMASK(15, 0), param->golden_delta);
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->rate_control_option;
+ else
+ dst[i++] = 0;
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ dst[i++] = param->num_pixel;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) |
+ FIELD_PREP(GENMASK(15, 0), param->max_psnr);
+ for (j = 0; j < 3; j++)
+ dst[i++] = param->maxpicturesize[j];
+ }
+
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->gop_ctrl_mode;
+ else
+ dst[i++] = 0;
+
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) |
+ FIELD_PREP(GENMASK(23, 16), param->num_b) |
+ FIELD_PREP(GENMASK(15, 0), param->gop_length);
+ dst[i++] = param->freq_idr;
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->enable_lt;
+ dst[i++] = param->freq_lt;
+ dst[i++] = param->gdr_mode;
+ if (version < MCU_MSG_VERSION_2019_2)
+ dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) |
+ FIELD_PREP(GENMASK(23, 16), param->num_b) |
+ FIELD_PREP(GENMASK(15, 0), param->gop_length);
+
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->tmpdqp;
+
+ dst[i++] = param->subframe_latency;
+ dst[i++] = param->lda_control_mode;
+ if (version < MCU_MSG_VERSION_2019_2)
+ dst[i++] = param->unknown41;
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ for (j = 0; j < 6; j++)
+ dst[i++] = param->lda_factors[j];
+ dst[i++] = param->max_num_merge_cand;
+ }
+
+ return i * sizeof(*dst);
+}
+
+static ssize_t
+allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg)
+{
+ enum mcu_msg_version version = msg->header.version;
+ unsigned int i = 0;
+
+ dst[i++] = msg->user_id;
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ dst[i++] = msg->blob_mcu_addr;
+ } else {
+ memcpy(&dst[i], msg->blob, msg->blob_size);
+ i += msg->blob_size / sizeof(*dst);
+ }
+
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = msg->ep1_addr;
+
+ return i * sizeof(*dst);
+}
+
+ssize_t allegro_decode_config_blob(struct create_channel_param *param,
+ struct mcu_msg_create_channel_response *msg,
+ u32 *src)
+{
+ enum mcu_msg_version version = msg->header.version;
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]);
+ param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]);
+ } else {
+ param->num_ref_idx_l0 = msg->num_ref_idx_l0;
+ param->num_ref_idx_l1 = msg->num_ref_idx_l1;
+ }
+
+ return 0;
+}
+
+static ssize_t
+allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg)
+{
+ unsigned int i = 0;
+
+ dst[i++] = msg->channel_id;
+
+ return i * sizeof(*dst);
+}
+
+static ssize_t
+allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg)
+{
+ unsigned int i = 0;
+ struct mcu_msg_push_buffers_internal_buffer *buffer;
+ unsigned int num_buffers = msg->num_buffers;
+ unsigned int j;
+
+ dst[i++] = msg->channel_id;
+
+ for (j = 0; j < num_buffers; j++) {
+ buffer = &msg->buffer[j];
+ dst[i++] = buffer->dma_addr;
+ dst[i++] = buffer->mcu_addr;
+ dst[i++] = buffer->size;
+ }
+
+ return i * sizeof(*dst);
+}
+
+static ssize_t
+allegro_enc_put_stream_buffer(u32 *dst,
+ struct mcu_msg_put_stream_buffer *msg)
+{
+ unsigned int i = 0;
+
+ dst[i++] = msg->channel_id;
+ dst[i++] = msg->dma_addr;
+ dst[i++] = msg->mcu_addr;
+ dst[i++] = msg->size;
+ dst[i++] = msg->offset;
+ dst[i++] = lower_32_bits(msg->dst_handle);
+ dst[i++] = upper_32_bits(msg->dst_handle);
+
+ return i * sizeof(*dst);
+}
+
+static ssize_t
+allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg)
+{
+ enum mcu_msg_version version = msg->header.version;
+ unsigned int i = 0;
+
+ dst[i++] = msg->channel_id;
+
+ dst[i++] = msg->reserved;
+ dst[i++] = msg->encoding_options;
+ dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) |
+ FIELD_PREP(GENMASK(15, 0), msg->pps_qp);
+
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ dst[i++] = 0;
+ dst[i++] = 0;
+ dst[i++] = 0;
+ dst[i++] = 0;
+ }
+
+ dst[i++] = lower_32_bits(msg->user_param);
+ dst[i++] = upper_32_bits(msg->user_param);
+ dst[i++] = lower_32_bits(msg->src_handle);
+ dst[i++] = upper_32_bits(msg->src_handle);
+ dst[i++] = msg->request_options;
+ dst[i++] = msg->src_y;
+ dst[i++] = msg->src_uv;
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = msg->is_10_bit;
+ dst[i++] = msg->stride;
+ if (version >= MCU_MSG_VERSION_2019_2)
+ dst[i++] = msg->format;
+ dst[i++] = msg->ep2;
+ dst[i++] = lower_32_bits(msg->ep2_v);
+ dst[i++] = upper_32_bits(msg->ep2_v);
+
+ return i * sizeof(*dst);
+}
+
+static ssize_t
+allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src)
+{
+ unsigned int i = 0;
+
+ msg->reserved0 = src[i++];
+
+ return i * sizeof(*src);
+}
+
+static ssize_t
+allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg,
+ u32 *src)
+{
+ enum mcu_msg_version version = msg->header.version;
+ unsigned int i = 0;
+
+ msg->channel_id = src[i++];
+ msg->user_id = src[i++];
+ /*
+ * Version >= MCU_MSG_VERSION_2019_2 is handled in
+ * allegro_decode_config_blob().
+ */
+ if (version < MCU_MSG_VERSION_2019_2) {
+ msg->options = src[i++];
+ msg->num_core = src[i++];
+ msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]);
+ msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]);
+ }
+ msg->int_buffers_count = src[i++];
+ msg->int_buffers_size = src[i++];
+ msg->rec_buffers_count = src[i++];
+ msg->rec_buffers_size = src[i++];
+ msg->reserved = src[i++];
+ msg->error_code = src[i++];
+
+ return i * sizeof(*src);
+}
+
+static ssize_t
+allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg,
+ u32 *src)
+{
+ unsigned int i = 0;
+
+ msg->channel_id = src[i++];
+
+ return i * sizeof(*src);
+}
+
+static ssize_t
+allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src)
+{
+ enum mcu_msg_version version = msg->header.version;
+ unsigned int i = 0;
+ unsigned int j;
+
+ msg->channel_id = src[i++];
+
+ msg->dst_handle = src[i++];
+ msg->dst_handle |= (((u64)src[i++]) << 32);
+ msg->user_param = src[i++];
+ msg->user_param |= (((u64)src[i++]) << 32);
+ msg->src_handle = src[i++];
+ msg->src_handle |= (((u64)src[i++]) << 32);
+ msg->skip = FIELD_GET(GENMASK(31, 16), src[i]);
+ msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]);
+ msg->initial_removal_delay = src[i++];
+ msg->dpb_output_delay = src[i++];
+ msg->size = src[i++];
+ msg->frame_tag_size = src[i++];
+ msg->stuffing = src[i++];
+ msg->filler = src[i++];
+ msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]);
+ msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]);
+ msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]);
+ msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]);
+ msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]);
+ msg->partition_table_offset = src[i++];
+ msg->partition_table_size = src[i++];
+ msg->sum_complex = src[i++];
+ for (j = 0; j < 4; j++)
+ msg->tile_width[j] = src[i++];
+ for (j = 0; j < 22; j++)
+ msg->tile_height[j] = src[i++];
+ msg->error_code = src[i++];
+ msg->slice_type = src[i++];
+ msg->pic_struct = src[i++];
+ msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]);
+ msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]);
+ msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]);
+ msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]);
+
+ msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]);
+ msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]);
+
+ msg->reserved2 = src[i++];
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ msg->reserved3 = src[i++];
+ msg->reserved4 = src[i++];
+ msg->reserved5 = src[i++];
+ msg->reserved6 = src[i++];
+ }
+
+ return i * sizeof(*src);
+}
+
+/**
+ * allegro_encode_mail() - Encode allegro messages to firmware format
+ * @dst: Pointer to the memory that will be filled with data
+ * @msg: The allegro message that will be encoded
+ */
+ssize_t allegro_encode_mail(u32 *dst, void *msg)
+{
+ const struct mcu_msg_header *header = msg;
+ ssize_t size;
+
+ if (!msg || !dst)
+ return -EINVAL;
+
+ switch (header->type) {
+ case MCU_MSG_TYPE_INIT:
+ size = allegro_enc_init(&dst[1], msg);
+ break;
+ case MCU_MSG_TYPE_CREATE_CHANNEL:
+ size = allegro_enc_create_channel(&dst[1], msg);
+ break;
+ case MCU_MSG_TYPE_DESTROY_CHANNEL:
+ size = allegro_enc_destroy_channel(&dst[1], msg);
+ break;
+ case MCU_MSG_TYPE_ENCODE_FRAME:
+ size = allegro_enc_encode_frame(&dst[1], msg);
+ break;
+ case MCU_MSG_TYPE_PUT_STREAM_BUFFER:
+ size = allegro_enc_put_stream_buffer(&dst[1], msg);
+ break;
+ case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE:
+ case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE:
+ size = allegro_enc_push_buffers(&dst[1], msg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * The encoded messages might have different length depending on
+ * the firmware version or certain fields. Therefore, we have to
+ * set the body length after encoding the message.
+ */
+ dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) |
+ FIELD_PREP(GENMASK(15, 0), size);
+
+ return size + sizeof(*dst);
+}
+
+/**
+ * allegro_decode_mail() - Parse allegro messages from the firmware.
+ * @msg: The mcu_msg_response that will be filled with parsed values.
+ * @src: Pointer to the memory that will be parsed
+ *
+ * The message format in the mailbox depends on the firmware. Parse the
+ * different formats into a uniform message format that can be used without
+ * taking care of the firmware version.
+ */
+int allegro_decode_mail(void *msg, u32 *src)
+{
+ struct mcu_msg_header *header;
+
+ if (!src || !msg)
+ return -EINVAL;
+
+ header = msg;
+ header->type = FIELD_GET(GENMASK(31, 16), src[0]);
+
+ src++;
+ switch (header->type) {
+ case MCU_MSG_TYPE_INIT:
+ allegro_dec_init(msg, src);
+ break;
+ case MCU_MSG_TYPE_CREATE_CHANNEL:
+ allegro_dec_create_channel(msg, src);
+ break;
+ case MCU_MSG_TYPE_DESTROY_CHANNEL:
+ allegro_dec_destroy_channel(msg, src);
+ break;
+ case MCU_MSG_TYPE_ENCODE_FRAME:
+ allegro_dec_encode_frame(msg, src);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h
new file mode 100644
index 000000000000..2c7bc509eac3
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-mail.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Allegro VCU firmware mailbox mail definitions
+ */
+
+#ifndef ALLEGRO_MAIL_H
+#define ALLEGRO_MAIL_H
+
+#include <linux/kernel.h>
+
+enum mcu_msg_type {
+ MCU_MSG_TYPE_INIT = 0x0000,
+ MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005,
+ MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006,
+ MCU_MSG_TYPE_ENCODE_FRAME = 0x0007,
+ MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012,
+ MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e,
+ MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f,
+};
+
+enum mcu_msg_version {
+ MCU_MSG_VERSION_2018_2,
+ MCU_MSG_VERSION_2019_2,
+};
+
+const char *msg_type_name(enum mcu_msg_type type);
+
+struct mcu_msg_header {
+ enum mcu_msg_type type;
+ enum mcu_msg_version version;
+};
+
+struct mcu_msg_init_request {
+ struct mcu_msg_header header;
+ u32 reserved0; /* maybe a unused channel id */
+ u32 suballoc_dma;
+ u32 suballoc_size;
+ s32 l2_cache[3];
+};
+
+struct mcu_msg_init_response {
+ struct mcu_msg_header header;
+ u32 reserved0;
+};
+
+struct create_channel_param {
+ enum mcu_msg_version version;
+ u32 layer_id;
+ u16 width;
+ u16 height;
+ u32 videomode;
+ u32 format;
+ u32 colorspace;
+ u32 src_mode;
+ u32 src_bit_depth;
+ u8 profile;
+ u16 constraint_set_flags;
+ u32 codec;
+ u16 level;
+ u16 tier;
+ u32 log2_max_poc;
+ u32 log2_max_frame_num;
+ u32 temporal_mvp_enable;
+ u32 enable_reordering;
+ u32 dbf_ovr_en;
+ u32 override_lf;
+ u32 num_ref_idx_l0;
+ u32 num_ref_idx_l1;
+ u32 custom_lda;
+ u32 rdo_cost_mode;
+ u32 lf;
+ u32 lf_x_tile;
+ u32 lf_x_slice;
+ s8 beta_offset;
+ s8 tc_offset;
+ u16 reserved10;
+ u32 unknown11;
+ u32 unknown12;
+ u16 num_slices;
+ u16 prefetch_auto;
+ u32 prefetch_mem_offset;
+ u32 prefetch_mem_size;
+ u16 clip_hrz_range;
+ u16 clip_vrt_range;
+ u16 me_range[4];
+ u8 max_cu_size;
+ u8 min_cu_size;
+ u8 max_tu_size;
+ u8 min_tu_size;
+ u8 max_transfo_depth_inter;
+ u8 max_transfo_depth_intra;
+ u16 reserved20;
+ u32 entropy_mode;
+ u32 wp_mode;
+
+ /* rate control param */
+ u32 rate_control_mode;
+ u32 initial_rem_delay;
+ u32 cpb_size;
+ u16 framerate;
+ u16 clk_ratio;
+ u32 target_bitrate;
+ u32 max_bitrate;
+ u16 initial_qp;
+ u16 min_qp;
+ u16 max_qp;
+ s16 ip_delta;
+ s16 pb_delta;
+ u16 golden_ref;
+ u16 golden_delta;
+ u16 golden_ref_frequency;
+ u32 rate_control_option;
+ u32 num_pixel;
+ u16 max_psnr;
+ u16 max_pixel_value;
+ u32 maxpicturesize[3];
+
+ /* gop param */
+ u32 gop_ctrl_mode;
+ u32 freq_idr;
+ u32 freq_lt;
+ u32 gdr_mode;
+ u16 gop_length;
+ u8 num_b;
+ u8 freq_golden_ref;
+ u32 enable_lt;
+ u32 tmpdqp;
+
+ u32 subframe_latency;
+ u32 lda_control_mode;
+ u32 unknown41;
+
+ u32 lda_factors[6];
+
+ u32 max_num_merge_cand;
+};
+
+struct mcu_msg_create_channel {
+ struct mcu_msg_header header;
+ u32 user_id;
+ u32 *blob;
+ size_t blob_size;
+ u32 blob_mcu_addr;
+ u32 ep1_addr;
+};
+
+struct mcu_msg_create_channel_response {
+ struct mcu_msg_header header;
+ u32 channel_id;
+ u32 user_id;
+ u32 options;
+ u32 num_core;
+ u32 num_ref_idx_l0;
+ u32 num_ref_idx_l1;
+ u32 int_buffers_count;
+ u32 int_buffers_size;
+ u32 rec_buffers_count;
+ u32 rec_buffers_size;
+ u32 reserved;
+ u32 error_code;
+};
+
+struct mcu_msg_destroy_channel {
+ struct mcu_msg_header header;
+ u32 channel_id;
+};
+
+struct mcu_msg_destroy_channel_response {
+ struct mcu_msg_header header;
+ u32 channel_id;
+};
+
+struct mcu_msg_push_buffers_internal_buffer {
+ u32 dma_addr;
+ u32 mcu_addr;
+ u32 size;
+};
+
+struct mcu_msg_push_buffers_internal {
+ struct mcu_msg_header header;
+ u32 channel_id;
+ size_t num_buffers;
+ struct mcu_msg_push_buffers_internal_buffer buffer[];
+};
+
+struct mcu_msg_put_stream_buffer {
+ struct mcu_msg_header header;
+ u32 channel_id;
+ u32 dma_addr;
+ u32 mcu_addr;
+ u32 size;
+ u32 offset;
+ u64 dst_handle;
+};
+
+struct mcu_msg_encode_frame {
+ struct mcu_msg_header header;
+ u32 channel_id;
+ u32 reserved;
+
+ u32 encoding_options;
+#define AL_OPT_USE_QP_TABLE BIT(0)
+#define AL_OPT_FORCE_LOAD BIT(1)
+#define AL_OPT_USE_L2 BIT(2)
+#define AL_OPT_DISABLE_INTRA BIT(3)
+#define AL_OPT_DEPENDENT_SLICES BIT(4)
+
+ s16 pps_qp;
+ u16 padding;
+ u64 user_param;
+ u64 src_handle;
+
+ u32 request_options;
+#define AL_OPT_SCENE_CHANGE BIT(0)
+#define AL_OPT_RESTART_GOP BIT(1)
+#define AL_OPT_USE_LONG_TERM BIT(2)
+#define AL_OPT_UPDATE_PARAMS BIT(3)
+
+ /* u32 scene_change_delay (optional) */
+ /* rate control param (optional) */
+ /* gop param (optional) */
+ /* dynamic resolution params (optional) */
+ u32 src_y;
+ u32 src_uv;
+ u32 is_10_bit;
+ u32 stride;
+ u32 format;
+ u32 ep2;
+ u64 ep2_v;
+};
+
+struct mcu_msg_encode_frame_response {
+ struct mcu_msg_header header;
+ u32 channel_id;
+ u64 dst_handle; /* see mcu_msg_put_stream_buffer */
+ u64 user_param; /* see mcu_msg_encode_frame */
+ u64 src_handle; /* see mcu_msg_encode_frame */
+ u16 skip;
+ u16 is_ref;
+ u32 initial_removal_delay;
+ u32 dpb_output_delay;
+ u32 size;
+ u32 frame_tag_size;
+ s32 stuffing;
+ s32 filler;
+ u16 num_column;
+ u16 num_row;
+ u16 qp;
+ u8 num_ref_idx_l0;
+ u8 num_ref_idx_l1;
+ u32 partition_table_offset;
+ s32 partition_table_size;
+ u32 sum_complex;
+ s32 tile_width[4];
+ s32 tile_height[22];
+ u32 error_code;
+
+ u32 slice_type;
+#define AL_ENC_SLICE_TYPE_B 0
+#define AL_ENC_SLICE_TYPE_P 1
+#define AL_ENC_SLICE_TYPE_I 2
+
+ u32 pic_struct;
+ u8 is_idr;
+ u8 is_first_slice;
+ u8 is_last_slice;
+ u8 reserved;
+ u16 pps_qp;
+ u16 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+ u32 reserved5;
+ u32 reserved6;
+};
+
+union mcu_msg_response {
+ struct mcu_msg_header header;
+ struct mcu_msg_init_response init;
+ struct mcu_msg_create_channel_response create_channel;
+ struct mcu_msg_destroy_channel_response destroy_channel;
+ struct mcu_msg_encode_frame_response encode_frame;
+};
+
+ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param);
+ssize_t allegro_decode_config_blob(struct create_channel_param *param,
+ struct mcu_msg_create_channel_response *msg,
+ u32 *src);
+
+int allegro_decode_mail(void *msg, u32 *src);
+ssize_t allegro_encode_mail(u32 *dst, void *msg);
+
+#endif
diff --git a/drivers/media/platform/allegro-dvt/nal-h264.c b/drivers/media/platform/allegro-dvt/nal-h264.c
new file mode 100644
index 000000000000..94dd9266d850
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-h264.c
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Convert NAL units between raw byte sequence payloads (RBSP) and C structs
+ *
+ * The conversion is defined in "ITU-T Rec. H.264 (04/2017) Advanced video
+ * coding for generic audiovisual services". Decoder drivers may use the
+ * parser to parse RBSP from encoded streams and configure the hardware, if
+ * the hardware is not able to parse RBSP itself. Encoder drivers may use the
+ * generator to generate the RBSP for SPS/PPS nal units and add them to the
+ * encoded stream if the hardware does not generate the units.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/v4l2-controls.h>
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/log2.h>
+
+#include "nal-h264.h"
+#include "nal-rbsp.h"
+
+/*
+ * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax
+ * element categories, and NAL unit type classes
+ */
+enum nal_unit_type {
+ SEQUENCE_PARAMETER_SET = 7,
+ PICTURE_PARAMETER_SET = 8,
+ FILLER_DATA = 12,
+};
+
+/**
+ * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile
+ * @profile: the profile as &enum v4l2_mpeg_video_h264_profile
+ *
+ * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified
+ * in Rec. ITU-T H.264 (04/2017) A.2.
+ *
+ * Return: the profile_idc for the passed level
+ */
+int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile)
+{
+ switch (profile) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ return 66;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ return 77;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+ return 88;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ return 100;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level
+ * @level: the level as &enum v4l2_mpeg_video_h264_level
+ *
+ * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in
+ * Rec. ITU-T H.264 (04/2017) A.3.2.
+ *
+ * Return: the level_idc for the passed level
+ */
+int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level)
+{
+ switch (level) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ return 10;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+ return 9;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ return 11;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ return 12;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ return 13;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ return 20;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ return 21;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ return 22;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ return 30;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ return 31;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ return 32;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ return 40;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ return 41;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ return 42;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ return 50;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ return 51;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void nal_h264_write_start_code_prefix(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i = 4;
+
+ if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ p[0] = 0x00;
+ p[1] = 0x00;
+ p[2] = 0x00;
+ p[3] = 0x01;
+
+ rbsp->pos += i * 8;
+}
+
+static void nal_h264_read_start_code_prefix(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i = 4;
+
+ if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ rbsp->pos += i * 8;
+}
+
+static void nal_h264_write_filler_data(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i;
+
+ /* Keep 1 byte extra for terminating the NAL unit */
+ i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1;
+ memset(p, 0xff, i);
+ rbsp->pos += i * 8;
+}
+
+static void nal_h264_read_filler_data(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+
+ while (*p == 0xff) {
+ if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ p++;
+ rbsp->pos += 8;
+ }
+}
+
+static void nal_h264_rbsp_hrd_parameters(struct rbsp *rbsp,
+ struct nal_h264_hrd_parameters *hrd)
+{
+ unsigned int i;
+
+ if (!hrd) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ rbsp_uev(rbsp, &hrd->cpb_cnt_minus1);
+ rbsp_bits(rbsp, 4, &hrd->bit_rate_scale);
+ rbsp_bits(rbsp, 4, &hrd->cpb_size_scale);
+
+ for (i = 0; i <= hrd->cpb_cnt_minus1; i++) {
+ rbsp_uev(rbsp, &hrd->bit_rate_value_minus1[i]);
+ rbsp_uev(rbsp, &hrd->cpb_size_value_minus1[i]);
+ rbsp_bit(rbsp, &hrd->cbr_flag[i]);
+ }
+
+ rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1);
+ rbsp_bits(rbsp, 5, &hrd->cpb_removal_delay_length_minus1);
+ rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1);
+ rbsp_bits(rbsp, 5, &hrd->time_offset_length);
+}
+
+static void nal_h264_rbsp_vui_parameters(struct rbsp *rbsp,
+ struct nal_h264_vui_parameters *vui)
+{
+ if (!vui) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ rbsp_bit(rbsp, &vui->aspect_ratio_info_present_flag);
+ if (vui->aspect_ratio_info_present_flag) {
+ rbsp_bits(rbsp, 8, &vui->aspect_ratio_idc);
+ if (vui->aspect_ratio_idc == 255) {
+ rbsp_bits(rbsp, 16, &vui->sar_width);
+ rbsp_bits(rbsp, 16, &vui->sar_height);
+ }
+ }
+
+ rbsp_bit(rbsp, &vui->overscan_info_present_flag);
+ if (vui->overscan_info_present_flag)
+ rbsp_bit(rbsp, &vui->overscan_appropriate_flag);
+
+ rbsp_bit(rbsp, &vui->video_signal_type_present_flag);
+ if (vui->video_signal_type_present_flag) {
+ rbsp_bits(rbsp, 3, &vui->video_format);
+ rbsp_bit(rbsp, &vui->video_full_range_flag);
+
+ rbsp_bit(rbsp, &vui->colour_description_present_flag);
+ if (vui->colour_description_present_flag) {
+ rbsp_bits(rbsp, 8, &vui->colour_primaries);
+ rbsp_bits(rbsp, 8, &vui->transfer_characteristics);
+ rbsp_bits(rbsp, 8, &vui->matrix_coefficients);
+ }
+ }
+
+ rbsp_bit(rbsp, &vui->chroma_loc_info_present_flag);
+ if (vui->chroma_loc_info_present_flag) {
+ rbsp_uev(rbsp, &vui->chroma_sample_loc_type_top_field);
+ rbsp_uev(rbsp, &vui->chroma_sample_loc_type_bottom_field);
+ }
+
+ rbsp_bit(rbsp, &vui->timing_info_present_flag);
+ if (vui->timing_info_present_flag) {
+ rbsp_bits(rbsp, 32, &vui->num_units_in_tick);
+ rbsp_bits(rbsp, 32, &vui->time_scale);
+ rbsp_bit(rbsp, &vui->fixed_frame_rate_flag);
+ }
+
+ rbsp_bit(rbsp, &vui->nal_hrd_parameters_present_flag);
+ if (vui->nal_hrd_parameters_present_flag)
+ nal_h264_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters);
+
+ rbsp_bit(rbsp, &vui->vcl_hrd_parameters_present_flag);
+ if (vui->vcl_hrd_parameters_present_flag)
+ nal_h264_rbsp_hrd_parameters(rbsp, &vui->vcl_hrd_parameters);
+
+ if (vui->nal_hrd_parameters_present_flag ||
+ vui->vcl_hrd_parameters_present_flag)
+ rbsp_bit(rbsp, &vui->low_delay_hrd_flag);
+
+ rbsp_bit(rbsp, &vui->pic_struct_present_flag);
+
+ rbsp_bit(rbsp, &vui->bitstream_restriction_flag);
+ if (vui->bitstream_restriction_flag) {
+ rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag);
+ rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom);
+ rbsp_uev(rbsp, &vui->max_bits_per_mb_denom);
+ rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal);
+ rbsp_uev(rbsp, &vui->log21_max_mv_length_vertical);
+ rbsp_uev(rbsp, &vui->max_num_reorder_frames);
+ rbsp_uev(rbsp, &vui->max_dec_frame_buffering);
+ }
+}
+
+static void nal_h264_rbsp_sps(struct rbsp *rbsp, struct nal_h264_sps *sps)
+{
+ unsigned int i;
+
+ if (!sps) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ rbsp_bits(rbsp, 8, &sps->profile_idc);
+ rbsp_bit(rbsp, &sps->constraint_set0_flag);
+ rbsp_bit(rbsp, &sps->constraint_set1_flag);
+ rbsp_bit(rbsp, &sps->constraint_set2_flag);
+ rbsp_bit(rbsp, &sps->constraint_set3_flag);
+ rbsp_bit(rbsp, &sps->constraint_set4_flag);
+ rbsp_bit(rbsp, &sps->constraint_set5_flag);
+ rbsp_bits(rbsp, 2, &sps->reserved_zero_2bits);
+ rbsp_bits(rbsp, 8, &sps->level_idc);
+
+ rbsp_uev(rbsp, &sps->seq_parameter_set_id);
+
+ if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
+ sps->profile_idc == 122 || sps->profile_idc == 244 ||
+ sps->profile_idc == 44 || sps->profile_idc == 83 ||
+ sps->profile_idc == 86 || sps->profile_idc == 118 ||
+ sps->profile_idc == 128 || sps->profile_idc == 138 ||
+ sps->profile_idc == 139 || sps->profile_idc == 134 ||
+ sps->profile_idc == 135) {
+ rbsp_uev(rbsp, &sps->chroma_format_idc);
+
+ if (sps->chroma_format_idc == 3)
+ rbsp_bit(rbsp, &sps->separate_colour_plane_flag);
+ rbsp_uev(rbsp, &sps->bit_depth_luma_minus8);
+ rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8);
+ rbsp_bit(rbsp, &sps->qpprime_y_zero_transform_bypass_flag);
+ rbsp_bit(rbsp, &sps->seq_scaling_matrix_present_flag);
+ if (sps->seq_scaling_matrix_present_flag)
+ rbsp->error = -EINVAL;
+ }
+
+ rbsp_uev(rbsp, &sps->log2_max_frame_num_minus4);
+
+ rbsp_uev(rbsp, &sps->pic_order_cnt_type);
+ switch (sps->pic_order_cnt_type) {
+ case 0:
+ rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4);
+ break;
+ case 1:
+ rbsp_bit(rbsp, &sps->delta_pic_order_always_zero_flag);
+ rbsp_sev(rbsp, &sps->offset_for_non_ref_pic);
+ rbsp_sev(rbsp, &sps->offset_for_top_to_bottom_field);
+
+ rbsp_uev(rbsp, &sps->num_ref_frames_in_pic_order_cnt_cycle);
+ for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++)
+ rbsp_sev(rbsp, &sps->offset_for_ref_frame[i]);
+ break;
+ default:
+ rbsp->error = -EINVAL;
+ break;
+ }
+
+ rbsp_uev(rbsp, &sps->max_num_ref_frames);
+ rbsp_bit(rbsp, &sps->gaps_in_frame_num_value_allowed_flag);
+ rbsp_uev(rbsp, &sps->pic_width_in_mbs_minus1);
+ rbsp_uev(rbsp, &sps->pic_height_in_map_units_minus1);
+
+ rbsp_bit(rbsp, &sps->frame_mbs_only_flag);
+ if (!sps->frame_mbs_only_flag)
+ rbsp_bit(rbsp, &sps->mb_adaptive_frame_field_flag);
+
+ rbsp_bit(rbsp, &sps->direct_8x8_inference_flag);
+
+ rbsp_bit(rbsp, &sps->frame_cropping_flag);
+ if (sps->frame_cropping_flag) {
+ rbsp_uev(rbsp, &sps->crop_left);
+ rbsp_uev(rbsp, &sps->crop_right);
+ rbsp_uev(rbsp, &sps->crop_top);
+ rbsp_uev(rbsp, &sps->crop_bottom);
+ }
+
+ rbsp_bit(rbsp, &sps->vui_parameters_present_flag);
+ if (sps->vui_parameters_present_flag)
+ nal_h264_rbsp_vui_parameters(rbsp, &sps->vui);
+}
+
+static void nal_h264_rbsp_pps(struct rbsp *rbsp, struct nal_h264_pps *pps)
+{
+ int i;
+
+ rbsp_uev(rbsp, &pps->pic_parameter_set_id);
+ rbsp_uev(rbsp, &pps->seq_parameter_set_id);
+ rbsp_bit(rbsp, &pps->entropy_coding_mode_flag);
+ rbsp_bit(rbsp, &pps->bottom_field_pic_order_in_frame_present_flag);
+ rbsp_uev(rbsp, &pps->num_slice_groups_minus1);
+ if (pps->num_slice_groups_minus1 > 0) {
+ rbsp_uev(rbsp, &pps->slice_group_map_type);
+ switch (pps->slice_group_map_type) {
+ case 0:
+ for (i = 0; i < pps->num_slice_groups_minus1; i++)
+ rbsp_uev(rbsp, &pps->run_length_minus1[i]);
+ break;
+ case 2:
+ for (i = 0; i < pps->num_slice_groups_minus1; i++) {
+ rbsp_uev(rbsp, &pps->top_left[i]);
+ rbsp_uev(rbsp, &pps->bottom_right[i]);
+ }
+ break;
+ case 3: case 4: case 5:
+ rbsp_bit(rbsp, &pps->slice_group_change_direction_flag);
+ rbsp_uev(rbsp, &pps->slice_group_change_rate_minus1);
+ break;
+ case 6:
+ rbsp_uev(rbsp, &pps->pic_size_in_map_units_minus1);
+ for (i = 0; i < pps->pic_size_in_map_units_minus1; i++)
+ rbsp_bits(rbsp,
+ order_base_2(pps->num_slice_groups_minus1 + 1),
+ &pps->slice_group_id[i]);
+ break;
+ default:
+ break;
+ }
+ }
+ rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1);
+ rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1);
+ rbsp_bit(rbsp, &pps->weighted_pred_flag);
+ rbsp_bits(rbsp, 2, &pps->weighted_bipred_idc);
+ rbsp_sev(rbsp, &pps->pic_init_qp_minus26);
+ rbsp_sev(rbsp, &pps->pic_init_qs_minus26);
+ rbsp_sev(rbsp, &pps->chroma_qp_index_offset);
+ rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag);
+ rbsp_bit(rbsp, &pps->constrained_intra_pred_flag);
+ rbsp_bit(rbsp, &pps->redundant_pic_cnt_present_flag);
+ if (/* more_rbsp_data() */ false) {
+ rbsp_bit(rbsp, &pps->transform_8x8_mode_flag);
+ rbsp_bit(rbsp, &pps->pic_scaling_matrix_present_flag);
+ if (pps->pic_scaling_matrix_present_flag)
+ rbsp->error = -EINVAL;
+ rbsp_sev(rbsp, &pps->second_chroma_qp_index_offset);
+ }
+}
+
+/**
+ * nal_h264_write_sps() - Write SPS NAL unit into RBSP format
+ * @dev: device pointer
+ * @dest: the buffer that is filled with RBSP data
+ * @n: maximum size of @dest in bytes
+ * @sps: &struct nal_h264_sps to convert to RBSP
+ *
+ * Convert @sps to RBSP data and write it into @dest.
+ *
+ * The size of the SPS NAL unit is not known in advance and this function will
+ * fail, if @dest does not hold sufficient space for the SPS NAL unit.
+ *
+ * Return: number of bytes written to @dest or negative error code
+ */
+ssize_t nal_h264_write_sps(const struct device *dev,
+ void *dest, size_t n, struct nal_h264_sps *sps)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_ref_idc = 0;
+ unsigned int nal_unit_type = SEQUENCE_PARAMETER_SET;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_h264_write_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 2, &nal_ref_idc);
+ rbsp_bits(&rbsp, 5, &nal_unit_type);
+
+ nal_h264_rbsp_sps(&rbsp, sps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_write_sps);
+
+/**
+ * nal_h264_read_sps() - Read SPS NAL unit from RBSP format
+ * @dev: device pointer
+ * @sps: the &struct nal_h264_sps to fill from the RBSP data
+ * @src: the buffer that contains the RBSP data
+ * @n: size of @src in bytes
+ *
+ * Read RBSP data from @src and use it to fill @sps.
+ *
+ * Return: number of bytes read from @src or negative error code
+ */
+ssize_t nal_h264_read_sps(const struct device *dev,
+ struct nal_h264_sps *sps, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_ref_idc;
+ unsigned int nal_unit_type;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_h264_read_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 2, &nal_ref_idc);
+ rbsp_bits(&rbsp, 5, &nal_unit_type);
+
+ if (rbsp.error ||
+ forbidden_zero_bit != 0 ||
+ nal_ref_idc != 0 ||
+ nal_unit_type != SEQUENCE_PARAMETER_SET)
+ return -EINVAL;
+
+ nal_h264_rbsp_sps(&rbsp, sps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_read_sps);
+
+/**
+ * nal_h264_write_pps() - Write PPS NAL unit into RBSP format
+ * @dev: device pointer
+ * @dest: the buffer that is filled with RBSP data
+ * @n: maximum size of @dest in bytes
+ * @pps: &struct nal_h264_pps to convert to RBSP
+ *
+ * Convert @pps to RBSP data and write it into @dest.
+ *
+ * The size of the PPS NAL unit is not known in advance and this function will
+ * fail, if @dest does not hold sufficient space for the PPS NAL unit.
+ *
+ * Return: number of bytes written to @dest or negative error code
+ */
+ssize_t nal_h264_write_pps(const struct device *dev,
+ void *dest, size_t n, struct nal_h264_pps *pps)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_ref_idc = 0;
+ unsigned int nal_unit_type = PICTURE_PARAMETER_SET;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_h264_write_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 2, &nal_ref_idc);
+ rbsp_bits(&rbsp, 5, &nal_unit_type);
+
+ nal_h264_rbsp_pps(&rbsp, pps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_write_pps);
+
+/**
+ * nal_h264_read_pps() - Read PPS NAL unit from RBSP format
+ * @dev: device pointer
+ * @pps: the &struct nal_h264_pps to fill from the RBSP data
+ * @src: the buffer that contains the RBSP data
+ * @n: size of @src in bytes
+ *
+ * Read RBSP data from @src and use it to fill @pps.
+ *
+ * Return: number of bytes read from @src or negative error code
+ */
+ssize_t nal_h264_read_pps(const struct device *dev,
+ struct nal_h264_pps *pps, void *src, size_t n)
+{
+ struct rbsp rbsp;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_h264_read_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp.pos += 8;
+
+ nal_h264_rbsp_pps(&rbsp, pps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_read_pps);
+
+/**
+ * nal_h264_write_filler() - Write filler data RBSP
+ * @dev: device pointer
+ * @dest: buffer to fill with filler data
+ * @n: size of the buffer to fill with filler data
+ *
+ * Write a filler data RBSP to @dest with a size of @n bytes and return the
+ * number of written filler data bytes.
+ *
+ * Use this function to generate dummy data in an RBSP data stream that can be
+ * safely ignored by h264 decoders.
+ *
+ * The RBSP format of the filler data is specified in Rec. ITU-T H.264
+ * (04/2017) 7.3.2.7 Filler data RBSP syntax.
+ *
+ * Return: number of filler data bytes (including marker) or negative error
+ */
+ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_ref_idc = 0;
+ unsigned int nal_unit_type = FILLER_DATA;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_h264_write_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 2, &nal_ref_idc);
+ rbsp_bits(&rbsp, 5, &nal_unit_type);
+
+ nal_h264_write_filler_data(&rbsp);
+
+ rbsp_trailing_bits(&rbsp);
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_write_filler);
+
+/**
+ * nal_h264_read_filler() - Read filler data RBSP
+ * @dev: device pointer
+ * @src: buffer with RBSP data that is read
+ * @n: maximum size of src that shall be read
+ *
+ * Read a filler data RBSP from @src up to a maximum size of @n bytes and
+ * return the size of the filler data in bytes including the marker.
+ *
+ * This function is used to parse filler data and skip the respective bytes in
+ * the RBSP data.
+ *
+ * The RBSP format of the filler data is specified in Rec. ITU-T H.264
+ * (04/2017) 7.3.2.7 Filler data RBSP syntax.
+ *
+ * Return: number of filler data bytes (including marker) or negative error
+ */
+ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_ref_idc;
+ unsigned int nal_unit_type;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_h264_read_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 2, &nal_ref_idc);
+ rbsp_bits(&rbsp, 5, &nal_unit_type);
+
+ if (rbsp.error)
+ return rbsp.error;
+ if (forbidden_zero_bit != 0 ||
+ nal_ref_idc != 0 ||
+ nal_unit_type != FILLER_DATA)
+ return -EINVAL;
+
+ nal_h264_read_filler_data(&rbsp);
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_h264_read_filler);
diff --git a/drivers/media/platform/allegro-dvt/nal-h264.h b/drivers/media/platform/allegro-dvt/nal-h264.h
new file mode 100644
index 000000000000..2ba7cbced7a5
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-h264.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Convert NAL units between raw byte sequence payloads (RBSP) and C structs.
+ */
+
+#ifndef __NAL_H264_H__
+#define __NAL_H264_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+/**
+ * struct nal_h264_hdr_parameters - HDR parameters
+ *
+ * C struct representation of the sequence parameter set NAL unit as defined by
+ * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax.
+ */
+struct nal_h264_hrd_parameters {
+ unsigned int cpb_cnt_minus1;
+ unsigned int bit_rate_scale;
+ unsigned int cpb_size_scale;
+ struct {
+ int bit_rate_value_minus1[16];
+ int cpb_size_value_minus1[16];
+ unsigned int cbr_flag[16];
+ };
+ unsigned int initial_cpb_removal_delay_length_minus1;
+ unsigned int cpb_removal_delay_length_minus1;
+ unsigned int dpb_output_delay_length_minus1;
+ unsigned int time_offset_length;
+};
+
+/**
+ * struct nal_h264_vui_parameters - VUI parameters
+ *
+ * C struct representation of the VUI parameters as defined by Rec. ITU-T
+ * H.264 (04/2017) E.1.1 VUI parameters syntax.
+ */
+struct nal_h264_vui_parameters {
+ unsigned int aspect_ratio_info_present_flag;
+ struct {
+ unsigned int aspect_ratio_idc;
+ unsigned int sar_width;
+ unsigned int sar_height;
+ };
+ unsigned int overscan_info_present_flag;
+ unsigned int overscan_appropriate_flag;
+ unsigned int video_signal_type_present_flag;
+ struct {
+ unsigned int video_format;
+ unsigned int video_full_range_flag;
+ unsigned int colour_description_present_flag;
+ struct {
+ unsigned int colour_primaries;
+ unsigned int transfer_characteristics;
+ unsigned int matrix_coefficients;
+ };
+ };
+ unsigned int chroma_loc_info_present_flag;
+ struct {
+ unsigned int chroma_sample_loc_type_top_field;
+ unsigned int chroma_sample_loc_type_bottom_field;
+ };
+ unsigned int timing_info_present_flag;
+ struct {
+ unsigned int num_units_in_tick;
+ unsigned int time_scale;
+ unsigned int fixed_frame_rate_flag;
+ };
+ unsigned int nal_hrd_parameters_present_flag;
+ struct nal_h264_hrd_parameters nal_hrd_parameters;
+ unsigned int vcl_hrd_parameters_present_flag;
+ struct nal_h264_hrd_parameters vcl_hrd_parameters;
+ unsigned int low_delay_hrd_flag;
+ unsigned int pic_struct_present_flag;
+ unsigned int bitstream_restriction_flag;
+ struct {
+ unsigned int motion_vectors_over_pic_boundaries_flag;
+ unsigned int max_bytes_per_pic_denom;
+ unsigned int max_bits_per_mb_denom;
+ unsigned int log2_max_mv_length_horizontal;
+ unsigned int log21_max_mv_length_vertical;
+ unsigned int max_num_reorder_frames;
+ unsigned int max_dec_frame_buffering;
+ };
+};
+
+/**
+ * struct nal_h264_sps - Sequence parameter set
+ *
+ * C struct representation of the sequence parameter set NAL unit as defined by
+ * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax.
+ */
+struct nal_h264_sps {
+ unsigned int profile_idc;
+ unsigned int constraint_set0_flag;
+ unsigned int constraint_set1_flag;
+ unsigned int constraint_set2_flag;
+ unsigned int constraint_set3_flag;
+ unsigned int constraint_set4_flag;
+ unsigned int constraint_set5_flag;
+ unsigned int reserved_zero_2bits;
+ unsigned int level_idc;
+ unsigned int seq_parameter_set_id;
+ struct {
+ unsigned int chroma_format_idc;
+ unsigned int separate_colour_plane_flag;
+ unsigned int bit_depth_luma_minus8;
+ unsigned int bit_depth_chroma_minus8;
+ unsigned int qpprime_y_zero_transform_bypass_flag;
+ unsigned int seq_scaling_matrix_present_flag;
+ };
+ unsigned int log2_max_frame_num_minus4;
+ unsigned int pic_order_cnt_type;
+ union {
+ unsigned int log2_max_pic_order_cnt_lsb_minus4;
+ struct {
+ unsigned int delta_pic_order_always_zero_flag;
+ int offset_for_non_ref_pic;
+ int offset_for_top_to_bottom_field;
+ unsigned int num_ref_frames_in_pic_order_cnt_cycle;
+ int offset_for_ref_frame[255];
+ };
+ };
+ unsigned int max_num_ref_frames;
+ unsigned int gaps_in_frame_num_value_allowed_flag;
+ unsigned int pic_width_in_mbs_minus1;
+ unsigned int pic_height_in_map_units_minus1;
+ unsigned int frame_mbs_only_flag;
+ unsigned int mb_adaptive_frame_field_flag;
+ unsigned int direct_8x8_inference_flag;
+ unsigned int frame_cropping_flag;
+ struct {
+ unsigned int crop_left;
+ unsigned int crop_right;
+ unsigned int crop_top;
+ unsigned int crop_bottom;
+ };
+ unsigned int vui_parameters_present_flag;
+ struct nal_h264_vui_parameters vui;
+};
+
+/**
+ * struct nal_h264_pps - Picture parameter set
+ *
+ * C struct representation of the picture parameter set NAL unit as defined by
+ * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax.
+ */
+struct nal_h264_pps {
+ unsigned int pic_parameter_set_id;
+ unsigned int seq_parameter_set_id;
+ unsigned int entropy_coding_mode_flag;
+ unsigned int bottom_field_pic_order_in_frame_present_flag;
+ unsigned int num_slice_groups_minus1;
+ unsigned int slice_group_map_type;
+ union {
+ unsigned int run_length_minus1[8];
+ struct {
+ unsigned int top_left[8];
+ unsigned int bottom_right[8];
+ };
+ struct {
+ unsigned int slice_group_change_direction_flag;
+ unsigned int slice_group_change_rate_minus1;
+ };
+ struct {
+ unsigned int pic_size_in_map_units_minus1;
+ unsigned int slice_group_id[8];
+ };
+ };
+ unsigned int num_ref_idx_l0_default_active_minus1;
+ unsigned int num_ref_idx_l1_default_active_minus1;
+ unsigned int weighted_pred_flag;
+ unsigned int weighted_bipred_idc;
+ int pic_init_qp_minus26;
+ int pic_init_qs_minus26;
+ int chroma_qp_index_offset;
+ unsigned int deblocking_filter_control_present_flag;
+ unsigned int constrained_intra_pred_flag;
+ unsigned int redundant_pic_cnt_present_flag;
+ struct {
+ unsigned int transform_8x8_mode_flag;
+ unsigned int pic_scaling_matrix_present_flag;
+ int second_chroma_qp_index_offset;
+ };
+};
+
+int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile);
+int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level);
+
+ssize_t nal_h264_write_sps(const struct device *dev,
+ void *dest, size_t n, struct nal_h264_sps *sps);
+ssize_t nal_h264_read_sps(const struct device *dev,
+ struct nal_h264_sps *sps, void *src, size_t n);
+void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps);
+
+ssize_t nal_h264_write_pps(const struct device *dev,
+ void *dest, size_t n, struct nal_h264_pps *pps);
+ssize_t nal_h264_read_pps(const struct device *dev,
+ struct nal_h264_pps *pps, void *src, size_t n);
+void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps);
+
+ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n);
+ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n);
+
+#endif /* __NAL_H264_H__ */
diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.c b/drivers/media/platform/allegro-dvt/nal-hevc.c
new file mode 100644
index 000000000000..5db540c69bfe
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Convert NAL units between raw byte sequence payloads (RBSP) and C structs.
+ *
+ * The conversion is defined in "ITU-T Rec. H.265 (02/2018) high efficiency
+ * video coding". Decoder drivers may use the parser to parse RBSP from
+ * encoded streams and configure the hardware, if the hardware is not able to
+ * parse RBSP itself. Encoder drivers may use the generator to generate the
+ * RBSP for VPS/SPS/PPS nal units and add them to the encoded stream if the
+ * hardware does not generate the units.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/v4l2-controls.h>
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/log2.h>
+
+#include "nal-hevc.h"
+#include "nal-rbsp.h"
+
+/*
+ * See Rec. ITU-T H.265 (02/2018) Table 7-1 – NAL unit type codes and NAL unit
+ * type classes
+ */
+enum nal_unit_type {
+ VPS_NUT = 32,
+ SPS_NUT = 33,
+ PPS_NUT = 34,
+ FD_NUT = 38,
+};
+
+int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile)
+{
+ switch (profile) {
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
+ return 1;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
+ return 2;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
+ return 3;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(nal_hevc_profile_from_v4l2);
+
+int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier)
+{
+ switch (tier) {
+ case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN:
+ return 0;
+ case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH:
+ return 1;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(nal_hevc_tier_from_v4l2);
+
+int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level)
+{
+ /*
+ * T-Rec-H.265 p. 280: general_level_idc and sub_layer_level_idc[ i ]
+ * shall be set equal to a value of 30 times the level number
+ * specified in Table A.6.
+ */
+ int factor = 30 / 10;
+
+ switch (level) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ return factor * 10;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ return factor * 20;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ return factor * 21;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ return factor * 30;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ return factor * 31;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ return factor * 40;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ return factor * 41;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ return factor * 50;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ return factor * 51;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
+ return factor * 52;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6:
+ return factor * 60;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1:
+ return factor * 61;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2:
+ return factor * 62;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(nal_hevc_level_from_v4l2);
+
+static void nal_hevc_write_start_code_prefix(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i = 4;
+
+ if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ p[0] = 0x00;
+ p[1] = 0x00;
+ p[2] = 0x00;
+ p[3] = 0x01;
+
+ rbsp->pos += i * 8;
+}
+
+static void nal_hevc_read_start_code_prefix(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i = 4;
+
+ if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ rbsp->pos += i * 8;
+}
+
+static void nal_hevc_write_filler_data(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+ int i;
+
+ /* Keep 1 byte extra for terminating the NAL unit */
+ i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1;
+ memset(p, 0xff, i);
+ rbsp->pos += i * 8;
+}
+
+static void nal_hevc_read_filler_data(struct rbsp *rbsp)
+{
+ u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8);
+
+ while (*p == 0xff) {
+ if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) {
+ rbsp->error = -EINVAL;
+ return;
+ }
+
+ p++;
+ rbsp->pos += 8;
+ }
+}
+
+static void nal_hevc_rbsp_profile_tier_level(struct rbsp *rbsp,
+ struct nal_hevc_profile_tier_level *ptl)
+{
+ unsigned int i;
+ unsigned int max_num_sub_layers_minus_1 = 0;
+
+ rbsp_bits(rbsp, 2, &ptl->general_profile_space);
+ rbsp_bit(rbsp, &ptl->general_tier_flag);
+ rbsp_bits(rbsp, 5, &ptl->general_profile_idc);
+ for (i = 0; i < 32; i++)
+ rbsp_bit(rbsp, &ptl->general_profile_compatibility_flag[i]);
+ rbsp_bit(rbsp, &ptl->general_progressive_source_flag);
+ rbsp_bit(rbsp, &ptl->general_interlaced_source_flag);
+ rbsp_bit(rbsp, &ptl->general_non_packed_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_frame_only_constraint_flag);
+ if (ptl->general_profile_idc == 4 ||
+ ptl->general_profile_compatibility_flag[4] ||
+ ptl->general_profile_idc == 5 ||
+ ptl->general_profile_compatibility_flag[5] ||
+ ptl->general_profile_idc == 6 ||
+ ptl->general_profile_compatibility_flag[6] ||
+ ptl->general_profile_idc == 7 ||
+ ptl->general_profile_compatibility_flag[7] ||
+ ptl->general_profile_idc == 8 ||
+ ptl->general_profile_compatibility_flag[8] ||
+ ptl->general_profile_idc == 9 ||
+ ptl->general_profile_compatibility_flag[9] ||
+ ptl->general_profile_idc == 10 ||
+ ptl->general_profile_compatibility_flag[10]) {
+ rbsp_bit(rbsp, &ptl->general_max_12bit_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_max_10bit_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_max_8bit_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_max_422chroma_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_max_420chroma_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_max_monochrome_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_intra_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag);
+ rbsp_bit(rbsp, &ptl->general_lower_bit_rate_constraint_flag);
+ if (ptl->general_profile_idc == 5 ||
+ ptl->general_profile_compatibility_flag[5] ||
+ ptl->general_profile_idc == 9 ||
+ ptl->general_profile_compatibility_flag[9] ||
+ ptl->general_profile_idc == 10 ||
+ ptl->general_profile_compatibility_flag[10]) {
+ rbsp_bit(rbsp, &ptl->general_max_14bit_constraint_flag);
+ rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_33bits);
+ rbsp_bits(rbsp, 33 - 32, &ptl->general_reserved_zero_33bits);
+ } else {
+ rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_34bits);
+ rbsp_bits(rbsp, 34 - 2, &ptl->general_reserved_zero_34bits);
+ }
+ } else if (ptl->general_profile_idc == 2 ||
+ ptl->general_profile_compatibility_flag[2]) {
+ rbsp_bits(rbsp, 7, &ptl->general_reserved_zero_7bits);
+ rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag);
+ rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_35bits);
+ rbsp_bits(rbsp, 35 - 32, &ptl->general_reserved_zero_35bits);
+ } else {
+ rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_43bits);
+ rbsp_bits(rbsp, 43 - 32, &ptl->general_reserved_zero_43bits);
+ }
+ if ((ptl->general_profile_idc >= 1 && ptl->general_profile_idc <= 5) ||
+ ptl->general_profile_idc == 9 ||
+ ptl->general_profile_compatibility_flag[1] ||
+ ptl->general_profile_compatibility_flag[2] ||
+ ptl->general_profile_compatibility_flag[3] ||
+ ptl->general_profile_compatibility_flag[4] ||
+ ptl->general_profile_compatibility_flag[5] ||
+ ptl->general_profile_compatibility_flag[9])
+ rbsp_bit(rbsp, &ptl->general_inbld_flag);
+ else
+ rbsp_bit(rbsp, &ptl->general_reserved_zero_bit);
+ rbsp_bits(rbsp, 8, &ptl->general_level_idc);
+ if (max_num_sub_layers_minus_1 > 0)
+ rbsp_unsupported(rbsp);
+}
+
+static void nal_hevc_rbsp_vps(struct rbsp *rbsp, struct nal_hevc_vps *vps)
+{
+ unsigned int i, j;
+ unsigned int reserved_0xffff_16bits = 0xffff;
+
+ rbsp_bits(rbsp, 4, &vps->video_parameter_set_id);
+ rbsp_bit(rbsp, &vps->base_layer_internal_flag);
+ rbsp_bit(rbsp, &vps->base_layer_available_flag);
+ rbsp_bits(rbsp, 6, &vps->max_layers_minus1);
+ rbsp_bits(rbsp, 3, &vps->max_sub_layers_minus1);
+ rbsp_bits(rbsp, 1, &vps->temporal_id_nesting_flag);
+ rbsp_bits(rbsp, 16, &reserved_0xffff_16bits);
+ nal_hevc_rbsp_profile_tier_level(rbsp, &vps->profile_tier_level);
+ rbsp_bit(rbsp, &vps->sub_layer_ordering_info_present_flag);
+ for (i = vps->sub_layer_ordering_info_present_flag ? 0 : vps->max_sub_layers_minus1;
+ i <= vps->max_sub_layers_minus1; i++) {
+ rbsp_uev(rbsp, &vps->max_dec_pic_buffering_minus1[i]);
+ rbsp_uev(rbsp, &vps->max_num_reorder_pics[i]);
+ rbsp_uev(rbsp, &vps->max_latency_increase_plus1[i]);
+ }
+ rbsp_bits(rbsp, 6, &vps->max_layer_id);
+ rbsp_uev(rbsp, &vps->num_layer_sets_minus1);
+ for (i = 0; i <= vps->num_layer_sets_minus1; i++)
+ for (j = 0; j <= vps->max_layer_id; j++)
+ rbsp_bit(rbsp, &vps->layer_id_included_flag[i][j]);
+ rbsp_bit(rbsp, &vps->timing_info_present_flag);
+ if (vps->timing_info_present_flag)
+ rbsp_unsupported(rbsp);
+ rbsp_bit(rbsp, &vps->extension_flag);
+ if (vps->extension_flag)
+ rbsp_unsupported(rbsp);
+}
+
+static void nal_hevc_rbsp_sps(struct rbsp *rbsp, struct nal_hevc_sps *sps)
+{
+ unsigned int i;
+
+ rbsp_bits(rbsp, 4, &sps->video_parameter_set_id);
+ rbsp_bits(rbsp, 3, &sps->max_sub_layers_minus1);
+ rbsp_bit(rbsp, &sps->temporal_id_nesting_flag);
+ nal_hevc_rbsp_profile_tier_level(rbsp, &sps->profile_tier_level);
+ rbsp_uev(rbsp, &sps->seq_parameter_set_id);
+
+ rbsp_uev(rbsp, &sps->chroma_format_idc);
+ if (sps->chroma_format_idc == 3)
+ rbsp_bit(rbsp, &sps->separate_colour_plane_flag);
+ rbsp_uev(rbsp, &sps->pic_width_in_luma_samples);
+ rbsp_uev(rbsp, &sps->pic_height_in_luma_samples);
+ rbsp_bit(rbsp, &sps->conformance_window_flag);
+ if (sps->conformance_window_flag) {
+ rbsp_uev(rbsp, &sps->conf_win_left_offset);
+ rbsp_uev(rbsp, &sps->conf_win_right_offset);
+ rbsp_uev(rbsp, &sps->conf_win_top_offset);
+ rbsp_uev(rbsp, &sps->conf_win_bottom_offset);
+ }
+ rbsp_uev(rbsp, &sps->bit_depth_luma_minus8);
+ rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8);
+
+ rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4);
+
+ rbsp_bit(rbsp, &sps->sub_layer_ordering_info_present_flag);
+ for (i = (sps->sub_layer_ordering_info_present_flag ? 0 : sps->max_sub_layers_minus1);
+ i <= sps->max_sub_layers_minus1; i++) {
+ rbsp_uev(rbsp, &sps->max_dec_pic_buffering_minus1[i]);
+ rbsp_uev(rbsp, &sps->max_num_reorder_pics[i]);
+ rbsp_uev(rbsp, &sps->max_latency_increase_plus1[i]);
+ }
+ rbsp_uev(rbsp, &sps->log2_min_luma_coding_block_size_minus3);
+ rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_coding_block_size);
+ rbsp_uev(rbsp, &sps->log2_min_luma_transform_block_size_minus2);
+ rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_transform_block_size);
+ rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_inter);
+ rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_intra);
+
+ rbsp_bit(rbsp, &sps->scaling_list_enabled_flag);
+ if (sps->scaling_list_enabled_flag)
+ rbsp_unsupported(rbsp);
+
+ rbsp_bit(rbsp, &sps->amp_enabled_flag);
+ rbsp_bit(rbsp, &sps->sample_adaptive_offset_enabled_flag);
+ rbsp_bit(rbsp, &sps->pcm_enabled_flag);
+ if (sps->pcm_enabled_flag) {
+ rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_luma_minus1);
+ rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_chroma_minus1);
+ rbsp_uev(rbsp, &sps->log2_min_pcm_luma_coding_block_size_minus3);
+ rbsp_uev(rbsp, &sps->log2_diff_max_min_pcm_luma_coding_block_size);
+ rbsp_bit(rbsp, &sps->pcm_loop_filter_disabled_flag);
+ }
+
+ rbsp_uev(rbsp, &sps->num_short_term_ref_pic_sets);
+ if (sps->num_short_term_ref_pic_sets > 0)
+ rbsp_unsupported(rbsp);
+
+ rbsp_bit(rbsp, &sps->long_term_ref_pics_present_flag);
+ if (sps->long_term_ref_pics_present_flag)
+ rbsp_unsupported(rbsp);
+
+ rbsp_bit(rbsp, &sps->sps_temporal_mvp_enabled_flag);
+ rbsp_bit(rbsp, &sps->strong_intra_smoothing_enabled_flag);
+ rbsp_bit(rbsp, &sps->vui_parameters_present_flag);
+ if (sps->vui_parameters_present_flag)
+ rbsp_unsupported(rbsp);
+
+ rbsp_bit(rbsp, &sps->extension_present_flag);
+ if (sps->extension_present_flag) {
+ rbsp_bit(rbsp, &sps->sps_range_extension_flag);
+ rbsp_bit(rbsp, &sps->sps_multilayer_extension_flag);
+ rbsp_bit(rbsp, &sps->sps_3d_extension_flag);
+ rbsp_bit(rbsp, &sps->sps_scc_extension_flag);
+ rbsp_bits(rbsp, 5, &sps->sps_extension_4bits);
+ }
+ if (sps->sps_range_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (sps->sps_multilayer_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (sps->sps_3d_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (sps->sps_scc_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (sps->sps_extension_4bits)
+ rbsp_unsupported(rbsp);
+}
+
+static void nal_hevc_rbsp_pps(struct rbsp *rbsp, struct nal_hevc_pps *pps)
+{
+ unsigned int i;
+
+ rbsp_uev(rbsp, &pps->pps_pic_parameter_set_id);
+ rbsp_uev(rbsp, &pps->pps_seq_parameter_set_id);
+ rbsp_bit(rbsp, &pps->dependent_slice_segments_enabled_flag);
+ rbsp_bit(rbsp, &pps->output_flag_present_flag);
+ rbsp_bits(rbsp, 3, &pps->num_extra_slice_header_bits);
+ rbsp_bit(rbsp, &pps->sign_data_hiding_enabled_flag);
+ rbsp_bit(rbsp, &pps->cabac_init_present_flag);
+ rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1);
+ rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1);
+ rbsp_sev(rbsp, &pps->init_qp_minus26);
+ rbsp_bit(rbsp, &pps->constrained_intra_pred_flag);
+ rbsp_bit(rbsp, &pps->transform_skip_enabled_flag);
+ rbsp_bit(rbsp, &pps->cu_qp_delta_enabled_flag);
+ if (pps->cu_qp_delta_enabled_flag)
+ rbsp_uev(rbsp, &pps->diff_cu_qp_delta_depth);
+ rbsp_sev(rbsp, &pps->pps_cb_qp_offset);
+ rbsp_sev(rbsp, &pps->pps_cr_qp_offset);
+ rbsp_bit(rbsp, &pps->pps_slice_chroma_qp_offsets_present_flag);
+ rbsp_bit(rbsp, &pps->weighted_pred_flag);
+ rbsp_bit(rbsp, &pps->weighted_bipred_flag);
+ rbsp_bit(rbsp, &pps->transquant_bypass_enabled_flag);
+ rbsp_bit(rbsp, &pps->tiles_enabled_flag);
+ rbsp_bit(rbsp, &pps->entropy_coding_sync_enabled_flag);
+ if (pps->tiles_enabled_flag) {
+ rbsp_uev(rbsp, &pps->num_tile_columns_minus1);
+ rbsp_uev(rbsp, &pps->num_tile_rows_minus1);
+ rbsp_bit(rbsp, &pps->uniform_spacing_flag);
+ if (!pps->uniform_spacing_flag) {
+ for (i = 0; i < pps->num_tile_columns_minus1; i++)
+ rbsp_uev(rbsp, &pps->column_width_minus1[i]);
+ for (i = 0; i < pps->num_tile_rows_minus1; i++)
+ rbsp_uev(rbsp, &pps->row_height_minus1[i]);
+ }
+ rbsp_bit(rbsp, &pps->loop_filter_across_tiles_enabled_flag);
+ }
+ rbsp_bit(rbsp, &pps->pps_loop_filter_across_slices_enabled_flag);
+ rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag);
+ if (pps->deblocking_filter_control_present_flag) {
+ rbsp_bit(rbsp, &pps->deblocking_filter_override_enabled_flag);
+ rbsp_bit(rbsp, &pps->pps_deblocking_filter_disabled_flag);
+ if (!pps->pps_deblocking_filter_disabled_flag) {
+ rbsp_sev(rbsp, &pps->pps_beta_offset_div2);
+ rbsp_sev(rbsp, &pps->pps_tc_offset_div2);
+ }
+ }
+ rbsp_bit(rbsp, &pps->pps_scaling_list_data_present_flag);
+ if (pps->pps_scaling_list_data_present_flag)
+ rbsp_unsupported(rbsp);
+ rbsp_bit(rbsp, &pps->lists_modification_present_flag);
+ rbsp_uev(rbsp, &pps->log2_parallel_merge_level_minus2);
+ rbsp_bit(rbsp, &pps->slice_segment_header_extension_present_flag);
+ rbsp_bit(rbsp, &pps->pps_extension_present_flag);
+ if (pps->pps_extension_present_flag) {
+ rbsp_bit(rbsp, &pps->pps_range_extension_flag);
+ rbsp_bit(rbsp, &pps->pps_multilayer_extension_flag);
+ rbsp_bit(rbsp, &pps->pps_3d_extension_flag);
+ rbsp_bit(rbsp, &pps->pps_scc_extension_flag);
+ rbsp_bits(rbsp, 4, &pps->pps_extension_4bits);
+ }
+ if (pps->pps_range_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (pps->pps_multilayer_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (pps->pps_3d_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (pps->pps_scc_extension_flag)
+ rbsp_unsupported(rbsp);
+ if (pps->pps_extension_4bits)
+ rbsp_unsupported(rbsp);
+}
+
+/**
+ * nal_hevc_write_vps() - Write PPS NAL unit into RBSP format
+ * @dev: device pointer
+ * @dest: the buffer that is filled with RBSP data
+ * @n: maximum size of @dest in bytes
+ * @vps: &struct nal_hevc_vps to convert to RBSP
+ *
+ * Convert @vps to RBSP data and write it into @dest.
+ *
+ * The size of the VPS NAL unit is not known in advance and this function will
+ * fail, if @dest does not hold sufficient space for the VPS NAL unit.
+ *
+ * Return: number of bytes written to @dest or negative error code
+ */
+ssize_t nal_hevc_write_vps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_vps *vps)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_unit_type = VPS_NUT;
+ unsigned int nuh_layer_id = 0;
+ unsigned int nuh_temporal_id_plus1 = 1;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_hevc_write_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ nal_hevc_rbsp_vps(&rbsp, vps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_write_vps);
+
+/**
+ * nal_hevc_read_vps() - Read VPS NAL unit from RBSP format
+ * @dev: device pointer
+ * @vps: the &struct nal_hevc_vps to fill from the RBSP data
+ * @src: the buffer that contains the RBSP data
+ * @n: size of @src in bytes
+ *
+ * Read RBSP data from @src and use it to fill @vps.
+ *
+ * Return: number of bytes read from @src or negative error code
+ */
+ssize_t nal_hevc_read_vps(const struct device *dev,
+ struct nal_hevc_vps *vps, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_unit_type;
+ unsigned int nuh_layer_id;
+ unsigned int nuh_temporal_id_plus1;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_hevc_read_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ if (rbsp.error ||
+ forbidden_zero_bit != 0 ||
+ nal_unit_type != VPS_NUT)
+ return -EINVAL;
+
+ nal_hevc_rbsp_vps(&rbsp, vps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_read_vps);
+
+/**
+ * nal_hevc_write_sps() - Write SPS NAL unit into RBSP format
+ * @dev: device pointer
+ * @dest: the buffer that is filled with RBSP data
+ * @n: maximum size of @dest in bytes
+ * @sps: &struct nal_hevc_sps to convert to RBSP
+ *
+ * Convert @sps to RBSP data and write it into @dest.
+ *
+ * The size of the SPS NAL unit is not known in advance and this function will
+ * fail, if @dest does not hold sufficient space for the SPS NAL unit.
+ *
+ * Return: number of bytes written to @dest or negative error code
+ */
+ssize_t nal_hevc_write_sps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_sps *sps)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_unit_type = SPS_NUT;
+ unsigned int nuh_layer_id = 0;
+ unsigned int nuh_temporal_id_plus1 = 1;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_hevc_write_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ nal_hevc_rbsp_sps(&rbsp, sps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_write_sps);
+
+/**
+ * nal_hevc_read_sps() - Read SPS NAL unit from RBSP format
+ * @dev: device pointer
+ * @sps: the &struct nal_hevc_sps to fill from the RBSP data
+ * @src: the buffer that contains the RBSP data
+ * @n: size of @src in bytes
+ *
+ * Read RBSP data from @src and use it to fill @sps.
+ *
+ * Return: number of bytes read from @src or negative error code
+ */
+ssize_t nal_hevc_read_sps(const struct device *dev,
+ struct nal_hevc_sps *sps, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_unit_type;
+ unsigned int nuh_layer_id;
+ unsigned int nuh_temporal_id_plus1;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_hevc_read_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ if (rbsp.error ||
+ forbidden_zero_bit != 0 ||
+ nal_unit_type != SPS_NUT)
+ return -EINVAL;
+
+ nal_hevc_rbsp_sps(&rbsp, sps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_read_sps);
+
+/**
+ * nal_hevc_write_pps() - Write PPS NAL unit into RBSP format
+ * @dev: device pointer
+ * @dest: the buffer that is filled with RBSP data
+ * @n: maximum size of @dest in bytes
+ * @pps: &struct nal_hevc_pps to convert to RBSP
+ *
+ * Convert @pps to RBSP data and write it into @dest.
+ *
+ * The size of the PPS NAL unit is not known in advance and this function will
+ * fail, if @dest does not hold sufficient space for the PPS NAL unit.
+ *
+ * Return: number of bytes written to @dest or negative error code
+ */
+ssize_t nal_hevc_write_pps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_pps *pps)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_unit_type = PPS_NUT;
+ unsigned int nuh_layer_id = 0;
+ unsigned int nuh_temporal_id_plus1 = 1;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_hevc_write_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ nal_hevc_rbsp_pps(&rbsp, pps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_write_pps);
+
+/**
+ * nal_hevc_read_pps() - Read PPS NAL unit from RBSP format
+ * @dev: device pointer
+ * @pps: the &struct nal_hevc_pps to fill from the RBSP data
+ * @src: the buffer that contains the RBSP data
+ * @n: size of @src in bytes
+ *
+ * Read RBSP data from @src and use it to fill @pps.
+ *
+ * Return: number of bytes read from @src or negative error code
+ */
+ssize_t nal_hevc_read_pps(const struct device *dev,
+ struct nal_hevc_pps *pps, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_unit_type;
+ unsigned int nuh_layer_id;
+ unsigned int nuh_temporal_id_plus1;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_hevc_read_start_code_prefix(&rbsp);
+
+ /* NAL unit header */
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ nal_hevc_rbsp_pps(&rbsp, pps);
+
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_read_pps);
+
+/**
+ * nal_hevc_write_filler() - Write filler data RBSP
+ * @dev: device pointer
+ * @dest: buffer to fill with filler data
+ * @n: size of the buffer to fill with filler data
+ *
+ * Write a filler data RBSP to @dest with a size of @n bytes and return the
+ * number of written filler data bytes.
+ *
+ * Use this function to generate dummy data in an RBSP data stream that can be
+ * safely ignored by hevc decoders.
+ *
+ * The RBSP format of the filler data is specified in Rec. ITU-T H.265
+ * (02/2018) 7.3.2.8 Filler data RBSP syntax.
+ *
+ * Return: number of filler data bytes (including marker) or negative error
+ */
+ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit = 0;
+ unsigned int nal_unit_type = FD_NUT;
+ unsigned int nuh_layer_id = 0;
+ unsigned int nuh_temporal_id_plus1 = 1;
+
+ if (!dest)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, dest, n, &write);
+
+ nal_hevc_write_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ nal_hevc_write_filler_data(&rbsp);
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_write_filler);
+
+/**
+ * nal_hevc_read_filler() - Read filler data RBSP
+ * @dev: device pointer
+ * @src: buffer with RBSP data that is read
+ * @n: maximum size of src that shall be read
+ *
+ * Read a filler data RBSP from @src up to a maximum size of @n bytes and
+ * return the size of the filler data in bytes including the marker.
+ *
+ * This function is used to parse filler data and skip the respective bytes in
+ * the RBSP data.
+ *
+ * The RBSP format of the filler data is specified in Rec. ITU-T H.265
+ * (02/2018) 7.3.2.8 Filler data RBSP syntax.
+ *
+ * Return: number of filler data bytes (including marker) or negative error
+ */
+ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n)
+{
+ struct rbsp rbsp;
+ unsigned int forbidden_zero_bit;
+ unsigned int nal_unit_type;
+ unsigned int nuh_layer_id;
+ unsigned int nuh_temporal_id_plus1;
+
+ if (!src)
+ return -EINVAL;
+
+ rbsp_init(&rbsp, src, n, &read);
+
+ nal_hevc_read_start_code_prefix(&rbsp);
+
+ rbsp_bit(&rbsp, &forbidden_zero_bit);
+ rbsp_bits(&rbsp, 6, &nal_unit_type);
+ rbsp_bits(&rbsp, 6, &nuh_layer_id);
+ rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1);
+
+ if (rbsp.error)
+ return rbsp.error;
+ if (forbidden_zero_bit != 0 ||
+ nal_unit_type != FD_NUT)
+ return -EINVAL;
+
+ nal_hevc_read_filler_data(&rbsp);
+ rbsp_trailing_bits(&rbsp);
+
+ if (rbsp.error)
+ return rbsp.error;
+
+ return DIV_ROUND_UP(rbsp.pos, 8);
+}
+EXPORT_SYMBOL_GPL(nal_hevc_read_filler);
diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h
new file mode 100644
index 000000000000..fc994d4242d8
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.h
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Convert NAL units between raw byte sequence payloads (RBSP) and C structs.
+ */
+
+#ifndef __NAL_HEVC_H__
+#define __NAL_HEVC_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+
+struct nal_hevc_profile_tier_level {
+ unsigned int general_profile_space;
+ unsigned int general_tier_flag;
+ unsigned int general_profile_idc;
+ unsigned int general_profile_compatibility_flag[32];
+ unsigned int general_progressive_source_flag;
+ unsigned int general_interlaced_source_flag;
+ unsigned int general_non_packed_constraint_flag;
+ unsigned int general_frame_only_constraint_flag;
+ union {
+ struct {
+ unsigned int general_max_12bit_constraint_flag;
+ unsigned int general_max_10bit_constraint_flag;
+ unsigned int general_max_8bit_constraint_flag;
+ unsigned int general_max_422chroma_constraint_flag;
+ unsigned int general_max_420chroma_constraint_flag;
+ unsigned int general_max_monochrome_constraint_flag;
+ unsigned int general_intra_constraint_flag;
+ unsigned int general_one_picture_only_constraint_flag;
+ unsigned int general_lower_bit_rate_constraint_flag;
+ union {
+ struct {
+ unsigned int general_max_14bit_constraint_flag;
+ unsigned int general_reserved_zero_33bits;
+ };
+ unsigned int general_reserved_zero_34bits;
+ };
+ };
+ struct {
+ unsigned int general_reserved_zero_7bits;
+ /* unsigned int general_one_picture_only_constraint_flag; */
+ unsigned int general_reserved_zero_35bits;
+ };
+ unsigned int general_reserved_zero_43bits;
+ };
+ union {
+ unsigned int general_inbld_flag;
+ unsigned int general_reserved_zero_bit;
+ };
+ unsigned int general_level_idc;
+};
+
+/**
+ * struct nal_hevc_vps - Video parameter set
+ *
+ * C struct representation of the video parameter set NAL unit as defined by
+ * Rec. ITU-T H.265 (02/2018) 7.3.2.1 Video parameter set RBSP syntax
+ */
+struct nal_hevc_vps {
+ unsigned int video_parameter_set_id;
+ unsigned int base_layer_internal_flag;
+ unsigned int base_layer_available_flag;
+ unsigned int max_layers_minus1;
+ unsigned int max_sub_layers_minus1;
+ unsigned int temporal_id_nesting_flag;
+ struct nal_hevc_profile_tier_level profile_tier_level;
+ unsigned int sub_layer_ordering_info_present_flag;
+ struct {
+ unsigned int max_dec_pic_buffering_minus1[7];
+ unsigned int max_num_reorder_pics[7];
+ unsigned int max_latency_increase_plus1[7];
+ };
+ unsigned int max_layer_id;
+ unsigned int num_layer_sets_minus1;
+ unsigned int layer_id_included_flag[1024][64];
+ unsigned int timing_info_present_flag;
+ struct {
+ unsigned int num_units_in_tick;
+ unsigned int time_scale;
+ unsigned int poc_proportional_to_timing_flag;
+ unsigned int num_ticks_poc_diff_one_minus1;
+ unsigned int num_hrd_parameters;
+ struct {
+ unsigned int hrd_layer_set_idx[0];
+ unsigned int cprms_present_flag[0];
+ };
+ /* hrd_parameters( cprms_present_flag[ i ], max_sub_layers_minus1 ) */
+ };
+ unsigned int extension_flag;
+ unsigned int extension_data_flag;
+};
+
+struct nal_hevc_sub_layer_hrd_parameters {
+ unsigned int bit_rate_value_minus1[1];
+ unsigned int cpb_size_value_minus1[1];
+ unsigned int cbr_flag[1];
+};
+
+struct nal_hevc_hrd_parameters {
+ unsigned int nal_hrd_parameters_present_flag;
+ unsigned int vcl_hrd_parameters_present_flag;
+ struct {
+ unsigned int sub_pic_hrd_params_present_flag;
+ struct {
+ unsigned int tick_divisor_minus2;
+ unsigned int du_cpb_removal_delay_increment_length_minus1;
+ unsigned int sub_pic_cpb_params_in_pic_timing_sei_flag;
+ unsigned int dpb_output_delay_du_length_minus1;
+ };
+ unsigned int bit_rate_scale;
+ unsigned int cpb_size_scale;
+ unsigned int cpb_size_du_scale;
+ unsigned int initial_cpb_removal_delay_length_minus1;
+ unsigned int au_cpb_removal_delay_length_minus1;
+ unsigned int dpb_output_delay_length_minus1;
+ };
+ struct {
+ unsigned int fixed_pic_rate_general_flag[1];
+ unsigned int fixed_pic_rate_within_cvs_flag[1];
+ unsigned int elemental_duration_in_tc_minus1[1];
+ unsigned int low_delay_hrd_flag[1];
+ unsigned int cpb_cnt_minus1[1];
+ struct nal_hevc_sub_layer_hrd_parameters nal_hrd[1];
+ struct nal_hevc_sub_layer_hrd_parameters vcl_hrd[1];
+ };
+};
+
+/**
+ * struct nal_hevc_vui_parameters - VUI parameters
+ *
+ * C struct representation of the VUI parameters as defined by Rec. ITU-T
+ * H.265 (02/2018) E.2.1 VUI parameters syntax.
+ */
+struct nal_hevc_vui_parameters {
+ unsigned int aspect_ratio_info_present_flag;
+ struct {
+ unsigned int aspect_ratio_idc;
+ unsigned int sar_width;
+ unsigned int sar_height;
+ };
+ unsigned int overscan_info_present_flag;
+ unsigned int overscan_appropriate_flag;
+ unsigned int video_signal_type_present_flag;
+ struct {
+ unsigned int video_format;
+ unsigned int video_full_range_flag;
+ unsigned int colour_description_present_flag;
+ struct {
+ unsigned int colour_primaries;
+ unsigned int transfer_characteristics;
+ unsigned int matrix_coeffs;
+ };
+ };
+ unsigned int chroma_loc_info_present_flag;
+ struct {
+ unsigned int chroma_sample_loc_type_top_field;
+ unsigned int chroma_sample_loc_type_bottom_field;
+ };
+ unsigned int neutral_chroma_indication_flag;
+ unsigned int field_seq_flag;
+ unsigned int frame_field_info_present_flag;
+ unsigned int default_display_window_flag;
+ struct {
+ unsigned int def_disp_win_left_offset;
+ unsigned int def_disp_win_right_offset;
+ unsigned int def_disp_win_top_offset;
+ unsigned int def_disp_win_bottom_offset;
+ };
+ unsigned int vui_timing_info_present_flag;
+ struct {
+ unsigned int vui_num_units_in_tick;
+ unsigned int vui_time_scale;
+ unsigned int vui_poc_proportional_to_timing_flag;
+ unsigned int vui_num_ticks_poc_diff_one_minus1;
+ unsigned int vui_hrd_parameters_present_flag;
+ struct nal_hevc_hrd_parameters nal_hrd_parameters;
+ };
+ unsigned int bitstream_restriction_flag;
+ struct {
+ unsigned int tiles_fixed_structure_flag;
+ unsigned int motion_vectors_over_pic_boundaries_flag;
+ unsigned int restricted_ref_pic_lists_flag;
+ unsigned int min_spatial_segmentation_idc;
+ unsigned int max_bytes_per_pic_denom;
+ unsigned int max_bits_per_min_cu_denom;
+ unsigned int log2_max_mv_length_horizontal;
+ unsigned int log2_max_mv_length_vertical;
+ };
+};
+
+/**
+ * struct nal_hevc_sps - Sequence parameter set
+ *
+ * C struct representation of the video parameter set NAL unit as defined by
+ * Rec. ITU-T H.265 (02/2018) 7.3.2.2 Sequence parameter set RBSP syntax
+ */
+struct nal_hevc_sps {
+ unsigned int video_parameter_set_id;
+ unsigned int max_sub_layers_minus1;
+ unsigned int temporal_id_nesting_flag;
+ struct nal_hevc_profile_tier_level profile_tier_level;
+ unsigned int seq_parameter_set_id;
+ unsigned int chroma_format_idc;
+ unsigned int separate_colour_plane_flag;
+ unsigned int pic_width_in_luma_samples;
+ unsigned int pic_height_in_luma_samples;
+ unsigned int conformance_window_flag;
+ struct {
+ unsigned int conf_win_left_offset;
+ unsigned int conf_win_right_offset;
+ unsigned int conf_win_top_offset;
+ unsigned int conf_win_bottom_offset;
+ };
+
+ unsigned int bit_depth_luma_minus8;
+ unsigned int bit_depth_chroma_minus8;
+ unsigned int log2_max_pic_order_cnt_lsb_minus4;
+ unsigned int sub_layer_ordering_info_present_flag;
+ struct {
+ unsigned int max_dec_pic_buffering_minus1[7];
+ unsigned int max_num_reorder_pics[7];
+ unsigned int max_latency_increase_plus1[7];
+ };
+ unsigned int log2_min_luma_coding_block_size_minus3;
+ unsigned int log2_diff_max_min_luma_coding_block_size;
+ unsigned int log2_min_luma_transform_block_size_minus2;
+ unsigned int log2_diff_max_min_luma_transform_block_size;
+ unsigned int max_transform_hierarchy_depth_inter;
+ unsigned int max_transform_hierarchy_depth_intra;
+
+ unsigned int scaling_list_enabled_flag;
+ unsigned int scaling_list_data_present_flag;
+ unsigned int amp_enabled_flag;
+ unsigned int sample_adaptive_offset_enabled_flag;
+ unsigned int pcm_enabled_flag;
+ struct {
+ unsigned int pcm_sample_bit_depth_luma_minus1;
+ unsigned int pcm_sample_bit_depth_chroma_minus1;
+ unsigned int log2_min_pcm_luma_coding_block_size_minus3;
+ unsigned int log2_diff_max_min_pcm_luma_coding_block_size;
+ unsigned int pcm_loop_filter_disabled_flag;
+ };
+
+ unsigned int num_short_term_ref_pic_sets;
+ unsigned int long_term_ref_pics_present_flag;
+ unsigned int sps_temporal_mvp_enabled_flag;
+ unsigned int strong_intra_smoothing_enabled_flag;
+ unsigned int vui_parameters_present_flag;
+ struct nal_hevc_vui_parameters vui;
+ unsigned int extension_present_flag;
+ struct {
+ unsigned int sps_range_extension_flag;
+ unsigned int sps_multilayer_extension_flag;
+ unsigned int sps_3d_extension_flag;
+ unsigned int sps_scc_extension_flag;
+ unsigned int sps_extension_4bits;
+ };
+};
+
+struct nal_hevc_pps {
+ unsigned int pps_pic_parameter_set_id;
+ unsigned int pps_seq_parameter_set_id;
+ unsigned int dependent_slice_segments_enabled_flag;
+ unsigned int output_flag_present_flag;
+ unsigned int num_extra_slice_header_bits;
+ unsigned int sign_data_hiding_enabled_flag;
+ unsigned int cabac_init_present_flag;
+ unsigned int num_ref_idx_l0_default_active_minus1;
+ unsigned int num_ref_idx_l1_default_active_minus1;
+ int init_qp_minus26;
+ unsigned int constrained_intra_pred_flag;
+ unsigned int transform_skip_enabled_flag;
+ unsigned int cu_qp_delta_enabled_flag;
+ unsigned int diff_cu_qp_delta_depth;
+ int pps_cb_qp_offset;
+ int pps_cr_qp_offset;
+ unsigned int pps_slice_chroma_qp_offsets_present_flag;
+ unsigned int weighted_pred_flag;
+ unsigned int weighted_bipred_flag;
+ unsigned int transquant_bypass_enabled_flag;
+ unsigned int tiles_enabled_flag;
+ unsigned int entropy_coding_sync_enabled_flag;
+ struct {
+ unsigned int num_tile_columns_minus1;
+ unsigned int num_tile_rows_minus1;
+ unsigned int uniform_spacing_flag;
+ struct {
+ unsigned int column_width_minus1[1];
+ unsigned int row_height_minus1[1];
+ };
+ unsigned int loop_filter_across_tiles_enabled_flag;
+ };
+ unsigned int pps_loop_filter_across_slices_enabled_flag;
+ unsigned int deblocking_filter_control_present_flag;
+ struct {
+ unsigned int deblocking_filter_override_enabled_flag;
+ unsigned int pps_deblocking_filter_disabled_flag;
+ struct {
+ int pps_beta_offset_div2;
+ int pps_tc_offset_div2;
+ };
+ };
+ unsigned int pps_scaling_list_data_present_flag;
+ unsigned int lists_modification_present_flag;
+ unsigned int log2_parallel_merge_level_minus2;
+ unsigned int slice_segment_header_extension_present_flag;
+ unsigned int pps_extension_present_flag;
+ struct {
+ unsigned int pps_range_extension_flag;
+ unsigned int pps_multilayer_extension_flag;
+ unsigned int pps_3d_extension_flag;
+ unsigned int pps_scc_extension_flag;
+ unsigned int pps_extension_4bits;
+ };
+};
+
+int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile);
+int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier);
+int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level);
+
+int nal_range_from_v4l2(enum v4l2_quantization quantization);
+int nal_color_primaries_from_v4l2(enum v4l2_colorspace colorspace);
+int nal_transfer_characteristics_from_v4l2(enum v4l2_colorspace colorspace,
+ enum v4l2_xfer_func xfer_func);
+int nal_matrix_coeffs_from_v4l2(enum v4l2_colorspace colorspace,
+ enum v4l2_ycbcr_encoding ycbcr_encoding);
+
+ssize_t nal_hevc_write_vps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_vps *vps);
+ssize_t nal_hevc_read_vps(const struct device *dev,
+ struct nal_hevc_vps *vps, void *src, size_t n);
+
+ssize_t nal_hevc_write_sps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_sps *sps);
+ssize_t nal_hevc_read_sps(const struct device *dev,
+ struct nal_hevc_sps *sps, void *src, size_t n);
+
+ssize_t nal_hevc_write_pps(const struct device *dev,
+ void *dest, size_t n, struct nal_hevc_pps *pps);
+ssize_t nal_hevc_read_pps(const struct device *dev,
+ struct nal_hevc_pps *pps, void *src, size_t n);
+
+ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n);
+ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n);
+
+#endif /* __NAL_HEVC_H__ */
diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.c b/drivers/media/platform/allegro-dvt/nal-rbsp.c
new file mode 100644
index 000000000000..d911322d0efa
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-rbsp.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ *
+ * Helper functions to generate a raw byte sequence payload from values.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/v4l2-controls.h>
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/log2.h>
+
+#include "nal-rbsp.h"
+
+void rbsp_init(struct rbsp *rbsp, void *addr, size_t size,
+ struct nal_rbsp_ops *ops)
+{
+ if (!rbsp)
+ return;
+
+ rbsp->data = addr;
+ rbsp->size = size;
+ rbsp->pos = 0;
+ rbsp->ops = ops;
+ rbsp->error = 0;
+}
+
+void rbsp_unsupported(struct rbsp *rbsp)
+{
+ rbsp->error = -EINVAL;
+}
+
+static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value);
+static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value);
+
+/*
+ * When reading or writing, the emulation_prevention_three_byte is detected
+ * only when the 2 one bits need to be inserted. Therefore, we are not
+ * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the
+ * next byte.
+ */
+#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6)
+
+static int add_emulation_prevention_three_byte(struct rbsp *rbsp)
+{
+ rbsp->num_consecutive_zeros = 0;
+ rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE);
+
+ return 0;
+}
+
+static int discard_emulation_prevention_three_byte(struct rbsp *rbsp)
+{
+ unsigned int tmp = 0;
+
+ rbsp->num_consecutive_zeros = 0;
+ rbsp_read_bits(rbsp, 8, &tmp);
+ if (tmp != EMULATION_PREVENTION_THREE_BYTE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int rbsp_read_bit(struct rbsp *rbsp)
+{
+ int shift;
+ int ofs;
+ int bit;
+ int err;
+
+ if (rbsp->num_consecutive_zeros == 22) {
+ err = discard_emulation_prevention_three_byte(rbsp);
+ if (err)
+ return err;
+ }
+
+ shift = 7 - (rbsp->pos % 8);
+ ofs = rbsp->pos / 8;
+ if (ofs >= rbsp->size)
+ return -EINVAL;
+
+ bit = (rbsp->data[ofs] >> shift) & 1;
+
+ rbsp->pos++;
+
+ if (bit == 1 ||
+ (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0)))
+ rbsp->num_consecutive_zeros = 0;
+ else
+ rbsp->num_consecutive_zeros++;
+
+ return bit;
+}
+
+static inline int rbsp_write_bit(struct rbsp *rbsp, bool value)
+{
+ int shift;
+ int ofs;
+
+ if (rbsp->num_consecutive_zeros == 22)
+ add_emulation_prevention_three_byte(rbsp);
+
+ shift = 7 - (rbsp->pos % 8);
+ ofs = rbsp->pos / 8;
+ if (ofs >= rbsp->size)
+ return -EINVAL;
+
+ rbsp->data[ofs] &= ~(1 << shift);
+ rbsp->data[ofs] |= value << shift;
+
+ rbsp->pos++;
+
+ if (value ||
+ (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) {
+ rbsp->num_consecutive_zeros = 0;
+ } else {
+ rbsp->num_consecutive_zeros++;
+ }
+
+ return 0;
+}
+
+static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value)
+{
+ int i;
+ int bit;
+ unsigned int tmp = 0;
+
+ if (n > 8 * sizeof(*value))
+ return -EINVAL;
+
+ for (i = n; i > 0; i--) {
+ bit = rbsp_read_bit(rbsp);
+ if (bit < 0)
+ return bit;
+ tmp |= bit << (i - 1);
+ }
+
+ if (value)
+ *value = tmp;
+
+ return 0;
+}
+
+static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value)
+{
+ int ret;
+
+ if (n > 8 * sizeof(value))
+ return -EINVAL;
+
+ while (n--) {
+ ret = rbsp_write_bit(rbsp, (value >> n) & 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value)
+{
+ int leading_zero_bits = 0;
+ unsigned int tmp = 0;
+ int ret;
+
+ while ((ret = rbsp_read_bit(rbsp)) == 0)
+ leading_zero_bits++;
+ if (ret < 0)
+ return ret;
+
+ if (leading_zero_bits > 0) {
+ ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp);
+ if (ret)
+ return ret;
+ }
+
+ if (value)
+ *value = (1 << leading_zero_bits) - 1 + tmp;
+
+ return 0;
+}
+
+static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value)
+{
+ int ret;
+ int leading_zero_bits;
+
+ if (!value)
+ return -EINVAL;
+
+ leading_zero_bits = ilog2(*value + 1);
+
+ ret = rbsp_write_bits(rbsp, leading_zero_bits, 0);
+ if (ret)
+ return ret;
+
+ return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1);
+}
+
+static int rbsp_read_sev(struct rbsp *rbsp, int *value)
+{
+ int ret;
+ unsigned int tmp;
+
+ ret = rbsp_read_uev(rbsp, &tmp);
+ if (ret)
+ return ret;
+
+ if (value) {
+ if (tmp & 1)
+ *value = (tmp + 1) / 2;
+ else
+ *value = -(tmp / 2);
+ }
+
+ return 0;
+}
+
+static int rbsp_write_sev(struct rbsp *rbsp, int *value)
+{
+ unsigned int tmp;
+
+ if (!value)
+ return -EINVAL;
+
+ if (*value > 0)
+ tmp = (2 * (*value)) | 1;
+ else
+ tmp = -2 * (*value);
+
+ return rbsp_write_uev(rbsp, &tmp);
+}
+
+static int __rbsp_write_bit(struct rbsp *rbsp, int *value)
+{
+ return rbsp_write_bit(rbsp, *value);
+}
+
+static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value)
+{
+ return rbsp_write_bits(rbsp, n, *value);
+}
+
+struct nal_rbsp_ops write = {
+ .rbsp_bit = __rbsp_write_bit,
+ .rbsp_bits = __rbsp_write_bits,
+ .rbsp_uev = rbsp_write_uev,
+ .rbsp_sev = rbsp_write_sev,
+};
+
+static int __rbsp_read_bit(struct rbsp *rbsp, int *value)
+{
+ int tmp = rbsp_read_bit(rbsp);
+
+ if (tmp < 0)
+ return tmp;
+ *value = tmp;
+
+ return 0;
+}
+
+struct nal_rbsp_ops read = {
+ .rbsp_bit = __rbsp_read_bit,
+ .rbsp_bits = rbsp_read_bits,
+ .rbsp_uev = rbsp_read_uev,
+ .rbsp_sev = rbsp_read_sev,
+};
+
+void rbsp_bit(struct rbsp *rbsp, int *value)
+{
+ if (rbsp->error)
+ return;
+ rbsp->error = rbsp->ops->rbsp_bit(rbsp, value);
+}
+
+void rbsp_bits(struct rbsp *rbsp, int n, int *value)
+{
+ if (rbsp->error)
+ return;
+ rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value);
+}
+
+void rbsp_uev(struct rbsp *rbsp, unsigned int *value)
+{
+ if (rbsp->error)
+ return;
+ rbsp->error = rbsp->ops->rbsp_uev(rbsp, value);
+}
+
+void rbsp_sev(struct rbsp *rbsp, int *value)
+{
+ if (rbsp->error)
+ return;
+ rbsp->error = rbsp->ops->rbsp_sev(rbsp, value);
+}
+
+void rbsp_trailing_bits(struct rbsp *rbsp)
+{
+ unsigned int rbsp_stop_one_bit = 1;
+ unsigned int rbsp_alignment_zero_bit = 0;
+
+ rbsp_bit(rbsp, &rbsp_stop_one_bit);
+ rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos,
+ &rbsp_alignment_zero_bit);
+}
diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.h b/drivers/media/platform/allegro-dvt/nal-rbsp.h
new file mode 100644
index 000000000000..c72f49fed8d3
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-rbsp.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de>
+ */
+
+#ifndef __NAL_RBSP_H__
+#define __NAL_RBSP_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+struct rbsp;
+
+struct nal_rbsp_ops {
+ int (*rbsp_bit)(struct rbsp *rbsp, int *val);
+ int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val);
+ int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val);
+ int (*rbsp_sev)(struct rbsp *rbsp, int *val);
+};
+
+/**
+ * struct rbsp - State object for handling a raw byte sequence payload
+ * @data: pointer to the data of the rbsp
+ * @size: maximum size of the data of the rbsp
+ * @pos: current bit position inside the rbsp
+ * @num_consecutive_zeros: number of zeros before @pos
+ * @ops: per datatype functions for interacting with the rbsp
+ * @error: an error occurred while handling the rbsp
+ *
+ * This struct is passed around the various parsing functions and tracks the
+ * current position within the raw byte sequence payload.
+ *
+ * The @ops field allows to separate the operation, i.e., reading/writing a
+ * value from/to that rbsp, from the structure of the NAL unit. This allows to
+ * have a single function for iterating the NAL unit, while @ops has function
+ * pointers for handling each type in the rbsp.
+ */
+struct rbsp {
+ u8 *data;
+ size_t size;
+ unsigned int pos;
+ unsigned int num_consecutive_zeros;
+ struct nal_rbsp_ops *ops;
+ int error;
+};
+
+extern struct nal_rbsp_ops write;
+extern struct nal_rbsp_ops read;
+
+void rbsp_init(struct rbsp *rbsp, void *addr, size_t size,
+ struct nal_rbsp_ops *ops);
+void rbsp_unsupported(struct rbsp *rbsp);
+
+void rbsp_bit(struct rbsp *rbsp, int *value);
+void rbsp_bits(struct rbsp *rbsp, int n, int *value);
+void rbsp_uev(struct rbsp *rbsp, unsigned int *value);
+void rbsp_sev(struct rbsp *rbsp, int *value);
+
+void rbsp_trailing_bits(struct rbsp *rbsp);
+
+#endif /* __NAL_RBSP_H__ */
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index 0fb9f9ba1219..6cdc77dda0e4 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -2365,7 +2365,7 @@ vpfe_get_pdata(struct vpfe_device *vpfe)
pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev(
&vpfe->notifier, of_fwnode_handle(rem),
- sizeof(struct v4l2_async_subdev));
+ struct v4l2_async_subdev);
of_node_put(rem);
if (IS_ERR(pdata->asd[i]))
goto cleanup;
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
index c46a79eace98..f2c4dadd6a0e 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed-video.c
@@ -1551,12 +1551,12 @@ static int aspeed_video_setup_video(struct aspeed_video *video)
V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask,
V4L2_JPEG_CHROMA_SUBSAMPLING_444);
- if (video->ctrl_handler.error) {
+ rc = video->ctrl_handler.error;
+ if (rc) {
v4l2_ctrl_handler_free(&video->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
- dev_err(video->dev, "Failed to init controls: %d\n",
- video->ctrl_handler.error);
+ dev_err(video->dev, "Failed to init controls: %d\n", rc);
return rc;
}
diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h
index 24b784b893d6..fab8eca58d93 100644
--- a/drivers/media/platform/atmel/atmel-isc.h
+++ b/drivers/media/platform/atmel/atmel-isc.h
@@ -41,6 +41,7 @@ struct isc_buffer {
struct isc_subdev_entity {
struct v4l2_subdev *sd;
struct v4l2_async_subdev *asd;
+ struct device_node *epn;
struct v4l2_async_notifier notifier;
u32 pfe_cfg0;
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index d74aa73f26be..0514be6153df 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -70,7 +70,6 @@ struct frame_buffer {
struct isi_graph_entity {
struct device_node *node;
- struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
};
@@ -1136,45 +1135,26 @@ static const struct v4l2_async_notifier_operations isi_graph_notify_ops = {
.complete = isi_graph_notify_complete,
};
-static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node)
-{
- struct device_node *ep = NULL;
- struct device_node *remote;
-
- ep = of_graph_get_next_endpoint(node, ep);
- if (!ep)
- return -EINVAL;
-
- remote = of_graph_get_remote_port_parent(ep);
- of_node_put(ep);
- if (!remote)
- return -EINVAL;
-
- /* Remote node to connect */
- isi->entity.node = remote;
- isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- isi->entity.asd.match.fwnode = of_fwnode_handle(remote);
- return 0;
-}
-
static int isi_graph_init(struct atmel_isi *isi)
{
+ struct v4l2_async_subdev *asd;
+ struct device_node *ep;
int ret;
- /* Parse the graph to extract a list of subdevice DT nodes. */
- ret = isi_graph_parse(isi, isi->dev->of_node);
- if (ret < 0) {
- dev_err(isi->dev, "Graph parsing failed\n");
- return ret;
- }
+ ep = of_graph_get_next_endpoint(isi->dev->of_node, NULL);
+ if (!ep)
+ return -EINVAL;
v4l2_async_notifier_init(&isi->notifier);
- ret = v4l2_async_notifier_add_subdev(&isi->notifier, &isi->entity.asd);
- if (ret) {
- of_node_put(isi->entity.node);
- return ret;
- }
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &isi->notifier,
+ of_fwnode_handle(ep),
+ struct v4l2_async_subdev);
+ of_node_put(ep);
+
+ if (IS_ERR(asd))
+ return PTR_ERR(asd);
isi->notifier.ops = &isi_graph_notify_ops;
diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
index a3304f49e499..0b78fecfd2a8 100644
--- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c
+++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
@@ -57,7 +57,7 @@
static int isc_parse_dt(struct device *dev, struct isc_device *isc)
{
struct device_node *np = dev->of_node;
- struct device_node *epn = NULL, *rem;
+ struct device_node *epn = NULL;
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
int ret;
@@ -71,17 +71,9 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
if (!epn)
return 0;
- rem = of_graph_get_remote_port_parent(epn);
- if (!rem) {
- dev_notice(dev, "Remote device at %pOF not found\n",
- epn);
- continue;
- }
-
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
&v4l2_epn);
if (ret) {
- of_node_put(rem);
ret = -EINVAL;
dev_err(dev, "Could not parse the endpoint\n");
break;
@@ -90,21 +82,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
GFP_KERNEL);
if (!subdev_entity) {
- of_node_put(rem);
- ret = -ENOMEM;
- break;
- }
-
- /* asd will be freed by the subsystem once it's added to the
- * notifier list
- */
- subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd),
- GFP_KERNEL);
- if (!subdev_entity->asd) {
- of_node_put(rem);
ret = -ENOMEM;
break;
}
+ subdev_entity->epn = epn;
flags = v4l2_epn.bus.parallel.flags;
@@ -121,12 +102,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
ISC_PFE_CFG0_CCIR656;
- subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
- subdev_entity->asd->match.fwnode = of_fwnode_handle(rem);
list_add_tail(&subdev_entity->list, &isc->subdev_entities);
}
-
of_node_put(epn);
+
return ret;
}
@@ -228,13 +207,20 @@ static int atmel_isc_probe(struct platform_device *pdev)
}
list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
+ struct v4l2_async_subdev *asd;
+
v4l2_async_notifier_init(&subdev_entity->notifier);
- ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier,
- subdev_entity->asd);
- if (ret) {
- fwnode_handle_put(subdev_entity->asd->match.fwnode);
- kfree(subdev_entity->asd);
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &subdev_entity->notifier,
+ of_fwnode_handle(subdev_entity->epn),
+ struct v4l2_async_subdev);
+
+ of_node_put(subdev_entity->epn);
+ subdev_entity->epn = NULL;
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
goto cleanup_subdev;
}
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index be9ec59774d6..c68a3eac62cd 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -81,7 +81,6 @@ struct csi2rx_priv {
struct media_pad pads[CSI2RX_PAD_MAX];
/* Remote source */
- struct v4l2_async_subdev asd;
struct v4l2_subdev *source_subdev;
int source_pad;
};
@@ -362,6 +361,7 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
{
struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
+ struct v4l2_async_subdev *asd;
struct fwnode_handle *fwh;
struct device_node *ep;
int ret;
@@ -395,17 +395,14 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
return -EINVAL;
}
- csi2rx->asd.match.fwnode = fwnode_graph_get_remote_port_parent(fwh);
- csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- of_node_put(ep);
-
v4l2_async_notifier_init(&csi2rx->notifier);
- ret = v4l2_async_notifier_add_subdev(&csi2rx->notifier, &csi2rx->asd);
- if (ret) {
- fwnode_handle_put(csi2rx->asd.match.fwnode);
- return ret;
- }
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi2rx->notifier,
+ fwh,
+ struct v4l2_async_subdev);
+ of_node_put(ep);
+ if (IS_ERR(asd))
+ return PTR_ERR(asd);
csi2rx->notifier.ops = &csi2rx_notifier_ops;
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index fe9468b180e6..5f0aeb744e81 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -628,7 +628,7 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
if (err) {
v4l2_err(&vpbe_dev->v4l2_dev,
"unable to initialize the OSD device");
- err = -ENOMEM;
+ ret = -ENOMEM;
goto fail_dev_unregister;
}
}
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 5e67994e62cc..f1ce10828b8e 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -41,7 +41,7 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME);
#define VPIF_CH2_MAX_MODES 15
#define VPIF_CH3_MAX_MODES 2
-spinlock_t vpif_lock;
+DEFINE_SPINLOCK(vpif_lock);
EXPORT_SYMBOL_GPL(vpif_lock);
void __iomem *vpif_base;
@@ -437,7 +437,6 @@ static int vpif_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev);
- spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
/*
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 72a0e94e2e21..8d2e165bf7de 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1584,7 +1584,7 @@ vpif_capture_get_pdata(struct platform_device *pdev)
pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev(
&vpif_obj.notifier, of_fwnode_handle(rem),
- sizeof(struct v4l2_async_subdev));
+ struct v4l2_async_subdev);
if (IS_ERR(pdata->asd[i]))
goto err_cleanup;
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 46afc029138f..e5f61d9b221d 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -1117,23 +1117,6 @@ static void free_vpif_objs(void)
kfree(vpif_obj.dev[i]);
}
-static int vpif_async_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
-{
- int i;
-
- for (i = 0; i < vpif_obj.config->subdev_count; i++)
- if (!strcmp(vpif_obj.config->subdevinfo[i].name,
- subdev->name)) {
- vpif_obj.sd[i] = subdev;
- vpif_obj.sd[i]->grp_id = 1 << i;
- return 0;
- }
-
- return -EINVAL;
-}
-
static int vpif_probe_complete(void)
{
struct common_obj *common;
@@ -1230,16 +1213,6 @@ probe_out:
return err;
}
-static int vpif_async_complete(struct v4l2_async_notifier *notifier)
-{
- return vpif_probe_complete();
-}
-
-static const struct v4l2_async_notifier_operations vpif_async_ops = {
- .bound = vpif_async_bound,
- .complete = vpif_async_complete,
-};
-
/*
* vpif_probe: This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
@@ -1294,52 +1267,28 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_unregister;
}
- v4l2_async_notifier_init(&vpif_obj.notifier);
-
- if (!vpif_obj.config->asd_sizes) {
- i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id);
- for (i = 0; i < subdev_count; i++) {
- vpif_obj.sd[i] =
- v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata[i].
- board_info,
- NULL);
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- err = -ENODEV;
- goto probe_subdev_out;
- }
-
- if (vpif_obj.sd[i])
- vpif_obj.sd[i]->grp_id = 1 << i;
- }
- err = vpif_probe_complete();
- if (err) {
+ i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id);
+ for (i = 0; i < subdev_count; i++) {
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata[i].board_info,
+ NULL);
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
goto probe_subdev_out;
}
- } else {
- for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) {
- err = v4l2_async_notifier_add_subdev(
- &vpif_obj.notifier, vpif_obj.config->asd[i]);
- if (err)
- goto probe_cleanup;
- }
- vpif_obj.notifier.ops = &vpif_async_ops;
- err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
- &vpif_obj.notifier);
- if (err) {
- vpif_err("Error registering async notifier\n");
- err = -EINVAL;
- goto probe_cleanup;
- }
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
}
+ err = vpif_probe_complete();
+ if (err)
+ goto probe_subdev_out;
return 0;
-probe_cleanup:
- v4l2_async_notifier_cleanup(&vpif_obj.notifier);
probe_subdev_out:
kfree(vpif_obj.sd);
vpif_unregister:
@@ -1358,11 +1307,6 @@ static int vpif_remove(struct platform_device *device)
struct channel_obj *ch;
int i;
- if (vpif_obj.config->asd_sizes) {
- v4l2_async_notifier_unregister(&vpif_obj.notifier);
- v4l2_async_notifier_cleanup(&vpif_obj.notifier);
- }
-
v4l2_device_unregister(&vpif_obj.v4l2_dev);
kfree(vpif_obj.sd);
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index f731a65eefd6..f98062e79167 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -118,7 +118,6 @@ struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
- struct v4l2_async_notifier notifier;
struct vpif_display_config *config;
};
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index e636c33e847b..8e1e892085ec 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -401,6 +401,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
int index = fmd->num_sensors;
struct fimc_source_info *pd = &fmd->sensor[index].pdata;
struct device_node *rem, *np;
+ struct v4l2_async_subdev *asd;
struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 };
int ret;
@@ -418,10 +419,10 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
pd->mux_id = (endpoint.base.port - 1) & 0x1;
rem = of_graph_get_remote_port_parent(ep);
- of_node_put(ep);
if (rem == NULL) {
v4l2_info(&fmd->v4l2_dev, "Remote device at %pOF not found\n",
ep);
+ of_node_put(ep);
return 0;
}
@@ -450,6 +451,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
* checking parent's node name.
*/
np = of_get_parent(rem);
+ of_node_put(rem);
if (of_node_name_eq(np, "i2c-isp"))
pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
@@ -458,20 +460,20 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
of_node_put(np);
if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) {
- of_node_put(rem);
+ of_node_put(ep);
return -EINVAL;
}
- fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- fmd->sensor[index].asd.match.fwnode = of_fwnode_handle(rem);
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &fmd->subdev_notifier, of_fwnode_handle(ep),
+ struct v4l2_async_subdev);
- ret = v4l2_async_notifier_add_subdev(&fmd->subdev_notifier,
- &fmd->sensor[index].asd);
- if (ret) {
- of_node_put(rem);
- return ret;
- }
+ of_node_put(ep);
+
+ if (IS_ERR(asd))
+ return PTR_ERR(asd);
+ fmd->sensor[index].asd = asd;
fmd->num_sensors++;
return 0;
@@ -1381,7 +1383,8 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
/* Find platform data for this sensor subdev */
for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
- if (fmd->sensor[i].asd.match.fwnode ==
+ if (fmd->sensor[i].asd &&
+ fmd->sensor[i].asd->match.fwnode ==
of_fwnode_handle(subdev->dev->of_node))
si = &fmd->sensor[i];
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 9447fafe23c6..a3876d668ea6 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -83,7 +83,7 @@ struct fimc_camclk_info {
*/
struct fimc_sensor_info {
struct fimc_source_info pdata;
- struct v4l2_async_subdev asd;
+ struct v4l2_async_subdev *asd;
struct v4l2_subdev *subdev;
struct fimc_dev *host;
};
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index 00f623d62c96..9c94a8b58b7c 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -489,6 +489,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
int ret;
struct cafe_camera *cam;
struct mcam_camera *mcam;
+ struct v4l2_async_subdev *asd;
/*
* Start putting together one of our big camera structures.
@@ -546,9 +547,16 @@ static int cafe_pci_probe(struct pci_dev *pdev,
if (ret)
goto out_pdown;
- mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C;
- mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter);
- mcam->asd.match.i2c.address = ov7670_info.addr;
+ v4l2_async_notifier_init(&mcam->notifier);
+
+ asd = v4l2_async_notifier_add_i2c_subdev(&mcam->notifier,
+ i2c_adapter_id(cam->i2c_adapter),
+ ov7670_info.addr,
+ struct v4l2_async_subdev);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto out_smbus_shutdown;
+ }
ret = mccic_register(mcam);
if (ret)
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index c012fd2e1d29..141bf5d97a04 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -931,6 +931,7 @@ static int mclk_enable(struct clk_hw *hw)
mclk_div = 2;
}
+ pm_runtime_get_sync(cam->dev);
clk_enable(cam->clk[0]);
mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div);
mcam_ctlr_power_up(cam);
@@ -944,6 +945,7 @@ static void mclk_disable(struct clk_hw *hw)
mcam_ctlr_power_down(cam);
clk_disable(cam->clk[0]);
+ pm_runtime_put(cam->dev);
}
static unsigned long mclk_recalc_rate(struct clk_hw *hw,
@@ -1866,16 +1868,6 @@ int mccic_register(struct mcam_camera *cam)
cam->pix_format = mcam_def_pix_format;
cam->mbus_code = mcam_def_mbus_code;
- /*
- * Register sensor notifier.
- */
- v4l2_async_notifier_init(&cam->notifier);
- ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd);
- if (ret) {
- cam_warn(cam, "failed to add subdev to a notifier");
- goto out;
- }
-
cam->notifier.ops = &mccic_notify_ops;
ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier);
if (ret < 0) {
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index b55545822fd2..f324d808d737 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -151,7 +151,6 @@ struct mcam_camera {
*/
struct video_device vdev;
struct v4l2_async_notifier notifier;
- struct v4l2_async_subdev asd;
struct v4l2_subdev *sensor;
/* Videobuf2 stuff */
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 032fdddbbecc..f2f09cea751d 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -180,6 +180,7 @@ static int mmpcam_probe(struct platform_device *pdev)
struct resource *res;
struct fwnode_handle *ep;
struct mmp_camera_platform_data *pdata;
+ struct v4l2_async_subdev *asd;
int ret;
cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
@@ -238,10 +239,15 @@ static int mmpcam_probe(struct platform_device *pdev)
if (!ep)
return -ENODEV;
- mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep);
+ v4l2_async_notifier_init(&mcam->notifier);
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(&mcam->notifier, ep,
+ struct v4l2_async_subdev);
fwnode_handle_put(ep);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto out;
+ }
/*
* Register the device with the core.
@@ -278,7 +284,6 @@ static int mmpcam_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
return 0;
out:
- fwnode_handle_put(mcam->asd.match.fwnode);
mccic_shutdown(mcam);
return ret;
diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/meson/ge2d/ge2d.c
index f526501bd473..153612ca96fc 100644
--- a/drivers/media/platform/meson/ge2d/ge2d.c
+++ b/drivers/media/platform/meson/ge2d/ge2d.c
@@ -988,6 +988,7 @@ static int ge2d_probe(struct platform_device *pdev)
vfd = video_device_alloc();
if (!vfd) {
v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
goto unreg_v4l2_dev;
}
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
index 724c7333b6e5..ace4528cdc5e 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
@@ -199,7 +199,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx,
pix_mp->ycbcr_enc = ctx->ycbcr_enc;
pix_mp->quantization = ctx->quant;
}
- memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
max_w = variant->pix_max->target_rot_dis_w;
max_h = variant->pix_max->target_rot_dis_h;
@@ -247,8 +246,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx,
pix_mp->plane_fmt[i].bytesperline = bpl;
if (pix_mp->plane_fmt[i].sizeimage < sizeimage)
pix_mp->plane_fmt[i].sizeimage = sizeimage;
- memset(pix_mp->plane_fmt[i].reserved, 0,
- sizeof(pix_mp->plane_fmt[i].reserved));
mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)", ctx->id,
i, bpl, pix_mp->plane_fmt[i].sizeimage, sizeimage);
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
index c768a587a944..56d86e59421e 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
@@ -657,7 +657,6 @@ static int vidioc_try_fmt(struct v4l2_format *f,
const struct mtk_video_fmt *fmt)
{
struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
- int i;
pix_fmt_mp->field = V4L2_FIELD_NONE;
@@ -715,12 +714,7 @@ static int vidioc_try_fmt(struct v4l2_format *f,
}
}
- for (i = 0; i < pix_fmt_mp->num_planes; i++)
- memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0,
- sizeof(pix_fmt_mp->plane_fmt[0].reserved));
-
pix_fmt_mp->flags = 0;
- memset(&pix_fmt_mp->reserved, 0x0, sizeof(pix_fmt_mp->reserved));
return 0;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
index 21de1431cfcb..8c917969c2f1 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
@@ -121,7 +121,6 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f,
return -EINVAL;
f->pixelformat = formats[f->index].fourcc;
- memset(f->reserved, 0, sizeof(f->reserved));
return 0;
}
@@ -252,7 +251,6 @@ static int vidioc_try_fmt(struct v4l2_format *f,
const struct mtk_video_fmt *fmt)
{
struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
- int i;
pix_fmt_mp->field = V4L2_FIELD_NONE;
@@ -320,13 +318,7 @@ static int vidioc_try_fmt(struct v4l2_format *f,
}
}
- for (i = 0; i < pix_fmt_mp->num_planes; i++)
- memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0,
- sizeof(pix_fmt_mp->plane_fmt[0].reserved));
-
pix_fmt_mp->flags = 0;
- memset(&pix_fmt_mp->reserved, 0x0,
- sizeof(pix_fmt_mp->reserved));
return 0;
}
@@ -532,8 +524,6 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv,
for (i = 0; i < pix->num_planes; i++) {
pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
- memset(&(pix->plane_fmt[i].reserved[0]), 0x0,
- sizeof(pix->plane_fmt[i].reserved));
}
pix->flags = 0;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
index dfb42e19bf81..be3842e6ca47 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
@@ -303,7 +303,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]);
goto err_res;
}
- mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_SYS]);
+ mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_SYS, dev->reg_base[VENC_SYS]);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
@@ -332,7 +332,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]);
goto err_res;
}
- mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_LT_SYS]);
+ mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_LT_SYS, dev->reg_base[VENC_LT_SYS]);
dev->enc_lt_irq = platform_get_irq(pdev, 1);
irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
index a3c7a380c930..70580c2525ba 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
@@ -27,13 +27,13 @@ int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command,
if (!ret) {
status = -1; /* timeout */
- mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!",
- ctx->id, ctx->type, command, timeout_ms,
- ctx->int_cond, ctx->int_type);
+ mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout time=%ums out %d %d!",
+ ctx->id, ctx->type, command, timeout_ms,
+ ctx->int_cond, ctx->int_type);
} else if (-ERESTARTSYS == ret) {
- mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d",
- ctx->id, ctx->type, command, ctx->int_cond,
- ctx->int_type);
+ mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout interrupted by a signal %d %d",
+ ctx->id, ctx->type, command, ctx->int_cond,
+ ctx->int_type);
status = -1;
}
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
index 5ea153a68522..d9880210b2ab 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
@@ -890,7 +890,8 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size);
if (vsi->show_frame & BIT(2)) {
- if (vpu_dec_start(&inst->vpu, NULL, 0)) {
+ ret = vpu_dec_start(&inst->vpu, NULL, 0);
+ if (ret) {
mtk_vcodec_err(inst, "vpu trig decoder failed");
goto DECODE_ERROR;
}
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index b1fc4518e275..a6bb7d9bf75f 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2126,21 +2126,6 @@ static void isp_parse_of_csi1_endpoint(struct device *dev,
buscfg->bus.ccp2.crc = 1;
}
-static int isp_alloc_isd(struct isp_async_subdev **isd,
- struct isp_bus_cfg **buscfg)
-{
- struct isp_async_subdev *__isd;
-
- __isd = kzalloc(sizeof(*__isd), GFP_KERNEL);
- if (!__isd)
- return -ENOMEM;
-
- *isd = __isd;
- *buscfg = &__isd->bus;
-
- return 0;
-}
-
static struct {
u32 phy;
u32 csi2_if;
@@ -2156,7 +2141,6 @@ static int isp_parse_of_endpoints(struct isp_device *isp)
{
struct fwnode_handle *ep;
struct isp_async_subdev *isd = NULL;
- struct isp_bus_cfg *buscfg;
unsigned int i;
ep = fwnode_graph_get_endpoint_by_id(
@@ -2174,20 +2158,13 @@ static int isp_parse_of_endpoints(struct isp_device *isp)
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (!ret) {
- ret = isp_alloc_isd(&isd, &buscfg);
- if (ret)
- return ret;
- }
-
- if (!ret) {
- isp_parse_of_parallel_endpoint(isp->dev, &vep, buscfg);
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(
- &isp->notifier, ep, &isd->asd);
+ isd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &isp->notifier, ep, struct isp_async_subdev);
+ if (!IS_ERR(isd))
+ isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus);
}
fwnode_handle_put(ep);
- if (ret)
- kfree(isd);
}
for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) {
@@ -2206,15 +2183,8 @@ static int isp_parse_of_endpoints(struct isp_device *isp)
dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i,
to_of_node(ep));
- ret = isp_alloc_isd(&isd, &buscfg);
- if (ret)
- return ret;
-
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- if (!ret) {
- buscfg->interface = isp_bus_interfaces[i].csi2_if;
- isp_parse_of_csi2_endpoint(isp->dev, &vep, buscfg);
- } else if (ret == -ENXIO) {
+ if (ret == -ENXIO) {
vep = (struct v4l2_fwnode_endpoint)
{ .bus_type = V4L2_MBUS_CSI1 };
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
@@ -2224,21 +2194,33 @@ static int isp_parse_of_endpoints(struct isp_device *isp)
{ .bus_type = V4L2_MBUS_CCP2 };
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
}
- if (!ret) {
- buscfg->interface =
- isp_bus_interfaces[i].csi1_if;
- isp_parse_of_csi1_endpoint(isp->dev, &vep,
- buscfg);
- }
}
- if (!ret)
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(
- &isp->notifier, ep, &isd->asd);
+ if (!ret) {
+ isd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &isp->notifier, ep, struct isp_async_subdev);
+
+ if (!IS_ERR(isd)) {
+ switch (vep.bus_type) {
+ case V4L2_MBUS_CSI2_DPHY:
+ isd->bus.interface =
+ isp_bus_interfaces[i].csi2_if;
+ isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus);
+ break;
+ case V4L2_MBUS_CSI1:
+ case V4L2_MBUS_CCP2:
+ isd->bus.interface =
+ isp_bus_interfaces[i].csi1_if;
+ isp_parse_of_csi1_endpoint(isp->dev, &vep,
+ &isd->bus);
+ break;
+ default:
+ break;
+ }
+ }
+ }
fwnode_handle_put(ep);
- if (ret)
- kfree(isd);
}
return 0;
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index b664ce7558a1..14077797f5e1 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -31,7 +31,6 @@
#include <linux/dma/pxa-dma.h>
#include <media/v4l2-async.h>
-#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -656,8 +655,6 @@ struct pxa_camera_dev {
const struct pxa_camera_format_xlate *current_fmt;
struct v4l2_pix_format current_pix;
- struct v4l2_async_subdev asd;
-
/*
* PXA27x is only supposed to handle one camera on its Quick Capture
* interface. If anyone ever builds hardware to enable more than
@@ -677,7 +674,6 @@ struct pxa_camera_dev {
unsigned long ciclk;
unsigned long mclk;
u32 mclk_divisor;
- struct v4l2_clk *mclk_clk;
u16 width_flags; /* max 10 bits */
struct list_head capture;
@@ -1386,6 +1382,9 @@ static int pxac_vb2_prepare(struct vb2_buffer *vb)
struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue);
struct pxa_buffer *buf = vb2_to_pxa_buffer(vb);
int ret = 0;
+#ifdef DEBUG
+ int i;
+#endif
switch (pcdev->channels) {
case 1:
@@ -2030,9 +2029,6 @@ static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = {
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-static const struct v4l2_clk_ops pxa_camera_mclk_ops = {
-};
-
static const struct video_device pxa_camera_videodev_template = {
.name = "pxa-camera",
.minor = -1,
@@ -2140,11 +2136,6 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier,
pxa_camera_destroy_formats(pcdev);
- if (pcdev->mclk_clk) {
- v4l2_clk_unregister(pcdev->mclk_clk);
- pcdev->mclk_clk = NULL;
- }
-
video_unregister_device(&pcdev->vdev);
pcdev->sensor = NULL;
@@ -2199,11 +2190,11 @@ static int pxa_camera_resume(struct device *dev)
}
static int pxa_camera_pdata_from_dt(struct device *dev,
- struct pxa_camera_dev *pcdev,
- struct v4l2_async_subdev *asd)
+ struct pxa_camera_dev *pcdev)
{
u32 mclk_rate;
- struct device_node *remote, *np = dev->of_node;
+ struct v4l2_async_subdev *asd;
+ struct device_node *np = dev->of_node;
struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
int err = of_property_read_u32(np, "clock-frequency",
&mclk_rate);
@@ -2255,13 +2246,12 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
pcdev->platform_flags |= PXA_CAMERA_PCLK_EN;
- asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
- remote = of_graph_get_remote_port_parent(np);
- if (remote)
- asd->match.fwnode = of_fwnode_handle(remote);
- else
- dev_notice(dev, "no remote for %pOF\n", np);
-
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &pcdev->notifier,
+ of_fwnode_handle(np),
+ struct v4l2_async_subdev);
+ if (IS_ERR(asd))
+ err = PTR_ERR(asd);
out:
of_node_put(np);
@@ -2278,7 +2268,6 @@ static int pxa_camera_probe(struct platform_device *pdev)
.src_maxburst = 8,
.direction = DMA_DEV_TO_MEM,
};
- char clk_name[V4L2_CLK_NAME_SIZE];
int irq;
int err = 0, i;
@@ -2297,18 +2286,23 @@ static int pxa_camera_probe(struct platform_device *pdev)
if (IS_ERR(pcdev->clk))
return PTR_ERR(pcdev->clk);
+ v4l2_async_notifier_init(&pcdev->notifier);
pcdev->res = res;
-
pcdev->pdata = pdev->dev.platform_data;
if (pcdev->pdata) {
+ struct v4l2_async_subdev *asd;
+
pcdev->platform_flags = pcdev->pdata->flags;
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
- pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C;
- pcdev->asd.match.i2c.adapter_id =
- pcdev->pdata->sensor_i2c_adapter_id;
- pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address;
+ asd = v4l2_async_notifier_add_i2c_subdev(
+ &pcdev->notifier,
+ pcdev->pdata->sensor_i2c_adapter_id,
+ pcdev->pdata->sensor_i2c_address,
+ struct v4l2_async_subdev);
+ if (IS_ERR(asd))
+ err = PTR_ERR(asd);
} else if (pdev->dev.of_node) {
- err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd);
+ err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev);
} else {
return -ENODEV;
}
@@ -2400,43 +2394,18 @@ static int pxa_camera_probe(struct platform_device *pdev)
if (err)
goto exit_deactivate;
- v4l2_async_notifier_init(&pcdev->notifier);
-
- err = v4l2_async_notifier_add_subdev(&pcdev->notifier, &pcdev->asd);
- if (err) {
- fwnode_handle_put(pcdev->asd.match.fwnode);
- goto exit_free_v4l2dev;
- }
-
- pcdev->notifier.ops = &pxa_camera_sensor_ops;
-
- if (!of_have_populated_dt())
- pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C;
-
err = pxa_camera_init_videobuf2(pcdev);
if (err)
goto exit_notifier_cleanup;
- v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
- pcdev->asd.match.i2c.adapter_id,
- pcdev->asd.match.i2c.address);
-
- pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL);
- if (IS_ERR(pcdev->mclk_clk)) {
- err = PTR_ERR(pcdev->mclk_clk);
- goto exit_notifier_cleanup;
- }
-
+ pcdev->notifier.ops = &pxa_camera_sensor_ops;
err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier);
if (err)
- goto exit_free_clk;
+ goto exit_notifier_cleanup;
return 0;
-exit_free_clk:
- v4l2_clk_unregister(pcdev->mclk_clk);
exit_notifier_cleanup:
v4l2_async_notifier_cleanup(&pcdev->notifier);
-exit_free_v4l2dev:
v4l2_device_unregister(&pcdev->v4l2_dev);
exit_deactivate:
pxa_camera_deactivate(pcdev);
@@ -2463,11 +2432,6 @@ static int pxa_camera_remove(struct platform_device *pdev)
v4l2_async_notifier_unregister(&pcdev->notifier);
v4l2_async_notifier_cleanup(&pcdev->notifier);
- if (pcdev->mclk_clk) {
- v4l2_clk_unregister(pcdev->mclk_clk);
- pcdev->mclk_clk = NULL;
- }
-
v4l2_device_unregister(&pcdev->v4l2_dev);
dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index bd9334af1c73..97cea7c4d769 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -579,7 +579,7 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
break;
}
- if (k < f->index)
+ if (k == -1 || k < f->index)
/*
* All the unique pixel formats matching the arguments
* have been enumerated (k >= 0 and f->index > 0), or
@@ -961,6 +961,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
video->nformats = ARRAY_SIZE(formats_rdi_8x96);
}
} else {
+ ret = -EINVAL;
goto error_video_register;
}
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 8fefce57bc49..7c0f669f8aa6 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -655,7 +655,6 @@ static int camss_of_parse_ports(struct camss *camss)
for_each_endpoint_of_node(dev->of_node, node) {
struct camss_async_subdev *csd;
- struct v4l2_async_subdev *asd;
if (!of_device_is_available(node))
continue;
@@ -667,17 +666,15 @@ static int camss_of_parse_ports(struct camss *camss)
goto err_cleanup;
}
- asd = v4l2_async_notifier_add_fwnode_subdev(
+ csd = v4l2_async_notifier_add_fwnode_subdev(
&camss->notifier, of_fwnode_handle(remote),
- sizeof(*csd));
+ struct camss_async_subdev);
of_node_put(remote);
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
+ if (IS_ERR(csd)) {
+ ret = PTR_ERR(csd);
goto err_cleanup;
}
- csd = container_of(asd, struct camss_async_subdev, asd);
-
ret = camss_of_parse_endpoint_node(dev, node, csd);
if (ret < 0)
goto err_cleanup;
diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile
index dfc636865709..91ee6be10292 100644
--- a/drivers/media/platform/qcom/venus/Makefile
+++ b/drivers/media/platform/qcom/venus/Makefile
@@ -3,7 +3,9 @@
venus-core-objs += core.o helpers.o firmware.o \
hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \
- hfi_parser.o pm_helpers.o dbgfs.o
+ hfi_parser.o pm_helpers.o dbgfs.o \
+ hfi_platform.o hfi_platform_v4.o \
+ hfi_platform_v6.o hfi_plat_bufs_v6.o \
venus-dec-objs += vdec.o vdec_ctrls.o
venus-enc-objs += venc.o venc_ctrls.o
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 7233a7311757..f9896c121fd8 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -7,6 +7,7 @@
#include <linux/interconnect.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
+#include <linux/devcoredump.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -22,6 +23,33 @@
#include "firmware.h"
#include "pm_helpers.h"
+static void venus_coredump(struct venus_core *core)
+{
+ struct device *dev;
+ phys_addr_t mem_phys;
+ size_t mem_size;
+ void *mem_va;
+ void *data;
+
+ dev = core->dev;
+ mem_phys = core->fw.mem_phys;
+ mem_size = core->fw.mem_size;
+
+ mem_va = memremap(mem_phys, mem_size, MEMREMAP_WC);
+ if (!mem_va)
+ return;
+
+ data = vmalloc(mem_size);
+ if (!data) {
+ memunmap(mem_va);
+ return;
+ }
+
+ memcpy(data, mem_va, mem_size);
+ memunmap(mem_va);
+ dev_coredumpv(dev, data, mem_size, GFP_KERNEL);
+}
+
static void venus_event_notify(struct venus_core *core, u32 event)
{
struct venus_inst *inst;
@@ -67,6 +95,8 @@ static void venus_sys_error_handler(struct work_struct *work)
venus_shutdown(core);
+ venus_coredump(core);
+
pm_runtime_put_sync(core->dev);
while (core->pmdomains[0] && pm_runtime_active(core->pmdomains[0]))
@@ -488,17 +518,6 @@ static const struct freq_tbl sdm845_freq_table[] = {
{ 244800, 100000000 }, /* 1920x1080@30 */
};
-static const struct codec_freq_data sdm845_codec_freq_data[] = {
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 },
-};
-
static const struct bw_tbl sdm845_bw_table_enc[] = {
{ 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */
{ 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */
@@ -520,8 +539,6 @@ static const struct venus_resources sdm845_res = {
.bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc),
.bw_tbl_dec = sdm845_bw_table_dec,
.bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec),
- .codec_freq_data = sdm845_codec_freq_data,
- .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
.clks = {"core", "iface", "bus" },
.clks_num = 3,
.vcodec0_clks = { "core", "bus" },
@@ -543,8 +560,6 @@ static const struct venus_resources sdm845_res_v2 = {
.bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc),
.bw_tbl_dec = sdm845_bw_table_dec,
.bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec),
- .codec_freq_data = sdm845_codec_freq_data,
- .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
.clks = {"core", "iface", "bus" },
.clks_num = 3,
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
@@ -594,8 +609,6 @@ static const struct venus_resources sc7180_res = {
.bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc),
.bw_tbl_dec = sc7180_bw_table_dec,
.bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec),
- .codec_freq_data = sdm845_codec_freq_data,
- .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data),
.clks = {"core", "iface", "bus" },
.clks_num = 3,
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index f03ed427accd..a252ed32cc14 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -14,6 +14,7 @@
#include "dbgfs.h"
#include "hfi.h"
+#include "hfi_platform.h"
#define VDBGL "VenusLow : "
#define VDBGM "VenusMed : "
@@ -36,13 +37,6 @@ struct reg_val {
u32 value;
};
-struct codec_freq_data {
- u32 pixfmt;
- u32 session_type;
- unsigned long vpp_freq;
- unsigned long vsp_freq;
-};
-
struct bw_tbl {
u32 mbs_per_sec;
u32 avg;
@@ -61,8 +55,6 @@ struct venus_resources {
unsigned int bw_tbl_dec_size;
const struct reg_val *reg_tbl;
unsigned int reg_tbl_size;
- const struct codec_freq_data *codec_freq_data;
- unsigned int codec_freq_data_size;
const char * const clks[VIDC_CLKS_NUM_MAX];
unsigned int clks_num;
const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
@@ -91,30 +83,6 @@ struct venus_format {
u32 flags;
};
-#define MAX_PLANES 4
-#define MAX_FMT_ENTRIES 32
-#define MAX_CAP_ENTRIES 32
-#define MAX_ALLOC_MODE_ENTRIES 16
-#define MAX_CODEC_NUM 32
-
-struct raw_formats {
- u32 buftype;
- u32 fmt;
-};
-
-struct venus_caps {
- u32 codec;
- u32 domain;
- bool cap_bufs_mode_dynamic;
- unsigned int num_caps;
- struct hfi_capability caps[MAX_CAP_ENTRIES];
- unsigned int num_pl;
- struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT];
- unsigned int num_fmts;
- struct raw_formats fmts[MAX_FMT_ENTRIES];
- bool valid; /* used only for Venus v1xx */
-};
-
/**
* struct venus_core - holds core parameters valid for all instances
*
@@ -123,7 +91,6 @@ struct venus_caps {
* @clks: an array of struct clk pointers
* @vcodec0_clks: an array of vcodec0 struct clk pointers
* @vcodec1_clks: an array of vcodec1 struct clk pointers
- * @pd_dl_venus: pmdomain device-link for venus domain
* @pmdomains: an array of pmdomains struct device pointers
* @vdev_dec: a reference to video device structure for decoder instances
* @vdev_enc: a reference to video device structure for encoder instances
@@ -145,7 +112,6 @@ struct venus_caps {
* @enc_codecs: encoders supported by this core
* @dec_codecs: decoders supported by this core
* @max_sessions_supported: holds the maximum number of sessions
- * @core_caps: core capabilities
* @priv: a private filed for HFI operations
* @ops: the core HFI operations
* @work: a delayed work for handling system fatal error
@@ -161,7 +127,6 @@ struct venus_core {
struct icc_path *cpucfg_path;
struct opp_table *opp_table;
bool has_opp_table;
- struct device_link *pd_dl_venus;
struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX];
struct device_link *opp_dl_venus;
struct device *opp_pmdomain;
@@ -177,6 +142,8 @@ struct venus_core {
struct device *dev;
struct iommu_domain *iommu_domain;
size_t mapped_mem_size;
+ phys_addr_t mem_phys;
+ size_t mem_size;
} fw;
struct mutex lock;
struct list_head instances;
@@ -191,15 +158,10 @@ struct venus_core {
unsigned long enc_codecs;
unsigned long dec_codecs;
unsigned int max_sessions_supported;
-#define ENC_ROTATION_CAPABILITY 0x1
-#define ENC_SCALING_CAPABILITY 0x2
-#define ENC_DEINTERLACE_CAPABILITY 0x4
-#define DEC_MULTI_STREAM_CAPABILITY 0x8
- unsigned int core_caps;
void *priv;
const struct hfi_ops *ops;
struct delayed_work work;
- struct venus_caps caps[MAX_CODEC_NUM];
+ struct hfi_plat_caps caps[MAX_CODEC_NUM];
unsigned int codecs_count;
unsigned int core0_usage_count;
unsigned int core1_usage_count;
@@ -230,10 +192,28 @@ struct venc_controls {
u32 h264_b_qp;
u32 h264_min_qp;
u32 h264_max_qp;
+ u32 h264_i_min_qp;
+ u32 h264_i_max_qp;
+ u32 h264_p_min_qp;
+ u32 h264_p_max_qp;
+ u32 h264_b_min_qp;
+ u32 h264_b_max_qp;
u32 h264_loop_filter_mode;
s32 h264_loop_filter_alpha;
s32 h264_loop_filter_beta;
+ u32 hevc_i_qp;
+ u32 hevc_p_qp;
+ u32 hevc_b_qp;
+ u32 hevc_min_qp;
+ u32 hevc_max_qp;
+ u32 hevc_i_min_qp;
+ u32 hevc_i_max_qp;
+ u32 hevc_p_min_qp;
+ u32 hevc_p_max_qp;
+ u32 hevc_b_min_qp;
+ u32 hevc_b_max_qp;
+
u32 vp8_min_qp;
u32 vp8_max_qp;
@@ -256,6 +236,8 @@ struct venc_controls {
u32 hevc;
u32 vp9;
} level;
+
+ u32 base_priority_id;
};
struct venus_buffer {
@@ -271,7 +253,8 @@ struct venus_buffer {
struct clock_data {
u32 core_id;
unsigned long freq;
- const struct codec_freq_data *codec_freq_data;
+ unsigned long vpp_freq;
+ unsigned long vsp_freq;
};
#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
@@ -285,7 +268,6 @@ enum venus_dec_state {
VENUS_DEC_STATE_DRAIN = 5,
VENUS_DEC_STATE_DECODING = 6,
VENUS_DEC_STATE_DRC = 7,
- VENUS_DEC_STATE_DRC_FLUSH_DONE = 8,
};
struct venus_ts_metadata {
@@ -350,7 +332,7 @@ struct venus_ts_metadata {
* @priv: a private for HFI operations callbacks
* @session_type: the type of the session (decoder or encoder)
* @hprop: a union used as a holder by get property
- * @last_buf: last capture buffer for dynamic-resoluton-change
+ * @next_buf_last: a flag to mark next queued capture buffer as last
*/
struct venus_inst {
struct list_head list;
@@ -413,7 +395,9 @@ struct venus_inst {
union hfi_get_property hprop;
unsigned int core_acquired: 1;
unsigned int bit_depth;
- struct vb2_buffer *last_buf;
+ unsigned int pic_struct;
+ bool next_buf_last;
+ bool drain_active;
};
#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
@@ -433,7 +417,7 @@ static inline void *to_hfi_priv(struct venus_core *core)
return core->priv;
}
-static inline struct venus_caps *
+static inline struct hfi_plat_caps *
venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain)
{
unsigned int c;
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index d03e2dd5808c..89defc21ea81 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -201,6 +201,9 @@ int venus_boot(struct venus_core *core)
return -EINVAL;
}
+ core->fw.mem_size = mem_size;
+ core->fw.mem_phys = mem_phys;
+
if (core->use_tz)
ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
else
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 50439eb1ffea..76ece2ff8d39 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -7,7 +7,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-mem2mem.h>
#include <asm/div64.h>
@@ -15,6 +15,8 @@
#include "helpers.h"
#include "hfi_helper.h"
#include "pm_helpers.h"
+#include "hfi_platform.h"
+#include "hfi_parser.h"
struct intbuf {
struct list_head list;
@@ -480,7 +482,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
static bool is_dynamic_bufmode(struct venus_inst *inst)
{
struct venus_core *core = inst->core;
- struct venus_caps *caps;
+ struct hfi_plat_caps *caps;
/*
* v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports
@@ -552,6 +554,51 @@ static u32 to_hfi_raw_fmt(u32 v4l2_fmt)
return 0;
}
+static int platform_get_bufreq(struct venus_inst *inst, u32 buftype,
+ struct hfi_buffer_requirements *req)
+{
+ enum hfi_version version = inst->core->res->hfi_version;
+ const struct hfi_platform *hfi_plat;
+ struct hfi_plat_buffers_params params;
+ bool is_dec = inst->session_type == VIDC_SESSION_TYPE_DEC;
+ struct venc_controls *enc_ctr = &inst->controls.enc;
+
+ hfi_plat = hfi_platform_get(version);
+
+ if (!hfi_plat || !hfi_plat->bufreq)
+ return -EINVAL;
+
+ params.version = version;
+ params.num_vpp_pipes = hfi_platform_num_vpp_pipes(version);
+
+ if (is_dec) {
+ params.width = inst->width;
+ params.height = inst->height;
+ params.codec = inst->fmt_out->pixfmt;
+ params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_cap->pixfmt);
+ params.dec.max_mbs_per_frame = mbs_per_frame_max(inst);
+ params.dec.buffer_size_limit = 0;
+ params.dec.is_secondary_output =
+ inst->opb_buftype == HFI_BUFFER_OUTPUT2;
+ params.dec.is_interlaced =
+ inst->pic_struct != HFI_INTERLACE_FRAME_PROGRESSIVE ?
+ true : false;
+ } else {
+ params.width = inst->out_width;
+ params.height = inst->out_height;
+ params.codec = inst->fmt_cap->pixfmt;
+ params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_out->pixfmt);
+ params.enc.work_mode = VIDC_WORK_MODE_2;
+ params.enc.rc_type = HFI_RATE_CONTROL_OFF;
+ if (enc_ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)
+ params.enc.rc_type = HFI_RATE_CONTROL_CQ;
+ params.enc.num_b_frames = enc_ctr->num_b_frames;
+ params.enc.is_tenbit = inst->bit_depth == VIDC_BITDEPTH_10;
+ }
+
+ return hfi_plat->bufreq(&params, inst->session_type, buftype, req);
+}
+
int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
struct hfi_buffer_requirements *req)
{
@@ -563,6 +610,10 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
if (req)
memset(req, 0, sizeof(*req));
+ ret = platform_get_bufreq(inst, type, req);
+ if (!ret)
+ return 0;
+
ret = hfi_session_get_property(inst, ptype, &hprop);
if (ret)
return ret;
@@ -986,6 +1037,8 @@ u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height)
if (compressed) {
sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2;
+ if (width < 1280 || height < 720)
+ sz *= 8;
return ALIGN(sz, SZ_4K);
}
@@ -1040,36 +1093,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode)
}
EXPORT_SYMBOL_GPL(venus_helper_set_work_mode);
-int venus_helper_init_codec_freq_data(struct venus_inst *inst)
-{
- const struct codec_freq_data *data;
- unsigned int i, data_size;
- u32 pixfmt;
- int ret = 0;
-
- if (!IS_V4(inst->core))
- return 0;
-
- data = inst->core->res->codec_freq_data;
- data_size = inst->core->res->codec_freq_data_size;
- pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ?
- inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt;
-
- for (i = 0; i < data_size; i++) {
- if (data[i].pixfmt == pixfmt &&
- data[i].session_type == inst->session_type) {
- inst->clk_data.codec_freq_data = &data[i];
- break;
- }
- }
-
- if (!inst->clk_data.codec_freq_data)
- ret = -EINVAL;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data);
-
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
unsigned int output2_bufs)
@@ -1284,14 +1307,9 @@ int venus_helper_vb2_buf_init(struct vb2_buffer *vb)
struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct venus_buffer *buf = to_venus_buffer(vbuf);
- struct sg_table *sgt;
-
- sgt = vb2_dma_sg_plane_desc(vb, 0);
- if (!sgt)
- return -EFAULT;
buf->size = vb2_plane_size(vb, 0);
- buf->dma_addr = sg_dma_address(sgt->sgl);
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
list_add_tail(&buf->reg_list, &inst->registeredbufs);
@@ -1343,28 +1361,29 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb)
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
int ret;
- mutex_lock(&inst->lock);
-
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+ /* Skip processing queued capture buffers after LAST flag */
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC &&
+ V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) &&
+ inst->codec_state == VENUS_DEC_STATE_DRC)
+ return;
+
cache_payload(inst, vb);
if (inst->session_type == VIDC_SESSION_TYPE_ENC &&
!(inst->streamon_out && inst->streamon_cap))
- goto unlock;
+ return;
if (vb2_start_streaming_called(vb->vb2_queue)) {
ret = is_buf_refed(inst, vbuf);
if (ret)
- goto unlock;
+ return;
ret = session_process_buf(inst, vbuf);
if (ret)
return_buf_error(inst, vbuf);
}
-
-unlock:
- mutex_unlock(&inst->lock);
}
EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue);
@@ -1529,6 +1548,29 @@ void venus_helper_m2m_job_abort(void *priv)
}
EXPORT_SYMBOL_GPL(venus_helper_m2m_job_abort);
+int venus_helper_session_init(struct venus_inst *inst)
+{
+ enum hfi_version version = inst->core->res->hfi_version;
+ u32 session_type = inst->session_type;
+ u32 codec;
+ int ret;
+
+ codec = inst->session_type == VIDC_SESSION_TYPE_DEC ?
+ inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt;
+
+ ret = hfi_session_init(inst, codec);
+ if (ret)
+ return ret;
+
+ inst->clk_data.vpp_freq = hfi_platform_get_codec_vpp_freq(version, codec,
+ session_type);
+ inst->clk_data.vsp_freq = hfi_platform_get_codec_vsp_freq(version, codec,
+ session_type);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_session_init);
+
void venus_helper_init_instance(struct venus_inst *inst)
{
if (inst->session_type == VIDC_SESSION_TYPE_DEC) {
@@ -1539,7 +1581,7 @@ void venus_helper_init_instance(struct venus_inst *inst)
}
EXPORT_SYMBOL_GPL(venus_helper_init_instance);
-static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt)
+static bool find_fmt_from_caps(struct hfi_plat_caps *caps, u32 buftype, u32 fmt)
{
unsigned int i;
@@ -1556,7 +1598,7 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt,
u32 *out_fmt, u32 *out2_fmt, bool ubwc)
{
struct venus_core *core = inst->core;
- struct venus_caps *caps;
+ struct hfi_plat_caps *caps;
u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt);
bool found, found_ubwc;
@@ -1620,3 +1662,21 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt,
return -EINVAL;
}
EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts);
+
+int venus_helper_set_stride(struct venus_inst *inst,
+ unsigned int width, unsigned int height)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO;
+
+ struct hfi_uncompressed_plane_actual_info plane_actual_info;
+
+ plane_actual_info.buffer_type = HFI_BUFFER_INPUT;
+ plane_actual_info.num_planes = 2;
+ plane_actual_info.plane_format[0].actual_stride = width;
+ plane_actual_info.plane_format[0].actual_plane_buffer_height = height;
+ plane_actual_info.plane_format[1].actual_stride = width;
+ plane_actual_info.plane_format[1].actual_plane_buffer_height = height / 2;
+
+ return hfi_session_set_property(inst, ptype, &plane_actual_info);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_stride);
diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
index a4a0562bc83f..351093845499 100644
--- a/drivers/media/platform/qcom/venus/helpers.h
+++ b/drivers/media/platform/qcom/venus/helpers.h
@@ -33,7 +33,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst,
unsigned int width, unsigned int height,
u32 buftype);
int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode);
-int venus_helper_init_codec_freq_data(struct venus_inst *inst);
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
unsigned int output2_bufs);
@@ -48,6 +47,7 @@ unsigned int venus_helper_get_opb_size(struct venus_inst *inst);
void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf);
void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx);
void venus_helper_init_instance(struct venus_inst *inst);
+int venus_helper_session_init(struct venus_inst *inst);
int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt,
u32 *out2_fmt, bool ubwc);
int venus_helper_alloc_dpb_bufs(struct venus_inst *inst);
@@ -63,4 +63,6 @@ void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us,
struct vb2_v4l2_buffer *vbuf);
int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level);
int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level);
+int venus_helper_set_stride(struct venus_inst *inst, unsigned int aligned_width,
+ unsigned int aligned_height);
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
index 638ed5cfe05e..0f2482367e06 100644
--- a/drivers/media/platform/qcom/venus/hfi.c
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -175,6 +175,8 @@ static int wait_session_msg(struct venus_inst *inst)
int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
{
struct venus_core *core = inst->core;
+ bool max;
+ int ret;
if (!ops)
return -EINVAL;
@@ -184,11 +186,19 @@ int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
inst->ops = ops;
mutex_lock(&core->lock);
- list_add_tail(&inst->list, &core->instances);
- atomic_inc(&core->insts_count);
+
+ max = atomic_add_unless(&core->insts_count, 1,
+ core->max_sessions_supported);
+ if (!max) {
+ ret = -EAGAIN;
+ } else {
+ list_add_tail(&inst->list, &core->instances);
+ ret = 0;
+ }
+
mutex_unlock(&core->lock);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(hfi_session_create);
@@ -211,7 +221,7 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
mutex_unlock(&core->lock);
if (inst->state != INST_UNINIT)
- return -EINVAL;
+ return -EALREADY;
inst->hfi_codec = to_codec_type(pixfmt);
reinit_completion(&inst->done);
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 7022368c1e63..4f7565834469 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -1205,6 +1205,18 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt,
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
break;
}
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: {
+ struct hfi_uncompressed_plane_actual_info *in = pdata;
+ struct hfi_uncompressed_plane_actual_info *info = prop_data;
+
+ info->buffer_type = in->buffer_type;
+ info->num_planes = in->num_planes;
+ info->plane_format[0] = in->plane_format[0];
+ if (in->num_planes > 1)
+ info->plane_format[1] = in->plane_format[1];
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ break;
+ }
case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE:
case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE:
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index 60ee2479f7a6..6b524c7cde5f 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -364,6 +364,13 @@
#define HFI_HEVC_TIER_MAIN 0x1
#define HFI_HEVC_TIER_HIGH0 0x2
+#define HFI_VPX_PROFILE_MAIN 0x00000001
+
+#define HFI_VPX_LEVEL_VERSION_0 0x00000001
+#define HFI_VPX_LEVEL_VERSION_1 0x00000002
+#define HFI_VPX_LEVEL_VERSION_2 0x00000004
+#define HFI_VPX_LEVEL_VERSION_3 0x00000008
+
/* VP9 Profile 0, 8-bit */
#define HFI_VP9_PROFILE_P0 0x00000001
/* VP9 Profile 2, 10-bit */
@@ -571,7 +578,18 @@ struct hfi_bitrate {
#define HFI_CAPABILITY_LCU_SIZE 0x14
#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15
#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16
+#define HFI_CAPABILITY_I_FRAME_QP 0x20
+#define HFI_CAPABILITY_P_FRAME_QP 0x21
+#define HFI_CAPABILITY_B_FRAME_QP 0x22
+#define HFI_CAPABILITY_RATE_CONTROL_MODES 0x23
+#define HFI_CAPABILITY_BLUR_WIDTH 0x24
+#define HFI_CAPABILITY_BLUR_HEIGHT 0x25
+#define HFI_CAPABILITY_SLICE_BYTE 0x27
+#define HFI_CAPABILITY_SLICE_MB 0x28
#define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b
+#define HFI_CAPABILITY_MAX_WORKMODES 0x2c
+#define HFI_CAPABILITY_ROTATION 0x2f
+#define HFI_CAPABILITY_COLOR_SPACE_CONVERSION 0x30
struct hfi_capability {
u32 capability_type;
@@ -908,13 +926,13 @@ struct hfi_uncompressed_plane_actual {
struct hfi_uncompressed_plane_actual_info {
u32 buffer_type;
u32 num_planes;
- struct hfi_uncompressed_plane_actual plane_format[1];
+ struct hfi_uncompressed_plane_actual plane_format[2];
};
struct hfi_uncompressed_plane_actual_constraints_info {
u32 buffer_type;
u32 num_planes;
- struct hfi_uncompressed_plane_constraints plane_format[1];
+ struct hfi_uncompressed_plane_constraints plane_format[2];
};
struct hfi_codec_supported {
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 363ee2a65453..7263c0c32695 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -11,12 +11,12 @@
#include "hfi_helper.h"
#include "hfi_parser.h"
-typedef void (*func)(struct venus_caps *cap, const void *data,
+typedef void (*func)(struct hfi_plat_caps *cap, const void *data,
unsigned int size);
static void init_codecs(struct venus_core *core)
{
- struct venus_caps *caps = core->caps, *cap;
+ struct hfi_plat_caps *caps = core->caps, *cap;
unsigned long bit;
for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) {
@@ -34,11 +34,11 @@ static void init_codecs(struct venus_core *core)
}
}
-static void for_each_codec(struct venus_caps *caps, unsigned int caps_num,
+static void for_each_codec(struct hfi_plat_caps *caps, unsigned int caps_num,
u32 codecs, u32 domain, func cb, void *data,
unsigned int size)
{
- struct venus_caps *cap;
+ struct hfi_plat_caps *cap;
unsigned int i;
for (i = 0; i < caps_num; i++) {
@@ -51,7 +51,7 @@ static void for_each_codec(struct venus_caps *caps, unsigned int caps_num,
}
static void
-fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num)
+fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num)
{
const u32 *type = data;
@@ -81,7 +81,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
}
}
-static void fill_profile_level(struct venus_caps *cap, const void *data,
+static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
unsigned int num)
{
const struct hfi_profile_level *pl = data;
@@ -107,7 +107,7 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
}
static void
-fill_caps(struct venus_caps *cap, const void *data, unsigned int num)
+fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num)
{
const struct hfi_capability *caps = data;
@@ -132,7 +132,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
fill_caps, caps_arr, num_caps);
}
-static void fill_raw_fmts(struct venus_caps *cap, const void *fmts,
+static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
unsigned int num_fmts)
{
const struct raw_formats *formats = fmts;
@@ -211,7 +211,7 @@ static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain)
static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain)
{
- struct venus_caps *caps, *cap;
+ struct hfi_plat_caps *caps, *cap;
unsigned int i;
u32 dom;
@@ -228,11 +228,49 @@ static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain)
}
}
+static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
+{
+ const struct hfi_platform *plat;
+ const struct hfi_plat_caps *caps = NULL;
+ u32 enc_codecs, dec_codecs, count = 0;
+ unsigned int entries;
+
+ if (inst)
+ return 0;
+
+ plat = hfi_platform_get(core->res->hfi_version);
+ if (!plat)
+ return -EINVAL;
+
+ if (plat->codecs)
+ plat->codecs(&enc_codecs, &dec_codecs, &count);
+
+ if (plat->capabilities)
+ caps = plat->capabilities(&entries);
+
+ if (!caps || !entries || !count)
+ return -EINVAL;
+
+ core->enc_codecs = enc_codecs;
+ core->dec_codecs = dec_codecs;
+ core->codecs_count = count;
+ core->max_sessions_supported = MAX_SESSIONS;
+ memset(core->caps, 0, sizeof(*caps) * MAX_CODEC_NUM);
+ memcpy(core->caps, caps, sizeof(*caps) * entries);
+
+ return 0;
+}
+
u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
u32 size)
{
unsigned int words_count = size >> 2;
u32 *word = buf, *data, codecs = 0, domain = 0;
+ int ret;
+
+ ret = hfi_platform_parser(core, inst);
+ if (!ret)
+ return HFI_ERR_NONE;
if (size % 4)
return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
@@ -276,6 +314,9 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
words_count--;
}
+ if (!core->max_sessions_supported)
+ core->max_sessions_supported = MAX_SESSIONS;
+
parser_fini(inst, codecs, domain);
return HFI_ERR_NONE;
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h
index 264e6dd2415f..5751d0140700 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.h
+++ b/drivers/media/platform/qcom/venus/hfi_parser.h
@@ -16,7 +16,7 @@ static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which)
{
struct venus_core *core = inst->core;
struct hfi_capability *cap = NULL;
- struct venus_caps *caps;
+ struct hfi_plat_caps *caps;
unsigned int i;
caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type);
@@ -112,4 +112,9 @@ static inline u32 core_num_max(struct venus_inst *inst)
return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES);
}
+static inline u32 mbs_per_frame_max(struct venus_inst *inst)
+{
+ return cap_max(inst, HFI_CAPABILITY_MBS_PER_FRAME);
+}
+
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs.h b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h
new file mode 100644
index 000000000000..52a51a3b964a
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __HFI_PLATFORM_BUFFERS_H__
+#define __HFI_PLATFORM_BUFFERS_H__
+
+#include <linux/types.h>
+#include "hfi_helper.h"
+
+struct hfi_plat_buffers_params {
+ u32 width;
+ u32 height;
+ u32 codec;
+ u32 hfi_color_fmt;
+ enum hfi_version version;
+ u32 num_vpp_pipes;
+ union {
+ struct {
+ u32 max_mbs_per_frame;
+ u32 buffer_size_limit;
+ bool is_secondary_output;
+ bool is_interlaced;
+ } dec;
+ struct {
+ u32 work_mode;
+ u32 rc_type;
+ u32 num_b_frames;
+ bool is_tenbit;
+ } enc;
+ };
+};
+
+int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type,
+ u32 buftype, struct hfi_buffer_requirements *bufreq);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
new file mode 100644
index 000000000000..d43d1a53e72d
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <linux/videodev2.h>
+
+#include "hfi.h"
+#include "hfi_plat_bufs.h"
+#include "helpers.h"
+
+#define MIN_INPUT_BUFFERS 4
+#define MIN_ENC_OUTPUT_BUFFERS 4
+
+#define NV12_UBWC_Y_TILE_WIDTH 32
+#define NV12_UBWC_Y_TILE_HEIGHT 8
+#define NV12_UBWC_UV_TILE_WIDTH 16
+#define NV12_UBWC_UV_TILE_HEIGHT 8
+#define TP10_UBWC_Y_TILE_WIDTH 48
+#define TP10_UBWC_Y_TILE_HEIGHT 4
+#define METADATA_STRIDE_MULTIPLE 64
+#define METADATA_HEIGHT_MULTIPLE 16
+#define HFI_DMA_ALIGNMENT 256
+
+#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640
+#define MAX_FE_NBR_DATA_CB_LINE_BUFFER_SIZE 320
+#define MAX_FE_NBR_DATA_CR_LINE_BUFFER_SIZE 320
+
+#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE (128 / 8)
+#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8)
+#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8)
+
+#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE (64 * 2 * 3)
+#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE (32 * 2 * 3)
+#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE (16 * 2 * 3)
+
+#define MAX_TILE_COLUMNS 32 /* 8K/256 */
+
+#define NUM_HW_PIC_BUF 10
+#define BIN_BUFFER_THRESHOLD (1280 * 736)
+#define H264D_MAX_SLICE 1800
+/* sizeof(h264d_buftab_t) aligned to 256 */
+#define SIZE_H264D_BUFTAB_T 256
+/* sizeof(h264d_hw_pic_t) aligned to 32 */
+#define SIZE_H264D_HW_PIC_T BIT(11)
+#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4)
+#define SIZE_H264D_VPP_CMD_PER_BUF 512
+
+/* Line Buffer definitions, One for Luma and 1/2 for each Chroma */
+#define SIZE_H264D_LB_FE_TOP_DATA(width, height) \
+ (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN((width), 16) * 3)
+
+#define SIZE_H264D_LB_FE_TOP_CTRL(width, height) \
+ (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4))
+
+#define SIZE_H264D_LB_FE_LEFT_CTRL(width, height) \
+ (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4))
+
+#define SIZE_H264D_LB_SE_TOP_CTRL(width, height) \
+ (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4))
+
+#define SIZE_H264D_LB_SE_LEFT_CTRL(width, height) \
+ (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4))
+
+#define SIZE_H264D_LB_PE_TOP_DATA(width, height) \
+ (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4))
+
+#define SIZE_H264D_LB_VSP_TOP(width, height) (((((width) + 15) >> 4) << 7))
+
+#define SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height) \
+ (ALIGN((height), 16) * 32)
+
+#define SIZE_H264D_QP(width, height) \
+ ((((width) + 63) >> 6) * (((height) + 63) >> 6) * 128)
+
+#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
+
+#define H264_CABAC_HDR_RATIO_HD_TOT 1
+#define H264_CABAC_RES_RATIO_HD_TOT 3
+
+/*
+ * Some content need more bin buffer, but limit buffer
+ * size for high resolution
+ */
+#define NUM_SLIST_BUF_H264 (256 + 32)
+#define SIZE_SLIST_BUF_H264 512
+#define LCU_MAX_SIZE_PELS 64
+#define LCU_MIN_SIZE_PELS 16
+
+#define H265D_MAX_SLICE 600
+#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T
+#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32))
+#define SIZE_H265D_VPP_CMD_PER_BUF 256
+
+#define SIZE_H265D_LB_FE_TOP_DATA(width, height) \
+ (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * (ALIGN(width, 64) + 8) * 2)
+
+#define SIZE_H265D_LB_FE_TOP_CTRL(width, height) \
+ (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \
+ (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS))
+
+#define SIZE_H265D_LB_FE_LEFT_CTRL(width, height) \
+ (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \
+ (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS))
+
+#define SIZE_H265D_LB_SE_TOP_CTRL(width, height) \
+ ((LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * (((width) + 15) >> 4))
+
+static inline u32 size_h265d_lb_se_left_ctrl(u32 width, u32 height)
+{
+ u32 x, y, z;
+
+ x = ((height + 16 - 1) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE;
+ y = ((height + 32 - 1) / 8) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE;
+ z = ((height + 64 - 1) / 8) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE;
+
+ return max3(x, y, z);
+}
+
+#define SIZE_H265D_LB_PE_TOP_DATA(width, height) \
+ (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * \
+ (ALIGN(width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS))
+
+#define SIZE_H265D_LB_VSP_TOP(width, height) ((((width) + 63) >> 6) * 128)
+
+#define SIZE_H265D_LB_VSP_LEFT(width, height) ((((height) + 63) >> 6) * 128)
+
+#define SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height) \
+ SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height)
+
+#define SIZE_H265D_QP(width, height) SIZE_H264D_QP(width, height)
+
+#define H265_CABAC_HDR_RATIO_HD_TOT 2
+#define H265_CABAC_RES_RATIO_HD_TOT 2
+
+/*
+ * Some content need more bin buffer, but limit buffer size
+ * for high resolution
+ */
+#define SIZE_SLIST_BUF_H265 BIT(10)
+#define NUM_SLIST_BUF_H265 (80 + 20)
+#define H265_NUM_TILE_COL 32
+#define H265_NUM_TILE_ROW 128
+#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1)
+
+static inline u32 size_vpxd_lb_fe_left_ctrl(u32 width, u32 height)
+{
+ u32 x, y, z;
+
+ x = ((height + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE;
+ y = ((height + 31) >> 5) * MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE;
+ z = ((height + 63) >> 6) * MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE;
+
+ return max3(x, y, z);
+}
+
+#define SIZE_VPXD_LB_FE_TOP_CTRL(width, height) \
+ (((ALIGN(width, 64) + 8) * 10 * 2)) /* small line */
+#define SIZE_VPXD_LB_SE_TOP_CTRL(width, height) \
+ ((((width) + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE)
+
+static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height)
+{
+ u32 x, y, z;
+
+ x = ((height + 15) >> 4) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE;
+ y = ((height + 31) >> 5) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE;
+ z = ((height + 63) >> 6) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE;
+
+ return max3(x, y, z);
+}
+
+#define SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height) \
+ ALIGN((ALIGN(height, 16) / (4 / 2)) * 64, 32)
+#define SIZE_VP8D_LB_FE_TOP_DATA(width, height) \
+ ((ALIGN(width, 16) + 8) * 10 * 2)
+#define SIZE_VP9D_LB_FE_TOP_DATA(width, height) \
+ ((ALIGN(ALIGN(width, 16), 64) + 8) * 10 * 2)
+#define SIZE_VP8D_LB_PE_TOP_DATA(width, height) \
+ ((ALIGN(width, 16) >> 4) * 64)
+#define SIZE_VP9D_LB_PE_TOP_DATA(width, height) \
+ ((ALIGN(ALIGN(width, 16), 64) >> 6) * 176)
+#define SIZE_VP8D_LB_VSP_TOP(width, height) \
+ (((ALIGN(width, 16) >> 4) * 64 / 2) + 256)
+#define SIZE_VP9D_LB_VSP_TOP(width, height) \
+ (((ALIGN(ALIGN(width, 16), 64) >> 6) * 64 * 8) + 256)
+
+#define HFI_IRIS2_VP9D_COMV_SIZE \
+ ((((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8))
+
+#define VPX_DECODER_FRAME_CONCURENCY_LVL 2
+#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM 1
+#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN 2
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM 3
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN 2
+
+#define VP8_NUM_FRAME_INFO_BUF (5 + 1)
+#define VP9_NUM_FRAME_INFO_BUF (8 + 2 + 1 + 8)
+#define VP8_NUM_PROBABILITY_TABLE_BUF VP8_NUM_FRAME_INFO_BUF
+#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
+#define VP8_PROB_TABLE_SIZE 3840
+#define VP9_PROB_TABLE_SIZE 3840
+
+#define VP9_UDC_HEADER_BUF_SIZE (3 * 128)
+#define MAX_SUPERFRAME_HEADER_LEN 34
+#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, 32)
+
+#define QMATRIX_SIZE (sizeof(u32) * 128 + 256)
+#define MP2D_QPDUMP_SIZE 115200
+#define HFI_IRIS2_ENC_PERSIST_SIZE 102400
+#define HFI_MAX_COL_FRAME 6
+#define HFI_VENUS_VENC_TRE_WB_BUFF_SIZE (65 << 4) /* in Bytes */
+#define HFI_VENUS_VENC_DB_LINE_BUFF_PER_MB 512
+#define HFI_VENUS_VPPSG_MAX_REGISTERS 2048
+#define HFI_VENUS_WIDTH_ALIGNMENT 128
+#define HFI_VENUS_WIDTH_TEN_BIT_ALIGNMENT 192
+#define HFI_VENUS_HEIGHT_ALIGNMENT 32
+
+#define SYSTEM_LAL_TILE10 192
+#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4))
+#define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4))
+#define MB_SIZE_IN_PIXEL (16 * 16)
+#define HDR10PLUS_PAYLOAD_SIZE 1024
+#define HDR10_HIST_EXTRADATA_SIZE 4096
+
+static u32 size_vpss_lb(u32 width, u32 height, u32 num_vpp_pipes)
+{
+ u32 vpss_4tap_top_buffer_size, vpss_div2_top_buffer_size;
+ u32 vpss_4tap_left_buffer_size, vpss_div2_left_buffer_size;
+ u32 opb_wr_top_line_luma_buf_size, opb_wr_top_line_chroma_buf_size;
+ u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size;
+ u32 macrotiling_size;
+ u32 size = 0;
+
+ vpss_4tap_top_buffer_size = 0;
+ vpss_div2_top_buffer_size = 0;
+ vpss_4tap_left_buffer_size = 0;
+ vpss_div2_left_buffer_size = 0;
+
+ macrotiling_size = 32;
+ opb_wr_top_line_luma_buf_size =
+ ALIGN(width, macrotiling_size) / macrotiling_size * 256;
+ opb_wr_top_line_luma_buf_size =
+ ALIGN(opb_wr_top_line_luma_buf_size, HFI_DMA_ALIGNMENT) +
+ (MAX_TILE_COLUMNS - 1) * 256;
+ opb_wr_top_line_luma_buf_size =
+ max(opb_wr_top_line_luma_buf_size, (32 * ALIGN(height, 16)));
+ opb_wr_top_line_chroma_buf_size = opb_wr_top_line_luma_buf_size;
+ opb_lb_wr_llb_y_buffer_size = ALIGN((ALIGN(height, 16) / 2) * 64, 32);
+ opb_lb_wr_llb_uv_buffer_size = opb_lb_wr_llb_y_buffer_size;
+ size = num_vpp_pipes *
+ 2 * (vpss_4tap_top_buffer_size + vpss_div2_top_buffer_size) +
+ 2 * (vpss_4tap_left_buffer_size + vpss_div2_left_buffer_size) +
+ opb_wr_top_line_luma_buf_size +
+ opb_wr_top_line_chroma_buf_size +
+ opb_lb_wr_llb_uv_buffer_size +
+ opb_lb_wr_llb_y_buffer_size;
+
+ return size;
+}
+
+static u32 size_h264d_hw_bin_buffer(u32 width, u32 height)
+{
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+ u32 size = 0;
+ u32 product;
+
+ product = width * height;
+ size_yuv = (product <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1);
+
+ size_bin_hdr = size_yuv * H264_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H264_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT);
+ size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT);
+ size = size_bin_hdr + size_bin_res;
+
+ return size;
+}
+
+static u32 h264d_scratch_size(u32 width, u32 height, bool is_interlaced)
+{
+ u32 aligned_width = ALIGN(width, 16);
+ u32 aligned_height = ALIGN(height, 16);
+ u32 size = 0;
+
+ if (!is_interlaced)
+ size = size_h264d_hw_bin_buffer(aligned_width, aligned_height);
+
+ return size;
+}
+
+static u32 size_h265d_hw_bin_buffer(u32 width, u32 height)
+{
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+ u32 size = 0;
+ u32 product;
+
+ product = width * height;
+ size_yuv = (product <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1);
+ size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H265_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT);
+ size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT);
+ size = size_bin_hdr + size_bin_res;
+
+ return size;
+}
+
+static u32 h265d_scratch_size(u32 width, u32 height, bool is_interlaced)
+{
+ u32 aligned_width = ALIGN(width, 16);
+ u32 aligned_height = ALIGN(height, 16);
+ u32 size = 0;
+
+ if (!is_interlaced)
+ size = size_h265d_hw_bin_buffer(aligned_width, aligned_height);
+
+ return size;
+}
+
+static u32 vpxd_scratch_size(u32 width, u32 height, bool is_interlaced)
+{
+ u32 aligned_width = ALIGN(width, 16);
+ u32 aligned_height = ALIGN(height, 16);
+ u32 size_yuv = aligned_width * aligned_height * 3 / 2;
+ u32 size = 0;
+
+ if (!is_interlaced) {
+ u32 binbuffer1_size, binbufer2_size;
+
+ binbuffer1_size = max_t(u32, size_yuv,
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1));
+ binbuffer1_size *= VPX_DECODER_FRAME_CONCURENCY_LVL *
+ VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM /
+ VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN;
+ binbufer2_size = max_t(u32, size_yuv,
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1));
+ binbufer2_size *= VPX_DECODER_FRAME_CONCURENCY_LVL *
+ VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM /
+ VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN;
+ size = ALIGN(binbuffer1_size + binbufer2_size,
+ HFI_DMA_ALIGNMENT);
+ }
+
+ return size;
+}
+
+static u32 mpeg2d_scratch_size(u32 width, u32 height, bool is_interlaced)
+{
+ return 0;
+}
+
+static u32 calculate_enc_output_frame_size(u32 width, u32 height, u32 rc_type)
+{
+ u32 aligned_width, aligned_height;
+ u32 mbs_per_frame;
+ u32 frame_size;
+
+ /*
+ * Encoder output size calculation: 32 Align width/height
+ * For resolution < 720p : YUVsize * 4
+ * For resolution > 720p & <= 4K : YUVsize / 2
+ * For resolution > 4k : YUVsize / 4
+ * Initially frame_size = YUVsize * 2;
+ */
+ aligned_width = ALIGN(width, 32);
+ aligned_height = ALIGN(height, 32);
+ mbs_per_frame = (ALIGN(aligned_height, 16) *
+ ALIGN(aligned_width, 16)) / 256;
+ frame_size = width * height * 3;
+
+ if (mbs_per_frame < NUM_MBS_720P)
+ frame_size = frame_size << 1;
+ else if (mbs_per_frame <= NUM_MBS_4K)
+ frame_size = frame_size >> 2;
+ else
+ frame_size = frame_size >> 3;
+
+ if (rc_type == HFI_RATE_CONTROL_OFF || rc_type == HFI_RATE_CONTROL_CQ)
+ frame_size = frame_size << 1;
+
+ /*
+ * In case of opaque color format bitdepth will be known
+ * with first ETB, buffers allocated already with 8 bit
+ * won't be sufficient for 10 bit
+ * calculate size considering 10-bit by default
+ * For 10-bit cases size = size * 1.25
+ */
+ frame_size *= 5;
+ frame_size /= 4;
+
+ return ALIGN(frame_size, SZ_4K);
+}
+
+static u32 calculate_enc_scratch_size(u32 width, u32 height, u32 work_mode,
+ u32 lcu_size, u32 num_vpp_pipes,
+ u32 rc_type)
+{
+ u32 aligned_width, aligned_height, bitstream_size;
+ u32 total_bitbin_buffers, size_single_pipe, bitbin_size;
+ u32 sao_bin_buffer_size, padded_bin_size, size;
+
+ aligned_width = ALIGN(width, lcu_size);
+ aligned_height = ALIGN(height, lcu_size);
+ bitstream_size =
+ calculate_enc_output_frame_size(width, height, rc_type);
+
+ bitstream_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT);
+
+ if (work_mode == VIDC_WORK_MODE_2) {
+ total_bitbin_buffers = 3;
+ bitbin_size = bitstream_size * 17 / 10;
+ bitbin_size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT);
+ } else {
+ total_bitbin_buffers = 1;
+ bitstream_size = aligned_width * aligned_height * 3;
+ bitbin_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT);
+ }
+
+ if (num_vpp_pipes > 2)
+ size_single_pipe = bitbin_size / 2;
+ else
+ size_single_pipe = bitbin_size;
+
+ size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT);
+ sao_bin_buffer_size =
+ (64 * (((width + 32) * (height + 32)) >> 10)) + 384;
+ padded_bin_size = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT);
+ size_single_pipe = sao_bin_buffer_size + padded_bin_size;
+ size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT);
+ bitbin_size = size_single_pipe * num_vpp_pipes;
+ size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT) *
+ total_bitbin_buffers + 512;
+
+ return size;
+}
+
+static u32 h264e_scratch_size(u32 width, u32 height, u32 work_mode,
+ u32 num_vpp_pipes, u32 rc_type)
+{
+ return calculate_enc_scratch_size(width, height, work_mode, 16,
+ num_vpp_pipes, rc_type);
+}
+
+static u32 h265e_scratch_size(u32 width, u32 height, u32 work_mode,
+ u32 num_vpp_pipes, u32 rc_type)
+{
+ return calculate_enc_scratch_size(width, height, work_mode, 32,
+ num_vpp_pipes, rc_type);
+}
+
+static u32 vp8e_scratch_size(u32 width, u32 height, u32 work_mode,
+ u32 num_vpp_pipes, u32 rc_type)
+{
+ return calculate_enc_scratch_size(width, height, work_mode, 16,
+ num_vpp_pipes, rc_type);
+}
+
+static u32 hfi_iris2_h264d_comv_size(u32 width, u32 height,
+ u32 yuv_buf_min_count)
+{
+ u32 frame_width_in_mbs = ((width + 15) >> 4);
+ u32 frame_height_in_mbs = ((height + 15) >> 4);
+ u32 col_mv_aligned_width = (frame_width_in_mbs << 6);
+ u32 col_zero_aligned_width = (frame_width_in_mbs << 2);
+ u32 col_zero_size = 0, size_colloc = 0, comv_size = 0;
+
+ col_mv_aligned_width = ALIGN(col_mv_aligned_width, 16);
+ col_zero_aligned_width = ALIGN(col_zero_aligned_width, 16);
+ col_zero_size =
+ col_zero_aligned_width * ((frame_height_in_mbs + 1) >> 1);
+ col_zero_size = ALIGN(col_zero_size, 64);
+ col_zero_size <<= 1;
+ col_zero_size = ALIGN(col_zero_size, 512);
+ size_colloc = col_mv_aligned_width * ((frame_height_in_mbs + 1) >> 1);
+ size_colloc = ALIGN(size_colloc, 64);
+ size_colloc <<= 1;
+ size_colloc = ALIGN(size_colloc, 512);
+ size_colloc += (col_zero_size + SIZE_H264D_BUFTAB_T * 2);
+ comv_size = size_colloc * yuv_buf_min_count;
+ comv_size += 512;
+
+ return comv_size;
+}
+
+static u32 size_h264d_bse_cmd_buf(u32 height)
+{
+ u32 aligned_height = ALIGN(height, 32);
+
+ return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4),
+ H264D_MAX_SLICE) * SIZE_H264D_BSE_CMD_PER_BUF;
+}
+
+static u32 size_h264d_vpp_cmd_buf(u32 height)
+{
+ u32 aligned_height = ALIGN(height, 32);
+
+ return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4),
+ H264D_MAX_SLICE) * SIZE_H264D_VPP_CMD_PER_BUF;
+}
+
+static u32 hfi_iris2_h264d_non_comv_size(u32 width, u32 height,
+ u32 num_vpp_pipes)
+{
+ u32 size_bse, size_vpp, size;
+
+ size_bse = size_h264d_bse_cmd_buf(height);
+ size_vpp = size_h264d_vpp_cmd_buf(height);
+ size =
+ ALIGN(size_bse, HFI_DMA_ALIGNMENT) +
+ ALIGN(size_vpp, HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_FE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_FE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_FE_LEFT_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_H264D_LB_SE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_SE_LEFT_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_H264D_LB_PE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height),
+ HFI_DMA_ALIGNMENT) * 2 +
+ ALIGN(SIZE_H264D_QP(width, height), HFI_DMA_ALIGNMENT);
+
+ return ALIGN(size, HFI_DMA_ALIGNMENT);
+}
+
+static u32 size_h265d_bse_cmd_buf(u32 width, u32 height)
+{
+ u32 size;
+
+ size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ NUM_HW_PIC_BUF;
+ size = min_t(u32, size, H265D_MAX_SLICE + 1);
+ size = 2 * size * SIZE_H265D_BSE_CMD_PER_BUF;
+
+ return ALIGN(size, HFI_DMA_ALIGNMENT);
+}
+
+static u32 size_h265d_vpp_cmd_buf(u32 width, u32 height)
+{
+ u32 size;
+
+ size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ NUM_HW_PIC_BUF;
+ size = min_t(u32, size, H265D_MAX_SLICE + 1);
+ size = ALIGN(size, 4);
+ size = 2 * size * SIZE_H265D_VPP_CMD_PER_BUF;
+
+ return ALIGN(size, HFI_DMA_ALIGNMENT);
+}
+
+static u32 hfi_iris2_h265d_comv_size(u32 width, u32 height,
+ u32 yuv_buf_count_min)
+{
+ u32 size;
+
+ size = ALIGN(((((width + 15) >> 4) * ((height + 15) >> 4)) << 8), 512);
+ size *= yuv_buf_count_min;
+ size += 512;
+
+ return size;
+}
+
+static u32 hfi_iris2_h265d_non_comv_size(u32 width, u32 height,
+ u32 num_vpp_pipes)
+{
+ u32 size_bse, size_vpp, size;
+
+ size_bse = size_h265d_bse_cmd_buf(width, height);
+ size_vpp = size_h265d_vpp_cmd_buf(width, height);
+ size =
+ ALIGN(size_bse, HFI_DMA_ALIGNMENT) +
+ ALIGN(size_vpp, HFI_DMA_ALIGNMENT) +
+ ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, HFI_DMA_ALIGNMENT) +
+ ALIGN(2 * sizeof(u16) *
+ (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_FE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_FE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_FE_LEFT_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_se_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_H265D_LB_SE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_PE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_H265D_LB_VSP_LEFT(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height),
+ HFI_DMA_ALIGNMENT)
+ * 4 +
+ ALIGN(SIZE_H265D_QP(width, height), HFI_DMA_ALIGNMENT);
+
+ return ALIGN(size, HFI_DMA_ALIGNMENT);
+}
+
+static u32 hfi_iris2_vp8d_comv_size(u32 width, u32 height,
+ u32 yuv_min_buf_count)
+{
+ return (((width + 15) >> 4) * ((height + 15) >> 4) * 8 * 2);
+}
+
+static u32 h264d_scratch1_size(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes)
+{
+ u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0;
+
+ co_mv_size = hfi_iris2_h264d_comv_size(width, height, min_buf_count);
+ nonco_mv_size = hfi_iris2_h264d_non_comv_size(width, height,
+ num_vpp_pipes);
+ if (split_mode_enabled)
+ vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes);
+
+ return co_mv_size + nonco_mv_size + vpss_lb_size;
+}
+
+static u32 h265d_scratch1_size(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes)
+{
+ u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0;
+
+ co_mv_size = hfi_iris2_h265d_comv_size(width, height, min_buf_count);
+ nonco_mv_size = hfi_iris2_h265d_non_comv_size(width, height,
+ num_vpp_pipes);
+ if (split_mode_enabled)
+ vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes);
+
+ return co_mv_size + nonco_mv_size + vpss_lb_size +
+ HDR10_HIST_EXTRADATA_SIZE;
+}
+
+static u32 vp8d_scratch1_size(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0, size;
+
+ size = hfi_iris2_vp8d_comv_size(width, height, 0);
+ size += ALIGN(size_vpxd_lb_fe_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_vpxd_lb_se_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT);
+ if (split_mode_enabled)
+ vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes);
+
+ size += vpss_lb_size;
+
+ return size;
+}
+
+static u32 vp9d_scratch1_size(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 size;
+
+ size =
+ ALIGN(size_vpxd_lb_fe_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_vpxd_lb_se_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_VP9D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP9D_LB_PE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP9D_LB_FE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT);
+
+ if (split_mode_enabled)
+ vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes);
+
+ size += vpss_lb_size + HDR10_HIST_EXTRADATA_SIZE;
+
+ return size;
+}
+
+static u32 mpeg2d_scratch1_size(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 size;
+
+ size =
+ ALIGN(size_vpxd_lb_fe_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_vpxd_lb_se_left_ctrl(width, height),
+ HFI_DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height),
+ HFI_DMA_ALIGNMENT);
+
+ if (split_mode_enabled)
+ vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes);
+
+ size += vpss_lb_size;
+
+ return size;
+}
+
+static u32
+calculate_enc_scratch1_size(u32 width, u32 height, u32 lcu_size, u32 num_ref,
+ bool ten_bit, u32 num_vpp_pipes, bool is_h265)
+{
+ u32 line_buf_ctrl_size, line_buf_data_size, leftline_buf_ctrl_size;
+ u32 line_buf_sde_size, sps_pps_slice_hdr, topline_buf_ctrl_size_FE;
+ u32 leftline_buf_ctrl_size_FE, line_buf_recon_pix_size;
+ u32 leftline_buf_recon_pix_size, lambda_lut_size, override_buffer_size;
+ u32 col_mv_buf_size, vpp_reg_buffer_size, ir_buffer_size;
+ u32 vpss_line_buf, leftline_buf_meta_recony, h265e_colrcbuf_size;
+ u32 h265e_framerc_bufsize, h265e_lcubitcnt_bufsize;
+ u32 h265e_lcubitmap_bufsize, se_stats_bufsize;
+ u32 bse_reg_buffer_size, bse_slice_cmd_buffer_size, slice_info_bufsize;
+ u32 line_buf_ctrl_size_buffid2, slice_cmd_buffer_size;
+ u32 width_lcu_num, height_lcu_num, width_coded, height_coded;
+ u32 frame_num_lcu, linebuf_meta_recon_uv, topline_bufsize_fe_1stg_sao;
+ u32 size, bit_depth, num_lcu_mb;
+ u32 vpss_line_buffer_size_1;
+
+ width_lcu_num = (width + lcu_size - 1) / lcu_size;
+ height_lcu_num = (height + lcu_size - 1) / lcu_size;
+ frame_num_lcu = width_lcu_num * height_lcu_num;
+ width_coded = width_lcu_num * lcu_size;
+ height_coded = height_lcu_num * lcu_size;
+ num_lcu_mb = (height_coded / lcu_size) *
+ ((width_coded + lcu_size * 8) / lcu_size);
+ slice_info_bufsize = 256 + (frame_num_lcu << 4);
+ slice_info_bufsize = ALIGN(slice_info_bufsize, HFI_DMA_ALIGNMENT);
+ line_buf_ctrl_size = ALIGN(width_coded, HFI_DMA_ALIGNMENT);
+ line_buf_ctrl_size_buffid2 = ALIGN(width_coded, HFI_DMA_ALIGNMENT);
+
+ bit_depth = ten_bit ? 10 : 8;
+ line_buf_data_size =
+ (((((bit_depth * width_coded + 1024) +
+ (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 1) +
+ (((((bit_depth * width_coded + 1024) >> 1) +
+ (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 2));
+
+ leftline_buf_ctrl_size = is_h265 ?
+ ((height_coded + 32) / 32 * 4 * 16) :
+ ((height_coded + 15) / 16 * 5 * 16);
+
+ if (num_vpp_pipes > 1) {
+ leftline_buf_ctrl_size += 512;
+ leftline_buf_ctrl_size =
+ ALIGN(leftline_buf_ctrl_size, 512) * num_vpp_pipes;
+ }
+
+ leftline_buf_ctrl_size =
+ ALIGN(leftline_buf_ctrl_size, HFI_DMA_ALIGNMENT);
+ leftline_buf_recon_pix_size = (((ten_bit + 1) * 2 *
+ (height_coded) + HFI_DMA_ALIGNMENT) +
+ (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) &
+ (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1;
+
+ topline_buf_ctrl_size_FE = is_h265 ? (64 * (width_coded >> 5)) :
+ (HFI_DMA_ALIGNMENT + 16 * (width_coded >> 4));
+ topline_buf_ctrl_size_FE =
+ ALIGN(topline_buf_ctrl_size_FE, HFI_DMA_ALIGNMENT);
+ leftline_buf_ctrl_size_FE =
+ (((HFI_DMA_ALIGNMENT + 64 * (height_coded >> 4)) +
+ (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) &
+ (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1) *
+ num_vpp_pipes;
+ leftline_buf_meta_recony = (HFI_DMA_ALIGNMENT + 64 *
+ ((height_coded) / (8 * (ten_bit ? 4 : 8))));
+ leftline_buf_meta_recony =
+ ALIGN(leftline_buf_meta_recony, HFI_DMA_ALIGNMENT);
+ leftline_buf_meta_recony = leftline_buf_meta_recony * num_vpp_pipes;
+ linebuf_meta_recon_uv = (HFI_DMA_ALIGNMENT + 64 *
+ ((height_coded) / (4 * (ten_bit ? 4 : 8))));
+ linebuf_meta_recon_uv = ALIGN(linebuf_meta_recon_uv, HFI_DMA_ALIGNMENT);
+ linebuf_meta_recon_uv = linebuf_meta_recon_uv * num_vpp_pipes;
+ line_buf_recon_pix_size = ((ten_bit ? 3 : 2) * width_coded);
+ line_buf_recon_pix_size =
+ ALIGN(line_buf_recon_pix_size, HFI_DMA_ALIGNMENT);
+ slice_cmd_buffer_size = ALIGN(20480, HFI_DMA_ALIGNMENT);
+ sps_pps_slice_hdr = 2048 + 4096;
+ col_mv_buf_size = is_h265 ? (16 * ((frame_num_lcu << 2) + 32)) :
+ (3 * 16 * (width_lcu_num * height_lcu_num + 32));
+ col_mv_buf_size =
+ ALIGN(col_mv_buf_size, HFI_DMA_ALIGNMENT) * (num_ref + 1);
+ h265e_colrcbuf_size =
+ (((width_lcu_num + 7) >> 3) * 16 * 2 * height_lcu_num);
+ if (num_vpp_pipes > 1)
+ h265e_colrcbuf_size =
+ ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) *
+ num_vpp_pipes;
+
+ h265e_colrcbuf_size = ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) *
+ HFI_MAX_COL_FRAME;
+ h265e_framerc_bufsize = (is_h265) ? (256 + 16 *
+ (14 + (((height_coded >> 5) + 7) >> 3))) :
+ (256 + 16 * (14 + (((height_coded >> 4) + 7) >> 3)));
+ h265e_framerc_bufsize *= 6; /* multiply by max numtilescol */
+ if (num_vpp_pipes > 1)
+ h265e_framerc_bufsize =
+ ALIGN(h265e_framerc_bufsize, HFI_DMA_ALIGNMENT) *
+ num_vpp_pipes;
+
+ h265e_framerc_bufsize = ALIGN(h265e_framerc_bufsize, 512) *
+ HFI_MAX_COL_FRAME;
+ h265e_lcubitcnt_bufsize = 256 + 4 * frame_num_lcu;
+ h265e_lcubitcnt_bufsize =
+ ALIGN(h265e_lcubitcnt_bufsize, HFI_DMA_ALIGNMENT);
+ h265e_lcubitmap_bufsize = 256 + (frame_num_lcu >> 3);
+ h265e_lcubitmap_bufsize =
+ ALIGN(h265e_lcubitmap_bufsize, HFI_DMA_ALIGNMENT);
+ line_buf_sde_size = 256 + 16 * (width_coded >> 4);
+ line_buf_sde_size = ALIGN(line_buf_sde_size, HFI_DMA_ALIGNMENT);
+ if ((width_coded * height_coded) > (4096 * 2160))
+ se_stats_bufsize = 0;
+ else if ((width_coded * height_coded) > (1920 * 1088))
+ se_stats_bufsize = (40 * 4 * frame_num_lcu + 256 + 256);
+ else
+ se_stats_bufsize = (1024 * frame_num_lcu + 256 + 256);
+
+ se_stats_bufsize = ALIGN(se_stats_bufsize, HFI_DMA_ALIGNMENT) * 2;
+ bse_slice_cmd_buffer_size = (((8192 << 2) + 7) & (~7)) * 6;
+ bse_reg_buffer_size = (((512 << 3) + 7) & (~7)) * 4;
+ vpp_reg_buffer_size =
+ (((HFI_VENUS_VPPSG_MAX_REGISTERS << 3) + 31) & (~31)) * 10;
+ lambda_lut_size = 256 * 11;
+ override_buffer_size = 16 * ((num_lcu_mb + 7) >> 3);
+ override_buffer_size =
+ ALIGN(override_buffer_size, HFI_DMA_ALIGNMENT) * 2;
+ ir_buffer_size = (((frame_num_lcu << 1) + 7) & (~7)) * 3;
+ vpss_line_buffer_size_1 = (((8192 >> 2) << 5) * num_vpp_pipes) + 64;
+ vpss_line_buf =
+ (((((max(width_coded, height_coded) + 3) >> 2) << 5) + 256) *
+ 16) + vpss_line_buffer_size_1;
+ topline_bufsize_fe_1stg_sao = 16 * (width_coded >> 5);
+ topline_bufsize_fe_1stg_sao =
+ ALIGN(topline_bufsize_fe_1stg_sao, HFI_DMA_ALIGNMENT);
+
+ size =
+ line_buf_ctrl_size + line_buf_data_size +
+ line_buf_ctrl_size_buffid2 + leftline_buf_ctrl_size +
+ vpss_line_buf + col_mv_buf_size + topline_buf_ctrl_size_FE +
+ leftline_buf_ctrl_size_FE + line_buf_recon_pix_size +
+ leftline_buf_recon_pix_size +
+ leftline_buf_meta_recony + linebuf_meta_recon_uv +
+ h265e_colrcbuf_size + h265e_framerc_bufsize +
+ h265e_lcubitcnt_bufsize + h265e_lcubitmap_bufsize +
+ line_buf_sde_size +
+ topline_bufsize_fe_1stg_sao + override_buffer_size +
+ bse_reg_buffer_size + vpp_reg_buffer_size + sps_pps_slice_hdr +
+ slice_cmd_buffer_size + bse_slice_cmd_buffer_size +
+ ir_buffer_size + slice_info_bufsize + lambda_lut_size +
+ se_stats_bufsize + 1024;
+
+ return size;
+}
+
+static u32 h264e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit,
+ u32 num_vpp_pipes)
+{
+ return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit,
+ num_vpp_pipes, false);
+}
+
+static u32 h265e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit,
+ u32 num_vpp_pipes)
+{
+ return calculate_enc_scratch1_size(width, height, 32, num_ref, ten_bit,
+ num_vpp_pipes, true);
+}
+
+static u32 vp8e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit,
+ u32 num_vpp_pipes)
+{
+ return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit,
+ 1, false);
+}
+
+static u32 ubwc_metadata_plane_stride(u32 width, u32 metadata_stride_multi,
+ u32 tile_width_pels)
+{
+ return ALIGN(((width + (tile_width_pels - 1)) / tile_width_pels),
+ metadata_stride_multi);
+}
+
+static u32 ubwc_metadata_plane_bufheight(u32 height, u32 metadata_height_multi,
+ u32 tile_height_pels)
+{
+ return ALIGN(((height + (tile_height_pels - 1)) / tile_height_pels),
+ metadata_height_multi);
+}
+
+static u32 ubwc_metadata_plane_buffer_size(u32 metadata_stride,
+ u32 metadata_buf_height)
+{
+ return ALIGN(metadata_stride * metadata_buf_height, SZ_4K);
+}
+
+static u32 enc_scratch2_size(u32 width, u32 height, u32 num_ref, bool ten_bit)
+{
+ u32 aligned_width, aligned_height, chroma_height, ref_buf_height;
+ u32 luma_size, chroma_size;
+ u32 metadata_stride, meta_buf_height, meta_size_y, meta_size_c;
+ u32 ref_luma_stride_bytes, ref_chroma_height_bytes;
+ u32 ref_buf_size, ref_stride;
+ u32 size;
+
+ if (!ten_bit) {
+ aligned_height = ALIGN(height, HFI_VENUS_HEIGHT_ALIGNMENT);
+ chroma_height = height >> 1;
+ chroma_height = ALIGN(chroma_height,
+ HFI_VENUS_HEIGHT_ALIGNMENT);
+ aligned_width = ALIGN(width, HFI_VENUS_WIDTH_ALIGNMENT);
+ metadata_stride =
+ ubwc_metadata_plane_stride(width, 64,
+ NV12_UBWC_Y_TILE_WIDTH);
+ meta_buf_height =
+ ubwc_metadata_plane_bufheight(height, 16,
+ NV12_UBWC_Y_TILE_HEIGHT);
+ meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride,
+ meta_buf_height);
+ meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride,
+ meta_buf_height);
+ size = (aligned_height + chroma_height) * aligned_width +
+ meta_size_y + meta_size_c;
+ size = (size * (num_ref + 3)) + 4096;
+ } else {
+ ref_buf_height = (height + (HFI_VENUS_HEIGHT_ALIGNMENT - 1))
+ & (~(HFI_VENUS_HEIGHT_ALIGNMENT - 1));
+ ref_luma_stride_bytes =
+ ((width + SYSTEM_LAL_TILE10 - 1) / SYSTEM_LAL_TILE10) *
+ SYSTEM_LAL_TILE10;
+ ref_stride = 4 * (ref_luma_stride_bytes / 3);
+ ref_stride = (ref_stride + (128 - 1)) & (~(128 - 1));
+ luma_size = ref_buf_height * ref_stride;
+ ref_chroma_height_bytes = (((height + 1) >> 1) +
+ (32 - 1)) & (~(32 - 1));
+ chroma_size = ref_stride * ref_chroma_height_bytes;
+ luma_size = (luma_size + (SZ_4K - 1)) & (~(SZ_4K - 1));
+ chroma_size = (chroma_size + (SZ_4K - 1)) & (~(SZ_4K - 1));
+ ref_buf_size = luma_size + chroma_size;
+ metadata_stride =
+ ubwc_metadata_plane_stride(width,
+ METADATA_STRIDE_MULTIPLE,
+ TP10_UBWC_Y_TILE_WIDTH);
+ meta_buf_height =
+ ubwc_metadata_plane_bufheight(height,
+ METADATA_HEIGHT_MULTIPLE,
+ TP10_UBWC_Y_TILE_HEIGHT);
+ meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride,
+ meta_buf_height);
+ meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride,
+ meta_buf_height);
+ size = ref_buf_size + meta_size_y + meta_size_c;
+ size = (size * (num_ref + 3)) + 4096;
+ }
+
+ return size;
+}
+
+static u32 enc_persist_size(void)
+{
+ return HFI_IRIS2_ENC_PERSIST_SIZE;
+}
+
+static u32 h264d_persist1_size(void)
+{
+ return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264),
+ HFI_DMA_ALIGNMENT);
+}
+
+static u32 h265d_persist1_size(void)
+{
+ return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_TILE
+ * sizeof(u32)), HFI_DMA_ALIGNMENT);
+}
+
+static u32 vp8d_persist1_size(void)
+{
+ return ALIGN(VP8_NUM_PROBABILITY_TABLE_BUF * VP8_PROB_TABLE_SIZE,
+ HFI_DMA_ALIGNMENT);
+}
+
+static u32 vp9d_persist1_size(void)
+{
+ return
+ ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE,
+ HFI_DMA_ALIGNMENT) +
+ ALIGN(HFI_IRIS2_VP9D_COMV_SIZE, HFI_DMA_ALIGNMENT) +
+ ALIGN(MAX_SUPERFRAME_HEADER_LEN, HFI_DMA_ALIGNMENT) +
+ ALIGN(VP9_UDC_HEADER_BUF_SIZE, HFI_DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE,
+ HFI_DMA_ALIGNMENT);
+}
+
+static u32 mpeg2d_persist1_size(void)
+{
+ return QMATRIX_SIZE + MP2D_QPDUMP_SIZE;
+}
+
+struct dec_bufsize_ops {
+ u32 (*scratch)(u32 width, u32 height, bool is_interlaced);
+ u32 (*scratch1)(u32 width, u32 height, u32 min_buf_count,
+ bool split_mode_enabled, u32 num_vpp_pipes);
+ u32 (*persist1)(void);
+};
+
+struct enc_bufsize_ops {
+ u32 (*scratch)(u32 width, u32 height, u32 work_mode, u32 num_vpp_pipes,
+ u32 rc_type);
+ u32 (*scratch1)(u32 width, u32 height, u32 num_ref, bool ten_bit,
+ u32 num_vpp_pipes);
+ u32 (*scratch2)(u32 width, u32 height, u32 num_ref, bool ten_bit);
+ u32 (*persist)(void);
+};
+
+static struct dec_bufsize_ops dec_h264_ops = {
+ .scratch = h264d_scratch_size,
+ .scratch1 = h264d_scratch1_size,
+ .persist1 = h264d_persist1_size,
+};
+
+static struct dec_bufsize_ops dec_h265_ops = {
+ .scratch = h265d_scratch_size,
+ .scratch1 = h265d_scratch1_size,
+ .persist1 = h265d_persist1_size,
+};
+
+static struct dec_bufsize_ops dec_vp8_ops = {
+ .scratch = vpxd_scratch_size,
+ .scratch1 = vp8d_scratch1_size,
+ .persist1 = vp8d_persist1_size,
+};
+
+static struct dec_bufsize_ops dec_vp9_ops = {
+ .scratch = vpxd_scratch_size,
+ .scratch1 = vp9d_scratch1_size,
+ .persist1 = vp9d_persist1_size,
+};
+
+static struct dec_bufsize_ops dec_mpeg2_ops = {
+ .scratch = mpeg2d_scratch_size,
+ .scratch1 = mpeg2d_scratch1_size,
+ .persist1 = mpeg2d_persist1_size,
+};
+
+static struct enc_bufsize_ops enc_h264_ops = {
+ .scratch = h264e_scratch_size,
+ .scratch1 = h264e_scratch1_size,
+ .scratch2 = enc_scratch2_size,
+ .persist = enc_persist_size,
+};
+
+static struct enc_bufsize_ops enc_h265_ops = {
+ .scratch = h265e_scratch_size,
+ .scratch1 = h265e_scratch1_size,
+ .scratch2 = enc_scratch2_size,
+ .persist = enc_persist_size,
+};
+
+static struct enc_bufsize_ops enc_vp8_ops = {
+ .scratch = vp8e_scratch_size,
+ .scratch1 = vp8e_scratch1_size,
+ .scratch2 = enc_scratch2_size,
+ .persist = enc_persist_size,
+};
+
+static u32
+calculate_dec_input_frame_size(u32 width, u32 height, u32 codec,
+ u32 max_mbs_per_frame, u32 buffer_size_limit)
+{
+ u32 frame_size, num_mbs;
+ u32 div_factor = 1;
+ u32 base_res_mbs = NUM_MBS_4K;
+
+ /*
+ * Decoder input size calculation:
+ * If clip is 8k buffer size is calculated for 8k : 8k mbs/4
+ * For 8k cases we expect width/height to be set always.
+ * In all other cases size is calculated for 4k:
+ * 4k mbs for VP8/VP9 and 4k/2 for remaining codecs
+ */
+ num_mbs = (ALIGN(height, 16) * ALIGN(width, 16)) / 256;
+ if (num_mbs > NUM_MBS_4K) {
+ div_factor = 4;
+ base_res_mbs = max_mbs_per_frame;
+ } else {
+ base_res_mbs = NUM_MBS_4K;
+ if (codec == V4L2_PIX_FMT_VP9)
+ div_factor = 1;
+ else
+ div_factor = 2;
+ }
+
+ frame_size = base_res_mbs * MB_SIZE_IN_PIXEL * 3 / 2 / div_factor;
+
+ /* multiply by 10/8 (1.25) to get size for 10 bit case */
+ if (codec == V4L2_PIX_FMT_VP9 || codec == V4L2_PIX_FMT_HEVC)
+ frame_size = frame_size + (frame_size >> 2);
+
+ if (buffer_size_limit && buffer_size_limit < frame_size)
+ frame_size = buffer_size_limit;
+
+ return ALIGN(frame_size, SZ_4K);
+}
+
+static int output_buffer_count(u32 session_type, u32 codec)
+{
+ u32 output_min_count;
+
+ if (session_type == VIDC_SESSION_TYPE_DEC) {
+ switch (codec) {
+ case V4L2_PIX_FMT_MPEG2:
+ case V4L2_PIX_FMT_VP8:
+ output_min_count = 6;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ output_min_count = 9;
+ break;
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_HEVC:
+ default:
+ output_min_count = 8;
+ break;
+ }
+ } else {
+ output_min_count = MIN_ENC_OUTPUT_BUFFERS;
+ }
+
+ return output_min_count;
+}
+
+static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype,
+ struct hfi_buffer_requirements *bufreq)
+{
+ enum hfi_version version = params->version;
+ u32 codec = params->codec;
+ u32 width = params->width, height = params->height, out_min_count;
+ struct dec_bufsize_ops *dec_ops;
+ bool is_secondary_output = params->dec.is_secondary_output;
+ bool is_interlaced = params->dec.is_interlaced;
+ u32 max_mbs_per_frame = params->dec.max_mbs_per_frame;
+ u32 buffer_size_limit = params->dec.buffer_size_limit;
+ u32 num_vpp_pipes = params->num_vpp_pipes;
+
+ switch (codec) {
+ case V4L2_PIX_FMT_H264:
+ dec_ops = &dec_h264_ops;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ dec_ops = &dec_h265_ops;
+ break;
+ case V4L2_PIX_FMT_VP8:
+ dec_ops = &dec_vp8_ops;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ dec_ops = &dec_vp9_ops;
+ break;
+ case V4L2_PIX_FMT_MPEG2:
+ dec_ops = &dec_mpeg2_ops;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ out_min_count = output_buffer_count(VIDC_SESSION_TYPE_DEC, codec);
+
+ bufreq->type = buftype;
+ bufreq->region_size = 0;
+ bufreq->count_min = 1;
+ bufreq->count_actual = 1;
+ bufreq->hold_count = 1;
+ bufreq->contiguous = 1;
+ bufreq->alignment = 256;
+
+ if (buftype == HFI_BUFFER_INPUT) {
+ bufreq->count_min = MIN_INPUT_BUFFERS;
+ bufreq->size =
+ calculate_dec_input_frame_size(width, height, codec,
+ max_mbs_per_frame,
+ buffer_size_limit);
+ } else if (buftype == HFI_BUFFER_OUTPUT ||
+ buftype == HFI_BUFFER_OUTPUT2) {
+ bufreq->count_min = out_min_count;
+ bufreq->size =
+ venus_helper_get_framesz_raw(params->hfi_color_fmt,
+ width, height);
+ } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) {
+ bufreq->size = dec_ops->scratch(width, height, is_interlaced);
+ } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) {
+ bufreq->size = dec_ops->scratch1(width, height, out_min_count,
+ is_secondary_output,
+ num_vpp_pipes);
+ } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST_1) {
+ bufreq->size = dec_ops->persist1();
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype,
+ struct hfi_buffer_requirements *bufreq)
+{
+ enum hfi_version version = params->version;
+ struct enc_bufsize_ops *enc_ops;
+ u32 width = params->width;
+ u32 height = params->height;
+ bool is_tenbit = params->enc.is_tenbit;
+ u32 num_bframes = params->enc.num_b_frames;
+ u32 codec = params->codec;
+ u32 work_mode = params->enc.work_mode;
+ u32 rc_type = params->enc.rc_type;
+ u32 num_vpp_pipes = params->num_vpp_pipes;
+ u32 num_ref;
+
+ switch (codec) {
+ case V4L2_PIX_FMT_H264:
+ enc_ops = &enc_h264_ops;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ enc_ops = &enc_h265_ops;
+ break;
+ case V4L2_PIX_FMT_VP8:
+ enc_ops = &enc_vp8_ops;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ num_ref = num_bframes > 0 ? num_bframes + 1 : 1;
+
+ bufreq->type = buftype;
+ bufreq->region_size = 0;
+ bufreq->count_min = 1;
+ bufreq->count_actual = 1;
+ bufreq->hold_count = 1;
+ bufreq->contiguous = 1;
+ bufreq->alignment = 256;
+
+ if (buftype == HFI_BUFFER_INPUT) {
+ bufreq->count_min = MIN_INPUT_BUFFERS;
+ bufreq->size =
+ venus_helper_get_framesz_raw(params->hfi_color_fmt,
+ width, height);
+ } else if (buftype == HFI_BUFFER_OUTPUT ||
+ buftype == HFI_BUFFER_OUTPUT2) {
+ bufreq->count_min =
+ output_buffer_count(VIDC_SESSION_TYPE_ENC, codec);
+ bufreq->size = calculate_enc_output_frame_size(width, height,
+ rc_type);
+ } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) {
+ bufreq->size = enc_ops->scratch(width, height, work_mode,
+ num_vpp_pipes, rc_type);
+ } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) {
+ bufreq->size = enc_ops->scratch1(width, height, num_ref,
+ is_tenbit, num_vpp_pipes);
+ } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_2(version)) {
+ bufreq->size = enc_ops->scratch2(width, height, num_ref,
+ is_tenbit);
+ } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST) {
+ bufreq->size = enc_ops->persist();
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type,
+ u32 buftype, struct hfi_buffer_requirements *bufreq)
+{
+ if (session_type == VIDC_SESSION_TYPE_DEC)
+ return bufreq_dec(params, buftype, bufreq);
+ else
+ return bufreq_enc(params, buftype, bufreq);
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c
new file mode 100644
index 000000000000..8f47804e973f
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_platform.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include "hfi_platform.h"
+
+const struct hfi_platform *hfi_platform_get(enum hfi_version version)
+{
+ switch (version) {
+ case HFI_VERSION_4XX:
+ return &hfi_plat_v4;
+ case HFI_VERSION_6XX:
+ return &hfi_plat_v6;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+unsigned long
+hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, u32 session_type)
+{
+ const struct hfi_platform *plat;
+ unsigned long freq = 0;
+
+ plat = hfi_platform_get(version);
+ if (!plat)
+ return 0;
+
+ if (plat->codec_vpp_freq)
+ freq = plat->codec_vpp_freq(session_type, codec);
+
+ return freq;
+}
+
+unsigned long
+hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session_type)
+{
+ const struct hfi_platform *plat;
+ unsigned long freq = 0;
+
+ plat = hfi_platform_get(version);
+ if (!plat)
+ return 0;
+
+ if (plat->codec_vpp_freq)
+ freq = plat->codec_vsp_freq(session_type, codec);
+
+ return freq;
+}
+
+u8 hfi_platform_num_vpp_pipes(enum hfi_version version)
+{
+ const struct hfi_platform *plat;
+
+ plat = hfi_platform_get(version);
+ if (!plat)
+ return 0;
+
+ if (plat->num_vpp_pipes)
+ return plat->num_vpp_pipes();
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h
new file mode 100644
index 000000000000..3819bb2b36bd
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_platform.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __HFI_PLATFORM_H__
+#define __HFI_PLATFORM_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include "hfi.h"
+#include "hfi_plat_bufs.h"
+#include "hfi_helper.h"
+
+#define MAX_PLANES 4
+#define MAX_FMT_ENTRIES 32
+#define MAX_CAP_ENTRIES 32
+#define MAX_ALLOC_MODE_ENTRIES 16
+#define MAX_CODEC_NUM 32
+#define MAX_SESSIONS 16
+
+struct raw_formats {
+ u32 buftype;
+ u32 fmt;
+};
+
+struct hfi_plat_caps {
+ u32 codec;
+ u32 domain;
+ bool cap_bufs_mode_dynamic;
+ unsigned int num_caps;
+ struct hfi_capability caps[MAX_CAP_ENTRIES];
+ unsigned int num_pl;
+ struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT];
+ unsigned int num_fmts;
+ struct raw_formats fmts[MAX_FMT_ENTRIES];
+ bool valid; /* used only for Venus v1xx */
+};
+
+struct hfi_platform_codec_freq_data {
+ u32 pixfmt;
+ u32 session_type;
+ unsigned long vpp_freq;
+ unsigned long vsp_freq;
+};
+
+struct hfi_platform {
+ unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec);
+ unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec);
+ void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count);
+ const struct hfi_plat_caps *(*capabilities)(unsigned int *entries);
+ u8 (*num_vpp_pipes)(void);
+ int (*bufreq)(struct hfi_plat_buffers_params *params, u32 session_type,
+ u32 buftype, struct hfi_buffer_requirements *bufreq);
+};
+
+extern const struct hfi_platform hfi_plat_v4;
+extern const struct hfi_platform hfi_plat_v6;
+
+const struct hfi_platform *hfi_platform_get(enum hfi_version version);
+unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec,
+ u32 session_type);
+unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec,
+ u32 session_type);
+u8 hfi_platform_num_vpp_pipes(enum hfi_version version);
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
new file mode 100644
index 000000000000..3848bb6d7408
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include "hfi_platform.h"
+
+static const struct hfi_plat_caps caps[] = {
+{
+ .codec = HFI_VIDEO_CODEC_H264,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52},
+ .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52},
+ .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52},
+ .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52},
+ .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52},
+ .num_pl = 5,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_HEVC,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28},
+ .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010},
+ .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10},
+ .num_fmts = 7,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP8,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0},
+ .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1},
+ .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2},
+ .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3},
+ .num_pl = 4,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP9,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_VP9_PROFILE_P0, 200},
+ .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010},
+ .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10},
+ .num_fmts = 7,
+}, {
+ .codec = HFI_VIDEO_CODEC_MPEG2,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14},
+ .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_H264,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1},
+ .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1},
+ .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .num_caps = 21,
+ .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52},
+ .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52},
+ .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52},
+ .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52},
+ .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52},
+ .num_pl = 5,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_HEVC,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1},
+ .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1},
+ .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .num_caps = 24,
+ .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP8,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
+ .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
+ .num_caps = 23,
+ .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0},
+ .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1},
+ .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2},
+ .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3},
+ .num_pl = 4,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+} };
+
+static const struct hfi_plat_caps *get_capabilities(unsigned int *entries)
+{
+ *entries = ARRAY_SIZE(caps);
+ return caps;
+}
+
+static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
+{
+ *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC |
+ HFI_VIDEO_CODEC_VP8;
+ *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC |
+ HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 |
+ HFI_VIDEO_CODEC_MPEG2;
+ *count = 8;
+}
+
+static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 },
+ { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 },
+};
+
+static const struct hfi_platform_codec_freq_data *
+get_codec_freq_data(u32 session_type, u32 pixfmt)
+{
+ const struct hfi_platform_codec_freq_data *data = codec_freq_data;
+ unsigned int i, data_size = ARRAY_SIZE(codec_freq_data);
+ const struct hfi_platform_codec_freq_data *found = NULL;
+
+ for (i = 0; i < data_size; i++) {
+ if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) {
+ found = &data[i];
+ break;
+ }
+ }
+
+ return found;
+}
+
+static unsigned long codec_vpp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->vpp_freq;
+
+ return 0;
+}
+
+static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->vsp_freq;
+
+ return 0;
+}
+
+const struct hfi_platform hfi_plat_v4 = {
+ .codec_vpp_freq = codec_vpp_freq,
+ .codec_vsp_freq = codec_vsp_freq,
+ .codecs = get_codecs,
+ .capabilities = get_capabilities,
+};
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
new file mode 100644
index 000000000000..2278be13cb90
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include "hfi_platform.h"
+
+static const struct hfi_plat_caps caps[] = {
+{
+ .codec = HFI_VIDEO_CODEC_H264,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 5760, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 5760, 1},
+ /* ((5760 * 2880) / 256) */
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 36, 64800, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 200000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 36, 1958400, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
+ .num_caps = 9,
+ .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52},
+ .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52},
+ .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52},
+ .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52},
+ .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52},
+ .num_pl = 5,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_HEVC,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010},
+ .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10},
+ .num_fmts = 7,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP8,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0},
+ .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1},
+ .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2},
+ .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3},
+ .num_pl = 4,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP9,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_VP9_PROFILE_P0, 200},
+ .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010},
+ .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10},
+ .num_fmts = 7,
+}, {
+ .codec = HFI_VIDEO_CODEC_MPEG2,
+ .domain = VIDC_SESSION_TYPE_DEC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1},
+ .num_caps = 10,
+ .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14},
+ .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12},
+ .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_H264,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1},
+ .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1},
+ .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .num_caps = 21,
+ .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52},
+ .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52},
+ .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52},
+ .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52},
+ .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52},
+ .num_pl = 5,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_HEVC,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1},
+ .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1},
+ .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .num_caps = 24,
+ .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
+ .num_pl = 2,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+}, {
+ .codec = HFI_VIDEO_CODEC_VP8,
+ .domain = VIDC_SESSION_TYPE_ENC,
+ .cap_bufs_mode_dynamic = true,
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1},
+ .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
+ .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
+ .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
+ .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
+ .num_caps = 23,
+ .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0},
+ .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1},
+ .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2},
+ .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3},
+ .num_pl = 4,
+ .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12},
+ .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC},
+ .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC},
+ .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010},
+ .num_fmts = 4,
+} };
+
+static const struct hfi_plat_caps *get_capabilities(unsigned int *entries)
+{
+ *entries = ARRAY_SIZE(caps);
+ return caps;
+}
+
+static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
+{
+ *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC |
+ HFI_VIDEO_CODEC_VP8;
+ *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC |
+ HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 |
+ HFI_VIDEO_CODEC_MPEG2;
+ *count = 8;
+}
+
+static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60 },
+ { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60 },
+ { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60 },
+};
+
+static const struct hfi_platform_codec_freq_data *
+get_codec_freq_data(u32 session_type, u32 pixfmt)
+{
+ const struct hfi_platform_codec_freq_data *data = codec_freq_data;
+ unsigned int i, data_size = ARRAY_SIZE(codec_freq_data);
+ const struct hfi_platform_codec_freq_data *found = NULL;
+
+ for (i = 0; i < data_size; i++) {
+ if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) {
+ found = &data[i];
+ break;
+ }
+ }
+
+ return found;
+}
+
+static unsigned long codec_vpp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->vpp_freq;
+
+ return 0;
+}
+
+static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->vsp_freq;
+
+ return 0;
+}
+
+static u8 num_vpp_pipes(void)
+{
+ return 4;
+}
+
+const struct hfi_platform hfi_plat_v6 = {
+ .codec_vpp_freq = codec_vpp_freq,
+ .codec_vsp_freq = codec_vsp_freq,
+ .codecs = get_codecs,
+ .capabilities = get_capabilities,
+ .num_vpp_pipes = num_vpp_pipes,
+ .bufreq = hfi_plat_bufreq_v6,
+};
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 4be4a75ddcb6..50e03f8fc278 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -372,7 +372,7 @@ static void venus_soft_int(struct venus_hfi_device *hdev)
}
static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev,
- void *pkt)
+ void *pkt, bool sync)
{
struct device *dev = hdev->core->dev;
struct hfi_pkt_hdr *cmd_packet;
@@ -394,18 +394,29 @@ static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev,
return ret;
}
+ if (sync) {
+ /*
+ * Inform video hardware to raise interrupt for synchronous
+ * commands
+ */
+ queue = &hdev->queues[IFACEQ_MSG_IDX];
+ queue->qhdr->rx_req = 1;
+ /* ensure rx_req is updated in memory */
+ wmb();
+ }
+
if (rx_req)
venus_soft_int(hdev);
return 0;
}
-static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt)
+static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt, bool sync)
{
int ret;
mutex_lock(&hdev->lock);
- ret = venus_iface_cmdq_write_nolock(hdev, pkt);
+ ret = venus_iface_cmdq_write_nolock(hdev, pkt, sync);
mutex_unlock(&hdev->lock);
return ret;
@@ -428,7 +439,7 @@ static int venus_hfi_core_set_resource(struct venus_core *core, u32 id,
if (ret)
return ret;
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -778,7 +789,7 @@ static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug)
pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug);
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -795,7 +806,7 @@ static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode)
pkt_sys_coverage_config(pkt, mode);
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -816,7 +827,7 @@ static int venus_sys_set_idle_message(struct venus_hfi_device *hdev,
pkt_sys_idle_indicator(pkt, enable);
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -834,7 +845,7 @@ static int venus_sys_set_power_control(struct venus_hfi_device *hdev,
pkt_sys_power_control(pkt, enable);
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -885,14 +896,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev)
return ret;
}
-static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type)
+static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type, bool sync)
{
struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
struct hfi_session_pkt pkt;
pkt_session_cmd(&pkt, pkt_type, inst);
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, sync);
}
static void venus_flush_debug_queue(struct venus_hfi_device *hdev)
@@ -922,7 +933,7 @@ static int venus_prepare_power_collapse(struct venus_hfi_device *hdev,
pkt_sys_pc_prep(&pkt);
- ret = venus_iface_cmdq_write(hdev, &pkt);
+ ret = venus_iface_cmdq_write(hdev, &pkt, false);
if (ret)
return ret;
@@ -1064,13 +1075,13 @@ static int venus_core_init(struct venus_core *core)
venus_set_state(hdev, VENUS_STATE_INIT);
- ret = venus_iface_cmdq_write(hdev, &pkt);
+ ret = venus_iface_cmdq_write(hdev, &pkt, false);
if (ret)
return ret;
pkt_sys_image_version(&version_pkt);
- ret = venus_iface_cmdq_write(hdev, &version_pkt);
+ ret = venus_iface_cmdq_write(hdev, &version_pkt, false);
if (ret)
dev_warn(dev, "failed to send image version pkt to fw\n");
@@ -1099,7 +1110,7 @@ static int venus_core_ping(struct venus_core *core, u32 cookie)
pkt_sys_ping(&pkt, cookie);
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, false);
}
static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type)
@@ -1112,7 +1123,7 @@ static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type)
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, false);
}
static int venus_session_init(struct venus_inst *inst, u32 session_type,
@@ -1130,7 +1141,7 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type,
if (ret)
goto err;
- ret = venus_iface_cmdq_write(hdev, &pkt);
+ ret = venus_iface_cmdq_write(hdev, &pkt, true);
if (ret)
goto err;
@@ -1151,7 +1162,7 @@ static int venus_session_end(struct venus_inst *inst)
dev_warn(dev, "fw coverage msg ON failed\n");
}
- return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END);
+ return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END, true);
}
static int venus_session_abort(struct venus_inst *inst)
@@ -1160,7 +1171,7 @@ static int venus_session_abort(struct venus_inst *inst)
venus_flush_debug_queue(hdev);
- return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT);
+ return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT, true);
}
static int venus_session_flush(struct venus_inst *inst, u32 flush_mode)
@@ -1173,22 +1184,22 @@ static int venus_session_flush(struct venus_inst *inst, u32 flush_mode)
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, true);
}
static int venus_session_start(struct venus_inst *inst)
{
- return venus_session_cmd(inst, HFI_CMD_SESSION_START);
+ return venus_session_cmd(inst, HFI_CMD_SESSION_START, true);
}
static int venus_session_stop(struct venus_inst *inst)
{
- return venus_session_cmd(inst, HFI_CMD_SESSION_STOP);
+ return venus_session_cmd(inst, HFI_CMD_SESSION_STOP, true);
}
static int venus_session_continue(struct venus_inst *inst)
{
- return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE);
+ return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE, false);
}
static int venus_session_etb(struct venus_inst *inst,
@@ -1205,7 +1216,7 @@ static int venus_session_etb(struct venus_inst *inst,
if (ret)
return ret;
- ret = venus_iface_cmdq_write(hdev, &pkt);
+ ret = venus_iface_cmdq_write(hdev, &pkt, false);
} else if (session_type == VIDC_SESSION_TYPE_ENC) {
struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt;
@@ -1213,7 +1224,7 @@ static int venus_session_etb(struct venus_inst *inst,
if (ret)
return ret;
- ret = venus_iface_cmdq_write(hdev, &pkt);
+ ret = venus_iface_cmdq_write(hdev, &pkt, false);
} else {
ret = -EINVAL;
}
@@ -1232,7 +1243,7 @@ static int venus_session_ftb(struct venus_inst *inst,
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, false);
}
static int venus_session_set_buffers(struct venus_inst *inst,
@@ -1252,7 +1263,7 @@ static int venus_session_set_buffers(struct venus_inst *inst,
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, pkt);
+ return venus_iface_cmdq_write(hdev, pkt, false);
}
static int venus_session_unset_buffers(struct venus_inst *inst,
@@ -1272,17 +1283,17 @@ static int venus_session_unset_buffers(struct venus_inst *inst,
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, pkt);
+ return venus_iface_cmdq_write(hdev, pkt, true);
}
static int venus_session_load_res(struct venus_inst *inst)
{
- return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES);
+ return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES, true);
}
static int venus_session_release_res(struct venus_inst *inst)
{
- return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES);
+ return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES, true);
}
static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
@@ -1299,7 +1310,7 @@ static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
if (ret)
return ret;
- ret = venus_iface_cmdq_write(hdev, pkt);
+ ret = venus_iface_cmdq_write(hdev, pkt, false);
if (ret)
return ret;
@@ -1320,7 +1331,7 @@ static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, pkt);
+ return venus_iface_cmdq_write(hdev, pkt, false);
}
static int venus_session_set_property(struct venus_inst *inst, u32 ptype,
@@ -1339,7 +1350,7 @@ static int venus_session_set_property(struct venus_inst *inst, u32 ptype,
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, pkt);
+ return venus_iface_cmdq_write(hdev, pkt, false);
}
static int venus_session_get_property(struct venus_inst *inst, u32 ptype)
@@ -1352,7 +1363,7 @@ static int venus_session_get_property(struct venus_inst *inst, u32 ptype)
if (ret)
return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
+ return venus_iface_cmdq_write(hdev, &pkt, true);
}
static int venus_resume(struct venus_core *core)
@@ -1591,9 +1602,6 @@ int venus_hfi_create(struct venus_core *core)
hdev->suspended = true;
core->priv = hdev;
core->ops = &venus_hfi_ops;
- core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY |
- ENC_DEINTERLACE_CAPABILITY |
- DEC_MULTI_STREAM_CAPABILITY;
ret = venus_interface_queues_init(hdev);
if (ret)
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index a3850261d697..43c4e3d9e281 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -18,6 +18,7 @@
#include "hfi_parser.h"
#include "hfi_venus_io.h"
#include "pm_helpers.h"
+#include "hfi_platform.h"
static bool legacy_binding;
@@ -510,7 +511,7 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
if (inst_pos->state != INST_START)
continue;
- vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq;
+ vpp_freq = inst_pos->clk_data.vpp_freq;
coreid = inst_pos->clk_data.core_id;
mbs_per_sec = load_per_instance(inst_pos);
@@ -559,7 +560,7 @@ static int decide_core(struct venus_inst *inst)
return 0;
inst_load = load_per_instance(inst);
- inst_load *= inst->clk_data.codec_freq_data->vpp_freq;
+ inst_load *= inst->clk_data.vpp_freq;
max_freq = core->res->freq_tbl[0].freq;
min_loaded_core(inst, &min_coreid, &min_load);
@@ -773,13 +774,6 @@ static int vcodec_domains_get(struct device *dev)
core->pmdomains[i] = pd;
}
- core->pd_dl_venus = device_link_add(dev, core->pmdomains[0],
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_STATELESS |
- DL_FLAG_RPM_ACTIVE);
- if (!core->pd_dl_venus)
- return -ENODEV;
-
skip_pmdomains:
if (!core->has_opp_table)
return 0;
@@ -806,14 +800,12 @@ skip_pmdomains:
opp_dl_add_err:
dev_pm_opp_detach_genpd(core->opp_table);
opp_attach_err:
- if (core->pd_dl_venus) {
- device_link_del(core->pd_dl_venus);
- for (i = 0; i < res->vcodec_pmdomains_num; i++) {
- if (IS_ERR_OR_NULL(core->pmdomains[i]))
- continue;
- dev_pm_domain_detach(core->pmdomains[i], true);
- }
+ for (i = 0; i < res->vcodec_pmdomains_num; i++) {
+ if (IS_ERR_OR_NULL(core->pmdomains[i]))
+ continue;
+ dev_pm_domain_detach(core->pmdomains[i], true);
}
+
return ret;
}
@@ -826,9 +818,6 @@ static void vcodec_domains_put(struct device *dev)
if (!res->vcodec_pmdomains_num)
goto skip_pmdomains;
- if (core->pd_dl_venus)
- device_link_del(core->pd_dl_venus);
-
for (i = 0; i < res->vcodec_pmdomains_num; i++) {
if (IS_ERR_OR_NULL(core->pmdomains[i]))
continue;
@@ -915,16 +904,30 @@ static void core_put_v4(struct device *dev)
static int core_power_v4(struct device *dev, int on)
{
struct venus_core *core = dev_get_drvdata(dev);
+ struct device *pmctrl = core->pmdomains[0];
int ret = 0;
if (on == POWER_ON) {
+ if (pmctrl) {
+ ret = pm_runtime_get_sync(pmctrl);
+ if (ret < 0) {
+ pm_runtime_put_noidle(pmctrl);
+ return ret;
+ }
+ }
+
ret = core_clks_enable(core);
+ if (ret < 0 && pmctrl)
+ pm_runtime_put_sync(pmctrl);
} else {
/* Drop the performance state vote */
if (core->opp_pmdomain)
dev_pm_opp_set_rate(dev, 0);
core_clks_disable(core);
+
+ if (pmctrl)
+ pm_runtime_put_sync(pmctrl);
}
return ret;
@@ -939,10 +942,13 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst,
mbs_per_sec = load_per_instance(inst);
- vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq;
+ if (inst->state != INST_START)
+ return 0;
+
+ vpp_freq = mbs_per_sec * inst->clk_data.vpp_freq;
/* 21 / 20 is overhead factor */
vpp_freq += vpp_freq / 20;
- vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq;
+ vsp_freq = mbs_per_sec * inst->clk_data.vsp_freq;
/* 10 / 7 is overhead factor */
if (inst->session_type == VIDC_SESSION_TYPE_ENC)
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 8488411204c3..e4dc97f00fc3 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -13,7 +13,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include "hfi_venus_io.h"
#include "hfi_parser.h"
@@ -519,8 +519,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
ret = hfi_session_process_buf(inst, &fdata);
- if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING)
+ if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) {
inst->codec_state = VENUS_DEC_STATE_DRAIN;
+ inst->drain_active = true;
+ }
}
unlock:
@@ -637,6 +639,7 @@ static int vdec_output_conf(struct venus_inst *inst)
{
struct venus_core *core = inst->core;
struct hfi_enable en = { .enable = 1 };
+ struct hfi_buffer_requirements bufreq;
u32 width = inst->out_width;
u32 height = inst->out_height;
u32 out_fmt, out2_fmt;
@@ -712,6 +715,23 @@ static int vdec_output_conf(struct venus_inst *inst)
}
if (IS_V3(core) || IS_V4(core)) {
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
+ if (ret)
+ return ret;
+
+ if (bufreq.size > inst->output_buf_size)
+ return -EINVAL;
+
+ if (inst->dpb_fmt) {
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT2,
+ &bufreq);
+ if (ret)
+ return ret;
+
+ if (bufreq.size > inst->output2_buf_size)
+ return -EINVAL;
+ }
+
if (inst->output2_buf_size) {
ret = venus_helper_set_bufsize(inst,
inst->output2_buf_size,
@@ -740,8 +760,8 @@ static int vdec_session_init(struct venus_inst *inst)
{
int ret;
- ret = hfi_session_init(inst, inst->fmt_out->pixfmt);
- if (ret == -EINVAL)
+ ret = venus_helper_session_init(inst);
+ if (ret == -EALREADY)
return 0;
else if (ret)
return ret;
@@ -751,10 +771,6 @@ static int vdec_session_init(struct venus_inst *inst)
if (ret)
goto deinit;
- ret = venus_helper_init_codec_freq_data(inst);
- if (ret)
- goto deinit;
-
return 0;
deinit:
hfi_session_deinit(inst);
@@ -917,10 +933,6 @@ static int vdec_start_capture(struct venus_inst *inst)
return 0;
reconfigure:
- ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true);
- if (ret)
- return ret;
-
ret = vdec_output_conf(inst);
if (ret)
return ret;
@@ -948,15 +960,21 @@ reconfigure:
venus_pm_load_scale(inst);
+ inst->next_buf_last = false;
+
ret = hfi_session_continue(inst);
if (ret)
goto free_dpb_bufs;
inst->codec_state = VENUS_DEC_STATE_DECODING;
+ if (inst->drain_active)
+ inst->codec_state = VENUS_DEC_STATE_DRAIN;
+
inst->streamon_cap = 1;
inst->sequence_cap = 0;
inst->reconfig = false;
+ inst->drain_active = false;
return 0;
@@ -972,7 +990,10 @@ static int vdec_start_output(struct venus_inst *inst)
if (inst->codec_state == VENUS_DEC_STATE_SEEK) {
ret = venus_helper_process_initial_out_bufs(inst);
- inst->codec_state = VENUS_DEC_STATE_DECODING;
+ if (inst->next_buf_last)
+ inst->codec_state = VENUS_DEC_STATE_DRC;
+ else
+ inst->codec_state = VENUS_DEC_STATE_DECODING;
goto done;
}
@@ -988,6 +1009,7 @@ static int vdec_start_output(struct venus_inst *inst)
venus_helper_init_instance(inst);
inst->sequence_out = 0;
inst->reconfig = false;
+ inst->next_buf_last = false;
ret = vdec_set_properties(inst);
if (ret)
@@ -1077,13 +1099,14 @@ static int vdec_stop_capture(struct venus_inst *inst)
ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true);
fallthrough;
case VENUS_DEC_STATE_DRAIN:
- vdec_cancel_dst_buffers(inst);
inst->codec_state = VENUS_DEC_STATE_STOPPED;
+ inst->drain_active = false;
+ fallthrough;
+ case VENUS_DEC_STATE_SEEK:
+ vdec_cancel_dst_buffers(inst);
break;
case VENUS_DEC_STATE_DRC:
- WARN_ON(1);
- fallthrough;
- case VENUS_DEC_STATE_DRC_FLUSH_DONE:
+ ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true);
inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP;
venus_helper_free_dpb_bufs(inst);
break;
@@ -1102,6 +1125,7 @@ static int vdec_stop_output(struct venus_inst *inst)
case VENUS_DEC_STATE_DECODING:
case VENUS_DEC_STATE_DRAIN:
case VENUS_DEC_STATE_STOPPED:
+ case VENUS_DEC_STATE_DRC:
ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true);
inst->codec_state = VENUS_DEC_STATE_SEEK;
break;
@@ -1207,10 +1231,28 @@ static void vdec_buf_cleanup(struct vb2_buffer *vb)
static void vdec_vb2_buf_queue(struct vb2_buffer *vb)
{
struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS };
vdec_pm_get_put(inst);
+ mutex_lock(&inst->lock);
+
+ if (inst->next_buf_last && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) &&
+ inst->codec_state == VENUS_DEC_STATE_DRC) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ vbuf->sequence = inst->sequence_cap++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vb2_set_plane_payload(vb, 0, 0);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ v4l2_event_queue_fh(&inst->fh, &eos);
+ inst->next_buf_last = false;
+ mutex_unlock(&inst->lock);
+ return;
+ }
+
venus_helper_vb2_buf_queue(vb);
+ mutex_unlock(&inst->lock);
}
static const struct vb2_ops vdec_vb2_ops = {
@@ -1253,20 +1295,15 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
vb->timestamp = timestamp_us * NSEC_PER_USEC;
vbuf->sequence = inst->sequence_cap++;
- if (inst->last_buf == vb) {
- inst->last_buf = NULL;
- vbuf->flags |= V4L2_BUF_FLAG_LAST;
- vb2_set_plane_payload(vb, 0, 0);
- vb->timestamp = 0;
- }
-
if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
v4l2_event_queue_fh(&inst->fh, &ev);
- if (inst->codec_state == VENUS_DEC_STATE_DRAIN)
+ if (inst->codec_state == VENUS_DEC_STATE_DRAIN) {
+ inst->drain_active = false;
inst->codec_state = VENUS_DEC_STATE_STOPPED;
+ }
}
if (!bytesused)
@@ -1334,22 +1371,22 @@ static void vdec_event_change(struct venus_inst *inst,
if (inst->bit_depth != ev_data->bit_depth)
inst->bit_depth = ev_data->bit_depth;
+ if (inst->pic_struct != ev_data->pic_struct)
+ inst->pic_struct = ev_data->pic_struct;
+
dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n",
sufficient ? "" : "not", ev_data->width, ev_data->height);
- if (sufficient) {
- hfi_session_continue(inst);
- } else {
- switch (inst->codec_state) {
- case VENUS_DEC_STATE_INIT:
- inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP;
- break;
- case VENUS_DEC_STATE_DECODING:
- inst->codec_state = VENUS_DEC_STATE_DRC;
- break;
- default:
- break;
- }
+ switch (inst->codec_state) {
+ case VENUS_DEC_STATE_INIT:
+ inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP;
+ break;
+ case VENUS_DEC_STATE_DECODING:
+ case VENUS_DEC_STATE_DRAIN:
+ inst->codec_state = VENUS_DEC_STATE_DRC;
+ break;
+ default:
+ break;
}
/*
@@ -1358,19 +1395,17 @@ static void vdec_event_change(struct venus_inst *inst,
* itself doesn't mark the last decoder output buffer with HFI EOS flag.
*/
- if (!sufficient && inst->codec_state == VENUS_DEC_STATE_DRC) {
- struct vb2_v4l2_buffer *last;
+ if (inst->codec_state == VENUS_DEC_STATE_DRC) {
int ret;
- last = v4l2_m2m_last_dst_buf(inst->m2m_ctx);
- if (last)
- inst->last_buf = &last->vb2_buf;
+ inst->next_buf_last = true;
ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false);
if (ret)
dev_dbg(dev, VDBGH "flush output error %d\n", ret);
}
+ inst->next_buf_last = true;
inst->reconfig = true;
v4l2_event_queue_fh(&inst->fh, &ev);
wake_up(&inst->reconf_wait);
@@ -1413,8 +1448,7 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event,
static void vdec_flush_done(struct venus_inst *inst)
{
- if (inst->codec_state == VENUS_DEC_STATE_DRC)
- inst->codec_state = VENUS_DEC_STATE_DRC_FLUSH_DONE;
+ dev_dbg(inst->core->dev_dec, VDBGH "flush done\n");
}
static const struct hfi_inst_ops vdec_hfi_ops = {
@@ -1461,7 +1495,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->ops = &vdec_vb2_ops;
- src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->drv_priv = inst;
src_vq->buf_struct_size = sizeof(struct venus_buffer);
src_vq->allow_zero_bytesused = 1;
@@ -1475,7 +1509,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->ops = &vdec_vb2_ops;
- dst_vq->mem_ops = &vb2_dma_sg_memops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->drv_priv = inst;
dst_vq->buf_struct_size = sizeof(struct venus_buffer);
dst_vq->allow_zero_bytesused = 1;
@@ -1508,6 +1542,7 @@ static int vdec_open(struct file *file)
inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT;
inst->core_acquired = false;
inst->bit_depth = VIDC_BITDEPTH_8;
+ inst->pic_struct = HFI_INTERLACE_FRAME_PROGRESSIVE;
init_waitqueue_head(&inst->reconf_wait);
venus_helper_init_instance(inst);
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 1c61602c5de1..6976ed553647 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -10,7 +10,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
@@ -190,8 +190,10 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
pixmp->height = clamp(pixmp->height, frame_height_min(inst),
frame_height_max(inst));
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ pixmp->width = ALIGN(pixmp->width, 128);
pixmp->height = ALIGN(pixmp->height, 32);
+ }
pixmp->width = ALIGN(pixmp->width, 2);
pixmp->height = ALIGN(pixmp->height, 2);
@@ -335,13 +337,13 @@ venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
switch (s->target) {
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
- s->r.width = inst->width;
- s->r.height = inst->height;
- break;
- case V4L2_SEL_TGT_CROP:
s->r.width = inst->out_width;
s->r.height = inst->out_height;
break;
+ case V4L2_SEL_TGT_CROP:
+ s->r.width = inst->width;
+ s->r.height = inst->height;
+ break;
default:
return -EINVAL;
}
@@ -360,12 +362,19 @@ venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
+ if (s->r.width > inst->out_width ||
+ s->r.height > inst->out_height)
+ return -EINVAL;
+
+ s->r.width = ALIGN(s->r.width, 2);
+ s->r.height = ALIGN(s->r.height, 2);
+
switch (s->target) {
case V4L2_SEL_TGT_CROP:
- if (s->r.width != inst->out_width ||
- s->r.height != inst->out_height ||
- s->r.top != 0 || s->r.left != 0)
- return -EINVAL;
+ s->r.top = 0;
+ s->r.left = 0;
+ inst->width = s->r.width;
+ inst->height = s->r.height;
break;
default:
return -EINVAL;
@@ -536,6 +545,7 @@ static int venc_set_properties(struct venus_inst *inst)
struct hfi_idr_period idrp;
struct hfi_quantization quant;
struct hfi_quantization_range quant_range;
+ struct hfi_enable en;
u32 ptype, rate_control, bitrate;
u32 profile, level;
int ret;
@@ -588,16 +598,19 @@ static int venc_set_properties(struct venus_inst *inst)
return ret;
}
- /* IDR periodicity, n:
- * n = 0 - only the first I-frame is IDR frame
- * n = 1 - all I-frames will be IDR frames
- * n > 1 - every n-th I-frame will be IDR frame
- */
- ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
- idrp.idr_period = 0;
- ret = hfi_session_set_property(inst, ptype, &idrp);
- if (ret)
- return ret;
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
+ inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ /* IDR periodicity, n:
+ * n = 0 - only the first I-frame is IDR frame
+ * n = 1 - all I-frames will be IDR frames
+ * n > 1 - every n-th I-frame will be IDR frame
+ */
+ ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
+ idrp.idr_period = 0;
+ ret = hfi_session_set_property(inst, ptype, &idrp);
+ if (ret)
+ return ret;
+ }
if (ctr->num_b_frames) {
u32 max_num_b_frames = NUM_B_FRAMES_MAX;
@@ -655,6 +668,19 @@ static int venc_set_properties(struct venus_inst *inst)
if (ret)
return ret;
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
+ inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER;
+ if (ctr->header_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)
+ en.enable = 0;
+ else
+ en.enable = 1;
+
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret)
+ return ret;
+ }
+
if (!ctr->bitrate_peak)
bitrate *= 2;
else
@@ -669,17 +695,28 @@ static int venc_set_properties(struct venus_inst *inst)
return ret;
ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP;
- quant.qp_i = ctr->h264_i_qp;
- quant.qp_p = ctr->h264_p_qp;
- quant.qp_b = ctr->h264_b_qp;
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ quant.qp_i = ctr->hevc_i_qp;
+ quant.qp_p = ctr->hevc_p_qp;
+ quant.qp_b = ctr->hevc_b_qp;
+ } else {
+ quant.qp_i = ctr->h264_i_qp;
+ quant.qp_p = ctr->h264_p_qp;
+ quant.qp_b = ctr->h264_b_qp;
+ }
quant.layer_id = 0;
ret = hfi_session_set_property(inst, ptype, &quant);
if (ret)
return ret;
ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE;
- quant_range.min_qp = ctr->h264_min_qp;
- quant_range.max_qp = ctr->h264_max_qp;
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ quant_range.min_qp = ctr->hevc_min_qp;
+ quant_range.max_qp = ctr->hevc_max_qp;
+ } else {
+ quant_range.min_qp = ctr->h264_min_qp;
+ quant_range.max_qp = ctr->h264_max_qp;
+ }
quant_range.layer_id = 0;
ret = hfi_session_set_property(inst, ptype, &quant_range);
if (ret)
@@ -724,10 +761,17 @@ static int venc_init_session(struct venus_inst *inst)
{
int ret;
- ret = hfi_session_init(inst, inst->fmt_cap->pixfmt);
- if (ret)
+ ret = venus_helper_session_init(inst);
+ if (ret == -EALREADY)
+ return 0;
+ else if (ret)
return ret;
+ ret = venus_helper_set_stride(inst, inst->out_width,
+ inst->out_height);
+ if (ret)
+ goto deinit;
+
ret = venus_helper_set_input_resolution(inst, inst->width,
inst->height);
if (ret)
@@ -743,10 +787,6 @@ static int venc_init_session(struct venus_inst *inst)
if (ret)
goto deinit;
- ret = venus_helper_init_codec_freq_data(inst);
- if (ret)
- goto deinit;
-
ret = venc_set_properties(inst);
if (ret)
goto deinit;
@@ -762,17 +802,13 @@ static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num)
struct hfi_buffer_requirements bufreq;
int ret;
- ret = venc_init_session(inst);
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
if (ret)
return ret;
- ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
-
*num = bufreq.count_actual;
- hfi_session_deinit(inst);
-
- return ret;
+ return 0;
}
static int venc_queue_setup(struct vb2_queue *q,
@@ -781,7 +817,7 @@ static int venc_queue_setup(struct vb2_queue *q,
{
struct venus_inst *inst = vb2_get_drv_priv(q);
unsigned int num, min = 4;
- int ret = 0;
+ int ret;
if (*num_planes) {
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
@@ -803,6 +839,13 @@ static int venc_queue_setup(struct vb2_queue *q,
return 0;
}
+ mutex_lock(&inst->lock);
+ ret = venc_init_session(inst);
+ mutex_unlock(&inst->lock);
+
+ if (ret)
+ return ret;
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
*num_planes = inst->fmt_out->num_planes;
@@ -816,8 +859,8 @@ static int venc_queue_setup(struct vb2_queue *q,
inst->num_input_bufs = *num_buffers;
sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt,
- inst->width,
- inst->height);
+ inst->out_width,
+ inst->out_height);
inst->input_buf_size = sizes[0];
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
@@ -838,6 +881,49 @@ static int venc_queue_setup(struct vb2_queue *q,
return ret;
}
+static int venc_buf_init(struct vb2_buffer *vb)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+
+ inst->buf_count++;
+
+ return venus_helper_vb2_buf_init(vb);
+}
+
+static void venc_release_session(struct venus_inst *inst)
+{
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ ret = hfi_session_deinit(inst);
+ if (ret || inst->session_error)
+ hfi_session_abort(inst);
+
+ mutex_unlock(&inst->lock);
+
+ venus_pm_load_scale(inst);
+ INIT_LIST_HEAD(&inst->registeredbufs);
+ venus_pm_release_core(inst);
+}
+
+static void venc_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct venus_buffer *buf = to_venus_buffer(vbuf);
+
+ mutex_lock(&inst->lock);
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (!list_empty(&inst->registeredbufs))
+ list_del_init(&buf->reg_list);
+ mutex_unlock(&inst->lock);
+
+ inst->buf_count--;
+ if (!inst->buf_count)
+ venc_release_session(inst);
+}
+
static int venc_verify_conf(struct venus_inst *inst)
{
enum hfi_version ver = inst->core->res->hfi_version;
@@ -888,38 +974,32 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count)
inst->sequence_cap = 0;
inst->sequence_out = 0;
- ret = venc_init_session(inst);
- if (ret)
- goto bufs_done;
-
ret = venus_pm_acquire_core(inst);
if (ret)
- goto deinit_sess;
+ goto error;
ret = venc_set_properties(inst);
if (ret)
- goto deinit_sess;
+ goto error;
ret = venc_verify_conf(inst);
if (ret)
- goto deinit_sess;
+ goto error;
ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs,
inst->num_output_bufs, 0);
if (ret)
- goto deinit_sess;
+ goto error;
ret = venus_helper_vb2_start_streaming(inst);
if (ret)
- goto deinit_sess;
+ goto error;
mutex_unlock(&inst->lock);
return 0;
-deinit_sess:
- hfi_session_deinit(inst);
-bufs_done:
+error:
venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED);
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
inst->streamon_out = 0;
@@ -929,13 +1009,23 @@ bufs_done:
return ret;
}
+static void venc_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+
+ mutex_lock(&inst->lock);
+ venus_helper_vb2_buf_queue(vb);
+ mutex_unlock(&inst->lock);
+}
+
static const struct vb2_ops venc_vb2_ops = {
.queue_setup = venc_queue_setup,
- .buf_init = venus_helper_vb2_buf_init,
+ .buf_init = venc_buf_init,
+ .buf_cleanup = venc_buf_cleanup,
.buf_prepare = venus_helper_vb2_buf_prepare,
.start_streaming = venc_start_streaming,
.stop_streaming = venus_helper_vb2_stop_streaming,
- .buf_queue = venus_helper_vb2_buf_queue,
+ .buf_queue = venc_vb2_buf_queue,
};
static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type,
@@ -1001,7 +1091,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->ops = &venc_vb2_ops;
- src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->drv_priv = inst;
src_vq->buf_struct_size = sizeof(struct venus_buffer);
src_vq->allow_zero_bytesused = 1;
@@ -1017,7 +1107,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->ops = &venc_vb2_ops;
- dst_vq->mem_ops = &vb2_dma_sg_memops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->drv_priv = inst;
dst_vq->buf_struct_size = sizeof(struct venus_buffer);
dst_vq->allow_zero_bytesused = 1;
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index cf860e6446c0..a52b80055173 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -135,9 +135,60 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
ctr->h264_min_qp = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP:
+ ctr->h264_i_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP:
+ ctr->h264_p_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP:
+ ctr->h264_b_min_qp = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
ctr->h264_max_qp = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP:
+ ctr->h264_i_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP:
+ ctr->h264_p_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP:
+ ctr->h264_b_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
+ ctr->hevc_i_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP:
+ ctr->hevc_p_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP:
+ ctr->hevc_b_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+ ctr->hevc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP:
+ ctr->hevc_i_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP:
+ ctr->hevc_p_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP:
+ ctr->hevc_b_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+ ctr->hevc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP:
+ ctr->hevc_i_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP:
+ ctr->hevc_p_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP:
+ ctr->hevc_b_max_qp = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
ctr->multi_slice_mode = ctrl->val;
break;
@@ -158,6 +209,20 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
ctr->header_mode = ctrl->val;
+ mutex_lock(&inst->lock);
+ if (inst->streamon_out && inst->streamon_cap) {
+ if (ctrl->val == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)
+ en.enable = 0;
+ else
+ en.enable = 1;
+ ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER;
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret) {
+ mutex_unlock(&inst->lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&inst->lock);
break;
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
break;
@@ -208,6 +273,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE:
ctr->frame_skip_mode = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID:
+ ctr->base_priority_id = ctrl->val;
+ break;
default:
return -EINVAL;
}
@@ -223,7 +291,7 @@ int venc_ctrl_init(struct venus_inst *inst)
{
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 33);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 51);
if (ret)
return ret;
@@ -289,7 +357,8 @@ int venc_ctrl_init(struct venus_inst *inst)
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_HEADER_MODE,
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
- 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ ~((1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
+ (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)),
V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE);
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
@@ -311,19 +380,70 @@ int venc_ctrl_init(struct venus_inst *inst)
BITRATE_STEP, BITRATE_DEFAULT_PEAK);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28);
+ V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30);
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1);
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51);
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP, 1, 51, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP, 1, 51, 1, 51);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP, 1, 51, 1, 51);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP, 1, 51, 1, 51);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, 1, 63, 1, 26);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, 1, 63, 1, 28);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, 1, 63, 1, 30);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, 1, 63, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP, 1, 63, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP, 1, 63, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP, 1, 63, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, 1, 63, 1, 63);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP, 1, 63, 1, 63);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP, 1, 63, 1, 63);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP, 1, 63, 1, 63);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN,
@@ -374,6 +494,10 @@ int venc_ctrl_init(struct venus_inst *inst)
(1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT)),
V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID, 0,
+ 6, 1, 0);
+
ret = inst->ctrl_handler.error;
if (ret)
goto err;
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index e48d666f2c63..cb3025992817 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -642,7 +642,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin)
}
asd = v4l2_async_notifier_add_fwnode_subdev(&vin->notifier, fwnode,
- sizeof(*asd));
+ struct v4l2_async_subdev);
if (IS_ERR(asd)) {
ret = PTR_ERR(asd);
goto out;
@@ -842,7 +842,8 @@ static int rvin_mc_parse_of(struct rvin_dev *vin, unsigned int id)
}
asd = v4l2_async_notifier_add_fwnode_subdev(&vin->group->notifier,
- fwnode, sizeof(*asd));
+ fwnode,
+ struct v4l2_async_subdev);
if (IS_ERR(asd)) {
ret = PTR_ERR(asd);
goto out;
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
index 945d2eb87233..e06cd512aba2 100644
--- a/drivers/media/platform/rcar-vin/rcar-csi2.c
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -910,7 +910,7 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv)
priv->notifier.ops = &rcar_csi2_notify_ops;
asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, fwnode,
- sizeof(*asd));
+ struct v4l2_async_subdev);
fwnode_handle_put(fwnode);
if (IS_ERR(asd))
return PTR_ERR(asd);
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 48280ddb15b9..f30dafbdf61c 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -1301,6 +1301,11 @@ void rvin_stop_streaming(struct rvin_dev *vin)
spin_lock_irqsave(&vin->qlock, flags);
+ if (vin->state == STOPPED) {
+ spin_unlock_irqrestore(&vin->qlock, flags);
+ return;
+ }
+
vin->state = STOPPING;
/* Wait until only scratch buffer is used, max 3 interrupts. */
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index e6ea2b7991b8..457a65bf6b66 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -966,12 +966,9 @@ void rvin_v4l2_unregister(struct rvin_dev *vin)
video_unregister_device(&vin->vdev);
}
-static void rvin_notify(struct v4l2_subdev *sd,
- unsigned int notification, void *arg)
+static void rvin_notify_video_device(struct rvin_dev *vin,
+ unsigned int notification, void *arg)
{
- struct rvin_dev *vin =
- container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
-
switch (notification) {
case V4L2_DEVICE_NOTIFY_EVENT:
v4l2_event_queue(&vin->vdev, arg);
@@ -981,6 +978,41 @@ static void rvin_notify(struct v4l2_subdev *sd,
}
}
+static void rvin_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct v4l2_subdev *remote;
+ struct rvin_group *group;
+ struct media_pad *pad;
+ struct rvin_dev *vin =
+ container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
+ unsigned int i;
+
+ /* If no media controller, no need to route the event. */
+ if (!vin->info->use_mc) {
+ rvin_notify_video_device(vin, notification, arg);
+ return;
+ }
+
+ group = vin->group;
+
+ for (i = 0; i < RCAR_VIN_NUM; i++) {
+ vin = group->vin[i];
+ if (!vin)
+ continue;
+
+ pad = media_entity_remote_pad(&vin->pad);
+ if (!pad)
+ continue;
+
+ remote = media_entity_to_v4l2_subdev(pad->entity);
+ if (remote != sd)
+ continue;
+
+ rvin_notify_video_device(vin, notification, arg);
+ }
+}
+
int rvin_v4l2_register(struct rvin_dev *vin)
{
struct video_device *vdev = &vin->vdev;
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
index f318cd4b8086..83bd9a412a56 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/rcar_drif.c
@@ -1231,7 +1231,7 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr)
}
asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
- sizeof(*asd));
+ struct v4l2_async_subdev);
fwnode_handle_put(fwnode);
if (IS_ERR(asd))
return PTR_ERR(asd);
diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c
index c9448de885b6..01c1fbb97bf6 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/rcar_fdp1.c
@@ -1439,8 +1439,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix,
pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline
* pix->height / vsub;
- memset(pix->plane_fmt[i].reserved, 0,
- sizeof(pix->plane_fmt[i].reserved));
}
if (fmt->num_planes == 3) {
@@ -1448,8 +1446,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix,
pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline;
pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage;
- memset(pix->plane_fmt[2].reserved, 0,
- sizeof(pix->plane_fmt[2].reserved));
}
}
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c
index 9b99ff368698..a7c198c17deb 100644
--- a/drivers/media/platform/rcar_jpu.c
+++ b/drivers/media/platform/rcar_jpu.c
@@ -648,6 +648,7 @@ static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width,
if (get_word_be(&jpeg_buffer, &word))
return 0;
skip(&jpeg_buffer, (long)word - 2);
+ break;
case 0:
break;
default:
@@ -793,7 +794,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo,
pix->colorspace = fmt->colorspace;
pix->field = V4L2_FIELD_NONE;
pix->num_planes = fmt->num_planes;
- memset(pix->reserved, 0, sizeof(pix->reserved));
jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX,
fmt->h_align, &pix->height, JPU_HEIGHT_MIN,
@@ -808,8 +808,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo,
pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE +
(JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h);
pix->plane_fmt[0].bytesperline = 0;
- memset(pix->plane_fmt[0].reserved, 0,
- sizeof(pix->plane_fmt[0].reserved));
} else {
unsigned int i, bpl = 0;
@@ -822,8 +820,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo,
for (i = 0; i < pix->num_planes; ++i) {
pix->plane_fmt[i].bytesperline = bpl;
pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8;
- memset(pix->plane_fmt[i].reserved, 0,
- sizeof(pix->plane_fmt[i].reserved));
}
}
diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c
index 4a633ad0e8fa..1678175c49bd 100644
--- a/drivers/media/platform/renesas-ceu.c
+++ b/drivers/media/platform/renesas-ceu.c
@@ -152,8 +152,8 @@ static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf)
* ceu_subdev - Wraps v4l2 sub-device and provides async subdevice.
*/
struct ceu_subdev {
- struct v4l2_subdev *v4l2_sd;
struct v4l2_async_subdev asd;
+ struct v4l2_subdev *v4l2_sd;
/* per-subdevice mbus configuration options */
unsigned int mbus_flags;
@@ -174,7 +174,7 @@ struct ceu_device {
struct v4l2_device v4l2_dev;
/* subdevices descriptors */
- struct ceu_subdev *subdevs;
+ struct ceu_subdev **subdevs;
/* the subdevice currently in use */
struct ceu_subdev *sd;
unsigned int sd_index;
@@ -1195,7 +1195,7 @@ static int ceu_enum_input(struct file *file, void *priv,
if (inp->index >= ceudev->num_sd)
return -EINVAL;
- ceusd = &ceudev->subdevs[inp->index];
+ ceusd = ceudev->subdevs[inp->index];
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->std = 0;
@@ -1230,7 +1230,7 @@ static int ceu_s_input(struct file *file, void *priv, unsigned int i)
return 0;
ceu_sd_old = ceudev->sd;
- ceudev->sd = &ceudev->subdevs[i];
+ ceudev->sd = ceudev->subdevs[i];
/*
* Make sure we can generate output image formats and apply
@@ -1423,7 +1423,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier)
* ceu formats.
*/
if (!ceudev->sd) {
- ceudev->sd = &ceudev->subdevs[0];
+ ceudev->sd = ceudev->subdevs[0];
ceudev->sd_index = 0;
}
@@ -1467,8 +1467,8 @@ static const struct v4l2_async_notifier_operations ceu_notify_ops = {
/*
* ceu_init_async_subdevs() - Initialize CEU subdevices and async_subdevs in
- * ceu device. Both DT and platform data parsing use
- * this routine.
+ * ceu device. Both DT and platform data parsing use
+ * this routine.
*
* Returns 0 for success, -ENOMEM for failure.
*/
@@ -1510,21 +1510,16 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev,
/* Setup the ceu subdevice and the async subdevice. */
async_sd = &pdata->subdevs[i];
- ceu_sd = &ceudev->subdevs[i];
-
- INIT_LIST_HEAD(&ceu_sd->asd.list);
-
- ceu_sd->mbus_flags = async_sd->flags;
- ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_I2C;
- ceu_sd->asd.match.i2c.adapter_id = async_sd->i2c_adapter_id;
- ceu_sd->asd.match.i2c.address = async_sd->i2c_address;
-
- ret = v4l2_async_notifier_add_subdev(&ceudev->notifier,
- &ceu_sd->asd);
- if (ret) {
+ ceu_sd = v4l2_async_notifier_add_i2c_subdev(&ceudev->notifier,
+ async_sd->i2c_adapter_id,
+ async_sd->i2c_address,
+ struct ceu_subdev);
+ if (IS_ERR(ceu_sd)) {
v4l2_async_notifier_cleanup(&ceudev->notifier);
- return ret;
+ return PTR_ERR(ceu_sd);
}
+ ceu_sd->mbus_flags = async_sd->flags;
+ ceudev->subdevs[i] = ceu_sd;
}
return pdata->num_subdevs;
@@ -1536,7 +1531,7 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev,
static int ceu_parse_dt(struct ceu_device *ceudev)
{
struct device_node *of = ceudev->dev->of_node;
- struct device_node *ep, *remote;
+ struct device_node *ep;
struct ceu_subdev *ceu_sd;
unsigned int i;
int num_ep;
@@ -1578,20 +1573,15 @@ static int ceu_parse_dt(struct ceu_device *ceudev)
}
/* Setup the ceu subdevice and the async subdevice. */
- ceu_sd = &ceudev->subdevs[i];
- INIT_LIST_HEAD(&ceu_sd->asd.list);
-
- remote = of_graph_get_remote_port_parent(ep);
- ceu_sd->mbus_flags = fw_ep.bus.parallel.flags;
- ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- ceu_sd->asd.match.fwnode = of_fwnode_handle(remote);
-
- ret = v4l2_async_notifier_add_subdev(&ceudev->notifier,
- &ceu_sd->asd);
- if (ret) {
- of_node_put(remote);
+ ceu_sd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &ceudev->notifier, of_fwnode_handle(ep),
+ struct ceu_subdev);
+ if (IS_ERR(ceu_sd)) {
+ ret = PTR_ERR(ceu_sd);
goto error_cleanup;
}
+ ceu_sd->mbus_flags = fw_ep.bus.parallel.flags;
+ ceudev->subdevs[i] = ceu_sd;
of_node_put(ep);
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
index f7e9fd305548..7474150b94ed 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
@@ -252,7 +252,7 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
struct v4l2_fwnode_endpoint vep = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
- struct rkisp1_sensor_async *rk_asd = NULL;
+ struct rkisp1_sensor_async *rk_asd;
struct fwnode_handle *ep;
ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev),
@@ -265,9 +265,10 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
if (ret)
goto err_parse;
- rk_asd = kzalloc(sizeof(*rk_asd), GFP_KERNEL);
- if (!rk_asd) {
- ret = -ENOMEM;
+ rk_asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep,
+ struct rkisp1_sensor_async);
+ if (IS_ERR(rk_asd)) {
+ ret = PTR_ERR(rk_asd);
goto err_parse;
}
@@ -275,11 +276,6 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
rk_asd->mbus_flags = vep.bus.mipi_csi2.flags;
rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes;
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep,
- &rk_asd->asd);
- if (ret)
- goto err_parse;
-
dev_dbg(rkisp1->dev, "registered ep id %d with %d lanes\n",
vep.base.id, rk_asd->lanes);
@@ -290,7 +286,6 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
continue;
err_parse:
fwnode_handle_put(ep);
- kfree(rk_asd);
v4l2_async_notifier_cleanup(ntf);
return ret;
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
index 889982d8ca41..2e5b57e3aedc 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -600,6 +600,39 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd,
return -EINVAL;
}
+static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct rkisp1_isp_mbus_info *mbus_info;
+
+ if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
+ fse->pad == RKISP1_ISP_PAD_SOURCE_STATS)
+ return -ENOTTY;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ mbus_info = rkisp1_isp_mbus_info_get(fse->code);
+ if (!mbus_info)
+ return -EINVAL;
+
+ if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) &&
+ fse->pad == RKISP1_ISP_PAD_SINK_VIDEO)
+ return -EINVAL;
+
+ if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) &&
+ fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
+ return -EINVAL;
+
+ fse->min_width = RKISP1_ISP_MIN_WIDTH;
+ fse->max_width = RKISP1_ISP_MAX_WIDTH;
+ fse->min_height = RKISP1_ISP_MIN_HEIGHT;
+ fse->max_height = RKISP1_ISP_MAX_HEIGHT;
+
+ return 0;
+}
+
static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg)
{
@@ -880,6 +913,7 @@ static int rkisp1_subdev_link_validate(struct media_link *link)
static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
.enum_mbus_code = rkisp1_isp_enum_mbus_code,
+ .enum_frame_size = rkisp1_isp_enum_frame_size,
.get_selection = rkisp1_isp_get_selection,
.set_selection = rkisp1_isp_set_selection,
.init_cfg = rkisp1_isp_init_config,
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index b22dc1d72527..4ac48441f22c 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -1355,7 +1355,7 @@ static int sh_vou_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver __refdata sh_vou = {
+static struct platform_driver sh_vou = {
.remove = sh_vou_remove,
.driver = {
.name = "sh-vou",
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 5ceb366648b3..a7a6ea666740 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -826,6 +826,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
dev_err(dev,
"reset gpio for tsin%d not valid (gpio=%d)\n",
tsin->tsin_id, tsin->rst_gpio);
+ ret = -EINVAL;
goto err_node_put;
}
diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
index 43f279e2a6a3..f59811e27f51 100644
--- a/drivers/media/platform/sti/hva/hva-hw.c
+++ b/drivers/media/platform/sti/hva/hva-hw.c
@@ -447,6 +447,7 @@ int hva_hw_runtime_resume(struct device *dev)
if (clk_set_rate(hva->clk, CLK_RATE)) {
dev_err(dev, "%s failed to set clock frequency\n",
HVA_PREFIX);
+ clk_disable_unprepare(hva->clk);
return -EINVAL;
}
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index b745f1342c2e..bbcc2254fa2e 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -99,13 +99,6 @@ enum state {
#define OVERRUN_ERROR_THRESHOLD 3
-struct dcmi_graph_entity {
- struct v4l2_async_subdev asd;
-
- struct device_node *remote_node;
- struct v4l2_subdev *source;
-};
-
struct dcmi_format {
u32 fourcc;
u32 mbus_code;
@@ -139,7 +132,7 @@ struct stm32_dcmi {
struct v4l2_device v4l2_dev;
struct video_device *vdev;
struct v4l2_async_notifier notifier;
- struct dcmi_graph_entity entity;
+ struct v4l2_subdev *source;
struct v4l2_format fmt;
struct v4l2_rect crop;
bool do_crop;
@@ -610,7 +603,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi,
struct v4l2_subdev_pad_config *pad_cfg,
struct v4l2_subdev_format *format)
{
- struct media_entity *entity = &dcmi->entity.source->entity;
+ struct media_entity *entity = &dcmi->source->entity;
struct v4l2_subdev *subdev;
struct media_pad *sink_pad = NULL;
struct media_pad *src_pad = NULL;
@@ -1018,7 +1011,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt,
+ ret = v4l2_subdev_call(dcmi->source, pad, set_fmt,
&pad_cfg, &format);
if (ret < 0)
return ret;
@@ -1152,7 +1145,7 @@ static int dcmi_get_sensor_format(struct stm32_dcmi *dcmi,
};
int ret;
- ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt);
+ ret = v4l2_subdev_call(dcmi->source, pad, get_fmt, NULL, &fmt);
if (ret)
return ret;
@@ -1181,7 +1174,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi,
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt,
+ ret = v4l2_subdev_call(dcmi->source, pad, set_fmt,
&pad_cfg, &format);
if (ret < 0)
return ret;
@@ -1204,7 +1197,7 @@ static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi,
/*
* Get sensor bounds first
*/
- ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection,
+ ret = v4l2_subdev_call(dcmi->source, pad, get_selection,
NULL, &bounds);
if (!ret)
*r = bounds.r;
@@ -1385,7 +1378,7 @@ static int dcmi_enum_framesizes(struct file *file, void *fh,
fse.code = sd_fmt->mbus_code;
- ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size,
+ ret = v4l2_subdev_call(dcmi->source, pad, enum_frame_size,
NULL, &fse);
if (ret)
return ret;
@@ -1402,7 +1395,7 @@ static int dcmi_g_parm(struct file *file, void *priv,
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p);
+ return v4l2_g_parm_cap(video_devdata(file), dcmi->source, p);
}
static int dcmi_s_parm(struct file *file, void *priv,
@@ -1410,7 +1403,7 @@ static int dcmi_s_parm(struct file *file, void *priv,
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p);
+ return v4l2_s_parm_cap(video_devdata(file), dcmi->source, p);
}
static int dcmi_enum_frameintervals(struct file *file, void *fh,
@@ -1432,7 +1425,7 @@ static int dcmi_enum_frameintervals(struct file *file, void *fh,
fie.code = sd_fmt->mbus_code;
- ret = v4l2_subdev_call(dcmi->entity.source, pad,
+ ret = v4l2_subdev_call(dcmi->source, pad,
enum_frame_interval, NULL, &fie);
if (ret)
return ret;
@@ -1452,7 +1445,7 @@ MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match);
static int dcmi_open(struct file *file)
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- struct v4l2_subdev *sd = dcmi->entity.source;
+ struct v4l2_subdev *sd = dcmi->source;
int ret;
if (mutex_lock_interruptible(&dcmi->lock))
@@ -1483,7 +1476,7 @@ unlock:
static int dcmi_release(struct file *file)
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- struct v4l2_subdev *sd = dcmi->entity.source;
+ struct v4l2_subdev *sd = dcmi->source;
bool fh_singular;
int ret;
@@ -1616,7 +1609,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
{
const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)];
unsigned int num_fmts = 0, i, j;
- struct v4l2_subdev *subdev = dcmi->entity.source;
+ struct v4l2_subdev *subdev = dcmi->source;
struct v4l2_subdev_mbus_code_enum mbus_code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
@@ -1675,7 +1668,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
static int dcmi_framesizes_init(struct stm32_dcmi *dcmi)
{
unsigned int num_fsize = 0;
- struct v4l2_subdev *subdev = dcmi->entity.source;
+ struct v4l2_subdev *subdev = dcmi->source;
struct v4l2_subdev_frame_size_enum fse = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.code = dcmi->sd_format->mbus_code,
@@ -1727,14 +1720,13 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier)
* we search for the source subdevice
* in order to expose it through V4L2 interface
*/
- dcmi->entity.source =
- media_entity_to_v4l2_subdev(dcmi_find_source(dcmi));
- if (!dcmi->entity.source) {
+ dcmi->source = media_entity_to_v4l2_subdev(dcmi_find_source(dcmi));
+ if (!dcmi->source) {
dev_err(dcmi->dev, "Source subdevice not found\n");
return -ENODEV;
}
- dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler;
+ dcmi->vdev->ctrl_handler = dcmi->source->ctrl_handler;
ret = dcmi_formats_init(dcmi);
if (ret) {
@@ -1813,46 +1805,29 @@ static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = {
.complete = dcmi_graph_notify_complete,
};
-static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node)
-{
- struct device_node *ep = NULL;
- struct device_node *remote;
-
- ep = of_graph_get_next_endpoint(node, ep);
- if (!ep)
- return -EINVAL;
-
- remote = of_graph_get_remote_port_parent(ep);
- of_node_put(ep);
- if (!remote)
- return -EINVAL;
-
- /* Remote node to connect */
- dcmi->entity.remote_node = remote;
- dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote);
- return 0;
-}
-
static int dcmi_graph_init(struct stm32_dcmi *dcmi)
{
+ struct v4l2_async_subdev *asd;
+ struct device_node *ep;
int ret;
- /* Parse the graph to extract a list of subdevice DT nodes. */
- ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node);
- if (ret < 0) {
- dev_err(dcmi->dev, "Failed to parse graph\n");
- return ret;
+ ep = of_graph_get_next_endpoint(dcmi->dev->of_node, NULL);
+ if (!ep) {
+ dev_err(dcmi->dev, "Failed to get next endpoint\n");
+ return -EINVAL;
}
v4l2_async_notifier_init(&dcmi->notifier);
- ret = v4l2_async_notifier_add_subdev(&dcmi->notifier,
- &dcmi->entity.asd);
- if (ret) {
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &dcmi->notifier, of_fwnode_handle(ep),
+ struct v4l2_async_subdev);
+
+ of_node_put(ep);
+
+ if (IS_ERR(asd)) {
dev_err(dcmi->dev, "Failed to add subdev notifier\n");
- of_node_put(dcmi->entity.remote_node);
- return ret;
+ return PTR_ERR(asd);
}
dcmi->notifier.ops = &dcmi_graph_notify_ops;
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
index ec46cff80fdb..8d40a7acba9c 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
@@ -118,6 +118,7 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi)
struct v4l2_fwnode_endpoint vep = {
.bus_type = V4L2_MBUS_PARALLEL,
};
+ struct v4l2_async_subdev *asd;
struct fwnode_handle *ep;
int ret;
@@ -134,10 +135,12 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi)
csi->bus = vep.bus.parallel;
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier,
- ep, &csi->asd);
- if (ret)
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, ep,
+ struct v4l2_async_subdev);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
goto out;
+ }
csi->notifier.ops = &sun4i_csi_notify_ops;
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
index 0f67ff652c2e..a5f61ee0ec4d 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
@@ -139,7 +139,6 @@ struct sun4i_csi {
struct v4l2_mbus_framefmt subdev_fmt;
/* V4L2 Async variables */
- struct v4l2_async_subdev asd;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *src_subdev;
int src_pad;
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
index 1a2f65d83a6c..4785faddf630 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
@@ -113,8 +113,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi,
pix->num_planes = _fmt->num_planes;
pix->pixelformat = _fmt->fourcc;
- memset(pix->reserved, 0, sizeof(pix->reserved));
-
/* Align the width and height on the subsampling */
width = ALIGN(pix->width, _fmt->hsub);
height = ALIGN(pix->height, _fmt->vsub);
@@ -131,8 +129,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi,
bpl = pix->width / hsub * _fmt->bpp[i] / 8;
pix->plane_fmt[i].bytesperline = bpl;
pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub;
- memset(pix->plane_fmt[i].reserved, 0,
- sizeof(pix->plane_fmt[i].reserved));
}
}
diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c
index 806cbf175d39..dd48017859cd 100644
--- a/drivers/media/platform/ti-vpe/cal-camerarx.c
+++ b/drivers/media/platform/ti-vpe/cal-camerarx.c
@@ -116,8 +116,7 @@ void cal_camerarx_disable(struct cal_camerarx *phy)
#define TCLK_MISS 1
#define TCLK_SETTLE 14
-static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate,
- const struct cal_fmt *fmt)
+static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate)
{
unsigned int reg0, reg1;
unsigned int ths_term, ths_settle;
@@ -132,9 +131,9 @@ static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate,
* CSI-2 is DDR and we only count used lanes.
*
* csi2_ddrclk_khz = external_rate / 1000
- * / (2 * num_lanes) * fmt->bpp;
+ * / (2 * num_lanes) * phy->fmtinfo->bpp;
*/
- csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp,
+ csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp,
2 * num_lanes * 1000);
phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz);
@@ -234,7 +233,42 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy)
phy_err(phy, "Timeout waiting for stop state\n");
}
-int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
+static void cal_camerarx_enable_irqs(struct cal_camerarx *phy)
+{
+ const u32 cio_err_mask =
+ CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK |
+ CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK |
+ CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK |
+ CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK;
+
+ /* Enable CIO error IRQs. */
+ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),
+ CAL_HL_IRQ_CIO_MASK(phy->instance));
+ cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),
+ cio_err_mask);
+}
+
+static void cal_camerarx_disable_irqs(struct cal_camerarx *phy)
+{
+ /* Disable CIO error irqs */
+ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),
+ CAL_HL_IRQ_CIO_MASK(phy->instance));
+ cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);
+}
+
+static void cal_camerarx_ppi_enable(struct cal_camerarx *phy)
+{
+ cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
+ 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+static void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
+{
+ cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
+ 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
+}
+
+static int cal_camerarx_start(struct cal_camerarx *phy)
{
s64 external_rate;
u32 sscounter;
@@ -251,6 +285,8 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
return ret;
}
+ cal_camerarx_enable_irqs(phy);
+
/*
* CSI-2 PHY Link Initialization Sequence, according to the DRA74xP /
* DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x /
@@ -289,7 +325,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
camerarx_read(phy, CAL_CSI2_PHY_REG0);
/* Program the PHY timing parameters. */
- cal_camerarx_config(phy, external_rate, fmt);
+ cal_camerarx_config(phy, external_rate);
/*
* b. Assert the FORCERXMODE signal.
@@ -340,6 +376,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1);
if (ret) {
v4l2_subdev_call(phy->sensor, core, s_power, 0);
+ cal_camerarx_disable_irqs(phy);
phy_err(phy, "stream on failed in subdev\n");
return ret;
}
@@ -359,14 +396,21 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt)
* implemented.
*/
+ /* Finally, enable the PHY Protocol Interface (PPI). */
+ cal_camerarx_ppi_enable(phy);
+
return 0;
}
-void cal_camerarx_stop(struct cal_camerarx *phy)
+static void cal_camerarx_stop(struct cal_camerarx *phy)
{
unsigned int i;
int ret;
+ cal_camerarx_ppi_disable(phy);
+
+ cal_camerarx_disable_irqs(phy);
+
cal_camerarx_power(phy, false);
/* Assert Complex IO Reset */
@@ -428,74 +472,6 @@ void cal_camerarx_i913_errata(struct cal_camerarx *phy)
camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10);
}
-/*
- * Enable the expected IRQ sources
- */
-void cal_camerarx_enable_irqs(struct cal_camerarx *phy)
-{
- u32 val;
-
- const u32 cio_err_mask =
- CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK |
- CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK |
- CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK |
- CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK;
-
- /* Enable CIO error irqs */
- cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),
- CAL_HL_IRQ_CIO_MASK(phy->instance));
- cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),
- cio_err_mask);
-
- /* Always enable OCPO error */
- cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK);
-
- /* Enable IRQ_WDMA_END 0/1 */
- val = 0;
- cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
- cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val);
- /* Enable IRQ_WDMA_START 0/1 */
- val = 0;
- cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
- cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val);
- /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
- cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000);
-}
-
-void cal_camerarx_disable_irqs(struct cal_camerarx *phy)
-{
- u32 val;
-
- /* Disable CIO error irqs */
- cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),
- CAL_HL_IRQ_CIO_MASK(phy->instance));
- cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);
-
- /* Disable IRQ_WDMA_END 0/1 */
- val = 0;
- cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
- cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val);
- /* Disable IRQ_WDMA_START 0/1 */
- val = 0;
- cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance));
- cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val);
- /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
- cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0);
-}
-
-void cal_camerarx_ppi_enable(struct cal_camerarx *phy)
-{
- cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3));
- cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
- 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
-}
-
-void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
-{
- cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
- 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
-}
-
static int cal_camerarx_regmap_init(struct cal_dev *cal,
struct cal_camerarx *phy)
{
@@ -533,8 +509,8 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal,
static int cal_camerarx_parse_dt(struct cal_camerarx *phy)
{
struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;
- struct device_node *ep_node;
char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2];
+ struct device_node *ep_node;
unsigned int i;
int ret;
@@ -582,9 +558,11 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy)
endpoint->bus.mipi_csi2.flags);
/* Retrieve the connected device and store it for later use. */
- phy->sensor_node = of_graph_get_remote_port_parent(ep_node);
+ phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node);
+ phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node);
if (!phy->sensor_node) {
phy_dbg(3, phy, "Can't get remote parent\n");
+ of_node_put(phy->sensor_ep_node);
ret = -EINVAL;
goto done;
}
@@ -596,11 +574,227 @@ done:
return ret;
}
+/* ------------------------------------------------------------------
+ * V4L2 Subdev Operations
+ * ------------------------------------------------------------------
+ */
+
+static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cal_camerarx, subdev);
+}
+
+static struct v4l2_mbus_framefmt *
+cal_camerarx_get_pad_format(struct cal_camerarx *phy,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&phy->subdev, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &phy->formats[pad];
+ default:
+ return NULL;
+ }
+}
+
+static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+
+ if (enable)
+ return cal_camerarx_start(phy);
+
+ cal_camerarx_stop(phy);
+ return 0;
+}
+
+static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+
+ /* No transcoding, source and sink codes must match. */
+ if (code->pad == CAL_CAMERARX_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = cal_camerarx_get_pad_format(phy, cfg,
+ CAL_CAMERARX_PAD_SINK,
+ code->which);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->index >= cal_num_formats)
+ return -EINVAL;
+
+ code->code = cal_formats[code->index].code;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+ const struct cal_format_info *fmtinfo;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ /* No transcoding, source and sink formats must match. */
+ if (fse->pad == CAL_CAMERARX_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = cal_camerarx_get_pad_format(phy, cfg,
+ CAL_CAMERARX_PAD_SINK,
+ fse->which);
+ if (fse->code != fmt->code)
+ return -EINVAL;
+
+ fse->min_width = fmt->width;
+ fse->max_width = fmt->width;
+ fse->min_height = fmt->height;
+ fse->max_height = fmt->height;
+
+ return 0;
+ }
+
+ fmtinfo = cal_format_by_code(fse->code);
+ if (!fmtinfo)
+ return -EINVAL;
+
+ fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
+ fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
+ fse->min_height = CAL_MIN_HEIGHT_LINES;
+ fse->max_height = CAL_MAX_HEIGHT_LINES;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = cal_camerarx_get_pad_format(phy, cfg, format->pad, format->which);
+ format->format = *fmt;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+ const struct cal_format_info *fmtinfo;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int bpp;
+
+ /* No transcoding, source and sink formats must match. */
+ if (format->pad == CAL_CAMERARX_PAD_SOURCE)
+ return cal_camerarx_sd_get_fmt(sd, cfg, format);
+
+ /*
+ * Default to the first format is the requested media bus code isn't
+ * supported.
+ */
+ fmtinfo = cal_format_by_code(format->format.code);
+ if (!fmtinfo)
+ fmtinfo = &cal_formats[0];
+
+ /*
+ * Clamp the size, update the code. The field and colorspace are
+ * accepted as-is.
+ */
+ bpp = ALIGN(fmtinfo->bpp, 8);
+
+ format->format.width = clamp_t(unsigned int, format->format.width,
+ CAL_MIN_WIDTH_BYTES * 8 / bpp,
+ CAL_MAX_WIDTH_BYTES * 8 / bpp);
+ format->format.height = clamp_t(unsigned int, format->format.height,
+ CAL_MIN_HEIGHT_LINES,
+ CAL_MAX_HEIGHT_LINES);
+ format->format.code = fmtinfo->code;
+
+ /* Store the format and propagate it to the source pad. */
+ fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SINK,
+ format->which);
+ *fmt = format->format;
+
+ fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SOURCE,
+ format->which);
+ *fmt = format->format;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ phy->fmtinfo = fmtinfo;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_subdev_format format = {
+ .which = cfg ? V4L2_SUBDEV_FORMAT_TRY
+ : V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = CAL_CAMERARX_PAD_SINK,
+ .format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ },
+ };
+
+ return cal_camerarx_sd_set_fmt(sd, cfg, &format);
+}
+
+static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = {
+ .s_stream = cal_camerarx_sd_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = {
+ .init_cfg = cal_camerarx_sd_init_cfg,
+ .enum_mbus_code = cal_camerarx_sd_enum_mbus_code,
+ .enum_frame_size = cal_camerarx_sd_enum_frame_size,
+ .get_fmt = cal_camerarx_sd_get_fmt,
+ .set_fmt = cal_camerarx_sd_set_fmt,
+};
+
+static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = {
+ .video = &cal_camerarx_video_ops,
+ .pad = &cal_camerarx_pad_ops,
+};
+
+static struct media_entity_operations cal_camerarx_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* ------------------------------------------------------------------
+ * Create and Destroy
+ * ------------------------------------------------------------------
+ */
+
struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
unsigned int instance)
{
struct platform_device *pdev = to_platform_device(cal->dev);
struct cal_camerarx *phy;
+ struct v4l2_subdev *sd;
int ret;
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
@@ -632,9 +826,31 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
if (ret)
goto error;
+ /* Initialize the V4L2 subdev and media entity. */
+ sd = &phy->subdev;
+ v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);
+ sd->dev = cal->dev;
+
+ phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.ops = &cal_camerarx_media_ops;
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads),
+ phy->pads);
+ if (ret)
+ goto error;
+
+ cal_camerarx_sd_init_cfg(sd, NULL);
+
+ ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd);
+ if (ret)
+ goto error;
+
return phy;
error:
+ media_entity_cleanup(&phy->subdev.entity);
kfree(phy);
return ERR_PTR(ret);
}
@@ -644,6 +860,9 @@ void cal_camerarx_destroy(struct cal_camerarx *phy)
if (!phy)
return;
+ v4l2_device_unregister_subdev(&phy->subdev);
+ media_entity_cleanup(&phy->subdev.entity);
+ of_node_put(phy->sensor_ep_node);
of_node_put(phy->sensor_node);
kfree(phy);
}
diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c
index df472a175e83..779f1e1bc529 100644
--- a/drivers/media/platform/ti-vpe/cal-video.c
+++ b/drivers/media/platform/ti-vpe/cal-video.c
@@ -9,7 +9,6 @@
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
-#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/pm_runtime.h>
#include <linux/videodev2.h>
@@ -26,107 +25,6 @@
#include "cal.h"
-/* ------------------------------------------------------------------
- * Format Handling
- * ------------------------------------------------------------------
- */
-
-static const struct cal_fmt cal_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .code = MEDIA_BUS_FMT_UYVY8_2X8,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .code = MEDIA_BUS_FMT_YVYU8_2X8,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
- .bpp = 24,
- }, {
- .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
- .bpp = 24,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
- .code = MEDIA_BUS_FMT_ARGB8888_1X32,
- .bpp = 32,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .bpp = 10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .bpp = 10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .bpp = 10,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .bpp = 10,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR12,
- .code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .bpp = 12,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG12,
- .code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .bpp = 12,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .bpp = 12,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB12,
- .code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .bpp = 12,
- },
-};
-
/* Print Four-character-code (FOURCC) */
static char *fourcc_to_str(u32 fmt)
{
@@ -146,31 +44,31 @@ static char *fourcc_to_str(u32 fmt)
* ------------------------------------------------------------------
*/
-static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx,
- u32 pixelformat)
+static const struct cal_format_info *find_format_by_pix(struct cal_ctx *ctx,
+ u32 pixelformat)
{
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
unsigned int k;
for (k = 0; k < ctx->num_active_fmt; k++) {
- fmt = ctx->active_fmt[k];
- if (fmt->fourcc == pixelformat)
- return fmt;
+ fmtinfo = ctx->active_fmt[k];
+ if (fmtinfo->fourcc == pixelformat)
+ return fmtinfo;
}
return NULL;
}
-static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx,
- u32 code)
+static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx,
+ u32 code)
{
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
unsigned int k;
for (k = 0; k < ctx->num_active_fmt; k++) {
- fmt = ctx->active_fmt[k];
- if (fmt->code == code)
- return fmt;
+ fmtinfo = ctx->active_fmt[k];
+ if (fmtinfo->code == code)
+ return fmtinfo;
}
return NULL;
@@ -193,14 +91,14 @@ static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
if (f->index >= ctx->num_active_fmt)
return -EINVAL;
- fmt = ctx->active_fmt[f->index];
+ fmtinfo = ctx->active_fmt[f->index];
- f->pixelformat = fmt->fourcc;
+ f->pixelformat = fmtinfo->fourcc;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
@@ -248,27 +146,23 @@ static int __subdev_set_format(struct cal_ctx *ctx,
return 0;
}
-static int cal_calc_format_size(struct cal_ctx *ctx,
- const struct cal_fmt *fmt,
- struct v4l2_format *f)
+static void cal_calc_format_size(struct cal_ctx *ctx,
+ const struct cal_format_info *fmtinfo,
+ struct v4l2_format *f)
{
u32 bpl, max_width;
- if (!fmt) {
- ctx_dbg(3, ctx, "No cal_fmt provided!\n");
- return -EINVAL;
- }
-
/*
* Maximum width is bound by the DMA max width in bytes.
* We need to recalculate the actual maxi width depending on the
* number of bytes per pixels required.
*/
- max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3);
+ max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmtinfo->bpp, 8) >> 3);
v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2,
- &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0);
+ &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES,
+ 0, 0);
- bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3;
+ bpl = (f->fmt.pix.width * ALIGN(fmtinfo->bpp, 8)) >> 3;
f->fmt.pix.bytesperline = ALIGN(bpl, 16);
f->fmt.pix.sizeimage = f->fmt.pix.height *
@@ -278,8 +172,6 @@ static int cal_calc_format_size(struct cal_ctx *ctx,
__func__, 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 int cal_g_fmt_vid_cap(struct file *file, void *priv,
@@ -296,18 +188,18 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse;
int ret, found;
- fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
- if (!fmt) {
+ fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+ if (!fmtinfo) {
ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
f->fmt.pix.pixelformat);
/* Just get the first one enumerated */
- fmt = ctx->active_fmt[0];
- f->fmt.pix.pixelformat = fmt->fourcc;
+ fmtinfo = ctx->active_fmt[0];
+ f->fmt.pix.pixelformat = fmtinfo->fourcc;
}
f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
@@ -316,7 +208,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv,
ret = 0;
found = false;
fse.pad = 0;
- fse.code = fmt->code;
+ fse.code = fmtinfo->code;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
for (fse.index = 0; ; fse.index++) {
ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size,
@@ -348,7 +240,8 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv,
* updated properly during s_fmt
*/
f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace;
- return cal_calc_format_size(ctx, fmt, f);
+ cal_calc_format_size(ctx, fmtinfo, f);
+ return 0;
}
static int cal_s_fmt_vid_cap(struct file *file, void *priv,
@@ -356,8 +249,11 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv,
{
struct cal_ctx *ctx = video_drvdata(file);
struct vb2_queue *q = &ctx->vb_vidq;
- const struct cal_fmt *fmt;
- struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = CAL_CAMERARX_PAD_SINK,
+ };
+ const struct cal_format_info *fmtinfo;
int ret;
if (vb2_is_busy(q)) {
@@ -369,28 +265,31 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv,
if (ret < 0)
return ret;
- fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+ fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+ v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmtinfo->code);
- ret = __subdev_set_format(ctx, &mbus_fmt);
+ ret = __subdev_set_format(ctx, &sd_fmt.format);
if (ret)
return ret;
/* Just double check nothing has gone wrong */
- if (mbus_fmt.code != fmt->code) {
+ if (sd_fmt.format.code != fmtinfo->code) {
ctx_dbg(3, ctx,
"%s subdev changed format on us, this should not happen\n",
__func__);
return -EINVAL;
}
- v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format);
ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
- cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
- ctx->fmt = fmt;
- ctx->m_fmt = mbus_fmt;
+ ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc;
+ ctx->v_fmt.fmt.pix.field = sd_fmt.format.field;
+ cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
+
+ v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt);
+
+ ctx->fmtinfo = fmtinfo;
*f = ctx->v_fmt;
return 0;
@@ -400,13 +299,13 @@ static int cal_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse;
int ret;
/* check for valid format */
- fmt = find_format_by_pix(ctx, fsize->pixel_format);
- if (!fmt) {
+ fmtinfo = find_format_by_pix(ctx, fsize->pixel_format);
+ if (!fmtinfo) {
ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
fsize->pixel_format);
return -EINVAL;
@@ -414,7 +313,7 @@ static int cal_enum_framesizes(struct file *file, void *fh,
fse.index = fsize->index;
fse.pad = 0;
- fse.code = fmt->code;
+ fse.code = fmtinfo->code;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL,
@@ -460,7 +359,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fival)
{
struct cal_ctx *ctx = video_drvdata(file);
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_interval_enum fie = {
.index = fival->index,
.width = fival->width,
@@ -469,11 +368,11 @@ static int cal_enum_frameintervals(struct file *file, void *priv,
};
int ret;
- fmt = find_format_by_pix(ctx, fival->pixel_format);
- if (!fmt)
+ fmtinfo = find_format_by_pix(ctx, fival->pixel_format);
+ if (!fmtinfo)
return -EINVAL;
- fie.code = fmt->code;
+ fie.code = fmtinfo->code;
ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval,
NULL, &fie);
if (ret)
@@ -488,7 +387,6 @@ static const struct v4l2_file_operations cal_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
- .read = vb2_fop_read,
.poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vb2_fop_mmap,
@@ -555,9 +453,6 @@ static int cal_buffer_prepare(struct vb2_buffer *vb)
vb.vb2_buf);
unsigned long size;
- if (WARN_ON(!ctx->fmt))
- return -EINVAL;
-
size = ctx->v_fmt.fmt.pix.sizeimage;
if (vb2_plane_size(vb, 0) < size) {
ctx_err(ctx,
@@ -575,121 +470,88 @@ static void cal_buffer_queue(struct vb2_buffer *vb)
struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct cal_buffer *buf = container_of(vb, struct cal_buffer,
vb.vb2_buf);
- struct cal_dmaqueue *vidq = &ctx->vidq;
unsigned long flags;
/* recheck locking */
- spin_lock_irqsave(&ctx->slock, flags);
- list_add_tail(&buf->list, &vidq->active);
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_lock_irqsave(&ctx->dma.lock, flags);
+ list_add_tail(&buf->list, &ctx->dma.queue);
+ spin_unlock_irqrestore(&ctx->dma.lock, flags);
}
-static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
+static void cal_release_buffers(struct cal_ctx *ctx,
+ enum vb2_buffer_state state)
{
- struct cal_ctx *ctx = vb2_get_drv_priv(vq);
- struct cal_dmaqueue *dma_q = &ctx->vidq;
struct cal_buffer *buf, *tmp;
- unsigned long addr;
- unsigned long flags;
- int ret;
- spin_lock_irqsave(&ctx->slock, flags);
- if (list_empty(&dma_q->active)) {
- spin_unlock_irqrestore(&ctx->slock, flags);
- ctx_dbg(3, ctx, "buffer queue is empty\n");
- return -EIO;
+ /* Release all queued buffers. */
+ spin_lock_irq(&ctx->dma.lock);
+
+ list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ if (ctx->dma.pending) {
+ vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state);
+ ctx->dma.pending = NULL;
+ }
+
+ if (ctx->dma.active) {
+ vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state);
+ ctx->dma.active = NULL;
}
- buf = list_entry(dma_q->active.next, struct cal_buffer, list);
- ctx->cur_frm = buf;
- ctx->next_frm = buf;
+ spin_unlock_irq(&ctx->dma.lock);
+}
+
+static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_buffer *buf;
+ dma_addr_t addr;
+ int ret;
+
+ spin_lock_irq(&ctx->dma.lock);
+ buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list);
+ ctx->dma.pending = buf;
list_del(&buf->list);
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock_irq(&ctx->dma.lock);
- addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0);
- ctx->sequence = 0;
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
pm_runtime_get_sync(ctx->cal->dev);
- cal_ctx_csi2_config(ctx);
- cal_ctx_pix_proc_config(ctx);
- cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline,
- ctx->v_fmt.fmt.pix.height);
+ cal_ctx_set_dma_addr(ctx, addr);
+ cal_ctx_start(ctx);
- cal_camerarx_enable_irqs(ctx->phy);
-
- ret = cal_camerarx_start(ctx->phy, ctx->fmt);
+ ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1);
if (ret)
goto err;
- cal_ctx_wr_dma_addr(ctx, addr);
- cal_camerarx_ppi_enable(ctx->phy);
-
if (cal_debug >= 4)
cal_quickdump_regs(ctx->cal);
return 0;
err:
- spin_lock_irqsave(&ctx->slock, flags);
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- ctx->cur_frm = NULL;
- ctx->next_frm = NULL;
- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- }
- spin_unlock_irqrestore(&ctx->slock, flags);
+ cal_ctx_stop(ctx);
+ pm_runtime_put_sync(ctx->cal->dev);
+
+ cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED);
return ret;
}
static void cal_stop_streaming(struct vb2_queue *vq)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
- struct cal_dmaqueue *dma_q = &ctx->vidq;
- struct cal_buffer *buf, *tmp;
- unsigned long timeout;
- unsigned long flags;
- bool dma_act;
-
- cal_camerarx_ppi_disable(ctx->phy);
-
- /* wait for stream and dma to finish */
- dma_act = true;
- timeout = jiffies + msecs_to_jiffies(500);
- while (dma_act && time_before(jiffies, timeout)) {
- msleep(50);
-
- spin_lock_irqsave(&ctx->slock, flags);
- dma_act = ctx->dma_act;
- spin_unlock_irqrestore(&ctx->slock, flags);
- }
-
- if (dma_act)
- ctx_err(ctx, "failed to disable dma cleanly\n");
-
- cal_camerarx_disable_irqs(ctx->phy);
- cal_camerarx_stop(ctx->phy);
- /* Release all active buffers */
- spin_lock_irqsave(&ctx->slock, flags);
- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
+ cal_ctx_stop(ctx);
- if (ctx->cur_frm == ctx->next_frm) {
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- } else {
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- vb2_buffer_done(&ctx->next_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- ctx->cur_frm = NULL;
- ctx->next_frm = NULL;
- spin_unlock_irqrestore(&ctx->slock, flags);
+ v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0);
pm_runtime_put_sync(ctx->cal->dev);
+
+ cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
}
static const struct vb2_ops cal_video_qops = {
@@ -713,20 +575,19 @@ static const struct video_device cal_videodev = {
.ioctl_ops = &cal_ioctl_ops,
.minor = -1,
.release = video_device_release_empty,
- .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE,
+ .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
};
static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
{
struct v4l2_subdev_mbus_code_enum mbus_code;
struct v4l2_mbus_framefmt mbus_fmt;
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
unsigned int i, j, k;
int ret = 0;
/* Enumerate sub device formats and enable all matching local formats */
- ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats),
+ ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats,
sizeof(*ctx->active_fmt), GFP_KERNEL);
ctx->num_active_fmt = 0;
@@ -744,15 +605,15 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
"subdev %s: code: %04x idx: %u\n",
ctx->phy->sensor->name, mbus_code.code, j);
- for (k = 0; k < ARRAY_SIZE(cal_formats); k++) {
- const struct cal_fmt *fmt = &cal_formats[k];
+ for (k = 0; k < cal_num_formats; k++) {
+ fmtinfo = &cal_formats[k];
- if (mbus_code.code == fmt->code) {
- ctx->active_fmt[i] = fmt;
+ if (mbus_code.code == fmtinfo->code) {
+ ctx->active_fmt[i] = fmtinfo;
ctx_dbg(2, ctx,
"matched fourcc: %s: code: %04x idx: %u\n",
- fourcc_to_str(fmt->fourcc),
- fmt->code, i);
+ fourcc_to_str(fmtinfo->fourcc),
+ fmtinfo->code, i);
ctx->num_active_fmt = ++i;
}
}
@@ -768,20 +629,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
if (ret)
return ret;
- fmt = find_format_by_code(ctx, mbus_fmt.code);
- if (!fmt) {
+ fmtinfo = find_format_by_code(ctx, mbus_fmt.code);
+ if (!fmtinfo) {
ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
mbus_fmt.code);
return -EINVAL;
}
- /* Save current subdev format */
+ /* Save current format */
v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
- cal_calc_format_size(ctx, fmt, &ctx->v_fmt);
- ctx->fmt = fmt;
- ctx->m_fmt = mbus_fmt;
+ ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc;
+ ctx->v_fmt.fmt.pix.field = mbus_fmt.field;
+ cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
+ ctx->fmtinfo = fmtinfo;
return 0;
}
@@ -809,6 +670,18 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx)
return ret;
}
+ ret = media_create_pad_link(&ctx->phy->subdev.entity,
+ CAL_CAMERARX_PAD_SOURCE,
+ &vfd->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ ctx_err(ctx, "Failed to create media link for context %u\n",
+ ctx->index);
+ video_unregister_device(vfd);
+ return ret;
+ }
+
ctx_info(ctx, "V4L2 device registered as %s\n",
video_device_node_name(vfd));
@@ -830,13 +703,14 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx)
struct vb2_queue *q = &ctx->vb_vidq;
int ret;
- INIT_LIST_HEAD(&ctx->vidq.active);
- spin_lock_init(&ctx->slock);
+ INIT_LIST_HEAD(&ctx->dma.queue);
+ spin_lock_init(&ctx->dma.lock);
mutex_init(&ctx->mutex);
+ init_waitqueue_head(&ctx->dma.wait);
/* Initialize the vb2 queue. */
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
q->drv_priv = ctx;
q->buf_struct_size = sizeof(struct cal_buffer);
q->ops = &cal_video_qops;
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
index 59a0266b1f39..fa0931788040 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -44,6 +44,133 @@ module_param_named(debug, cal_debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
/* ------------------------------------------------------------------
+ * Format Handling
+ * ------------------------------------------------------------------
+ */
+
+const struct cal_format_info cal_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .bpp = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .bpp = 8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .bpp = 10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .bpp = 12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .bpp = 12,
+ },
+};
+
+const unsigned int cal_num_formats = ARRAY_SIZE(cal_formats);
+
+const struct cal_format_info *cal_format_by_fourcc(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) {
+ if (cal_formats[i].fourcc == fourcc)
+ return &cal_formats[i];
+ }
+
+ return NULL;
+}
+
+const struct cal_format_info *cal_format_by_code(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) {
+ if (cal_formats[i].code == code)
+ return &cal_formats[i];
+ }
+
+ return NULL;
+}
+
+/* ------------------------------------------------------------------
* Platform Data
* ------------------------------------------------------------------
*/
@@ -136,12 +263,9 @@ void cal_quickdump_regs(struct cal_dev *cal)
(__force const void *)cal->base,
resource_size(cal->res), false);
- for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) {
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
struct cal_camerarx *phy = cal->phy[i];
- if (!phy)
- continue;
-
cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i,
&phy->res->start);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
@@ -156,7 +280,7 @@ void cal_quickdump_regs(struct cal_dev *cal)
* ------------------------------------------------------------------
*/
-void cal_ctx_csi2_config(struct cal_ctx *ctx)
+static void cal_ctx_csi2_config(struct cal_ctx *ctx)
{
u32 val;
@@ -181,11 +305,11 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx)
cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)));
}
-void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
+static void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
{
u32 val, extract, pack;
- switch (ctx->fmt->bpp) {
+ switch (ctx->fmtinfo->bpp) {
case 8:
extract = CAL_PIX_PROC_EXTRACT_B8;
pack = CAL_PIX_PROC_PACK_B8;
@@ -214,7 +338,7 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
*/
dev_warn_once(ctx->cal->dev,
"%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n",
- __FILE__, __LINE__, __func__, ctx->fmt->bpp);
+ __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp);
extract = CAL_PIX_PROC_EXTRACT_B8;
pack = CAL_PIX_PROC_PACK_B8;
break;
@@ -232,14 +356,15 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)));
}
-void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
- unsigned int height)
+static void cal_ctx_wr_dma_config(struct cal_ctx *ctx)
{
+ unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline;
u32 val;
val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index));
cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK);
- cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK);
+ cal_set_field(&val, ctx->v_fmt.fmt.pix.height,
+ CAL_WR_DMA_CTRL_YSIZE_MASK);
cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
CAL_WR_DMA_CTRL_DTAG_MASK);
cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
@@ -251,14 +376,8 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index,
cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)));
- /*
- * width/16 not sure but giving it a whirl.
- * zero does not work right
- */
- cal_write_field(ctx->cal,
- CAL_WR_DMA_OFST(ctx->index),
- (width / 16),
- CAL_WR_DMA_OFST_MASK);
+ cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index),
+ stride / 16, CAL_WR_DMA_OFST_MASK);
ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index,
cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index)));
@@ -266,11 +385,11 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
/* 64 bit word means no skipping */
cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
/*
- * (width*8)/64 this should be size of an entire line
- * in 64bit word but 0 means all data until the end
- * is detected automagically
+ * The XSIZE field is expressed in 64-bit units and prevents overflows
+ * in case of synchronization issues by limiting the number of bytes
+ * written per line.
*/
- cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK);
+ cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK);
cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val);
ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index,
cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)));
@@ -287,9 +406,74 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL));
}
-void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
+void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr)
+{
+ cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr);
+}
+
+static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx)
+{
+ u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index));
+
+ cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS,
+ CAL_WR_DMA_CTRL_MODE_MASK);
+ cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val);
+}
+
+static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx)
{
- cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr);
+ bool stopped;
+
+ spin_lock_irq(&ctx->dma.lock);
+ stopped = ctx->dma.state == CAL_DMA_STOPPED;
+ spin_unlock_irq(&ctx->dma.lock);
+
+ return stopped;
+}
+
+void cal_ctx_start(struct cal_ctx *ctx)
+{
+ ctx->sequence = 0;
+ ctx->dma.state = CAL_DMA_RUNNING;
+
+ /* Configure the CSI-2, pixel processing and write DMA contexts. */
+ cal_ctx_csi2_config(ctx);
+ cal_ctx_pix_proc_config(ctx);
+ cal_ctx_wr_dma_config(ctx);
+
+ /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */
+ cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1),
+ CAL_HL_IRQ_MASK(ctx->index));
+ cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2),
+ CAL_HL_IRQ_MASK(ctx->index));
+}
+
+void cal_ctx_stop(struct cal_ctx *ctx)
+{
+ long timeout;
+
+ /*
+ * Request DMA stop and wait until it completes. If completion times
+ * out, forcefully disable the DMA.
+ */
+ spin_lock_irq(&ctx->dma.lock);
+ ctx->dma.state = CAL_DMA_STOP_REQUESTED;
+ spin_unlock_irq(&ctx->dma.lock);
+
+ timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx),
+ msecs_to_jiffies(500));
+ if (!timeout) {
+ ctx_err(ctx, "failed to disable dma cleanly\n");
+ cal_ctx_wr_dma_disable(ctx);
+ }
+
+ /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */
+ cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1),
+ CAL_HL_IRQ_MASK(ctx->index));
+ cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2),
+ CAL_HL_IRQ_MASK(ctx->index));
+
+ ctx->dma.state = CAL_DMA_STOPPED;
}
/* ------------------------------------------------------------------
@@ -297,35 +481,70 @@ void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
* ------------------------------------------------------------------
*/
-static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
+static inline void cal_irq_wdma_start(struct cal_ctx *ctx)
{
- struct cal_dmaqueue *dma_q = &ctx->vidq;
- struct cal_buffer *buf;
- unsigned long addr;
+ spin_lock(&ctx->dma.lock);
+
+ if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) {
+ /*
+ * If a stop is requested, disable the write DMA context
+ * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed,
+ * the current frame will complete and the DMA will then stop.
+ */
+ cal_ctx_wr_dma_disable(ctx);
+ ctx->dma.state = CAL_DMA_STOP_PENDING;
+ } else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) {
+ /*
+ * Otherwise, if a new buffer is available, queue it to the
+ * hardware.
+ */
+ struct cal_buffer *buf;
+ dma_addr_t addr;
+
+ buf = list_first_entry(&ctx->dma.queue, struct cal_buffer,
+ list);
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ cal_ctx_set_dma_addr(ctx, addr);
- buf = list_entry(dma_q->active.next, struct cal_buffer, list);
- ctx->next_frm = buf;
- list_del(&buf->list);
+ ctx->dma.pending = buf;
+ list_del(&buf->list);
+ }
- addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
- cal_ctx_wr_dma_addr(ctx, addr);
+ spin_unlock(&ctx->dma.lock);
}
-static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
+static inline void cal_irq_wdma_end(struct cal_ctx *ctx)
{
- ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
- ctx->cur_frm->vb.field = ctx->m_fmt.field;
- ctx->cur_frm->vb.sequence = ctx->sequence++;
+ struct cal_buffer *buf = NULL;
+
+ spin_lock(&ctx->dma.lock);
+
+ /* If the DMA context was stopping, it is now stopped. */
+ if (ctx->dma.state == CAL_DMA_STOP_PENDING) {
+ ctx->dma.state = CAL_DMA_STOPPED;
+ wake_up(&ctx->dma.wait);
+ }
- vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
- ctx->cur_frm = ctx->next_frm;
+ /* If a new buffer was queued, complete the current buffer. */
+ if (ctx->dma.pending) {
+ buf = ctx->dma.active;
+ ctx->dma.active = ctx->dma.pending;
+ ctx->dma.pending = NULL;
+ }
+
+ spin_unlock(&ctx->dma.lock);
+
+ if (buf) {
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ buf->vb.field = ctx->v_fmt.fmt.pix.field;
+ buf->vb.sequence = ctx->sequence++;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
}
static irqreturn_t cal_irq(int irq_cal, void *data)
{
struct cal_dev *cal = data;
- struct cal_ctx *ctx;
- struct cal_dmaqueue *dma_q;
u32 status;
status = cal_read(cal, CAL_HL_IRQSTATUS(0));
@@ -337,7 +556,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data)
if (status & CAL_HL_IRQ_OCPO_ERR_MASK)
dev_err_ratelimited(cal->dev, "OCPO ERROR\n");
- for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) {
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
if (status & CAL_HL_IRQ_CIO_MASK(i)) {
u32 cio_stat = cal_read(cal,
CAL_CSI2_COMPLEXIO_IRQSTATUS(i));
@@ -360,17 +579,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data)
cal_write(cal, CAL_HL_IRQSTATUS(1), status);
for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
- if (status & CAL_HL_IRQ_MASK(i)) {
- ctx = cal->ctx[i];
-
- spin_lock(&ctx->slock);
- ctx->dma_act = false;
-
- if (ctx->cur_frm != ctx->next_frm)
- cal_process_buffer_complete(ctx);
-
- spin_unlock(&ctx->slock);
- }
+ if (status & CAL_HL_IRQ_MASK(i))
+ cal_irq_wdma_end(cal->ctx[i]);
}
}
@@ -383,17 +593,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data)
cal_write(cal, CAL_HL_IRQSTATUS(2), status);
for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
- if (status & CAL_HL_IRQ_MASK(i)) {
- ctx = cal->ctx[i];
- dma_q = &ctx->vidq;
-
- spin_lock(&ctx->slock);
- ctx->dma_act = true;
- if (!list_empty(&dma_q->active) &&
- ctx->cur_frm == ctx->next_frm)
- cal_schedule_next_buffer(ctx);
- spin_unlock(&ctx->slock);
- }
+ if (status & CAL_HL_IRQ_MASK(i))
+ cal_irq_wdma_start(cal->ctx[i]);
}
}
@@ -406,7 +607,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data)
*/
struct cal_v4l2_async_subdev {
- struct v4l2_async_subdev asd;
+ struct v4l2_async_subdev asd; /* Must be first */
struct cal_camerarx *phy;
};
@@ -421,6 +622,8 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_subdev *asd)
{
struct cal_camerarx *phy = to_cal_asd(asd)->phy;
+ int pad;
+ int ret;
if (phy->sensor) {
phy_info(phy, "Rejecting subdev %s (Already set!!)",
@@ -431,6 +634,25 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
phy->sensor = subdev;
phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name);
+ pad = media_entity_get_fwnode_pad(&subdev->entity,
+ of_fwnode_handle(phy->sensor_ep_node),
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ phy_err(phy, "Sensor %s has no connected source pad\n",
+ subdev->name);
+ return pad;
+ }
+
+ ret = media_create_pad_link(&subdev->entity, pad,
+ &phy->subdev.entity, CAL_CAMERARX_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ phy_err(phy, "Failed to create media link for sensor %s\n",
+ subdev->name);
+ return ret;
+ }
+
return 0;
}
@@ -460,26 +682,24 @@ static int cal_async_notifier_register(struct cal_dev *cal)
v4l2_async_notifier_init(&cal->notifier);
cal->notifier.ops = &cal_async_notifier_ops;
- for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) {
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
struct cal_camerarx *phy = cal->phy[i];
struct cal_v4l2_async_subdev *casd;
- struct v4l2_async_subdev *asd;
struct fwnode_handle *fwnode;
- if (!phy || !phy->sensor_node)
+ if (!phy->sensor_node)
continue;
fwnode = of_fwnode_handle(phy->sensor_node);
- asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier,
- fwnode,
- sizeof(*asd));
- if (IS_ERR(asd)) {
+ casd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier,
+ fwnode,
+ struct cal_v4l2_async_subdev);
+ if (IS_ERR(casd)) {
phy_err(phy, "Failed to add subdev to notifier\n");
- ret = PTR_ERR(asd);
+ ret = PTR_ERR(casd);
goto error;
}
- casd = to_cal_asd(asd);
casd->phy = phy;
}
@@ -797,6 +1017,11 @@ static int cal_probe(struct platform_device *pdev)
cal_get_hwinfo(cal);
pm_runtime_put_sync(&pdev->dev);
+ /* Initialize the media device. */
+ ret = cal_media_init(cal);
+ if (ret < 0)
+ goto error_pm_runtime;
+
/* Create CAMERARX PHYs. */
for (i = 0; i < cal->data->num_csi2_phy; ++i) {
cal->phy[i] = cal_camerarx_create(cal, i);
@@ -816,11 +1041,6 @@ static int cal_probe(struct platform_device *pdev)
goto error_camerarx;
}
- /* Initialize the media device. */
- ret = cal_media_init(cal);
- if (ret < 0)
- goto error_camerarx;
-
/* Create contexts. */
for (i = 0; i < cal->data->num_csi2_phy; ++i) {
if (!cal->phy[i]->sensor_node)
@@ -848,12 +1068,12 @@ error_context:
cal_ctx_v4l2_cleanup(ctx);
}
- cal_media_cleanup(cal);
-
error_camerarx:
- for (i = 0; i < ARRAY_SIZE(cal->phy); i++)
+ for (i = 0; i < cal->data->num_csi2_phy; i++)
cal_camerarx_destroy(cal->phy[i]);
+ cal_media_cleanup(cal);
+
error_pm_runtime:
pm_runtime_disable(&pdev->dev);
@@ -878,7 +1098,7 @@ static int cal_remove(struct platform_device *pdev)
cal_media_cleanup(cal);
- for (i = 0; i < ARRAY_SIZE(cal->phy); i++)
+ for (i = 0; i < cal->data->num_csi2_phy; i++)
cal_camerarx_destroy(cal->phy[i]);
pm_runtime_put_sync(&pdev->dev);
@@ -890,16 +1110,23 @@ static int cal_remove(struct platform_device *pdev)
static int cal_runtime_resume(struct device *dev)
{
struct cal_dev *cal = dev_get_drvdata(dev);
+ unsigned int i;
if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) {
/*
* Apply errata on both port everytime we (re-)enable
* the clock
*/
- cal_camerarx_i913_errata(cal->phy[0]);
- cal_camerarx_i913_errata(cal->phy[1]);
+ for (i = 0; i < cal->data->num_csi2_phy; i++)
+ cal_camerarx_i913_errata(cal->phy[i]);
}
+ /*
+ * Enable global interrupts that are not related to a particular
+ * CAMERARAX or context.
+ */
+ cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK);
+
return 0;
}
diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h
index 4123405ee0cf..d471b7f82519 100644
--- a/drivers/media/platform/ti-vpe/cal.h
+++ b/drivers/media/platform/ti-vpe/cal.h
@@ -17,6 +17,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/videodev2.h>
+#include <linux/wait.h>
#include <media/media-device.h>
#include <media/v4l2-async.h>
@@ -24,21 +25,32 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
#include <media/videobuf2-v4l2.h>
#define CAL_MODULE_NAME "cal"
#define CAL_NUM_CONTEXT 2
#define CAL_NUM_CSI2_PORTS 2
-#define MAX_WIDTH_BYTES (8192 * 8)
-#define MAX_HEIGHT_LINES 16383
+/*
+ * The width is limited by the size of the CAL_WR_DMA_XSIZE_j.XSIZE field,
+ * expressed in multiples of 64 bits. The height is limited by the size of the
+ * CAL_CSI2_CTXi_j.CTXi_LINES and CAL_WR_DMA_CTRL_j.YSIZE fields, expressed in
+ * lines.
+ */
+#define CAL_MIN_WIDTH_BYTES 16
+#define CAL_MAX_WIDTH_BYTES (8192 * 8)
+#define CAL_MIN_HEIGHT_LINES 1
+#define CAL_MAX_HEIGHT_LINES 16383
+
+#define CAL_CAMERARX_PAD_SINK 0
+#define CAL_CAMERARX_PAD_SOURCE 1
struct device;
struct device_node;
struct resource;
struct regmap;
struct regmap_fied;
-struct v4l2_subdev;
/* CTRL_CORE_CAMERRX_CONTROL register field id */
enum cal_camerarx_field {
@@ -49,7 +61,14 @@ enum cal_camerarx_field {
F_MAX_FIELDS,
};
-struct cal_fmt {
+enum cal_dma_state {
+ CAL_DMA_RUNNING,
+ CAL_DMA_STOP_REQUESTED,
+ CAL_DMA_STOP_PENDING,
+ CAL_DMA_STOPPED,
+};
+
+struct cal_format_info {
u32 fourcc;
u32 code;
/* Bits per pixel */
@@ -63,8 +82,38 @@ struct cal_buffer {
struct list_head list;
};
+/**
+ * struct cal_dmaqueue - Queue of DMA buffers
+ * @active: Buffer being DMA'ed to for the current frame
+ */
struct cal_dmaqueue {
- struct list_head active;
+ /**
+ * Protects all fields in the cal_dmaqueue.
+ */
+ spinlock_t lock;
+
+ /**
+ * Buffers queued to the driver and waiting for DMA processing.
+ * Buffers are added to the list by the vb2 .buffer_queue() operation,
+ * and move to @pending when they are scheduled for the next frame.
+ */
+ struct list_head queue;
+ /**
+ * Buffer provided to the hardware to DMA the next frame. Will move to
+ * @active at the end of the current frame.
+ */
+ struct cal_buffer *pending;
+ /**
+ * Buffer being DMA'ed to for the current frame. Will be retired and
+ * given back to vb2 at the end of the current frame if a @pending
+ * buffer has been scheduled to replace it.
+ */
+ struct cal_buffer *active;
+
+ /** State of the DMA engine. */
+ enum cal_dma_state state;
+ /** Wait queue to signal a @state transition to CAL_DMA_STOPPED. */
+ struct wait_queue_head wait;
};
struct cal_camerarx_data {
@@ -108,8 +157,14 @@ struct cal_camerarx {
unsigned int instance;
struct v4l2_fwnode_endpoint endpoint;
+ struct device_node *sensor_ep_node;
struct device_node *sensor_node;
struct v4l2_subdev *sensor;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[2];
+ struct v4l2_mbus_framefmt formats[2];
+ const struct cal_format_info *fmtinfo;
};
struct cal_dev {
@@ -149,33 +204,22 @@ struct cal_ctx {
/* v4l2_ioctl mutex */
struct mutex mutex;
- /* v4l2 buffers lock */
- spinlock_t slock;
- struct cal_dmaqueue vidq;
+ struct cal_dmaqueue dma;
/* video capture */
- const struct cal_fmt *fmt;
+ const struct cal_format_info *fmtinfo;
/* Used to store current pixel format */
- struct v4l2_format v_fmt;
- /* Used to store current mbus frame format */
- struct v4l2_mbus_framefmt m_fmt;
+ struct v4l2_format v_fmt;
/* Current subdev enumerated format */
- const struct cal_fmt **active_fmt;
+ const struct cal_format_info **active_fmt;
unsigned int num_active_fmt;
unsigned int sequence;
struct vb2_queue vb_vidq;
unsigned int index;
unsigned int cport;
-
- /* Pointer pointing to current v4l2_buffer */
- struct cal_buffer *cur_frm;
- /* Pointer pointing to next v4l2_buffer */
- struct cal_buffer *next_frm;
-
- bool dma_act;
};
extern unsigned int cal_debug;
@@ -215,7 +259,7 @@ static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val)
iowrite32(val, cal->base + offset);
}
-static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask)
+static __always_inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask)
{
return FIELD_GET(mask, cal_read(cal, offset));
}
@@ -239,25 +283,22 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask)
*valp = val;
}
+extern const struct cal_format_info cal_formats[];
+extern const unsigned int cal_num_formats;
+const struct cal_format_info *cal_format_by_fourcc(u32 fourcc);
+const struct cal_format_info *cal_format_by_code(u32 code);
+
void cal_quickdump_regs(struct cal_dev *cal);
void cal_camerarx_disable(struct cal_camerarx *phy);
-int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt);
-void cal_camerarx_stop(struct cal_camerarx *phy);
-void cal_camerarx_enable_irqs(struct cal_camerarx *phy);
-void cal_camerarx_disable_irqs(struct cal_camerarx *phy);
-void cal_camerarx_ppi_enable(struct cal_camerarx *phy);
-void cal_camerarx_ppi_disable(struct cal_camerarx *phy);
void cal_camerarx_i913_errata(struct cal_camerarx *phy);
struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
unsigned int instance);
void cal_camerarx_destroy(struct cal_camerarx *phy);
-void cal_ctx_csi2_config(struct cal_ctx *ctx);
-void cal_ctx_pix_proc_config(struct cal_ctx *ctx);
-void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width,
- unsigned int height);
-void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr);
+void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr);
+void cal_ctx_start(struct cal_ctx *ctx);
+void cal_ctx_stop(struct cal_ctx *ctx);
int cal_ctx_v4l2_register(struct cal_ctx *ctx);
void cal_ctx_v4l2_unregister(struct cal_ctx *ctx);
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 779dd74b82d0..10251b787674 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -1683,7 +1683,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
}
}
- memset(pix->reserved, 0, sizeof(pix->reserved));
for (i = 0; i < pix->num_planes; i++) {
plane_fmt = &pix->plane_fmt[i];
depth = fmt->vpdma_fmt[i]->depth;
@@ -1713,7 +1712,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
plane_fmt->bytesperline *
depth) >> 3;
}
- memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
}
return 0;
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index 53570250a25d..133122e38515 100644
--- a/drivers/media/platform/video-mux.c
+++ b/drivers/media/platform/video-mux.c
@@ -370,19 +370,13 @@ static int video_mux_async_register(struct video_mux *vmux,
if (!ep)
continue;
- asd = kzalloc(sizeof(*asd), GFP_KERNEL);
- if (!asd) {
- fwnode_handle_put(ep);
- return -ENOMEM;
- }
-
- ret = v4l2_async_notifier_add_fwnode_remote_subdev(
- &vmux->notifier, ep, asd);
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &vmux->notifier, ep, struct v4l2_async_subdev);
fwnode_handle_put(ep);
- if (ret) {
- kfree(asd);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
/* OK if asd already exists */
if (ret != -EEXIST)
return ret;
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 56c62122a81a..37cf33c7e6ca 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -44,16 +44,16 @@ struct vsp1_uif;
#define VSP1_MAX_UIF 2
#define VSP1_MAX_WPF 4
-#define VSP1_HAS_LUT (1 << 1)
-#define VSP1_HAS_SRU (1 << 2)
-#define VSP1_HAS_BRU (1 << 3)
-#define VSP1_HAS_CLU (1 << 4)
-#define VSP1_HAS_WPF_VFLIP (1 << 5)
-#define VSP1_HAS_WPF_HFLIP (1 << 6)
-#define VSP1_HAS_HGO (1 << 7)
-#define VSP1_HAS_HGT (1 << 8)
-#define VSP1_HAS_BRS (1 << 9)
-#define VSP1_HAS_EXT_DL (1 << 10)
+#define VSP1_HAS_LUT BIT(1)
+#define VSP1_HAS_SRU BIT(2)
+#define VSP1_HAS_BRU BIT(3)
+#define VSP1_HAS_CLU BIT(4)
+#define VSP1_HAS_WPF_VFLIP BIT(5)
+#define VSP1_HAS_WPF_HFLIP BIT(6)
+#define VSP1_HAS_HGO BIT(7)
+#define VSP1_HAS_HGT BIT(8)
+#define VSP1_HAS_BRS BIT(9)
+#define VSP1_HAS_EXT_DL BIT(10)
struct vsp1_device_info {
u32 version;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index dc62533cf32c..aa66e4f5f3f3 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -882,8 +882,10 @@ static int vsp1_probe(struct platform_device *pdev)
}
done:
- if (ret)
+ if (ret) {
pm_runtime_disable(&pdev->dev);
+ rcar_fcp_put(vsp1->fcp);
+ }
return ret;
}
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index cc2856efea59..bf4015d852e3 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -359,7 +359,7 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev,
dev_dbg(xdev->dev, "parsing node %p\n", fwnode);
while (1) {
- struct v4l2_async_subdev *asd;
+ struct xvip_graph_entity *xge;
ep = fwnode_graph_get_next_endpoint(fwnode, ep);
if (ep == NULL)
@@ -382,12 +382,12 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev,
continue;
}
- asd = v4l2_async_notifier_add_fwnode_subdev(
+ xge = v4l2_async_notifier_add_fwnode_subdev(
&xdev->notifier, remote,
- sizeof(struct xvip_graph_entity));
+ struct xvip_graph_entity);
fwnode_handle_put(remote);
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
+ if (IS_ERR(xge)) {
+ ret = PTR_ERR(xge);
goto err_notifier_cleanup;
}
}