From d74d4e2359ec7985831192f9b5ee22ed5e55b81c Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 2 Dec 2020 14:30:37 +0100 Subject: media: allegro: move driver out of staging The stateful encoder API was finalized. Nothing is blocking the driver from being moved out of staging. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 15 + drivers/media/platform/Makefile | 1 + drivers/media/platform/allegro-dvt/Makefile | 5 + drivers/media/platform/allegro-dvt/allegro-core.c | 3226 +++++++++++++++++++++ drivers/media/platform/allegro-dvt/allegro-mail.c | 543 ++++ drivers/media/platform/allegro-dvt/allegro-mail.h | 294 ++ drivers/media/platform/allegro-dvt/nal-h264.c | 1001 +++++++ drivers/media/platform/allegro-dvt/nal-h264.h | 208 ++ drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/allegro-dvt/Kconfig | 16 - drivers/staging/media/allegro-dvt/Makefile | 5 - drivers/staging/media/allegro-dvt/TODO | 4 - drivers/staging/media/allegro-dvt/allegro-core.c | 3226 --------------------- drivers/staging/media/allegro-dvt/allegro-mail.c | 543 ---- drivers/staging/media/allegro-dvt/allegro-mail.h | 294 -- drivers/staging/media/allegro-dvt/nal-h264.c | 1001 ------- drivers/staging/media/allegro-dvt/nal-h264.h | 208 -- 19 files changed, 5294 insertions(+), 5301 deletions(-) create mode 100644 drivers/media/platform/allegro-dvt/Makefile create mode 100644 drivers/media/platform/allegro-dvt/allegro-core.c create mode 100644 drivers/media/platform/allegro-dvt/allegro-mail.c create mode 100644 drivers/media/platform/allegro-dvt/allegro-mail.h create mode 100644 drivers/media/platform/allegro-dvt/nal-h264.c create mode 100644 drivers/media/platform/allegro-dvt/nal-h264.h delete mode 100644 drivers/staging/media/allegro-dvt/Kconfig delete mode 100644 drivers/staging/media/allegro-dvt/Makefile delete mode 100644 drivers/staging/media/allegro-dvt/TODO delete mode 100644 drivers/staging/media/allegro-dvt/allegro-core.c delete mode 100644 drivers/staging/media/allegro-dvt/allegro-mail.c delete mode 100644 drivers/staging/media/allegro-dvt/allegro-mail.h delete mode 100644 drivers/staging/media/allegro-dvt/nal-h264.c delete mode 100644 drivers/staging/media/allegro-dvt/nal-h264.h diff --git a/MAINTAINERS b/MAINTAINERS index 546aa66428c9..a40345e0477c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -699,7 +699,7 @@ M: Michael Tretter R: Pengutronix Kernel Team L: linux-media@vger.kernel.org S: Maintained -F: drivers/staging/media/allegro-dvt/ +F: drivers/media/platform/allegro-dvt/ ALLWINNER A10 CSI DRIVER M: Maxime Ripard diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 35a18d388f3f..e419b18613c6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -199,6 +199,21 @@ menuconfig V4L_MEM2MEM_DRIVERS if V4L_MEM2MEM_DRIVERS +config VIDEO_ALLEGRO_DVT + tristate "Allegro DVT Video IP Core" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_ZYNQMP || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + Support for the encoder video IP core by Allegro DVT. This core is + found for example on the Xilinx ZynqMP SoC in the EV family and is + called VCU in the reference manual. + + To compile this driver as a module, choose M here: the module + will be called allegro. + config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 1d63aa956bcd..9d4d6370908d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,6 +3,7 @@ # Makefile for the video capture/playback device drivers. # +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o obj-$(CONFIG_VIDEO_CADENCE) += cadence/ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile new file mode 100644 index 000000000000..8e306dcdc55c --- /dev/null +++ b/drivers/media/platform/allegro-dvt/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +allegro-objs := allegro-core.o nal-h264.o allegro-mail.o + +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c new file mode 100644 index 000000000000..640451134072 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -0,0 +1,3226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Allegro DVT video encoder driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "allegro-mail.h" +#include "nal-h264.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_64 + +#define SIZE_MACROBLOCK 16 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +struct allegro_buffer { + void *vaddr; + dma_addr_t paddr; + size_t size; + struct list_head head; +}; + +struct allegro_dev; +struct allegro_channel; + +struct allegro_mbox { + struct allegro_dev *dev; + unsigned int head; + unsigned int tail; + unsigned int data; + size_t size; + /* protect mailbox from simultaneous accesses */ + struct mutex lock; +}; + +struct allegro_dev { + struct v4l2_device v4l2_dev; + struct video_device video_dev; + struct v4l2_m2m_dev *m2m_dev; + struct platform_device *plat_dev; + + /* mutex protecting vb2_queue structure */ + struct mutex lock; + + struct regmap *regmap; + struct regmap *sram; + + const struct fw_info *fw_info; + struct allegro_buffer firmware; + struct allegro_buffer suballocator; + + struct completion init_complete; + + /* The mailbox interface */ + struct allegro_mbox *mbox_command; + struct allegro_mbox *mbox_status; + + /* + * The downstream driver limits the users to 64 users, thus I can use + * a bitfield for the user_ids that are in use. See also user_id in + * struct allegro_channel. + */ + unsigned long channel_user_ids; + struct list_head channels; +}; + +static struct regmap_config allegro_regmap_config = { + .name = "regmap", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xfff, + .cache_type = REGCACHE_NONE, +}; + +static struct regmap_config allegro_sram_config = { + .name = "sram", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x7fff, + .cache_type = REGCACHE_NONE, +}; + +enum allegro_state { + ALLEGRO_STATE_ENCODING, + ALLEGRO_STATE_DRAIN, + ALLEGRO_STATE_WAIT_FOR_BUFFER, + ALLEGRO_STATE_STOPPED, +}; + +#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; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + unsigned int sizeimage_encoded; + unsigned int csequence; + + bool frame_rc_enable; + unsigned int bitrate; + unsigned int bitrate_peak; + unsigned int cpb_size; + unsigned int gop_size; + + struct allegro_buffer config_blob; + + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + + 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_frame_rc_enable; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + }; + struct v4l2_ctrl *mpeg_video_cpb_size; + struct v4l2_ctrl *mpeg_video_gop_size; + + /* user_id is used to identify the channel during CREATE_CHANNEL */ + /* not sure, what to set here and if this is actually required */ + int user_id; + /* channel_id is set by the mcu and used by all later commands */ + int mcu_channel_id; + + struct list_head buffers_reference; + struct list_head buffers_intermediate; + + struct list_head source_shadow_list; + struct list_head stream_shadow_list; + /* protect shadow lists of buffers passed to firmware */ + struct mutex shadow_list_lock; + + struct list_head list; + struct completion completion; + + unsigned int error; + enum allegro_state state; +}; + +static inline int +allegro_set_state(struct allegro_channel *channel, enum allegro_state state) +{ + channel->state = state; + + return 0; +} + +static inline enum allegro_state +allegro_get_state(struct allegro_channel *channel) +{ + return channel->state; +} + +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 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 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 const struct fw_info * +allegro_get_firmware_info(struct allegro_dev *dev, + const struct firmware *fw, + const struct firmware *fw_codec) +{ + int i; + unsigned int id = fw->size; + unsigned int id_codec = fw_codec->size; + + for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) + if (supported_firmware[i].id == id && + supported_firmware[i].id_codec == id_codec) + return &supported_firmware[i]; + + return NULL; +} + +/* + * Buffers that are used internally by the MCU. + */ + +static int allegro_alloc_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer, size_t size) +{ + buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, + &buffer->paddr, GFP_KERNEL); + if (!buffer->vaddr) + return -ENOMEM; + buffer->size = size; + + return 0; +} + +static void allegro_free_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer) +{ + if (buffer->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buffer->size, + buffer->vaddr, buffer->paddr); + buffer->vaddr = NULL; + buffer->size = 0; + } +} + +/* + * Mailbox interface to send messages to the MCU. + */ + +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) +{ + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; + + mbox->head = base; + mbox->tail = base + 0x4; + mbox->data = base + 0x8; + mbox->size = size; + mutex_init(&mbox->lock); + + regmap_write(dev->sram, mbox->head, 0); + regmap_write(dev->sram, mbox->tail, 0); + + return mbox; +} + +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) +{ + struct regmap *sram = mbox->dev->sram; + unsigned int tail; + size_t size_no_wrap; + int err = 0; + int stride = regmap_get_reg_stride(sram); + + if (!src) + return -EINVAL; + + if (size > mbox->size) + return -EINVAL; + + mutex_lock(&mbox->lock); + regmap_read(sram, mbox->tail, &tail); + if (tail > mbox->size) { + err = -EIO; + goto out; + } + size_no_wrap = min(size, mbox->size - (size_t)tail); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); + +out: + mutex_unlock(&mbox->lock); + + return err; +} + +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) +{ + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; + unsigned int head; + ssize_t size; + size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); + + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) + return -EIO; + + /* Assume that the header does not wrap. */ + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; + size = header->length + sizeof(*header); + if (size > mbox->size || size & 0x3) + return -EIO; + if (size > nbyte) + return -EINVAL; + + /* + * The message might wrap within the mailbox. If the message does not + * wrap, the first read will read the entire message, otherwise the + * first read will read message until the end of the mailbox and the + * second read will read the remaining bytes from the beginning of the + * mailbox. + * + * Skip the header, as was already read to get the size of the body. + */ + body_no_wrap = min((size_t)header->length, + (size_t)(mbox->size - (head + sizeof(*header)))); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); + + regmap_write(sram, mbox->head, (head + size) % mbox->size); + + return size; +} + +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) +{ + struct allegro_dev *dev = mbox->dev; + ssize_t size; + int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); + + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + u32 *tmp; + int err; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + msg->header.version = dev->fw_info->mailbox_version; + + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); + if (size < 0) + goto out; + + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(tmp); + kfree(msg); +} + +static void allegro_mcu_send_init(struct allegro_dev *dev, + dma_addr_t suballoc_dma, size_t suballoc_size) +{ + struct mcu_msg_init_request msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_INIT; + msg.header.version = dev->fw_info->mailbox_version; + + msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); + msg.suballoc_size = to_mcu_size(dev, suballoc_size); + + /* disable L2 cache */ + msg.l2_cache[0] = -1; + msg.l2_cache[1] = -1; + msg.l2_cache[2] = -1; + + allegro_mbox_send(dev->mbox_command, &msg); +} + +static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ + return 0x100 | 0x88; + default: + return -EINVAL; + } +} + +static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) +{ + switch (colorspace) { + case V4L2_COLORSPACE_REC709: + return 2; + case V4L2_COLORSPACE_SMPTE170M: + return 3; + case V4L2_COLORSPACE_SMPTE240M: + return 4; + case V4L2_COLORSPACE_SRGB: + return 7; + default: + /* UNKNOWN */ + return 0; + } +} + +static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + default: + return 66; + } +} + +static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 51; + } +} + +static 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 int fill_create_channel_param(struct allegro_channel *channel, + struct create_channel_param *param) +{ + int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); + int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); + int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); + + 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->profile = v4l2_profile_to_mcu_profile(channel->profile); + param->constraint_set_flags = BIT(1); + param->codec = channel->codec; + param->level = v4l2_level_to_mcu_level(channel->level); + param->tier = 0; + + param->log2_max_poc = 10; + param->log2_max_frame_num = 4; + param->temporal_mvp_enable = 1; + + param->dbf_ovr_en = 1; + param->rdo_cost_mode = 1; + param->custom_lda = 1; + param->lf = 1; + param->lf_x_tile = 1; + param->lf_x_slice = 1; + + param->src_bit_depth = 8; + + param->beta_offset = -1; + param->tc_offset = -1; + param->num_slices = 1; + param->me_range[0] = 8; + param->me_range[1] = 8; + param->me_range[2] = 16; + param->me_range[3] = 16; + param->max_cu_size = ilog2(SIZE_MACROBLOCK); + param->min_cu_size = ilog2(8); + param->max_tu_size = 2; + param->min_tu_size = 2; + param->max_transfo_depth_intra = 1; + param->max_transfo_depth_inter = 1; + + param->prefetch_auto = 0; + param->prefetch_mem_offset = 0; + param->prefetch_mem_size = 0; + + param->rate_control_mode = channel->frame_rc_enable ? + v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; + + param->cpb_size = v4l2_cpb_size_to_mcu(channel->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 = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); + param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + 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 = channel->gop_size; + param->freq_lt = 0; + param->gdr_mode = 0x00000000; + param->gop_length = channel->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(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; + size = allegro_encode_config_blob(blob->vaddr, ¶m); + + 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 stream_id) +{ + 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.stream_id = stream_id; + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t src_y, dma_addr_t src_uv, + u64 src_handle) +{ + struct mcu_msg_encode_frame msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + msg.encoding_options = AL_OPT_FORCE_LOAD; + msg.pps_qp = 26; /* qp are relative to 26 */ + msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ + /* src_handle is copied to mcu_msg_encode_frame_response */ + msg.src_handle = src_handle; + msg.src_y = to_codec_addr(dev, src_y); + msg.src_uv = to_codec_addr(dev, src_uv); + msg.stride = channel->stride; + msg.ep2 = 0x0; + msg.ep2_v = to_mcu_addr(dev, msg.ep2); + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, + unsigned long timeout_ms) +{ + unsigned long tmo; + + tmo = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (tmo == 0) + return -ETIMEDOUT; + + reinit_completion(&dev->init_complete); + return 0; +} + +static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, + enum mcu_msg_type type) +{ + struct allegro_dev *dev = channel->dev; + struct mcu_msg_push_buffers_internal *msg; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = 0; + size_t size; + struct allegro_buffer *al_buffer; + struct list_head *list; + int err; + + switch (type) { + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + list = &channel->buffers_reference; + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + list = &channel->buffers_intermediate; + break; + default: + return -EINVAL; + } + + list_for_each_entry(al_buffer, list, head) + num_buffers++; + size = struct_size(msg, buffer, num_buffers); + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->header.type = type; + msg->header.version = dev->fw_info->mailbox_version; + + msg->channel_id = channel->mcu_channel_id; + msg->num_buffers = num_buffers; + + buffer = msg->buffer; + list_for_each_entry(al_buffer, list, head) { + buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); + buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); + buffer->size = to_mcu_size(dev, al_buffer->size); + buffer++; + } + + err = allegro_mbox_send(dev->mbox_command, msg); + + kfree(msg); + return err; +} + +static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allocate_buffers_internal(struct allegro_channel *channel, + struct list_head *list, + size_t n, size_t size) +{ + struct allegro_dev *dev = channel->dev; + unsigned int i; + int err; + struct allegro_buffer *buffer, *tmp; + + for (i = 0; i < n; i++) { + buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + err = -ENOMEM; + goto err; + } + INIT_LIST_HEAD(&buffer->head); + + err = allegro_alloc_buffer(dev, buffer, size); + if (err) + goto err; + list_add(&buffer->head, list); + } + + return 0; + +err: + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } + return err; +} + +static void destroy_buffers_internal(struct allegro_channel *channel, + struct list_head *list) +{ + struct allegro_dev *dev = channel->dev; + struct allegro_buffer *buffer, *tmp; + + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } +} + +static void destroy_reference_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, &channel->buffers_reference); +} + +static void destroy_intermediate_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, + &channel->buffers_intermediate); +} + +static int allocate_intermediate_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_intermediate, + n, size); +} + +static int allocate_reference_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_reference, + n, PAGE_ALIGN(size)); +} + +static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_sps *sps; + ssize_t size; + unsigned int size_mb = SIZE_MACROBLOCK; + /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ + unsigned int crop_unit_x = 2; + unsigned int crop_unit_y = 2; + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); + sps->constraint_set0_flag = 0; + sps->constraint_set1_flag = 1; + sps->constraint_set2_flag = 0; + sps->constraint_set3_flag = 0; + sps->constraint_set4_flag = 0; + sps->constraint_set5_flag = 0; + sps->level_idc = nal_h264_level_from_v4l2(channel->level); + sps->seq_parameter_set_id = 0; + sps->log2_max_frame_num_minus4 = 0; + sps->pic_order_cnt_type = 0; + sps->log2_max_pic_order_cnt_lsb_minus4 = 6; + sps->max_num_ref_frames = 3; + sps->gaps_in_frame_num_value_allowed_flag = 0; + sps->pic_width_in_mbs_minus1 = + DIV_ROUND_UP(channel->width, size_mb) - 1; + sps->pic_height_in_map_units_minus1 = + DIV_ROUND_UP(channel->height, size_mb) - 1; + sps->frame_mbs_only_flag = 1; + sps->mb_adaptive_frame_field_flag = 0; + sps->direct_8x8_inference_flag = 1; + sps->frame_cropping_flag = + (channel->width % size_mb) || (channel->height % size_mb); + if (sps->frame_cropping_flag) { + sps->crop_left = 0; + sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; + sps->crop_top = 0; + sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; + } + sps->vui_parameters_present_flag = 1; + sps->vui.aspect_ratio_info_present_flag = 0; + sps->vui.overscan_info_present_flag = 0; + sps->vui.video_signal_type_present_flag = 1; + sps->vui.video_format = 1; + sps->vui.video_full_range_flag = 0; + sps->vui.colour_description_present_flag = 1; + sps->vui.colour_primaries = 5; + sps->vui.transfer_characteristics = 5; + sps->vui.matrix_coefficients = 5; + sps->vui.chroma_loc_info_present_flag = 1; + sps->vui.chroma_sample_loc_type_top_field = 0; + sps->vui.chroma_sample_loc_type_bottom_field = 0; + + sps->vui.timing_info_present_flag = 1; + sps->vui.num_units_in_tick = channel->framerate.denominator; + sps->vui.time_scale = 2 * channel->framerate.numerator; + + sps->vui.fixed_frame_rate_flag = 1; + sps->vui.nal_hrd_parameters_present_flag = 0; + sps->vui.vcl_hrd_parameters_present_flag = 1; + sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; + sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; + sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ + sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = + channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ + sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = + (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.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 bool allegro_channel_is_at_eos(struct allegro_channel *channel) +{ + bool is_at_eos = false; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_STOPPED: + is_at_eos = true; + break; + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + mutex_lock(&channel->shadow_list_lock); + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && + list_empty(&channel->source_shadow_list)) + is_at_eos = true; + mutex_unlock(&channel->shadow_list_lock); + break; + default: + break; + } + + return is_at_eos; +} + +static void allegro_channel_buf_done(struct allegro_channel *channel, + struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + if (allegro_channel_is_at_eos(channel)) { + buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&channel->fh, &eos_event); + + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + } + + v4l2_m2m_buf_done(buf, state); +} + +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->stream_id); + 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; + + 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 (msg->is_idr) { + len = allegro_h264_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) { + len = allegro_h264_write_pps(channel, 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 { + len = nal_h264_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) + allegro_channel_buf_done(channel, 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(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: intermediate buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->int_buffers_count, msg->int_buffers_size); + err = allocate_intermediate_buffers(channel, msg->int_buffers_count, + msg->int_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate intermediate buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_intermediate(channel); + if (err) + goto out; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: reference buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->rec_buffers_count, msg->rec_buffers_size); + err = allocate_reference_buffers(channel, msg->rec_buffers_count, + msg->rec_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate reference buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_reference(channel); + if (err) + goto out; + +out: + channel->error = err; + complete(&channel->completion); + + /* Handled successfully, error is passed via channel->error */ + return 0; +} + +static int +allegro_handle_destroy_channel(struct allegro_dev *dev, + struct mcu_msg_destroy_channel_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "user %d: vcu destroyed channel %d\n", + channel->user_id, channel->mcu_channel_id); + complete(&channel->completion); + + return 0; +} + +static int +allegro_handle_encode_frame(struct allegro_dev *dev, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + allegro_channel_finish_frame(channel, msg); + + return 0; +} + +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) +{ + switch (msg->header.type) { + case MCU_MSG_TYPE_INIT: + allegro_handle_init(dev, &msg->init); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_handle_create_channel(dev, &msg->create_channel); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_handle_destroy_channel(dev, &msg->destroy_channel); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_handle_encode_frame(dev, &msg->encode_frame); + break; + default: + v4l2_warn(&dev->v4l2_dev, + "%s: unknown message %s\n", + __func__, msg_type_name(msg->header.type)); + break; + } +} + +static irqreturn_t allegro_hardirq(int irq, void *data) +{ + struct allegro_dev *dev = data; + unsigned int status; + + regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); + if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) + return IRQ_NONE; + + regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t allegro_irq_thread(int irq, void *data) +{ + struct allegro_dev *dev = data; + + allegro_mbox_notify(dev->mbox_status); + + return IRQ_HANDLED; +} + +static void allegro_copy_firmware(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err = 0; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy mcu firmware (%zu B) to SRAM\n", size); + err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); + if (err) + v4l2_err(&dev->v4l2_dev, + "failed to copy firmware: %d\n", err); +} + +static void allegro_copy_fw_codec(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err; + dma_addr_t icache_offset, dcache_offset; + + /* + * The downstream allocates 600 KB for the codec firmware to have some + * extra space for "possible extensions." My tests were fine with + * allocating just enough memory for the actual firmware, but I am not + * sure that the firmware really does not use the remaining space. + */ + err = allegro_alloc_buffer(dev, &dev->firmware, size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for firmware\n", size); + return; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy codec firmware (%zd B) to phys %pad\n", + size, &dev->firmware.paddr); + memcpy(dev->firmware.vaddr, buf, size); + + regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, + upper_32_bits(dev->firmware.paddr)); + + icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "icache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(icache_offset), lower_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, + upper_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, + lower_32_bits(icache_offset)); + + dcache_offset = + (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "dcache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, + upper_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, + lower_32_bits(dcache_offset)); +} + +static void allegro_free_fw_codec(struct allegro_dev *dev) +{ + allegro_free_buffer(dev, &dev->firmware); +} + +/* + * Control functions for the MCU + */ + +static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); +} + +static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); +} + +static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status != AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static int allegro_mcu_start(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + int err; + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); + if (err) + return err; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status == AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + return 0; +} + +static int allegro_mcu_reset(struct allegro_dev *dev) +{ + int err; + + /* + * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu + * does not go to sleep after the reset. + */ + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + err = regmap_write(dev->regmap, + AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); + if (err < 0) + return err; + + err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); + if (err < 0) + return err; + + return allegro_mcu_wait_for_sleep(dev); +} + +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + +static void allegro_destroy_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + + if (channel_exists(channel)) { + reinit_completion(&channel->completion); + allegro_mcu_send_destroy_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + v4l2_warn(&dev->v4l2_dev, + "channel %d: timeout while destroying\n", + channel->mcu_channel_id); + + channel->mcu_channel_id = -1; + } + + destroy_intermediate_buffers(channel); + destroy_reference_buffers(channel); + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); + + if (channel->user_id != -1) { + clear_bit(channel->user_id, &dev->channel_user_ids); + channel->user_id = -1; + } +} + +/* + * Create the MCU channel + * + * After the channel has been created, the picture size, format, colorspace + * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be + * changed anymore. + * + * The channel can be created only once. The MCU will accept source buffers + * and stream buffers only after a channel has been created. + */ +static int allegro_create_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + enum v4l2_mpeg_video_h264_level min_level; + + 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)); + + min_level = select_minimum_h264_level(channel->width, channel->height); + if (channel->level < min_level) { + v4l2_warn(&dev->v4l2_dev, + "user %d: selected Level %s too low: increasing to Level %s\n", + channel->user_id, + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); + channel->level = min_level; + } + + 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_frame_rc_enable, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); + + reinit_completion(&channel->completion); + allegro_mcu_send_create_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + channel->error = -ETIMEDOUT; + if (channel->error) + goto err; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: accepting buffers\n", + channel->mcu_channel_id); + + return 0; + +err: + allegro_destroy_channel(channel); + + return channel->error; +} + +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; + channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + channel->bitrate = maximum_bitrate(channel->level); + channel->bitrate_peak = maximum_bitrate(channel->level); + channel->cpb_size = maximum_cpb_size(channel->level); + channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; +} + +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 (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && + V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return -EBUSY; + + 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); + + if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); + 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"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + channel->osequence = 0; + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + 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); + + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + 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); + } +} + +static const struct vb2_ops allegro_queue_ops = { + .queue_setup = allegro_queue_setup, + .buf_prepare = allegro_buf_prepare, + .buf_queue = allegro_buf_queue, + .start_streaming = allegro_start_streaming, + .stop_streaming = allegro_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int allegro_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int err; + struct allegro_channel *channel = priv; + + src_vq->dev = &channel->dev->plat_dev->dev; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = channel; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &allegro_queue_ops; + src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + src_vq->lock = &channel->dev->lock; + err = vb2_queue_init(src_vq); + if (err) + return err; + + dst_vq->dev = &channel->dev->plat_dev->dev; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = channel; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &allegro_queue_ops; + dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + dst_vq->lock = &channel->dev->lock; + err = vb2_queue_init(dst_vq); + if (err) + return err; + + return 0; +} + +static int allegro_clamp_qp(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *next_ctrl; + + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_p_frame_qp; + else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_b_frame_qp; + else + return 0; + + /* Modify range automatically updates the value */ + __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); + + return allegro_clamp_qp(channel, next_ctrl); +} + +static int allegro_clamp_bitrate(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; + struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + ctrl_bitrate_peak->val < ctrl_bitrate->val) + ctrl_bitrate_peak->val = ctrl_bitrate->val; + + return 0; +} + +static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + allegro_clamp_bitrate(channel, ctrl); + break; + } + + return 0; +} + +static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + channel->level = ctrl->val; + break; + 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_CPB_SIZE: + channel->cpb_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + channel->gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + allegro_clamp_qp(channel, ctrl); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops allegro_ctrl_ops = { + .try_ctrl = allegro_try_ctrl, + .s_ctrl = allegro_s_ctrl, +}; + +static int allegro_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + struct allegro_channel *channel = NULL; + struct v4l2_ctrl_handler *handler; + u64 mask; + int ret; + + 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_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); + channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate); + channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate_peak); + channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->cpb_size); + 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, channel->gop_size); + 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); + + 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); + + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; +} + +static int allegro_release(struct file *file) +{ + struct allegro_channel *channel = fh_to_channel(file->private_data); + + v4l2_m2m_ctx_release(channel->fh.m2m_ctx); + + list_del(&channel->list); + + v4l2_ctrl_handler_free(&channel->ctrl_handler); + + v4l2_fh_del(&channel->fh); + v4l2_fh_exit(&channel->fh); + + kfree(channel); + + return 0; +} + +static int allegro_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&dev->plat_dev->dev)); + + return 0; +} + +static int allegro_enum_fmt_vid(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->pixelformat = V4L2_PIX_FMT_H264; + 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); + + 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_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; + + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + return 0; +} + +static int allegro_channel_cmd_stop(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *dst_buf; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_ENCODING: + allegro_set_state(channel, ALLEGRO_STATE_DRAIN); + break; + default: + return 0; + } + + /* If there are output buffers, they must be encoded */ + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: continue encoding src buffers\n", + channel->mcu_channel_id); + return 0; + } + + /* If there are capture buffers, use it to signal EOS */ + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + if (dst_buf) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: signaling EOS\n", + channel->mcu_channel_id); + allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); + return 0; + } + + /* + * If there are no capture buffers, we need to wait for the next + * buffer to signal EOS. + */ + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", + channel->mcu_channel_id); + allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); + + return 0; +} + +static int allegro_channel_cmd_start(struct allegro_channel *channel) +{ + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_STOPPED: + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + break; + default: + return 0; + } + + 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; + + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + err = allegro_channel_cmd_stop(channel); + break; + case V4L2_ENC_CMD_START: + err = allegro_channel_cmd_start(channel); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int allegro_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + 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_try_fmt_vid_cap, + .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = allegro_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = allegro_encoder_cmd, + .vidioc_enum_framesizes = allegro_enum_framesizes, + + .vidioc_g_parm = allegro_g_parm, + .vidioc_s_parm = allegro_s_parm, + + .vidioc_subscribe_event = allegro_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations allegro_fops = { + .owner = THIS_MODULE, + .open = allegro_open, + .release = allegro_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int allegro_register_device(struct allegro_dev *dev) +{ + struct video_device *video_dev = &dev->video_dev; + + strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); + video_dev->fops = &allegro_fops; + video_dev->ioctl_ops = &allegro_ioctl_ops; + video_dev->release = video_device_release_empty; + video_dev->lock = &dev->lock; + video_dev->v4l2_dev = &dev->v4l2_dev; + video_dev->vfl_dir = VFL_DIR_M2M; + video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(video_dev, dev); + + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); +} + +static void allegro_device_run(void *priv) +{ + struct allegro_channel *channel = priv; + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + dma_addr_t src_y; + dma_addr_t src_uv; + dma_addr_t dst_addr; + unsigned long dst_size; + u64 src_handle; + u64 dst_handle; + + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); + dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, + dst_buf); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, + dst_handle); + + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + src_buf->sequence = channel->osequence++; + src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_uv = src_y + (channel->stride * channel->height); + src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, + src_buf); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); +} + +static const struct v4l2_m2m_ops allegro_m2m_ops = { + .device_run = allegro_device_run, +}; + +static int allegro_mcu_hw_init(struct allegro_dev *dev, + const struct fw_info *info) +{ + int err; + + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } + + allegro_mcu_enable_interrupts(dev); + + /* The mcu sends INIT after reset. */ + allegro_mcu_start(dev); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu did not send INIT after reset\n"); + err = -EIO; + goto err_disable_interrupts; + } + + err = allegro_alloc_buffer(dev, &dev->suballocator, + info->suballocator_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for suballocator\n", + info->suballocator_size); + goto err_reset_mcu; + } + + allegro_mcu_send_init(dev, dev->suballocator.paddr, + dev->suballocator.size); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu failed to configure sub-allocator\n"); + err = -EIO; + goto err_free_suballocator; + } + + return 0; + +err_free_suballocator: + allegro_free_buffer(dev, &dev->suballocator); +err_reset_mcu: + allegro_mcu_reset(dev); +err_disable_interrupts: + allegro_mcu_disable_interrupts(dev); + + return err; +} + +static int allegro_mcu_hw_deinit(struct allegro_dev *dev) +{ + int err; + + err = allegro_mcu_reset(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "mcu failed to enter sleep state\n"); + + err = allegro_mcu_disable_interrupts(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "failed to disable interrupts\n"); + + allegro_free_buffer(dev, &dev->suballocator); + + return 0; +} + +static void allegro_fw_callback(const struct firmware *fw, void *context) +{ + struct allegro_dev *dev = context; + const char *fw_codec_name = "al5e.fw"; + const struct firmware *fw_codec; + int err; + + if (!fw) + return; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting codec firmware '%s'\n", fw_codec_name); + err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); + if (err) + goto err_release_firmware; + + dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!dev->fw_info) { + v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); + goto err_release_firmware_codec; + } + + v4l2_info(&dev->v4l2_dev, + "using mcu firmware version '%s'\n", dev->fw_info->version); + + /* Ensure that the mcu is sleeping at the reset vector */ + err = allegro_mcu_reset(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); + goto err_release_firmware_codec; + } + + allegro_copy_firmware(dev, fw->data, fw->size); + allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); + + err = allegro_mcu_hw_init(dev, dev->fw_info); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); + goto err_free_fw_codec; + } + + dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); + goto err_mcu_hw_deinit; + } + + err = allegro_register_device(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); + goto err_m2m_release; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "allegro codec registered as /dev/video%d\n", + dev->video_dev.num); + + release_firmware(fw_codec); + release_firmware(fw); + + return; + +err_m2m_release: + v4l2_m2m_release(dev->m2m_dev); + dev->m2m_dev = NULL; +err_mcu_hw_deinit: + allegro_mcu_hw_deinit(dev); +err_free_fw_codec: + allegro_free_fw_codec(dev); +err_release_firmware_codec: + release_firmware(fw_codec); +err_release_firmware: + release_firmware(fw); +} + +static int allegro_firmware_request_nowait(struct allegro_dev *dev) +{ + const char *fw = "al5e_b.fw"; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting firmware '%s'\n", fw); + return request_firmware_nowait(THIS_MODULE, true, fw, + &dev->plat_dev->dev, GFP_KERNEL, dev, + allegro_fw_callback); +} + +static int allegro_probe(struct platform_device *pdev) +{ + struct allegro_dev *dev; + struct resource *res, *sram_res; + int ret; + int irq; + void __iomem *regs, *sram_regs; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->plat_dev = pdev; + init_completion(&dev->init_complete); + INIT_LIST_HEAD(&dev->channels); + + mutex_init(&dev->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + dev_err(&pdev->dev, + "regs resource missing from device tree\n"); + return -EINVAL; + } + regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "failed to map registers\n"); + return -ENOMEM; + } + dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &allegro_regmap_config); + if (IS_ERR(dev->regmap)) { + dev_err(&pdev->dev, "failed to init regmap\n"); + return PTR_ERR(dev->regmap); + } + + sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!sram_res) { + dev_err(&pdev->dev, + "sram resource missing from device tree\n"); + return -EINVAL; + } + sram_regs = devm_ioremap(&pdev->dev, + sram_res->start, + resource_size(sram_res)); + if (!sram_regs) { + dev_err(&pdev->dev, "failed to map sram\n"); + return -ENOMEM; + } + dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, + &allegro_sram_config); + if (IS_ERR(dev->sram)) { + dev_err(&pdev->dev, "failed to init sram\n"); + return PTR_ERR(dev->sram); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_threaded_irq(&pdev->dev, irq, + allegro_hardirq, + allegro_irq_thread, + IRQF_SHARED, dev_name(&pdev->dev), dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dev); + + ret = allegro_firmware_request_nowait(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to request firmware: %d\n", ret); + return ret; + } + + return 0; +} + +static int allegro_remove(struct platform_device *pdev) +{ + struct allegro_dev *dev = platform_get_drvdata(pdev); + + video_unregister_device(&dev->video_dev); + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + allegro_mcu_hw_deinit(dev); + allegro_free_fw_codec(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +static const struct of_device_id allegro_dt_ids[] = { + { .compatible = "allegro,al5e-1.1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, allegro_dt_ids); + +static struct platform_driver allegro_driver = { + .probe = allegro_probe, + .remove = allegro_remove, + .driver = { + .name = "allegro", + .of_match_table = of_match_ptr(allegro_dt_ids), + }, +}; + +module_platform_driver(allegro_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Tretter "); +MODULE_DESCRIPTION("Allegro DVT encoder driver"); diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c new file mode 100644 index 000000000000..9286d2162377 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Helper functions for handling messages that are send via mailbox to the + * Allegro VCU firmware. + */ + +#include +#include +#include +#include +#include + +#include "allegro-mail.h" + +const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} +EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } + + return i * sizeof(*dst); +} + +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + u32 pixelformat = param->codec; + + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + 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) | + FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + dst[i++] = val; + + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } + + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; + + return i * sizeof(*dst); +} + +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } + + return 0; +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = msg->num_buffers; + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->stream_id); + dst[i++] = upper_32_bits(msg->stream_id); + + 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->stream_id = src[i++]; + msg->stream_id |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (header->type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h new file mode 100644 index 000000000000..486ecb12b098 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Allegro VCU firmware mailbox mail definitions + */ + +#ifndef ALLEGRO_MAIL_H +#define ALLEGRO_MAIL_H + +#include + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, +}; + +const char *msg_type_name(enum mcu_msg_type type); + +struct mcu_msg_header { + enum mcu_msg_type type; + enum mcu_msg_version version; +}; + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +}; + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +}; + +struct create_channel_param { + enum mcu_msg_version version; + u32 layer_id; + u16 width; + u16 height; + u32 videomode; + u32 format; + u32 colorspace; + u32 src_mode; + u32 src_bit_depth; + u8 profile; + u16 constraint_set_flags; + u32 codec; + u16 level; + u16 tier; + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 enable_reordering; + u32 dbf_ovr_en; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 custom_lda; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_idr; + u32 freq_lt; + u32 gdr_mode; + u16 gop_length; + u8 num_b; + u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; + + u32 subframe_latency; + u32 lda_control_mode; + u32 unknown41; + + u32 lda_factors[6]; + + u32 max_num_merge_cand; +}; + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; + u32 ep1_addr; +}; + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +}; + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +}; + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + size_t num_buffers; + struct mcu_msg_push_buffers_internal_buffer buffer[]; +}; + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 stream_id; +}; + +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 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; +}; + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + +#endif diff --git a/drivers/media/platform/allegro-dvt/nal-h264.c b/drivers/media/platform/allegro-dvt/nal-h264.c new file mode 100644 index 000000000000..bd48b8883572 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include "nal-h264.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, +}; + +struct rbsp; + +struct nal_h264_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_h264_ops *ops; + int error; +}; + +static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_h264_ops *ops) +{ + if (!rbsp) + return; + + rbsp->data = addr; + rbsp->size = size; + rbsp->pos = 0; + rbsp->ops = ops; + rbsp->error = 0; +} + +/** + * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile + * @profile: the profile as &enum v4l2_mpeg_video_h264_profile + * + * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified + * in Rec. ITU-T H.264 (04/2017) A.2. + * + * Return: the profile_idc for the passed level + */ +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return 88; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + default: + return -EINVAL; + } +} + +/** + * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level + * @level: the level as &enum v4l2_mpeg_video_h264_level + * + * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in + * Rec. ITU-T H.264 (04/2017) A.3.2. + * + * Return: the level_idc for the passed level + */ +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 9; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return 51; + default: + return -EINVAL; + } +} + +static 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); +} + +static struct nal_h264_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; +} + +static struct nal_h264_ops read = { + .rbsp_bit = __rbsp_read_bit, + .rbsp_bits = rbsp_read_bits, + .rbsp_uev = rbsp_read_uev, + .rbsp_sev = rbsp_read_sev, +}; + +static inline void rbsp_bit(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); +} + +static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); +} + +static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); +} + +static inline void rbsp_sev(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); +} + +static void nal_h264_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); +} + +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); + + nal_h264_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); + + nal_h264_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); + + nal_h264_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); + + nal_h264_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); + + nal_h264_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); + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_filler); diff --git a/drivers/media/platform/allegro-dvt/nal-h264.h b/drivers/media/platform/allegro-dvt/nal-h264.h new file mode 100644 index 000000000000..2ba7cbced7a5 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_H264_H__ +#define __NAL_H264_H__ + +#include +#include + +/** + * struct nal_h264_hdr_parameters - HDR parameters + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. + */ +struct nal_h264_hrd_parameters { + unsigned int cpb_cnt_minus1; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + struct { + int bit_rate_value_minus1[16]; + int cpb_size_value_minus1[16]; + unsigned int cbr_flag[16]; + }; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + unsigned int time_offset_length; +}; + +/** + * struct nal_h264_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.264 (04/2017) E.1.1 VUI parameters syntax. + */ +struct nal_h264_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int fixed_frame_rate_flag; + }; + unsigned int nal_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters nal_hrd_parameters; + unsigned int vcl_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters vcl_hrd_parameters; + unsigned int low_delay_hrd_flag; + unsigned int pic_struct_present_flag; + unsigned int bitstream_restriction_flag; + struct { + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log21_max_mv_length_vertical; + unsigned int max_num_reorder_frames; + unsigned int max_dec_frame_buffering; + }; +}; + +/** + * struct nal_h264_sps - Sequence parameter set + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. + */ +struct nal_h264_sps { + unsigned int profile_idc; + unsigned int constraint_set0_flag; + unsigned int constraint_set1_flag; + unsigned int constraint_set2_flag; + unsigned int constraint_set3_flag; + unsigned int constraint_set4_flag; + unsigned int constraint_set5_flag; + unsigned int reserved_zero_2bits; + unsigned int level_idc; + unsigned int seq_parameter_set_id; + struct { + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int qpprime_y_zero_transform_bypass_flag; + unsigned int seq_scaling_matrix_present_flag; + }; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + union { + unsigned int log2_max_pic_order_cnt_lsb_minus4; + struct { + unsigned int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_pic_order_cnt_cycle; + int offset_for_ref_frame[255]; + }; + }; + unsigned int max_num_ref_frames; + unsigned int gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + unsigned int frame_mbs_only_flag; + unsigned int mb_adaptive_frame_field_flag; + unsigned int direct_8x8_inference_flag; + unsigned int frame_cropping_flag; + struct { + unsigned int crop_left; + unsigned int crop_right; + unsigned int crop_top; + unsigned int crop_bottom; + }; + unsigned int vui_parameters_present_flag; + struct nal_h264_vui_parameters vui; +}; + +/** + * struct nal_h264_pps - Picture parameter set + * + * C struct representation of the picture parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. + */ +struct nal_h264_pps { + unsigned int pic_parameter_set_id; + unsigned int seq_parameter_set_id; + unsigned int entropy_coding_mode_flag; + unsigned int bottom_field_pic_order_in_frame_present_flag; + unsigned int num_slice_groups_minus1; + unsigned int slice_group_map_type; + union { + unsigned int run_length_minus1[8]; + struct { + unsigned int top_left[8]; + unsigned int bottom_right[8]; + }; + struct { + unsigned int slice_group_change_direction_flag; + unsigned int slice_group_change_rate_minus1; + }; + struct { + unsigned int pic_size_in_map_units_minus1; + unsigned int slice_group_id[8]; + }; + }; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + unsigned int deblocking_filter_control_present_flag; + unsigned int constrained_intra_pred_flag; + unsigned int redundant_pic_cnt_present_flag; + struct { + unsigned int transform_8x8_mode_flag; + unsigned int pic_scaling_matrix_present_flag; + int second_chroma_qp_index_offset; + }; +}; + +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); + +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps); +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n); +void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); + +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps); +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n); +void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); + +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_H264_H__ */ diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index e8996b1c3b35..ca59986b20f8 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -20,8 +20,6 @@ menuconfig STAGING_MEDIA if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order -source "drivers/staging/media/allegro-dvt/Kconfig" - source "drivers/staging/media/atomisp/Kconfig" source "drivers/staging/media/hantro/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 24b5873ff760..716929a1a313 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ diff --git a/drivers/staging/media/allegro-dvt/Kconfig b/drivers/staging/media/allegro-dvt/Kconfig deleted file mode 100644 index 6b7107d9995c..000000000000 --- a/drivers/staging/media/allegro-dvt/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VIDEO_ALLEGRO_DVT - tristate "Allegro DVT Video IP Core" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_ZYNQMP || COMPILE_TEST - select V4L2_MEM2MEM_DEV - select VIDEOBUF2_DMA_CONTIG - select REGMAP - 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/staging/media/allegro-dvt/Makefile b/drivers/staging/media/allegro-dvt/Makefile deleted file mode 100644 index 8e306dcdc55c..000000000000 --- a/drivers/staging/media/allegro-dvt/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -allegro-objs := allegro-core.o nal-h264.o allegro-mail.o - -obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/staging/media/allegro-dvt/TODO b/drivers/staging/media/allegro-dvt/TODO deleted file mode 100644 index 99e19be0e45a..000000000000 --- a/drivers/staging/media/allegro-dvt/TODO +++ /dev/null @@ -1,4 +0,0 @@ -TODO: - -- This driver is waiting for the stateful encoder spec and corresponding - v4l2-compliance tests to be finalized. diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c deleted file mode 100644 index 640451134072..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ /dev/null @@ -1,3226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Allegro DVT video encoder driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "allegro-mail.h" -#include "nal-h264.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_64 - -#define SIZE_MACROBLOCK 16 - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -struct allegro_buffer { - void *vaddr; - dma_addr_t paddr; - size_t size; - struct list_head head; -}; - -struct allegro_dev; -struct allegro_channel; - -struct allegro_mbox { - struct allegro_dev *dev; - unsigned int head; - unsigned int tail; - unsigned int data; - size_t size; - /* protect mailbox from simultaneous accesses */ - struct mutex lock; -}; - -struct allegro_dev { - struct v4l2_device v4l2_dev; - struct video_device video_dev; - struct v4l2_m2m_dev *m2m_dev; - struct platform_device *plat_dev; - - /* mutex protecting vb2_queue structure */ - struct mutex lock; - - struct regmap *regmap; - struct regmap *sram; - - const struct fw_info *fw_info; - struct allegro_buffer firmware; - struct allegro_buffer suballocator; - - struct completion init_complete; - - /* The mailbox interface */ - struct allegro_mbox *mbox_command; - struct allegro_mbox *mbox_status; - - /* - * The downstream driver limits the users to 64 users, thus I can use - * a bitfield for the user_ids that are in use. See also user_id in - * struct allegro_channel. - */ - unsigned long channel_user_ids; - struct list_head channels; -}; - -static struct regmap_config allegro_regmap_config = { - .name = "regmap", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0xfff, - .cache_type = REGCACHE_NONE, -}; - -static struct regmap_config allegro_sram_config = { - .name = "sram", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x7fff, - .cache_type = REGCACHE_NONE, -}; - -enum allegro_state { - ALLEGRO_STATE_ENCODING, - ALLEGRO_STATE_DRAIN, - ALLEGRO_STATE_WAIT_FOR_BUFFER, - ALLEGRO_STATE_STOPPED, -}; - -#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; - enum v4l2_mpeg_video_h264_profile profile; - enum v4l2_mpeg_video_h264_level level; - unsigned int sizeimage_encoded; - unsigned int csequence; - - bool frame_rc_enable; - unsigned int bitrate; - unsigned int bitrate_peak; - unsigned int cpb_size; - unsigned int gop_size; - - struct allegro_buffer config_blob; - - unsigned int num_ref_idx_l0; - unsigned int num_ref_idx_l1; - - 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_frame_rc_enable; - struct { /* video bitrate mode control cluster */ - struct v4l2_ctrl *mpeg_video_bitrate_mode; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_bitrate_peak; - }; - struct v4l2_ctrl *mpeg_video_cpb_size; - struct v4l2_ctrl *mpeg_video_gop_size; - - /* user_id is used to identify the channel during CREATE_CHANNEL */ - /* not sure, what to set here and if this is actually required */ - int user_id; - /* channel_id is set by the mcu and used by all later commands */ - int mcu_channel_id; - - struct list_head buffers_reference; - struct list_head buffers_intermediate; - - struct list_head source_shadow_list; - struct list_head stream_shadow_list; - /* protect shadow lists of buffers passed to firmware */ - struct mutex shadow_list_lock; - - struct list_head list; - struct completion completion; - - unsigned int error; - enum allegro_state state; -}; - -static inline int -allegro_set_state(struct allegro_channel *channel, enum allegro_state state) -{ - channel->state = state; - - return 0; -} - -static inline enum allegro_state -allegro_get_state(struct allegro_channel *channel) -{ - return channel->state; -} - -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 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 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 const struct fw_info * -allegro_get_firmware_info(struct allegro_dev *dev, - const struct firmware *fw, - const struct firmware *fw_codec) -{ - int i; - unsigned int id = fw->size; - unsigned int id_codec = fw_codec->size; - - for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) - if (supported_firmware[i].id == id && - supported_firmware[i].id_codec == id_codec) - return &supported_firmware[i]; - - return NULL; -} - -/* - * Buffers that are used internally by the MCU. - */ - -static int allegro_alloc_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer, size_t size) -{ - buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, - &buffer->paddr, GFP_KERNEL); - if (!buffer->vaddr) - return -ENOMEM; - buffer->size = size; - - return 0; -} - -static void allegro_free_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer) -{ - if (buffer->vaddr) { - dma_free_coherent(&dev->plat_dev->dev, buffer->size, - buffer->vaddr, buffer->paddr); - buffer->vaddr = NULL; - buffer->size = 0; - } -} - -/* - * Mailbox interface to send messages to the MCU. - */ - -static void allegro_mcu_interrupt(struct allegro_dev *dev); -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg); - -static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, - unsigned int base, size_t size) -{ - struct allegro_mbox *mbox; - - mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); - if (!mbox) - return ERR_PTR(-ENOMEM); - - mbox->dev = dev; - - mbox->head = base; - mbox->tail = base + 0x4; - mbox->data = base + 0x8; - mbox->size = size; - mutex_init(&mbox->lock); - - regmap_write(dev->sram, mbox->head, 0); - regmap_write(dev->sram, mbox->tail, 0); - - return mbox; -} - -static int allegro_mbox_write(struct allegro_mbox *mbox, - const u32 *src, size_t size) -{ - struct regmap *sram = mbox->dev->sram; - unsigned int tail; - size_t size_no_wrap; - int err = 0; - int stride = regmap_get_reg_stride(sram); - - if (!src) - return -EINVAL; - - if (size > mbox->size) - return -EINVAL; - - mutex_lock(&mbox->lock); - regmap_read(sram, mbox->tail, &tail); - if (tail > mbox->size) { - err = -EIO; - goto out; - } - size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(sram, mbox->data + tail, - src, size_no_wrap / stride); - regmap_bulk_write(sram, mbox->data, - src + (size_no_wrap / sizeof(*src)), - (size - size_no_wrap) / stride); - regmap_write(sram, mbox->tail, (tail + size) % mbox->size); - -out: - mutex_unlock(&mbox->lock); - - return err; -} - -static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, - u32 *dst, size_t nbyte) -{ - struct { - u16 length; - u16 type; - } __attribute__ ((__packed__)) *header; - struct regmap *sram = mbox->dev->sram; - unsigned int head; - ssize_t size; - size_t body_no_wrap; - int stride = regmap_get_reg_stride(sram); - - regmap_read(sram, mbox->head, &head); - if (head > mbox->size) - return -EIO; - - /* Assume that the header does not wrap. */ - regmap_bulk_read(sram, mbox->data + head, - dst, sizeof(*header) / stride); - header = (void *)dst; - size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) - return -EIO; - if (size > nbyte) - return -EINVAL; - - /* - * The message might wrap within the mailbox. If the message does not - * wrap, the first read will read the entire message, otherwise the - * first read will read message until the end of the mailbox and the - * second read will read the remaining bytes from the beginning of the - * mailbox. - * - * Skip the header, as was already read to get the size of the body. - */ - body_no_wrap = min((size_t)header->length, - (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(sram, mbox->data + head + sizeof(*header), - dst + (sizeof(*header) / sizeof(*dst)), - body_no_wrap / stride); - regmap_bulk_read(sram, mbox->data, - dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), - (header->length - body_no_wrap) / stride); - - regmap_write(sram, mbox->head, (head + size) % mbox->size); - - return size; -} - -/** - * allegro_mbox_send() - Send a message via the mailbox - * @mbox: the mailbox which is used to send the message - * @msg: the message to send - */ -static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) -{ - struct allegro_dev *dev = mbox->dev; - ssize_t size; - int err; - u32 *tmp; - - tmp = kzalloc(mbox->size, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto out; - } - - size = allegro_encode_mail(tmp, msg); - - err = allegro_mbox_write(mbox, tmp, size); - kfree(tmp); - if (err) - goto out; - - allegro_mcu_interrupt(dev); - -out: - return err; -} - -/** - * allegro_mbox_notify() - Notify the mailbox about a new message - * @mbox: The allegro_mbox to notify - */ -static void allegro_mbox_notify(struct allegro_mbox *mbox) -{ - struct allegro_dev *dev = mbox->dev; - union mcu_msg_response *msg; - ssize_t size; - u32 *tmp; - int err; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return; - - msg->header.version = dev->fw_info->mailbox_version; - - tmp = kmalloc(mbox->size, GFP_KERNEL); - if (!tmp) - goto out; - - size = allegro_mbox_read(mbox, tmp, mbox->size); - if (size < 0) - goto out; - - err = allegro_decode_mail(msg, tmp); - if (err) - goto out; - - allegro_handle_message(dev, msg); - -out: - kfree(tmp); - kfree(msg); -} - -static void allegro_mcu_send_init(struct allegro_dev *dev, - dma_addr_t suballoc_dma, size_t suballoc_size) -{ - struct mcu_msg_init_request msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.version = dev->fw_info->mailbox_version; - - msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); - msg.suballoc_size = to_mcu_size(dev, suballoc_size); - - /* disable L2 cache */ - msg.l2_cache[0] = -1; - msg.l2_cache[1] = -1; - msg.l2_cache[2] = -1; - - allegro_mbox_send(dev->mbox_command, &msg); -} - -static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_NV12: - /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ - return 0x100 | 0x88; - default: - return -EINVAL; - } -} - -static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) -{ - switch (colorspace) { - case V4L2_COLORSPACE_REC709: - return 2; - case V4L2_COLORSPACE_SMPTE170M: - return 3; - case V4L2_COLORSPACE_SMPTE240M: - return 4; - case V4L2_COLORSPACE_SRGB: - return 7; - default: - /* UNKNOWN */ - return 0; - } -} - -static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) -{ - switch (profile) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - default: - return 66; - } -} - -static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 10; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 20; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 30; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 40; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 51; - } -} - -static 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 int fill_create_channel_param(struct allegro_channel *channel, - struct create_channel_param *param) -{ - int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); - int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); - int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); - int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); - - 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->profile = v4l2_profile_to_mcu_profile(channel->profile); - param->constraint_set_flags = BIT(1); - param->codec = channel->codec; - param->level = v4l2_level_to_mcu_level(channel->level); - param->tier = 0; - - param->log2_max_poc = 10; - param->log2_max_frame_num = 4; - param->temporal_mvp_enable = 1; - - param->dbf_ovr_en = 1; - param->rdo_cost_mode = 1; - param->custom_lda = 1; - param->lf = 1; - param->lf_x_tile = 1; - param->lf_x_slice = 1; - - param->src_bit_depth = 8; - - param->beta_offset = -1; - param->tc_offset = -1; - param->num_slices = 1; - param->me_range[0] = 8; - param->me_range[1] = 8; - param->me_range[2] = 16; - param->me_range[3] = 16; - param->max_cu_size = ilog2(SIZE_MACROBLOCK); - param->min_cu_size = ilog2(8); - param->max_tu_size = 2; - param->min_tu_size = 2; - param->max_transfo_depth_intra = 1; - param->max_transfo_depth_inter = 1; - - param->prefetch_auto = 0; - param->prefetch_mem_offset = 0; - param->prefetch_mem_size = 0; - - param->rate_control_mode = channel->frame_rc_enable ? - v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; - - param->cpb_size = v4l2_cpb_size_to_mcu(channel->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 = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); - param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); - 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 = channel->gop_size; - param->freq_lt = 0; - param->gdr_mode = 0x00000000; - param->gop_length = channel->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(¶m, 0, sizeof(param)); - fill_create_channel_param(channel, ¶m); - allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); - param.version = dev->fw_info->mailbox_version; - size = allegro_encode_config_blob(blob->vaddr, ¶m); - - 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 stream_id) -{ - 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.stream_id = stream_id; - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, - struct allegro_channel *channel, - dma_addr_t src_y, dma_addr_t src_uv, - u64 src_handle) -{ - struct mcu_msg_encode_frame msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - msg.encoding_options = AL_OPT_FORCE_LOAD; - msg.pps_qp = 26; /* qp are relative to 26 */ - msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ - /* src_handle is copied to mcu_msg_encode_frame_response */ - msg.src_handle = src_handle; - msg.src_y = to_codec_addr(dev, src_y); - msg.src_uv = to_codec_addr(dev, src_uv); - msg.stride = channel->stride; - msg.ep2 = 0x0; - msg.ep2_v = to_mcu_addr(dev, msg.ep2); - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, - unsigned long timeout_ms) -{ - unsigned long tmo; - - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) - return -ETIMEDOUT; - - reinit_completion(&dev->init_complete); - return 0; -} - -static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, - enum mcu_msg_type type) -{ - struct allegro_dev *dev = channel->dev; - struct mcu_msg_push_buffers_internal *msg; - struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = 0; - size_t size; - struct allegro_buffer *al_buffer; - struct list_head *list; - int err; - - switch (type) { - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - list = &channel->buffers_reference; - break; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - list = &channel->buffers_intermediate; - break; - default: - return -EINVAL; - } - - list_for_each_entry(al_buffer, list, head) - num_buffers++; - size = struct_size(msg, buffer, num_buffers); - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->header.type = type; - msg->header.version = dev->fw_info->mailbox_version; - - msg->channel_id = channel->mcu_channel_id; - msg->num_buffers = num_buffers; - - buffer = msg->buffer; - list_for_each_entry(al_buffer, list, head) { - buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); - buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); - buffer->size = to_mcu_size(dev, al_buffer->size); - buffer++; - } - - err = allegro_mbox_send(dev->mbox_command, msg); - - kfree(msg); - return err; -} - -static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allocate_buffers_internal(struct allegro_channel *channel, - struct list_head *list, - size_t n, size_t size) -{ - struct allegro_dev *dev = channel->dev; - unsigned int i; - int err; - struct allegro_buffer *buffer, *tmp; - - for (i = 0; i < n; i++) { - buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - err = -ENOMEM; - goto err; - } - INIT_LIST_HEAD(&buffer->head); - - err = allegro_alloc_buffer(dev, buffer, size); - if (err) - goto err; - list_add(&buffer->head, list); - } - - return 0; - -err: - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } - return err; -} - -static void destroy_buffers_internal(struct allegro_channel *channel, - struct list_head *list) -{ - struct allegro_dev *dev = channel->dev; - struct allegro_buffer *buffer, *tmp; - - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } -} - -static void destroy_reference_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, &channel->buffers_reference); -} - -static void destroy_intermediate_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, - &channel->buffers_intermediate); -} - -static int allocate_intermediate_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_intermediate, - n, size); -} - -static int allocate_reference_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_reference, - n, PAGE_ALIGN(size)); -} - -static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, - void *dest, size_t n) -{ - struct allegro_dev *dev = channel->dev; - struct nal_h264_sps *sps; - ssize_t size; - unsigned int size_mb = SIZE_MACROBLOCK; - /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ - unsigned int crop_unit_x = 2; - unsigned int crop_unit_y = 2; - - sps = kzalloc(sizeof(*sps), GFP_KERNEL); - if (!sps) - return -ENOMEM; - - sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); - sps->constraint_set0_flag = 0; - sps->constraint_set1_flag = 1; - sps->constraint_set2_flag = 0; - sps->constraint_set3_flag = 0; - sps->constraint_set4_flag = 0; - sps->constraint_set5_flag = 0; - sps->level_idc = nal_h264_level_from_v4l2(channel->level); - sps->seq_parameter_set_id = 0; - sps->log2_max_frame_num_minus4 = 0; - sps->pic_order_cnt_type = 0; - sps->log2_max_pic_order_cnt_lsb_minus4 = 6; - sps->max_num_ref_frames = 3; - sps->gaps_in_frame_num_value_allowed_flag = 0; - sps->pic_width_in_mbs_minus1 = - DIV_ROUND_UP(channel->width, size_mb) - 1; - sps->pic_height_in_map_units_minus1 = - DIV_ROUND_UP(channel->height, size_mb) - 1; - sps->frame_mbs_only_flag = 1; - sps->mb_adaptive_frame_field_flag = 0; - sps->direct_8x8_inference_flag = 1; - sps->frame_cropping_flag = - (channel->width % size_mb) || (channel->height % size_mb); - if (sps->frame_cropping_flag) { - sps->crop_left = 0; - sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; - sps->crop_top = 0; - sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; - } - sps->vui_parameters_present_flag = 1; - sps->vui.aspect_ratio_info_present_flag = 0; - sps->vui.overscan_info_present_flag = 0; - sps->vui.video_signal_type_present_flag = 1; - sps->vui.video_format = 1; - sps->vui.video_full_range_flag = 0; - sps->vui.colour_description_present_flag = 1; - sps->vui.colour_primaries = 5; - sps->vui.transfer_characteristics = 5; - sps->vui.matrix_coefficients = 5; - sps->vui.chroma_loc_info_present_flag = 1; - sps->vui.chroma_sample_loc_type_top_field = 0; - sps->vui.chroma_sample_loc_type_bottom_field = 0; - - sps->vui.timing_info_present_flag = 1; - sps->vui.num_units_in_tick = channel->framerate.denominator; - sps->vui.time_scale = 2 * channel->framerate.numerator; - - sps->vui.fixed_frame_rate_flag = 1; - sps->vui.nal_hrd_parameters_present_flag = 0; - sps->vui.vcl_hrd_parameters_present_flag = 1; - sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; - sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; - sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ - sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = - channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ - sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = - (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.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 bool allegro_channel_is_at_eos(struct allegro_channel *channel) -{ - bool is_at_eos = false; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_STOPPED: - is_at_eos = true; - break; - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - mutex_lock(&channel->shadow_list_lock); - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && - list_empty(&channel->source_shadow_list)) - is_at_eos = true; - mutex_unlock(&channel->shadow_list_lock); - break; - default: - break; - } - - return is_at_eos; -} - -static void allegro_channel_buf_done(struct allegro_channel *channel, - struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) -{ - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - if (allegro_channel_is_at_eos(channel)) { - buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&channel->fh, &eos_event); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - } - - v4l2_m2m_buf_done(buf, state); -} - -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->stream_id); - 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; - - 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 (msg->is_idr) { - len = allegro_h264_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) { - len = allegro_h264_write_pps(channel, 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 { - len = nal_h264_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) - allegro_channel_buf_done(channel, 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(¶m, msg, channel->config_blob.vaddr); - allegro_free_buffer(channel->dev, &channel->config_blob); - if (err) - goto out; - - channel->num_ref_idx_l0 = param.num_ref_idx_l0; - channel->num_ref_idx_l1 = param.num_ref_idx_l1; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: intermediate buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->int_buffers_count, msg->int_buffers_size); - err = allocate_intermediate_buffers(channel, msg->int_buffers_count, - msg->int_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate intermediate buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_intermediate(channel); - if (err) - goto out; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: reference buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->rec_buffers_count, msg->rec_buffers_size); - err = allocate_reference_buffers(channel, msg->rec_buffers_count, - msg->rec_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate reference buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_reference(channel); - if (err) - goto out; - -out: - channel->error = err; - complete(&channel->completion); - - /* Handled successfully, error is passed via channel->error */ - return 0; -} - -static int -allegro_handle_destroy_channel(struct allegro_dev *dev, - struct mcu_msg_destroy_channel_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "user %d: vcu destroyed channel %d\n", - channel->user_id, channel->mcu_channel_id); - complete(&channel->completion); - - return 0; -} - -static int -allegro_handle_encode_frame(struct allegro_dev *dev, - struct mcu_msg_encode_frame_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - allegro_channel_finish_frame(channel, msg); - - return 0; -} - -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg) -{ - switch (msg->header.type) { - case MCU_MSG_TYPE_INIT: - allegro_handle_init(dev, &msg->init); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - allegro_handle_create_channel(dev, &msg->create_channel); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - allegro_handle_destroy_channel(dev, &msg->destroy_channel); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - allegro_handle_encode_frame(dev, &msg->encode_frame); - break; - default: - v4l2_warn(&dev->v4l2_dev, - "%s: unknown message %s\n", - __func__, msg_type_name(msg->header.type)); - break; - } -} - -static irqreturn_t allegro_hardirq(int irq, void *data) -{ - struct allegro_dev *dev = data; - unsigned int status; - - regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); - if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) - return IRQ_NONE; - - regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); - - return IRQ_WAKE_THREAD; -} - -static irqreturn_t allegro_irq_thread(int irq, void *data) -{ - struct allegro_dev *dev = data; - - allegro_mbox_notify(dev->mbox_status); - - return IRQ_HANDLED; -} - -static void allegro_copy_firmware(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err = 0; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy mcu firmware (%zu B) to SRAM\n", size); - err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); - if (err) - v4l2_err(&dev->v4l2_dev, - "failed to copy firmware: %d\n", err); -} - -static void allegro_copy_fw_codec(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err; - dma_addr_t icache_offset, dcache_offset; - - /* - * The downstream allocates 600 KB for the codec firmware to have some - * extra space for "possible extensions." My tests were fine with - * allocating just enough memory for the actual firmware, but I am not - * sure that the firmware really does not use the remaining space. - */ - err = allegro_alloc_buffer(dev, &dev->firmware, size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for firmware\n", size); - return; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy codec firmware (%zd B) to phys %pad\n", - size, &dev->firmware.paddr); - memcpy(dev->firmware.vaddr, buf, size); - - regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, - upper_32_bits(dev->firmware.paddr)); - - icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "icache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(icache_offset), lower_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, - upper_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, - lower_32_bits(icache_offset)); - - dcache_offset = - (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "dcache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, - upper_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, - lower_32_bits(dcache_offset)); -} - -static void allegro_free_fw_codec(struct allegro_dev *dev) -{ - allegro_free_buffer(dev, &dev->firmware); -} - -/* - * Control functions for the MCU - */ - -static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); -} - -static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); -} - -static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status != AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} - -static int allegro_mcu_start(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - int err; - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); - if (err) - return err; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status == AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - return 0; -} - -static int allegro_mcu_reset(struct allegro_dev *dev) -{ - int err; - - /* - * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu - * does not go to sleep after the reset. - */ - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - err = regmap_write(dev->regmap, - AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); - if (err < 0) - return err; - - err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); - if (err < 0) - return err; - - return allegro_mcu_wait_for_sleep(dev); -} - -static void allegro_mcu_interrupt(struct allegro_dev *dev) -{ - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); -} - -static void allegro_destroy_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - - if (channel_exists(channel)) { - reinit_completion(&channel->completion); - allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - v4l2_warn(&dev->v4l2_dev, - "channel %d: timeout while destroying\n", - channel->mcu_channel_id); - - channel->mcu_channel_id = -1; - } - - destroy_intermediate_buffers(channel); - destroy_reference_buffers(channel); - - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); - - if (channel->user_id != -1) { - clear_bit(channel->user_id, &dev->channel_user_ids); - channel->user_id = -1; - } -} - -/* - * Create the MCU channel - * - * After the channel has been created, the picture size, format, colorspace - * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be - * changed anymore. - * - * The channel can be created only once. The MCU will accept source buffers - * and stream buffers only after a channel has been created. - */ -static int allegro_create_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - enum v4l2_mpeg_video_h264_level min_level; - - 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)); - - min_level = select_minimum_h264_level(channel->width, channel->height); - if (channel->level < min_level) { - v4l2_warn(&dev->v4l2_dev, - "user %d: selected Level %s too low: increasing to Level %s\n", - channel->user_id, - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); - channel->level = min_level; - } - - 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_frame_rc_enable, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); - - reinit_completion(&channel->completion); - allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - channel->error = -ETIMEDOUT; - if (channel->error) - goto err; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: accepting buffers\n", - channel->mcu_channel_id); - - return 0; - -err: - allegro_destroy_channel(channel); - - return channel->error; -} - -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; - channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - channel->bitrate = maximum_bitrate(channel->level); - channel->bitrate_peak = maximum_bitrate(channel->level); - channel->cpb_size = maximum_cpb_size(channel->level); - channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; -} - -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 (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && - V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) - return -EBUSY; - - 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); - - if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); - 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"); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - channel->osequence = 0; - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - 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); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) - v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - 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); - } -} - -static const struct vb2_ops allegro_queue_ops = { - .queue_setup = allegro_queue_setup, - .buf_prepare = allegro_buf_prepare, - .buf_queue = allegro_buf_queue, - .start_streaming = allegro_start_streaming, - .stop_streaming = allegro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int allegro_queue_init(void *priv, - struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int err; - struct allegro_channel *channel = priv; - - src_vq->dev = &channel->dev->plat_dev->dev; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->drv_priv = channel; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->ops = &allegro_queue_ops; - src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - src_vq->lock = &channel->dev->lock; - err = vb2_queue_init(src_vq); - if (err) - return err; - - dst_vq->dev = &channel->dev->plat_dev->dev; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->drv_priv = channel; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->ops = &allegro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - dst_vq->lock = &channel->dev->lock; - err = vb2_queue_init(dst_vq); - if (err) - return err; - - return 0; -} - -static int allegro_clamp_qp(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *next_ctrl; - - if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_p_frame_qp; - else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_b_frame_qp; - else - return 0; - - /* Modify range automatically updates the value */ - __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); - - return allegro_clamp_qp(channel, next_ctrl); -} - -static int allegro_clamp_bitrate(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; - struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; - - if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - ctrl_bitrate_peak->val < ctrl_bitrate->val) - ctrl_bitrate_peak->val = ctrl_bitrate->val; - - return 0; -} - -static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - allegro_clamp_bitrate(channel, ctrl); - break; - } - - return 0; -} - -static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - channel->level = ctrl->val; - break; - 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_CPB_SIZE: - channel->cpb_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - channel->gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: - allegro_clamp_qp(channel, ctrl); - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops allegro_ctrl_ops = { - .try_ctrl = allegro_try_ctrl, - .s_ctrl = allegro_s_ctrl, -}; - -static int allegro_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - struct allegro_channel *channel = NULL; - struct v4l2_ctrl_handler *handler; - u64 mask; - int ret; - - 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_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); - channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate); - channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate_peak); - channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, - 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->cpb_size); - 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, channel->gop_size); - 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); - - 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); - - return 0; - -error: - v4l2_ctrl_handler_free(handler); - kfree(channel); - return ret; -} - -static int allegro_release(struct file *file) -{ - struct allegro_channel *channel = fh_to_channel(file->private_data); - - v4l2_m2m_ctx_release(channel->fh.m2m_ctx); - - list_del(&channel->list); - - v4l2_ctrl_handler_free(&channel->ctrl_handler); - - v4l2_fh_del(&channel->fh); - v4l2_fh_exit(&channel->fh); - - kfree(channel); - - return 0; -} - -static int allegro_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&dev->plat_dev->dev)); - - return 0; -} - -static int allegro_enum_fmt_vid(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - f->pixelformat = V4L2_PIX_FMT_NV12; - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - f->pixelformat = V4L2_PIX_FMT_H264; - 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); - - 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_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; - - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - return 0; -} - -static int allegro_channel_cmd_stop(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *dst_buf; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_ENCODING: - allegro_set_state(channel, ALLEGRO_STATE_DRAIN); - break; - default: - return 0; - } - - /* If there are output buffers, they must be encoded */ - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: continue encoding src buffers\n", - channel->mcu_channel_id); - return 0; - } - - /* If there are capture buffers, use it to signal EOS */ - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - if (dst_buf) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: signaling EOS\n", - channel->mcu_channel_id); - allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); - return 0; - } - - /* - * If there are no capture buffers, we need to wait for the next - * buffer to signal EOS. - */ - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", - channel->mcu_channel_id); - allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); - - return 0; -} - -static int allegro_channel_cmd_start(struct allegro_channel *channel) -{ - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_STOPPED: - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - break; - default: - return 0; - } - - 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; - - switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: - err = allegro_channel_cmd_stop(channel); - break; - case V4L2_ENC_CMD_START: - err = allegro_channel_cmd_start(channel); - break; - default: - err = -EINVAL; - break; - } - - return err; -} - -static int allegro_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - switch (fsize->pixel_format) { - 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_try_fmt_vid_cap, - .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, - .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, - - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_streamon = allegro_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = allegro_encoder_cmd, - .vidioc_enum_framesizes = allegro_enum_framesizes, - - .vidioc_g_parm = allegro_g_parm, - .vidioc_s_parm = allegro_s_parm, - - .vidioc_subscribe_event = allegro_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations allegro_fops = { - .owner = THIS_MODULE, - .open = allegro_open, - .release = allegro_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int allegro_register_device(struct allegro_dev *dev) -{ - struct video_device *video_dev = &dev->video_dev; - - strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); - video_dev->fops = &allegro_fops; - video_dev->ioctl_ops = &allegro_ioctl_ops; - video_dev->release = video_device_release_empty; - video_dev->lock = &dev->lock; - video_dev->v4l2_dev = &dev->v4l2_dev; - video_dev->vfl_dir = VFL_DIR_M2M; - video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - video_set_drvdata(video_dev, dev); - - return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); -} - -static void allegro_device_run(void *priv) -{ - struct allegro_channel *channel = priv; - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *src_buf; - struct vb2_v4l2_buffer *dst_buf; - dma_addr_t src_y; - dma_addr_t src_uv; - dma_addr_t dst_addr; - unsigned long dst_size; - u64 src_handle; - u64 dst_handle; - - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); - dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, - dst_buf); - allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, - dst_handle); - - src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); - src_buf->sequence = channel->osequence++; - src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_uv = src_y + (channel->stride * channel->height); - src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, - src_buf); - allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); - - v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); -} - -static const struct v4l2_m2m_ops allegro_m2m_ops = { - .device_run = allegro_device_run, -}; - -static int allegro_mcu_hw_init(struct allegro_dev *dev, - const struct fw_info *info) -{ - int err; - - dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, - info->mailbox_size); - dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, - info->mailbox_size); - if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { - v4l2_err(&dev->v4l2_dev, - "failed to initialize mailboxes\n"); - return -EIO; - } - - allegro_mcu_enable_interrupts(dev); - - /* The mcu sends INIT after reset. */ - allegro_mcu_start(dev); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu did not send INIT after reset\n"); - err = -EIO; - goto err_disable_interrupts; - } - - err = allegro_alloc_buffer(dev, &dev->suballocator, - info->suballocator_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for suballocator\n", - info->suballocator_size); - goto err_reset_mcu; - } - - allegro_mcu_send_init(dev, dev->suballocator.paddr, - dev->suballocator.size); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu failed to configure sub-allocator\n"); - err = -EIO; - goto err_free_suballocator; - } - - return 0; - -err_free_suballocator: - allegro_free_buffer(dev, &dev->suballocator); -err_reset_mcu: - allegro_mcu_reset(dev); -err_disable_interrupts: - allegro_mcu_disable_interrupts(dev); - - return err; -} - -static int allegro_mcu_hw_deinit(struct allegro_dev *dev) -{ - int err; - - err = allegro_mcu_reset(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "mcu failed to enter sleep state\n"); - - err = allegro_mcu_disable_interrupts(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "failed to disable interrupts\n"); - - allegro_free_buffer(dev, &dev->suballocator); - - return 0; -} - -static void allegro_fw_callback(const struct firmware *fw, void *context) -{ - struct allegro_dev *dev = context; - const char *fw_codec_name = "al5e.fw"; - const struct firmware *fw_codec; - int err; - - if (!fw) - return; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting codec firmware '%s'\n", fw_codec_name); - err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); - if (err) - goto err_release_firmware; - - dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!dev->fw_info) { - v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); - goto err_release_firmware_codec; - } - - v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", dev->fw_info->version); - - /* Ensure that the mcu is sleeping at the reset vector */ - err = allegro_mcu_reset(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); - goto err_release_firmware_codec; - } - - allegro_copy_firmware(dev, fw->data, fw->size); - allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - - err = allegro_mcu_hw_init(dev, dev->fw_info); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); - goto err_free_fw_codec; - } - - dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); - goto err_mcu_hw_deinit; - } - - err = allegro_register_device(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); - goto err_m2m_release; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "allegro codec registered as /dev/video%d\n", - dev->video_dev.num); - - release_firmware(fw_codec); - release_firmware(fw); - - return; - -err_m2m_release: - v4l2_m2m_release(dev->m2m_dev); - dev->m2m_dev = NULL; -err_mcu_hw_deinit: - allegro_mcu_hw_deinit(dev); -err_free_fw_codec: - allegro_free_fw_codec(dev); -err_release_firmware_codec: - release_firmware(fw_codec); -err_release_firmware: - release_firmware(fw); -} - -static int allegro_firmware_request_nowait(struct allegro_dev *dev) -{ - const char *fw = "al5e_b.fw"; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting firmware '%s'\n", fw); - return request_firmware_nowait(THIS_MODULE, true, fw, - &dev->plat_dev->dev, GFP_KERNEL, dev, - allegro_fw_callback); -} - -static int allegro_probe(struct platform_device *pdev) -{ - struct allegro_dev *dev; - struct resource *res, *sram_res; - int ret; - int irq; - void __iomem *regs, *sram_regs; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - dev->plat_dev = pdev; - init_completion(&dev->init_complete); - INIT_LIST_HEAD(&dev->channels); - - mutex_init(&dev->lock); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - if (!res) { - dev_err(&pdev->dev, - "regs resource missing from device tree\n"); - return -EINVAL; - } - regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - return -ENOMEM; - } - dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &allegro_regmap_config); - if (IS_ERR(dev->regmap)) { - dev_err(&pdev->dev, "failed to init regmap\n"); - return PTR_ERR(dev->regmap); - } - - sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); - if (!sram_res) { - dev_err(&pdev->dev, - "sram resource missing from device tree\n"); - return -EINVAL; - } - sram_regs = devm_ioremap(&pdev->dev, - sram_res->start, - resource_size(sram_res)); - if (!sram_regs) { - dev_err(&pdev->dev, "failed to map sram\n"); - return -ENOMEM; - } - dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, - &allegro_sram_config); - if (IS_ERR(dev->sram)) { - dev_err(&pdev->dev, "failed to init sram\n"); - return PTR_ERR(dev->sram); - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - ret = devm_request_threaded_irq(&pdev->dev, irq, - allegro_hardirq, - allegro_irq_thread, - IRQF_SHARED, dev_name(&pdev->dev), dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - platform_set_drvdata(pdev, dev); - - ret = allegro_firmware_request_nowait(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to request firmware: %d\n", ret); - return ret; - } - - return 0; -} - -static int allegro_remove(struct platform_device *pdev) -{ - struct allegro_dev *dev = platform_get_drvdata(pdev); - - video_unregister_device(&dev->video_dev); - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - allegro_mcu_hw_deinit(dev); - allegro_free_fw_codec(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - return 0; -} - -static const struct of_device_id allegro_dt_ids[] = { - { .compatible = "allegro,al5e-1.1" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, allegro_dt_ids); - -static struct platform_driver allegro_driver = { - .probe = allegro_probe, - .remove = allegro_remove, - .driver = { - .name = "allegro", - .of_match_table = of_match_ptr(allegro_dt_ids), - }, -}; - -module_platform_driver(allegro_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Tretter "); -MODULE_DESCRIPTION("Allegro DVT encoder driver"); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c deleted file mode 100644 index 9286d2162377..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ /dev/null @@ -1,543 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Helper functions for handling messages that are send via mailbox to the - * Allegro VCU firmware. - */ - -#include -#include -#include -#include -#include - -#include "allegro-mail.h" - -const char *msg_type_name(enum mcu_msg_type type) -{ - static char buf[9]; - - switch (type) { - case MCU_MSG_TYPE_INIT: - return "INIT"; - case MCU_MSG_TYPE_CREATE_CHANNEL: - return "CREATE_CHANNEL"; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - return "DESTROY_CHANNEL"; - case MCU_MSG_TYPE_ENCODE_FRAME: - return "ENCODE_FRAME"; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - return "PUT_STREAM_BUFFER"; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - return "PUSH_BUFFER_INTERMEDIATE"; - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - return "PUSH_BUFFER_REFERENCE"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", type); - return buf; - } -} -EXPORT_SYMBOL(msg_type_name); - -static ssize_t -allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) -{ - unsigned int i = 0; - enum mcu_msg_version version = msg->header.version; - - dst[i++] = msg->reserved0; - dst[i++] = msg->suballoc_dma; - dst[i++] = msg->suballoc_size; - dst[i++] = msg->l2_cache[0]; - dst[i++] = msg->l2_cache[1]; - dst[i++] = msg->l2_cache[2]; - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = -1; - dst[i++] = 0; - } - - return i * sizeof(*dst); -} - -static inline u32 settings_get_mcu_codec(struct create_channel_param *param) -{ - enum mcu_msg_version version = param->version; - u32 pixelformat = param->codec; - - if (version < MCU_MSG_VERSION_2019_2) { - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; - } - } else { - switch (pixelformat) { - 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) | - FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); - dst[i++] = val; - - val = 0; - val |= param->dbf_ovr_en ? BIT(2) : 0; - dst[i++] = val; - - if (version >= MCU_MSG_VERSION_2019_2) { - val = 0; - val |= param->custom_lda ? BIT(2) : 0; - val |= param->rdo_cost_mode ? BIT(20) : 0; - dst[i++] = val; - - val = 0; - val |= param->lf ? BIT(2) : 0; - val |= param->lf_x_tile ? BIT(3) : 0; - val |= param->lf_x_slice ? BIT(4) : 0; - dst[i++] = val; - } else { - val = 0; - dst[i++] = val; - } - - dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | - FIELD_PREP(GENMASK(7, 0), param->tc_offset); - dst[i++] = param->unknown11; - dst[i++] = param->unknown12; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->num_slices; - else - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | - FIELD_PREP(GENMASK(15, 0), param->num_slices); - dst[i++] = param->prefetch_mem_offset; - dst[i++] = param->prefetch_mem_size; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | - FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | - FIELD_PREP(GENMASK(15, 0), param->me_range[0]); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | - FIELD_PREP(GENMASK(15, 0), param->me_range[2]); - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | - FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | - FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | - FIELD_PREP(GENMASK(8, 0), param->max_cu_size); - dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | - FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); - dst[i++] = param->entropy_mode; - dst[i++] = param->wp_mode; - - dst[i++] = param->rate_control_mode; - dst[i++] = param->initial_rem_delay; - dst[i++] = param->cpb_size; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | - FIELD_PREP(GENMASK(15, 0), param->framerate); - dst[i++] = param->target_bitrate; - dst[i++] = param->max_bitrate; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | - FIELD_PREP(GENMASK(15, 0), param->initial_qp); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | - FIELD_PREP(GENMASK(15, 0), param->max_qp); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | - FIELD_PREP(GENMASK(15, 0), param->pb_delta); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | - FIELD_PREP(GENMASK(15, 0), param->golden_delta); - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->rate_control_option; - else - dst[i++] = 0; - - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = param->num_pixel; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | - FIELD_PREP(GENMASK(15, 0), param->max_psnr); - for (j = 0; j < 3; j++) - dst[i++] = param->maxpicturesize[j]; - } - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->gop_ctrl_mode; - else - dst[i++] = 0; - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); - dst[i++] = param->freq_idr; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->enable_lt; - dst[i++] = param->freq_lt; - dst[i++] = param->gdr_mode; - if (version < MCU_MSG_VERSION_2019_2) - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->tmpdqp; - - dst[i++] = param->subframe_latency; - dst[i++] = param->lda_control_mode; - if (version < MCU_MSG_VERSION_2019_2) - dst[i++] = param->unknown41; - - if (version >= MCU_MSG_VERSION_2019_2) { - for (j = 0; j < 6; j++) - dst[i++] = param->lda_factors[j]; - dst[i++] = param->max_num_merge_cand; - } - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) -{ - enum mcu_msg_version version = msg->header.version; - unsigned int i = 0; - - dst[i++] = msg->user_id; - - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = msg->blob_mcu_addr; - } else { - memcpy(&dst[i], msg->blob, msg->blob_size); - i += msg->blob_size / sizeof(*dst); - } - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = msg->ep1_addr; - - return i * sizeof(*dst); -} - -ssize_t allegro_decode_config_blob(struct create_channel_param *param, - struct mcu_msg_create_channel_response *msg, - u32 *src) -{ - enum mcu_msg_version version = msg->header.version; - - if (version >= MCU_MSG_VERSION_2019_2) { - param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); - param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); - } else { - param->num_ref_idx_l0 = msg->num_ref_idx_l0; - param->num_ref_idx_l1 = msg->num_ref_idx_l1; - } - - return 0; -} - -static ssize_t -allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) -{ - unsigned int i = 0; - - dst[i++] = msg->channel_id; - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) -{ - unsigned int i = 0; - struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = msg->num_buffers; - unsigned int j; - - dst[i++] = msg->channel_id; - - for (j = 0; j < num_buffers; j++) { - buffer = &msg->buffer[j]; - dst[i++] = buffer->dma_addr; - dst[i++] = buffer->mcu_addr; - dst[i++] = buffer->size; - } - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_put_stream_buffer(u32 *dst, - struct mcu_msg_put_stream_buffer *msg) -{ - unsigned int i = 0; - - dst[i++] = msg->channel_id; - dst[i++] = msg->dma_addr; - dst[i++] = msg->mcu_addr; - dst[i++] = msg->size; - dst[i++] = msg->offset; - dst[i++] = lower_32_bits(msg->stream_id); - dst[i++] = upper_32_bits(msg->stream_id); - - 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->stream_id = src[i++]; - msg->stream_id |= (((u64)src[i++]) << 32); - msg->user_param = src[i++]; - msg->user_param |= (((u64)src[i++]) << 32); - msg->src_handle = src[i++]; - msg->src_handle |= (((u64)src[i++]) << 32); - msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); - msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->initial_removal_delay = src[i++]; - msg->dpb_output_delay = src[i++]; - msg->size = src[i++]; - msg->frame_tag_size = src[i++]; - msg->stuffing = src[i++]; - msg->filler = src[i++]; - msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); - msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); - msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); - msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->partition_table_offset = src[i++]; - msg->partition_table_size = src[i++]; - msg->sum_complex = src[i++]; - for (j = 0; j < 4; j++) - msg->tile_width[j] = src[i++]; - for (j = 0; j < 22; j++) - msg->tile_height[j] = src[i++]; - msg->error_code = src[i++]; - msg->slice_type = src[i++]; - msg->pic_struct = src[i++]; - msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); - msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); - msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); - msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); - - msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); - msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); - - msg->reserved2 = src[i++]; - if (version >= MCU_MSG_VERSION_2019_2) { - msg->reserved3 = src[i++]; - msg->reserved4 = src[i++]; - msg->reserved5 = src[i++]; - msg->reserved6 = src[i++]; - } - - return i * sizeof(*src); -} - -/** - * allegro_encode_mail() - Encode allegro messages to firmware format - * @dst: Pointer to the memory that will be filled with data - * @msg: The allegro message that will be encoded - */ -ssize_t allegro_encode_mail(u32 *dst, void *msg) -{ - const struct mcu_msg_header *header = msg; - ssize_t size; - - if (!msg || !dst) - return -EINVAL; - - switch (header->type) { - case MCU_MSG_TYPE_INIT: - size = allegro_enc_init(&dst[1], msg); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - size = allegro_enc_create_channel(&dst[1], msg); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - size = allegro_enc_destroy_channel(&dst[1], msg); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - size = allegro_enc_encode_frame(&dst[1], msg); - break; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - size = allegro_enc_put_stream_buffer(&dst[1], msg); - break; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - size = allegro_enc_push_buffers(&dst[1], msg); - break; - default: - return -EINVAL; - } - - /* - * The encoded messages might have different length depending on - * the firmware version or certain fields. Therefore, we have to - * set the body length after encoding the message. - */ - dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | - FIELD_PREP(GENMASK(15, 0), size); - - return size + sizeof(*dst); -} - -/** - * allegro_decode_mail() - Parse allegro messages from the firmware. - * @msg: The mcu_msg_response that will be filled with parsed values. - * @src: Pointer to the memory that will be parsed - * - * The message format in the mailbox depends on the firmware. Parse the - * different formats into a uniform message format that can be used without - * taking care of the firmware version. - */ -int allegro_decode_mail(void *msg, u32 *src) -{ - struct mcu_msg_header *header; - - if (!src || !msg) - return -EINVAL; - - header = msg; - header->type = FIELD_GET(GENMASK(31, 16), src[0]); - - src++; - switch (header->type) { - case MCU_MSG_TYPE_INIT: - allegro_dec_init(msg, src); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - allegro_dec_create_channel(msg, src); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - allegro_dec_destroy_channel(msg, src); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - allegro_dec_encode_frame(msg, src); - break; - default: - return -EINVAL; - } - - return 0; -} diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h deleted file mode 100644 index 486ecb12b098..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ /dev/null @@ -1,294 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Allegro VCU firmware mailbox mail definitions - */ - -#ifndef ALLEGRO_MAIL_H -#define ALLEGRO_MAIL_H - -#include - -enum mcu_msg_type { - MCU_MSG_TYPE_INIT = 0x0000, - MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, - MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, - MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, - MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, - MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, - MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, -}; - -enum mcu_msg_version { - MCU_MSG_VERSION_2018_2, - MCU_MSG_VERSION_2019_2, -}; - -const char *msg_type_name(enum mcu_msg_type type); - -struct mcu_msg_header { - enum mcu_msg_type type; - enum mcu_msg_version version; -}; - -struct mcu_msg_init_request { - struct mcu_msg_header header; - u32 reserved0; /* maybe a unused channel id */ - u32 suballoc_dma; - u32 suballoc_size; - s32 l2_cache[3]; -}; - -struct mcu_msg_init_response { - struct mcu_msg_header header; - u32 reserved0; -}; - -struct create_channel_param { - enum mcu_msg_version version; - u32 layer_id; - u16 width; - u16 height; - u32 videomode; - u32 format; - u32 colorspace; - u32 src_mode; - u32 src_bit_depth; - u8 profile; - u16 constraint_set_flags; - u32 codec; - u16 level; - u16 tier; - u32 log2_max_poc; - u32 log2_max_frame_num; - u32 temporal_mvp_enable; - u32 enable_reordering; - u32 dbf_ovr_en; - u32 num_ref_idx_l0; - u32 num_ref_idx_l1; - u32 custom_lda; - u32 rdo_cost_mode; - u32 lf; - u32 lf_x_tile; - u32 lf_x_slice; - s8 beta_offset; - s8 tc_offset; - u16 reserved10; - u32 unknown11; - u32 unknown12; - u16 num_slices; - u16 prefetch_auto; - u32 prefetch_mem_offset; - u32 prefetch_mem_size; - u16 clip_hrz_range; - u16 clip_vrt_range; - u16 me_range[4]; - u8 max_cu_size; - u8 min_cu_size; - u8 max_tu_size; - u8 min_tu_size; - u8 max_transfo_depth_inter; - u8 max_transfo_depth_intra; - u16 reserved20; - u32 entropy_mode; - u32 wp_mode; - - /* rate control param */ - u32 rate_control_mode; - u32 initial_rem_delay; - u32 cpb_size; - u16 framerate; - u16 clk_ratio; - u32 target_bitrate; - u32 max_bitrate; - u16 initial_qp; - u16 min_qp; - u16 max_qp; - s16 ip_delta; - s16 pb_delta; - u16 golden_ref; - u16 golden_delta; - u16 golden_ref_frequency; - u32 rate_control_option; - u32 num_pixel; - u16 max_psnr; - u16 max_pixel_value; - u32 maxpicturesize[3]; - - /* gop param */ - u32 gop_ctrl_mode; - u32 freq_idr; - u32 freq_lt; - u32 gdr_mode; - u16 gop_length; - u8 num_b; - u8 freq_golden_ref; - u32 enable_lt; - u32 tmpdqp; - - u32 subframe_latency; - u32 lda_control_mode; - u32 unknown41; - - u32 lda_factors[6]; - - u32 max_num_merge_cand; -}; - -struct mcu_msg_create_channel { - struct mcu_msg_header header; - u32 user_id; - u32 *blob; - size_t blob_size; - u32 blob_mcu_addr; - u32 ep1_addr; -}; - -struct mcu_msg_create_channel_response { - struct mcu_msg_header header; - u32 channel_id; - u32 user_id; - u32 options; - u32 num_core; - u32 num_ref_idx_l0; - u32 num_ref_idx_l1; - u32 int_buffers_count; - u32 int_buffers_size; - u32 rec_buffers_count; - u32 rec_buffers_size; - u32 reserved; - u32 error_code; -}; - -struct mcu_msg_destroy_channel { - struct mcu_msg_header header; - u32 channel_id; -}; - -struct mcu_msg_destroy_channel_response { - struct mcu_msg_header header; - u32 channel_id; -}; - -struct mcu_msg_push_buffers_internal_buffer { - u32 dma_addr; - u32 mcu_addr; - u32 size; -}; - -struct mcu_msg_push_buffers_internal { - struct mcu_msg_header header; - u32 channel_id; - size_t num_buffers; - struct mcu_msg_push_buffers_internal_buffer buffer[]; -}; - -struct mcu_msg_put_stream_buffer { - struct mcu_msg_header header; - u32 channel_id; - u32 dma_addr; - u32 mcu_addr; - u32 size; - u32 offset; - u64 stream_id; -}; - -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 stream_id; /* 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/staging/media/allegro-dvt/nal-h264.c b/drivers/staging/media/allegro-dvt/nal-h264.c deleted file mode 100644 index bd48b8883572..000000000000 --- a/drivers/staging/media/allegro-dvt/nal-h264.c +++ /dev/null @@ -1,1001 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * 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 -#include -#include -#include - -#include -#include -#include - -#include "nal-h264.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, -}; - -struct rbsp; - -struct nal_h264_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_h264_ops *ops; - int error; -}; - -static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, - struct nal_h264_ops *ops) -{ - if (!rbsp) - return; - - rbsp->data = addr; - rbsp->size = size; - rbsp->pos = 0; - rbsp->ops = ops; - rbsp->error = 0; -} - -/** - * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile - * @profile: the profile as &enum v4l2_mpeg_video_h264_profile - * - * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified - * in Rec. ITU-T H.264 (04/2017) A.2. - * - * Return: the profile_idc for the passed level - */ -int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) -{ - switch (profile) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - return 66; - case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: - return 77; - case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: - return 88; - case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: - return 100; - default: - return -EINVAL; - } -} - -/** - * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level - * @level: the level as &enum v4l2_mpeg_video_h264_level - * - * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in - * Rec. ITU-T H.264 (04/2017) A.3.2. - * - * Return: the level_idc for the passed level - */ -int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 10; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 9; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 20; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 30; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 40; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - return 51; - default: - return -EINVAL; - } -} - -static 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); -} - -static struct nal_h264_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; -} - -static struct nal_h264_ops read = { - .rbsp_bit = __rbsp_read_bit, - .rbsp_bits = rbsp_read_bits, - .rbsp_uev = rbsp_read_uev, - .rbsp_sev = rbsp_read_sev, -}; - -static inline void rbsp_bit(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); -} - -static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); -} - -static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); -} - -static inline void rbsp_sev(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); -} - -static void nal_h264_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); -} - -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); - - nal_h264_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); - - nal_h264_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); - - nal_h264_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); - - nal_h264_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); - - nal_h264_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); - nal_h264_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/staging/media/allegro-dvt/nal-h264.h b/drivers/staging/media/allegro-dvt/nal-h264.h deleted file mode 100644 index 2ba7cbced7a5..000000000000 --- a/drivers/staging/media/allegro-dvt/nal-h264.h +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. - */ - -#ifndef __NAL_H264_H__ -#define __NAL_H264_H__ - -#include -#include - -/** - * struct nal_h264_hdr_parameters - HDR parameters - * - * C struct representation of the sequence parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. - */ -struct nal_h264_hrd_parameters { - unsigned int cpb_cnt_minus1; - unsigned int bit_rate_scale; - unsigned int cpb_size_scale; - struct { - int bit_rate_value_minus1[16]; - int cpb_size_value_minus1[16]; - unsigned int cbr_flag[16]; - }; - unsigned int initial_cpb_removal_delay_length_minus1; - unsigned int cpb_removal_delay_length_minus1; - unsigned int dpb_output_delay_length_minus1; - unsigned int time_offset_length; -}; - -/** - * struct nal_h264_vui_parameters - VUI parameters - * - * C struct representation of the VUI parameters as defined by Rec. ITU-T - * H.264 (04/2017) E.1.1 VUI parameters syntax. - */ -struct nal_h264_vui_parameters { - unsigned int aspect_ratio_info_present_flag; - struct { - unsigned int aspect_ratio_idc; - unsigned int sar_width; - unsigned int sar_height; - }; - unsigned int overscan_info_present_flag; - unsigned int overscan_appropriate_flag; - unsigned int video_signal_type_present_flag; - struct { - unsigned int video_format; - unsigned int video_full_range_flag; - unsigned int colour_description_present_flag; - struct { - unsigned int colour_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coefficients; - }; - }; - unsigned int chroma_loc_info_present_flag; - struct { - unsigned int chroma_sample_loc_type_top_field; - unsigned int chroma_sample_loc_type_bottom_field; - }; - unsigned int timing_info_present_flag; - struct { - unsigned int num_units_in_tick; - unsigned int time_scale; - unsigned int fixed_frame_rate_flag; - }; - unsigned int nal_hrd_parameters_present_flag; - struct nal_h264_hrd_parameters nal_hrd_parameters; - unsigned int vcl_hrd_parameters_present_flag; - struct nal_h264_hrd_parameters vcl_hrd_parameters; - unsigned int low_delay_hrd_flag; - unsigned int pic_struct_present_flag; - unsigned int bitstream_restriction_flag; - struct { - unsigned int motion_vectors_over_pic_boundaries_flag; - unsigned int max_bytes_per_pic_denom; - unsigned int max_bits_per_mb_denom; - unsigned int log2_max_mv_length_horizontal; - unsigned int log21_max_mv_length_vertical; - unsigned int max_num_reorder_frames; - unsigned int max_dec_frame_buffering; - }; -}; - -/** - * struct nal_h264_sps - Sequence parameter set - * - * C struct representation of the sequence parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. - */ -struct nal_h264_sps { - unsigned int profile_idc; - unsigned int constraint_set0_flag; - unsigned int constraint_set1_flag; - unsigned int constraint_set2_flag; - unsigned int constraint_set3_flag; - unsigned int constraint_set4_flag; - unsigned int constraint_set5_flag; - unsigned int reserved_zero_2bits; - unsigned int level_idc; - unsigned int seq_parameter_set_id; - struct { - unsigned int chroma_format_idc; - unsigned int separate_colour_plane_flag; - unsigned int bit_depth_luma_minus8; - unsigned int bit_depth_chroma_minus8; - unsigned int qpprime_y_zero_transform_bypass_flag; - unsigned int seq_scaling_matrix_present_flag; - }; - unsigned int log2_max_frame_num_minus4; - unsigned int pic_order_cnt_type; - union { - unsigned int log2_max_pic_order_cnt_lsb_minus4; - struct { - unsigned int delta_pic_order_always_zero_flag; - int offset_for_non_ref_pic; - int offset_for_top_to_bottom_field; - unsigned int num_ref_frames_in_pic_order_cnt_cycle; - int offset_for_ref_frame[255]; - }; - }; - unsigned int max_num_ref_frames; - unsigned int gaps_in_frame_num_value_allowed_flag; - unsigned int pic_width_in_mbs_minus1; - unsigned int pic_height_in_map_units_minus1; - unsigned int frame_mbs_only_flag; - unsigned int mb_adaptive_frame_field_flag; - unsigned int direct_8x8_inference_flag; - unsigned int frame_cropping_flag; - struct { - unsigned int crop_left; - unsigned int crop_right; - unsigned int crop_top; - unsigned int crop_bottom; - }; - unsigned int vui_parameters_present_flag; - struct nal_h264_vui_parameters vui; -}; - -/** - * struct nal_h264_pps - Picture parameter set - * - * C struct representation of the picture parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. - */ -struct nal_h264_pps { - unsigned int pic_parameter_set_id; - unsigned int seq_parameter_set_id; - unsigned int entropy_coding_mode_flag; - unsigned int bottom_field_pic_order_in_frame_present_flag; - unsigned int num_slice_groups_minus1; - unsigned int slice_group_map_type; - union { - unsigned int run_length_minus1[8]; - struct { - unsigned int top_left[8]; - unsigned int bottom_right[8]; - }; - struct { - unsigned int slice_group_change_direction_flag; - unsigned int slice_group_change_rate_minus1; - }; - struct { - unsigned int pic_size_in_map_units_minus1; - unsigned int slice_group_id[8]; - }; - }; - unsigned int num_ref_idx_l0_default_active_minus1; - unsigned int num_ref_idx_l1_default_active_minus1; - unsigned int weighted_pred_flag; - unsigned int weighted_bipred_idc; - int pic_init_qp_minus26; - int pic_init_qs_minus26; - int chroma_qp_index_offset; - unsigned int deblocking_filter_control_present_flag; - unsigned int constrained_intra_pred_flag; - unsigned int redundant_pic_cnt_present_flag; - struct { - unsigned int transform_8x8_mode_flag; - unsigned int pic_scaling_matrix_present_flag; - int second_chroma_qp_index_offset; - }; -}; - -int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); -int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); - -ssize_t nal_h264_write_sps(const struct device *dev, - void *dest, size_t n, struct nal_h264_sps *sps); -ssize_t nal_h264_read_sps(const struct device *dev, - struct nal_h264_sps *sps, void *src, size_t n); -void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); - -ssize_t nal_h264_write_pps(const struct device *dev, - void *dest, size_t n, struct nal_h264_pps *pps); -ssize_t nal_h264_read_pps(const struct device *dev, - struct nal_h264_pps *pps, void *src, size_t n); -void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); - -ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); -ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); - -#endif /* __NAL_H264_H__ */ -- cgit