summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/allegro-dvt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/media/platform/allegro-dvt
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/media/platform/allegro-dvt')
-rw-r--r--drivers/media/platform/allegro-dvt/Kconfig19
-rw-r--r--drivers/media/platform/allegro-dvt/Makefile6
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c4018
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.c549
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.h297
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.c605
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.h404
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.c884
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h519
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.c310
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.h61
11 files changed, 7672 insertions, 0 deletions
diff --git a/drivers/media/platform/allegro-dvt/Kconfig b/drivers/media/platform/allegro-dvt/Kconfig
new file mode 100644
index 000000000..2182e1277
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Allegro DVT media platform drivers"
+
+config VIDEO_ALLEGRO_DVT
+ tristate "Allegro DVT Video IP Core"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ 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.
diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile
new file mode 100644
index 000000000..66108a303
--- /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 000000000..da61f9beb
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-core.c
@@ -0,0 +1,4018 @@
+// 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/clk.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/mfd/syscon.h>
+#include <linux/mfd/syscon/xlnx-vcu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.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
+
+/*
+ * This control allows applications to explicitly disable the encoder buffer.
+ * This value is Allegro specific.
+ */
+#define V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER (V4L2_CID_USER_ALLEGRO_BASE + 0)
+
+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_encoder_buffer {
+ unsigned int size;
+ unsigned int color_depth;
+ unsigned int num_cores;
+ unsigned int clk_rate;
+};
+
+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;
+ struct regmap *settings;
+
+ struct clk *clk_core;
+ struct clk *clk_mcu;
+
+ const struct fw_info *fw_info;
+ struct allegro_buffer firmware;
+ struct allegro_buffer suballocator;
+ bool has_encoder_buffer;
+ struct allegro_encoder_buffer encoder_buffer;
+
+ struct completion init_complete;
+ bool initialized;
+
+ /* 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;
+
+ struct v4l2_ctrl *encoder_buffer;
+
+ /* 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 int allegro_encoder_buffer_init(struct allegro_dev *dev,
+ struct allegro_encoder_buffer *buffer)
+{
+ int err;
+ struct regmap *settings = dev->settings;
+ unsigned int supports_10_bit;
+ unsigned int memory_depth;
+ unsigned int num_cores;
+ unsigned int color_depth;
+ unsigned long clk_rate;
+
+ /* We don't support the encoder buffer pre Firmware version 2019.2 */
+ if (dev->fw_info->mailbox_version < MCU_MSG_VERSION_2019_2)
+ return -ENODEV;
+
+ if (!settings)
+ return -EINVAL;
+
+ err = regmap_read(settings, VCU_ENC_COLOR_DEPTH, &supports_10_bit);
+ if (err < 0)
+ return err;
+ err = regmap_read(settings, VCU_MEMORY_DEPTH, &memory_depth);
+ if (err < 0)
+ return err;
+ err = regmap_read(settings, VCU_NUM_CORE, &num_cores);
+ if (err < 0)
+ return err;
+
+ clk_rate = clk_get_rate(dev->clk_core);
+ if (clk_rate == 0)
+ return -EINVAL;
+
+ color_depth = supports_10_bit ? 10 : 8;
+ /* The firmware expects the encoder buffer size in bits. */
+ buffer->size = color_depth * 32 * memory_depth;
+ buffer->color_depth = color_depth;
+ buffer->num_cores = num_cores;
+ buffer->clk_rate = clk_rate;
+
+ v4l2_dbg(1, debug, &dev->v4l2_dev,
+ "using %d bits encoder buffer with %d-bit color depth\n",
+ buffer->size, color_depth);
+
+ return 0;
+}
+
+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);
+
+ if (dev->has_encoder_buffer) {
+ msg.encoder_buffer_size = dev->encoder_buffer.size;
+ msg.encoder_buffer_color_depth = dev->encoder_buffer.color_depth;
+ msg.num_cores = dev->encoder_buffer.num_cores;
+ msg.clk_rate = dev->encoder_buffer.clk_rate;
+ } else {
+ msg.encoder_buffer_size = -1;
+ msg.encoder_buffer_color_depth = -1;
+ msg.num_cores = -1;
+ msg.clk_rate = -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->encoder_buffer_enabled = v4l2_ctrl_g_ctrl(channel->encoder_buffer);
+ param->encoder_buffer_offset = 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;
+ bool use_encoder_buffer = v4l2_ctrl_g_ctrl(channel->encoder_buffer);
+
+ 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;
+ if (use_encoder_buffer)
+ msg.encoding_options |= AL_OPT_USE_L2;
+ 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;
+
+ 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(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(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 = 5; /* unspecified */
+ sps->vui.video_full_range_flag = nal_h264_full_range(channel->quantization);
+ sps->vui.colour_description_present_flag = 1;
+ sps->vui.colour_primaries = nal_h264_color_primaries(channel->colorspace);
+ sps->vui.transfer_characteristics =
+ nal_h264_transfer_characteristics(channel->colorspace, channel->xfer_func);
+ sps->vui.matrix_coefficients =
+ nal_h264_matrix_coeffs(channel->colorspace, channel->ycbcr_enc);
+
+ 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;
+ /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */
+ sps->vui.vcl_hrd_parameters.bit_rate_scale =
+ ffs(channel->bitrate_peak) - 6;
+ 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(profile);
+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
+ ptl->general_tier_flag = nal_hevc_tier(tier);
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+ ptl->general_level_idc = nal_hevc_level(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;
+ struct nal_hevc_vui_parameters *vui;
+ struct nal_hevc_hrd_parameters *hrd;
+ ssize_t size;
+ unsigned int cpb_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(profile);
+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
+ ptl->general_tier_flag = nal_hevc_tier(tier);
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+ ptl->general_level_idc = nal_hevc_level(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;
+
+ sps->vui_parameters_present_flag = 1;
+ vui = &sps->vui;
+
+ vui->video_signal_type_present_flag = 1;
+ vui->video_format = 5; /* unspecified */
+ vui->video_full_range_flag = nal_hevc_full_range(channel->quantization);
+ vui->colour_description_present_flag = 1;
+ vui->colour_primaries = nal_hevc_color_primaries(channel->colorspace);
+ vui->transfer_characteristics = nal_hevc_transfer_characteristics(channel->colorspace,
+ channel->xfer_func);
+ vui->matrix_coeffs = nal_hevc_matrix_coeffs(channel->colorspace, channel->ycbcr_enc);
+
+ vui->chroma_loc_info_present_flag = 1;
+ vui->chroma_sample_loc_type_top_field = 0;
+ vui->chroma_sample_loc_type_bottom_field = 0;
+
+ vui->vui_timing_info_present_flag = 1;
+ vui->vui_num_units_in_tick = channel->framerate.denominator;
+ vui->vui_time_scale = channel->framerate.numerator;
+
+ vui->bitstream_restriction_flag = 1;
+ vui->motion_vectors_over_pic_boundaries_flag = 1;
+ vui->restricted_ref_pic_lists_flag = 1;
+ vui->log2_max_mv_length_horizontal = 15;
+ vui->log2_max_mv_length_vertical = 15;
+
+ vui->vui_hrd_parameters_present_flag = 1;
+ hrd = &vui->nal_hrd_parameters;
+ hrd->vcl_hrd_parameters_present_flag = 1;
+
+ hrd->initial_cpb_removal_delay_length_minus1 = 31;
+ hrd->au_cpb_removal_delay_length_minus1 = 30;
+ hrd->dpb_output_delay_length_minus1 = 30;
+
+ hrd->bit_rate_scale = ffs(channel->bitrate_peak) - 6;
+ hrd->vcl_hrd[0].bit_rate_value_minus1[0] =
+ (channel->bitrate_peak >> (6 + hrd->bit_rate_scale)) - 1;
+
+ cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size) * 1000;
+ hrd->cpb_size_scale = ffs(cpb_size) - 4;
+ hrd->vcl_hrd[0].cpb_size_value_minus1[0] = (cpb_size >> (4 + hrd->cpb_size_scale)) - 1;
+
+ hrd->vcl_hrd[0].cbr_flag[0] = !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable);
+
+ 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;
+
+ /*
+ * The firmware is initialized after the mailbox is setup. We further
+ * check the AL5_ITC_CPU_IRQ_STA register, if the firmware actually
+ * triggered the interrupt. Although this should not happen, make sure
+ * that we ignore interrupts, if the mailbox is not initialized.
+ */
+ if (!dev->mbox_status)
+ return IRQ_NONE;
+
+ 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);
+
+ v4l2_ctrl_grab(channel->encoder_buffer, 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);
+
+ v4l2_ctrl_grab(channel->encoder_buffer, 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++)
+ vb2_set_plane_payload(vb, i, 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;
+ case V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER:
+ if (!channel->dev->has_encoder_buffer)
+ ctrl->val = 0;
+ 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 const struct v4l2_ctrl_config allegro_encoder_buffer_ctrl_config = {
+ .id = V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER,
+ .name = "Encoder Buffer Enable",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+};
+
+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);
+ channel->encoder_buffer = v4l2_ctrl_new_custom(handler,
+ &allegro_encoder_buffer_ctrl_config, NULL);
+ 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)
+{
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card));
+
+ 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;
+ }
+
+ err = allegro_encoder_buffer_init(dev, &dev->encoder_buffer);
+ dev->has_encoder_buffer = (err == 0);
+ if (!dev->has_encoder_buffer)
+ v4l2_info(&dev->v4l2_dev, "encoder buffer not available\n");
+
+ 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);
+
+ pm_runtime_enable(&dev->plat_dev->dev);
+ err = pm_runtime_resume_and_get(&dev->plat_dev->dev);
+ if (err)
+ goto err_release_firmware_codec;
+
+ /* 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_suspend;
+ }
+
+ 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);
+
+ dev->initialized = true;
+
+ 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_suspend:
+ pm_runtime_put(&dev->plat_dev->dev);
+ pm_runtime_disable(&dev->plat_dev->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);
+
+ dev->initialized = false;
+
+ 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);
+ }
+
+ dev->settings = syscon_regmap_lookup_by_compatible("xlnx,vcu-settings");
+ if (IS_ERR(dev->settings))
+ dev_warn(&pdev->dev, "failed to open settings\n");
+
+ dev->clk_core = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(dev->clk_core))
+ return PTR_ERR(dev->clk_core);
+
+ dev->clk_mcu = devm_clk_get(&pdev->dev, "mcu_clk");
+ if (IS_ERR(dev->clk_mcu))
+ return PTR_ERR(dev->clk_mcu);
+
+ 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 void allegro_remove(struct platform_device *pdev)
+{
+ struct allegro_dev *dev = platform_get_drvdata(pdev);
+
+ if (dev->initialized) {
+ 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);
+ }
+
+ pm_runtime_put(&dev->plat_dev->dev);
+ pm_runtime_disable(&dev->plat_dev->dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+static int allegro_runtime_resume(struct device *device)
+{
+ struct allegro_dev *dev = dev_get_drvdata(device);
+ struct regmap *settings = dev->settings;
+ unsigned int clk_mcu;
+ unsigned int clk_core;
+ int err;
+
+ if (!settings)
+ return -EINVAL;
+
+#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000)
+
+ err = regmap_read(settings, VCU_CORE_CLK, &clk_core);
+ if (err < 0)
+ return err;
+ err = clk_set_rate(dev->clk_core, MHZ_TO_HZ(clk_core));
+ if (err < 0)
+ return err;
+ err = clk_prepare_enable(dev->clk_core);
+ if (err)
+ return err;
+
+ err = regmap_read(settings, VCU_MCU_CLK, &clk_mcu);
+ if (err < 0)
+ goto disable_clk_core;
+ err = clk_set_rate(dev->clk_mcu, MHZ_TO_HZ(clk_mcu));
+ if (err < 0)
+ goto disable_clk_core;
+ err = clk_prepare_enable(dev->clk_mcu);
+ if (err)
+ goto disable_clk_core;
+
+#undef MHZ_TO_HZ
+
+ return 0;
+
+disable_clk_core:
+ clk_disable_unprepare(dev->clk_core);
+
+ return err;
+}
+
+static int allegro_runtime_suspend(struct device *device)
+{
+ struct allegro_dev *dev = dev_get_drvdata(device);
+
+ clk_disable_unprepare(dev->clk_mcu);
+ clk_disable_unprepare(dev->clk_core);
+
+ 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 const struct dev_pm_ops allegro_pm_ops = {
+ .runtime_resume = allegro_runtime_resume,
+ .runtime_suspend = allegro_runtime_suspend,
+};
+
+static struct platform_driver allegro_driver = {
+ .probe = allegro_probe,
+ .remove_new = allegro_remove,
+ .driver = {
+ .name = "allegro",
+ .of_match_table = allegro_dt_ids,
+ .pm = &allegro_pm_ops,
+ },
+};
+
+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 000000000..16effad10
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-mail.c
@@ -0,0 +1,549 @@
+// 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->encoder_buffer_size;
+ dst[i++] = msg->encoder_buffer_color_depth;
+ dst[i++] = msg->num_cores;
+ if (version >= MCU_MSG_VERSION_2019_2) {
+ dst[i++] = msg->clk_rate;
+ 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;
+ dst[i++] = param->num_slices;
+ dst[i++] = param->encoder_buffer_offset;
+ dst[i++] = param->encoder_buffer_enabled;
+
+ 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_row = FIELD_GET(GENMASK(31, 16), src[i]);
+ msg->num_column = 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 000000000..a5686058d
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/allegro-mail.h
@@ -0,0 +1,297 @@
+/* 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 encoder_buffer_size;
+ s32 encoder_buffer_color_depth;
+ s32 num_cores;
+ s32 clk_rate;
+};
+
+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;
+ u32 encoder_buffer_offset;
+ u32 encoder_buffer_enabled;
+ 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 000000000..326637663
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-h264.c
@@ -0,0 +1,605 @@
+// 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,
+};
+
+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 000000000..34db07cda
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-h264.h
@@ -0,0 +1,404 @@
+/* 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+#include <linux/videodev2.h>
+
+/*
+ * struct nal_h264_hrd_parameters - HRD 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;
+ };
+};
+
+/**
+ * nal_h264_profile() - 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
+ */
+static inline int nal_h264_profile(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() - 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
+ */
+static inline int nal_h264_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_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;
+ }
+}
+
+/**
+ * nal_h264_full_range() - Get video_full_range_flag for v4l2 quantization
+ * @quantization: the quantization type as &enum v4l2_quantization
+ *
+ * Convert the &enum v4l2_quantization to video_full_range_flag as specified in
+ * Rec. ITU-T H.264 (04/2017) E.2.1.
+ *
+ * Return: the video_full_range_flag value for the passed quantization
+ */
+static inline int nal_h264_full_range(enum v4l2_quantization quantization)
+{
+ switch (quantization) {
+ case V4L2_QUANTIZATION_FULL_RANGE:
+ return 1;
+ case V4L2_QUANTIZATION_LIM_RANGE:
+ return 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * nal_h264_color_primaries() - Get color_primaries for v4l2 colorspace
+ * @colorspace: the color space as &enum v4l2_colorspace
+ *
+ * Convert the &enum v4l2_colorspace to color_primaries as specified in
+ * Rec. ITU-T H.264 (04/2017) E.2.1.
+ *
+ * Return: the color_primaries value for the passed colorspace
+ */
+static inline int nal_h264_color_primaries(enum v4l2_colorspace colorspace)
+{
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ return 6;
+ case V4L2_COLORSPACE_SMPTE240M:
+ return 7;
+ case V4L2_COLORSPACE_REC709:
+ return 1;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ return 4;
+ case V4L2_COLORSPACE_JPEG:
+ case V4L2_COLORSPACE_SRGB:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return 5;
+ case V4L2_COLORSPACE_BT2020:
+ return 9;
+ case V4L2_COLORSPACE_DEFAULT:
+ case V4L2_COLORSPACE_OPRGB:
+ case V4L2_COLORSPACE_RAW:
+ case V4L2_COLORSPACE_DCI_P3:
+ default:
+ return 2;
+ }
+}
+
+/**
+ * nal_h264_transfer_characteristics() - Get transfer_characteristics for v4l2 xfer_func
+ * @colorspace: the color space as &enum v4l2_colorspace
+ * @xfer_func: the transfer function as &enum v4l2_xfer_func
+ *
+ * Convert the &enum v4l2_xfer_func to transfer_characteristics as specified in
+ * Rec. ITU-T H.264 (04/2017) E.2.1.
+ *
+ * Return: the transfer_characteristics value for the passed transfer function
+ */
+static inline int nal_h264_transfer_characteristics(enum v4l2_colorspace colorspace,
+ enum v4l2_xfer_func xfer_func)
+{
+ if (xfer_func == V4L2_XFER_FUNC_DEFAULT)
+ xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(colorspace);
+
+ switch (xfer_func) {
+ case V4L2_XFER_FUNC_709:
+ return 6;
+ case V4L2_XFER_FUNC_SMPTE2084:
+ return 16;
+ case V4L2_XFER_FUNC_SRGB:
+ case V4L2_XFER_FUNC_OPRGB:
+ case V4L2_XFER_FUNC_NONE:
+ case V4L2_XFER_FUNC_DCI_P3:
+ case V4L2_XFER_FUNC_SMPTE240M:
+ default:
+ return 2;
+ }
+}
+
+/**
+ * nal_h264_matrix_coeffs() - Get matrix_coefficients for v4l2 v4l2_ycbcr_encoding
+ * @colorspace: the color space as &enum v4l2_colorspace
+ * @ycbcr_encoding: the ycbcr encoding as &enum v4l2_ycbcr_encoding
+ *
+ * Convert the &enum v4l2_ycbcr_encoding to matrix_coefficients as specified in
+ * Rec. ITU-T H.264 (04/2017) E.2.1.
+ *
+ * Return: the matrix_coefficients value for the passed encoding
+ */
+static inline int nal_h264_matrix_coeffs(enum v4l2_colorspace colorspace,
+ enum v4l2_ycbcr_encoding ycbcr_encoding)
+{
+ if (ycbcr_encoding == V4L2_YCBCR_ENC_DEFAULT)
+ ycbcr_encoding = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace);
+
+ switch (ycbcr_encoding) {
+ case V4L2_YCBCR_ENC_601:
+ case V4L2_YCBCR_ENC_XV601:
+ return 5;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_XV709:
+ return 1;
+ case V4L2_YCBCR_ENC_BT2020:
+ return 9;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ return 10;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ default:
+ return 2;
+ }
+}
+
+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 000000000..9cdf2756e
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.c
@@ -0,0 +1,884 @@
+// 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,
+};
+
+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_sub_layer_hrd_parameters(struct rbsp *rbsp,
+ struct nal_hevc_sub_layer_hrd_parameters *hrd)
+{
+ unsigned int i;
+ unsigned int cpb_cnt = 1;
+
+ for (i = 0; i < cpb_cnt; 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]);
+ }
+}
+
+static void nal_hevc_rbsp_hrd_parameters(struct rbsp *rbsp,
+ struct nal_hevc_hrd_parameters *hrd)
+{
+ unsigned int i;
+ unsigned int max_num_sub_layers_minus_1 = 0;
+
+ rbsp_bit(rbsp, &hrd->nal_hrd_parameters_present_flag);
+ rbsp_bit(rbsp, &hrd->vcl_hrd_parameters_present_flag);
+ if (hrd->nal_hrd_parameters_present_flag || hrd->vcl_hrd_parameters_present_flag) {
+ rbsp_bit(rbsp, &hrd->sub_pic_hrd_params_present_flag);
+ if (hrd->sub_pic_hrd_params_present_flag) {
+ rbsp_bits(rbsp, 8, &hrd->tick_divisor_minus2);
+ rbsp_bits(rbsp, 5, &hrd->du_cpb_removal_delay_increment_length_minus1);
+ rbsp_bit(rbsp, &hrd->sub_pic_cpb_params_in_pic_timing_sei_flag);
+ rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_du_length_minus1);
+ }
+ rbsp_bits(rbsp, 4, &hrd->bit_rate_scale);
+ rbsp_bits(rbsp, 4, &hrd->cpb_size_scale);
+ if (hrd->sub_pic_hrd_params_present_flag)
+ rbsp_bits(rbsp, 4, &hrd->cpb_size_du_scale);
+ rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1);
+ rbsp_bits(rbsp, 5, &hrd->au_cpb_removal_delay_length_minus1);
+ rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1);
+ }
+ for (i = 0; i <= max_num_sub_layers_minus_1; i++) {
+ rbsp_bit(rbsp, &hrd->fixed_pic_rate_general_flag[i]);
+ if (!hrd->fixed_pic_rate_general_flag[i])
+ rbsp_bit(rbsp, &hrd->fixed_pic_rate_within_cvs_flag[i]);
+ if (hrd->fixed_pic_rate_within_cvs_flag[i])
+ rbsp_uev(rbsp, &hrd->elemental_duration_in_tc_minus1[i]);
+ else
+ rbsp_bit(rbsp, &hrd->low_delay_hrd_flag[i]);
+ if (!hrd->low_delay_hrd_flag[i])
+ rbsp_uev(rbsp, &hrd->cpb_cnt_minus1[i]);
+ if (hrd->nal_hrd_parameters_present_flag)
+ nal_hevc_rbsp_sub_layer_hrd_parameters(rbsp, &hrd->vcl_hrd[i]);
+ if (hrd->vcl_hrd_parameters_present_flag)
+ nal_hevc_rbsp_sub_layer_hrd_parameters(rbsp, &hrd->vcl_hrd[i]);
+ }
+}
+
+static void nal_hevc_rbsp_vui_parameters(struct rbsp *rbsp,
+ struct nal_hevc_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_coeffs);
+ }
+ }
+
+ 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->neutral_chroma_indication_flag);
+ rbsp_bit(rbsp, &vui->field_seq_flag);
+ rbsp_bit(rbsp, &vui->frame_field_info_present_flag);
+ rbsp_bit(rbsp, &vui->default_display_window_flag);
+ if (vui->default_display_window_flag) {
+ rbsp_uev(rbsp, &vui->def_disp_win_left_offset);
+ rbsp_uev(rbsp, &vui->def_disp_win_right_offset);
+ rbsp_uev(rbsp, &vui->def_disp_win_top_offset);
+ rbsp_uev(rbsp, &vui->def_disp_win_bottom_offset);
+ }
+
+ rbsp_bit(rbsp, &vui->vui_timing_info_present_flag);
+ if (vui->vui_timing_info_present_flag) {
+ rbsp_bits(rbsp, 32, &vui->vui_num_units_in_tick);
+ rbsp_bits(rbsp, 32, &vui->vui_time_scale);
+ rbsp_bit(rbsp, &vui->vui_poc_proportional_to_timing_flag);
+ if (vui->vui_poc_proportional_to_timing_flag)
+ rbsp_uev(rbsp, &vui->vui_num_ticks_poc_diff_one_minus1);
+ rbsp_bit(rbsp, &vui->vui_hrd_parameters_present_flag);
+ if (vui->vui_hrd_parameters_present_flag)
+ nal_hevc_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters);
+ }
+
+ rbsp_bit(rbsp, &vui->bitstream_restriction_flag);
+ if (vui->bitstream_restriction_flag) {
+ rbsp_bit(rbsp, &vui->tiles_fixed_structure_flag);
+ rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag);
+ rbsp_bit(rbsp, &vui->restricted_ref_pic_lists_flag);
+ rbsp_uev(rbsp, &vui->min_spatial_segmentation_idc);
+ rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom);
+ rbsp_uev(rbsp, &vui->max_bits_per_min_cu_denom);
+ rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal);
+ rbsp_uev(rbsp, &vui->log2_max_mv_length_vertical);
+ }
+}
+
+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)
+ nal_hevc_rbsp_vui_parameters(rbsp, &sps->vui);
+
+ 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 000000000..eb46f12aa
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.h
@@ -0,0 +1,519 @@
+/* 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+#include <linux/videodev2.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;
+ };
+};
+
+/**
+ * nal_hevc_profile() - Get profile_idc for v4l2 hevc profile
+ * @profile: the profile as &enum v4l2_mpeg_video_hevc_profile
+ *
+ * Convert the &enum v4l2_mpeg_video_hevc_profile to profile_idc as specified
+ * in Rec. ITU-T H.265 (02/2018) A.3.
+ *
+ * Return: the profile_idc for the passed level
+ */
+static inline int nal_hevc_profile(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;
+ }
+}
+
+/**
+ * nal_hevc_tier() - Get tier_flag for v4l2 hevc tier
+ * @tier: the tier as &enum v4l2_mpeg_video_hevc_tier
+ *
+ * Convert the &enum v4l2_mpeg_video_hevc_tier to tier_flag as specified
+ * in Rec. ITU-T H.265 (02/2018) A.4.1.
+ *
+ * Return: the tier_flag for the passed tier
+ */
+static inline int nal_hevc_tier(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;
+ }
+}
+
+/**
+ * nal_hevc_level() - Get level_idc for v4l2 hevc level
+ * @level: the level as &enum v4l2_mpeg_video_hevc_level
+ *
+ * Convert the &enum v4l2_mpeg_video_hevc_level to level_idc as specified in
+ * Rec. ITU-T H.265 (02/2018) A.4.1.
+ *
+ * Return: the level_idc for the passed level
+ */
+static inline int nal_hevc_level(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;
+ }
+}
+
+static inline int nal_hevc_full_range(enum v4l2_quantization quantization)
+{
+ switch (quantization) {
+ case V4L2_QUANTIZATION_FULL_RANGE:
+ return 1;
+ case V4L2_QUANTIZATION_LIM_RANGE:
+ return 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static inline int nal_hevc_color_primaries(enum v4l2_colorspace colorspace)
+{
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ return 6;
+ case V4L2_COLORSPACE_SMPTE240M:
+ return 7;
+ case V4L2_COLORSPACE_REC709:
+ return 1;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ return 4;
+ case V4L2_COLORSPACE_JPEG:
+ case V4L2_COLORSPACE_SRGB:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return 5;
+ case V4L2_COLORSPACE_BT2020:
+ return 9;
+ case V4L2_COLORSPACE_DEFAULT:
+ case V4L2_COLORSPACE_OPRGB:
+ case V4L2_COLORSPACE_RAW:
+ case V4L2_COLORSPACE_DCI_P3:
+ default:
+ return 2;
+ }
+}
+
+static inline int nal_hevc_transfer_characteristics(enum v4l2_colorspace colorspace,
+ enum v4l2_xfer_func xfer_func)
+{
+ if (xfer_func == V4L2_XFER_FUNC_DEFAULT)
+ xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(colorspace);
+
+ switch (xfer_func) {
+ case V4L2_XFER_FUNC_709:
+ return 6;
+ case V4L2_XFER_FUNC_SMPTE2084:
+ return 16;
+ case V4L2_XFER_FUNC_SRGB:
+ case V4L2_XFER_FUNC_OPRGB:
+ case V4L2_XFER_FUNC_NONE:
+ case V4L2_XFER_FUNC_DCI_P3:
+ case V4L2_XFER_FUNC_SMPTE240M:
+ default:
+ return 2;
+ }
+}
+
+static inline int nal_hevc_matrix_coeffs(enum v4l2_colorspace colorspace,
+ enum v4l2_ycbcr_encoding ycbcr_encoding)
+{
+ if (ycbcr_encoding == V4L2_YCBCR_ENC_DEFAULT)
+ ycbcr_encoding = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace);
+
+ switch (ycbcr_encoding) {
+ case V4L2_YCBCR_ENC_601:
+ case V4L2_YCBCR_ENC_XV601:
+ return 5;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_XV709:
+ return 1;
+ case V4L2_YCBCR_ENC_BT2020:
+ return 9;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ return 10;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ default:
+ return 2;
+ }
+}
+
+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 000000000..d911322d0
--- /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 000000000..c72f49fed
--- /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__ */