Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/platform/allegro-dvt/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 110 kB image not shown  

Quelle  allegro-core.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
 *
 * Allegro DVT video encoder driver
 */


#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/xlnx-vcu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>

#include "allegro-mail.h"
#include "nal-h264.h"
#include "nal-hevc.h"

/*
 * Support up to 4k video streams. The hardware actually supports higher
 * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video
 * Codec Unit v1.1) Chapter 3.
 */

#define ALLEGRO_WIDTH_MIN 128
#define ALLEGRO_WIDTH_DEFAULT 1920
#define ALLEGRO_WIDTH_MAX 3840
#define ALLEGRO_HEIGHT_MIN 64
#define ALLEGRO_HEIGHT_DEFAULT 1080
#define ALLEGRO_HEIGHT_MAX 2160

#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 })

#define ALLEGRO_GOP_SIZE_DEFAULT 25
#define ALLEGRO_GOP_SIZE_MAX 1000

/*
 * MCU Control Registers
 *
 * The Zynq UltraScale+ Devices Register Reference documents the registers
 * with an offset of 0x9000, which equals the size of the SRAM and one page
 * gap. The driver handles SRAM and registers separately and, therefore, is
 * oblivious of the offset.
 */

#define AL5_MCU_RESET                   0x0000
#define AL5_MCU_RESET_SOFT              BIT(0)
#define AL5_MCU_RESET_REGS              BIT(1)
#define AL5_MCU_RESET_MODE              0x0004
#define AL5_MCU_RESET_MODE_SLEEP        BIT(0)
#define AL5_MCU_RESET_MODE_HALT         BIT(1)
#define AL5_MCU_STA                     0x0008
#define AL5_MCU_STA_SLEEP               BIT(0)
#define AL5_MCU_WAKEUP                  0x000c

#define AL5_ICACHE_ADDR_OFFSET_MSB      0x0010
#define AL5_ICACHE_ADDR_OFFSET_LSB      0x0014
#define AL5_DCACHE_ADDR_OFFSET_MSB      0x0018
#define AL5_DCACHE_ADDR_OFFSET_LSB      0x001c

#define AL5_MCU_INTERRUPT               0x0100
#define AL5_ITC_CPU_IRQ_MSK             0x0104
#define AL5_ITC_CPU_IRQ_CLR             0x0108
#define AL5_ITC_CPU_IRQ_STA             0x010C
#define AL5_ITC_CPU_IRQ_STA_TRIGGERED   BIT(0)

#define AXI_ADDR_OFFSET_IP              0x0208

/*
 * The MCU accesses the system memory with a 2G offset compared to CPU
 * physical addresses.
 */

#define MCU_CACHE_OFFSET SZ_2G

/*
 * The driver needs to reserve some space at the beginning of capture buffers,
 * because it needs to write SPS/PPS NAL units. The encoder writes the actual
 * frame data after the offset.
 */

#define ENCODER_STREAM_OFFSET SZ_128

#define SIZE_MACROBLOCK 16

/* Encoding options */
#define LOG2_MAX_FRAME_NUM  4
#define LOG2_MAX_PIC_ORDER_CNT  10
#define BETA_OFFSET_DIV_2  -1
#define TC_OFFSET_DIV_2   -1

/*
 * This control allows applications to explicitly disable the encoder buffer.
 * This value is Allegro specific.
 */

#define V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER (V4L2_CID_USER_ALLEGRO_BASE + 0)

static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-2)");

struct allegro_buffer {
 void *vaddr;
 dma_addr_t paddr;
 size_t size;
 struct list_head head;
};

struct allegro_dev;
struct allegro_channel;

struct allegro_mbox {
 struct allegro_dev *dev;
 unsigned int head;
 unsigned int tail;
 unsigned int data;
 size_t size;
 /* protect mailbox from simultaneous accesses */
 struct mutex lock;
};

struct allegro_encoder_buffer {
 unsigned int size;
 unsigned int color_depth;
 unsigned int num_cores;
 unsigned int clk_rate;
};

struct allegro_dev {
 struct v4l2_device v4l2_dev;
 struct video_device video_dev;
 struct v4l2_m2m_dev *m2m_dev;
 struct platform_device *plat_dev;

 /* mutex protecting vb2_queue structure */
 struct mutex lock;

 struct regmap *regmap;
 struct regmap *sram;
 struct regmap *settings;

 struct clk *clk_core;
 struct clk *clk_mcu;

 const struct fw_info *fw_info;
 struct allegro_buffer firmware;
 struct allegro_buffer suballocator;
 bool has_encoder_buffer;
 struct allegro_encoder_buffer encoder_buffer;

 struct completion init_complete;
 bool initialized;

 /* The mailbox interface */
 struct allegro_mbox *mbox_command;
 struct allegro_mbox *mbox_status;

 /*
 * The downstream driver limits the users to 64 users, thus I can use
 * a bitfield for the user_ids that are in use. See also user_id in
 * struct allegro_channel.
 */

 unsigned long channel_user_ids;
 struct list_head channels;
};

static const 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 const struct regmap_config allegro_sram_config = {
 .name = "sram",
 .reg_bits = 32,
 .val_bits = 32,
 .reg_stride = 4,
 .max_register = 0x7fff,
 .cache_type = REGCACHE_NONE,
};

#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh)

struct allegro_channel {
 struct allegro_dev *dev;
 struct v4l2_fh fh;
 struct v4l2_ctrl_handler ctrl_handler;

 unsigned int width;
 unsigned int height;
 unsigned int stride;
 struct v4l2_fract framerate;

 enum v4l2_colorspace colorspace;
 enum v4l2_ycbcr_encoding ycbcr_enc;
 enum v4l2_quantization quantization;
 enum v4l2_xfer_func xfer_func;

 u32 pixelformat;
 unsigned int sizeimage_raw;
 unsigned int osequence;

 u32 codec;
 unsigned int sizeimage_encoded;
 unsigned int csequence;

 bool frame_rc_enable;
 unsigned int bitrate;
 unsigned int bitrate_peak;

 struct allegro_buffer config_blob;

 unsigned int log2_max_frame_num;
 bool temporal_mvp_enable;

 bool enable_loop_filter_across_tiles;
 bool enable_loop_filter_across_slices;
 bool enable_deblocking_filter_override;
 bool enable_reordering;
 bool dbf_ovr_en;

 unsigned int num_ref_idx_l0;
 unsigned int num_ref_idx_l1;

 /* Maximum range for motion estimation */
 int b_hrz_me_range;
 int b_vrt_me_range;
 int p_hrz_me_range;
 int p_vrt_me_range;
 /* Size limits of coding unit */
 int min_cu_size;
 int max_cu_size;
 /* Size limits of transform unit */
 int min_tu_size;
 int max_tu_size;
 int max_transfo_depth_intra;
 int max_transfo_depth_inter;

 struct v4l2_ctrl *mpeg_video_h264_profile;
 struct v4l2_ctrl *mpeg_video_h264_level;
 struct v4l2_ctrl *mpeg_video_h264_i_frame_qp;
 struct v4l2_ctrl *mpeg_video_h264_max_qp;
 struct v4l2_ctrl *mpeg_video_h264_min_qp;
 struct v4l2_ctrl *mpeg_video_h264_p_frame_qp;
 struct v4l2_ctrl *mpeg_video_h264_b_frame_qp;

 struct v4l2_ctrl *mpeg_video_hevc_profile;
 struct v4l2_ctrl *mpeg_video_hevc_level;
 struct v4l2_ctrl *mpeg_video_hevc_tier;
 struct v4l2_ctrl *mpeg_video_hevc_i_frame_qp;
 struct v4l2_ctrl *mpeg_video_hevc_max_qp;
 struct v4l2_ctrl *mpeg_video_hevc_min_qp;
 struct v4l2_ctrl *mpeg_video_hevc_p_frame_qp;
 struct v4l2_ctrl *mpeg_video_hevc_b_frame_qp;

 struct v4l2_ctrl *mpeg_video_frame_rc_enable;
 struct { /* video bitrate mode control cluster */
  struct v4l2_ctrl *mpeg_video_bitrate_mode;
  struct v4l2_ctrl *mpeg_video_bitrate;
  struct v4l2_ctrl *mpeg_video_bitrate_peak;
 };
 struct v4l2_ctrl *mpeg_video_cpb_size;
 struct v4l2_ctrl *mpeg_video_gop_size;

 struct v4l2_ctrl *encoder_buffer;

 /* user_id is used to identify the channel during CREATE_CHANNEL */
 /* not sure, what to set here and if this is actually required */
 int user_id;
 /* channel_id is set by the mcu and used by all later commands */
 int mcu_channel_id;

 struct list_head buffers_reference;
 struct list_head buffers_intermediate;

 struct list_head source_shadow_list;
 struct list_head stream_shadow_list;
 /* protect shadow lists of buffers passed to firmware */
 struct mutex shadow_list_lock;

 struct list_head list;
 struct completion completion;

 unsigned int error;
};

static inline int
allegro_channel_get_i_frame_qp(struct allegro_channel *channel)
{
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_i_frame_qp);
 else
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp);
}

static inline int
allegro_channel_get_p_frame_qp(struct allegro_channel *channel)
{
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_p_frame_qp);
 else
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp);
}

static inline int
allegro_channel_get_b_frame_qp(struct allegro_channel *channel)
{
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_b_frame_qp);
 else
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp);
}

static inline int
allegro_channel_get_min_qp(struct allegro_channel *channel)
{
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_min_qp);
 else
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp);
}

static inline int
allegro_channel_get_max_qp(struct allegro_channel *channel)
{
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_max_qp);
 else
  return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp);
}

struct allegro_m2m_buffer {
 struct v4l2_m2m_buffer buf;
 struct list_head head;
};

#define to_allegro_m2m_buffer(__buf) \
 container_of(__buf, struct allegro_m2m_buffer, buf)

struct fw_info {
 unsigned int id;
 unsigned int id_codec;
 char *version;
 unsigned int mailbox_cmd;
 unsigned int mailbox_status;
 size_t mailbox_size;
 enum mcu_msg_version mailbox_version;
 size_t suballocator_size;
};

static const struct fw_info supported_firmware[] = {
 {
  .id = 18296,
  .id_codec = 96272,
  .version = "v2018.2",
  .mailbox_cmd = 0x7800,
  .mailbox_status = 0x7c00,
  .mailbox_size = 0x400 - 0x8,
  .mailbox_version = MCU_MSG_VERSION_2018_2,
  .suballocator_size = SZ_16M,
 }, {
  .id = 14680,
  .id_codec = 126572,
  .version = "v2019.2",
  .mailbox_cmd = 0x7000,
  .mailbox_status = 0x7800,
  .mailbox_size = 0x800 - 0x8,
  .mailbox_version = MCU_MSG_VERSION_2019_2,
  .suballocator_size = SZ_32M,
 },
};

static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys)
{
 if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET))
  v4l2_warn(&dev->v4l2_dev,
     "address %pad is outside mcu window\n", &phys);

 return lower_32_bits(phys) | MCU_CACHE_OFFSET;
}

static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size)
{
 return lower_32_bits(size);
}

static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys)
{
 if (upper_32_bits(phys))
  v4l2_warn(&dev->v4l2_dev,
     "address %pad cannot be used by codec\n", &phys);

 return lower_32_bits(phys);
}

static inline u64 ptr_to_u64(const void *ptr)
{
 return (uintptr_t)ptr;
}

/* Helper functions for channel and user operations */

static unsigned long allegro_next_user_id(struct allegro_dev *dev)
{
 if (dev->channel_user_ids == ~0UL)
  return -EBUSY;

 return ffz(dev->channel_user_ids);
}

static struct allegro_channel *
allegro_find_channel_by_user_id(struct allegro_dev *dev,
    unsigned int user_id)
{
 struct allegro_channel *channel;

 list_for_each_entry(channel, &dev->channels, list) {
  if (channel->user_id == user_id)
   return channel;
 }

 return ERR_PTR(-EINVAL);
}

static struct allegro_channel *
allegro_find_channel_by_channel_id(struct allegro_dev *dev,
       unsigned int channel_id)
{
 struct allegro_channel *channel;

 list_for_each_entry(channel, &dev->channels, list) {
  if (channel->mcu_channel_id == channel_id)
   return channel;
 }

 return ERR_PTR(-EINVAL);
}

static inline bool channel_exists(struct allegro_channel *channel)
{
 return channel->mcu_channel_id != -1;
}

#define AL_ERROR   0x80
#define AL_ERR_INIT_FAILED  0x81
#define AL_ERR_NO_FRAME_DECODED  0x82
#define AL_ERR_RESOLUTION_CHANGE 0x85
#define AL_ERR_NO_MEMORY  0x87
#define AL_ERR_STREAM_OVERFLOW  0x88
#define AL_ERR_TOO_MANY_SLICES  0x89
#define AL_ERR_BUF_NOT_READY  0x8c
#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d
#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e
#define AL_ERR_NOT_ENOUGH_CORES  0x8f
#define AL_ERR_REQUEST_MALFORMED 0x90
#define AL_ERR_CMD_NOT_ALLOWED  0x91
#define AL_ERR_INVALID_CMD_VALUE 0x92

static inline const char *allegro_err_to_string(unsigned int err)
{
 switch (err) {
 case AL_ERR_INIT_FAILED:
  return "initialization failed";
 case AL_ERR_NO_FRAME_DECODED:
  return "no frame decoded";
 case AL_ERR_RESOLUTION_CHANGE:
  return "resolution change";
 case AL_ERR_NO_MEMORY:
  return "out of memory";
 case AL_ERR_STREAM_OVERFLOW:
  return "stream buffer overflow";
 case AL_ERR_TOO_MANY_SLICES:
  return "too many slices";
 case AL_ERR_BUF_NOT_READY:
  return "buffer not ready";
 case AL_ERR_NO_CHANNEL_AVAILABLE:
  return "no channel available";
 case AL_ERR_RESOURCE_UNAVAILABLE:
  return "resource unavailable";
 case AL_ERR_NOT_ENOUGH_CORES:
  return "not enough cores";
 case AL_ERR_REQUEST_MALFORMED:
  return "request malformed";
 case AL_ERR_CMD_NOT_ALLOWED:
  return "command not allowed";
 case AL_ERR_INVALID_CMD_VALUE:
  return "invalid command value";
 case AL_ERROR:
 default:
  return "unknown error";
 }
}

static unsigned int estimate_stream_size(unsigned int width,
      unsigned int height)
{
 unsigned int offset = ENCODER_STREAM_OFFSET;
 unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) *
     DIV_ROUND_UP(height, SIZE_MACROBLOCK);
 unsigned int pcm_size = SZ_256;
 unsigned int partition_table = SZ_256;

 return round_up(offset + num_blocks * pcm_size + partition_table, 32);
}

static enum v4l2_mpeg_video_h264_level
select_minimum_h264_level(unsigned int width, unsigned int height)
{
 unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK);
 unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK);
 unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb;
 enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;

 /*
 * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and
 * also specify limits regarding bit rate and CBP size. Only approximate
 * the levels using the frame size.
 *
 * Level 5.1 allows up to 4k video resolution.
 */

 if (frame_size_in_mb <= 99)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
 else if (frame_size_in_mb <= 396)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
 else if (frame_size_in_mb <= 792)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
 else if (frame_size_in_mb <= 1620)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
 else if (frame_size_in_mb <= 3600)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
 else if (frame_size_in_mb <= 5120)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
 else if (frame_size_in_mb <= 8192)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
 else if (frame_size_in_mb <= 8704)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
 else if (frame_size_in_mb <= 22080)
  level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
 else
  level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1;

 return level;
}

static unsigned int h264_maximum_bitrate(enum v4l2_mpeg_video_h264_level level)
{
 switch (level) {
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
  return 64000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
  return 128000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
  return 192000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
  return 384000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
  return 768000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
  return 2000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
  return 4000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
  return 4000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
  return 10000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
  return 14000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
  return 20000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
  return 20000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
  return 50000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
  return 50000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
  return 135000000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
 default:
  return 240000000;
 }
}

static unsigned int h264_maximum_cpb_size(enum v4l2_mpeg_video_h264_level level)
{
 switch (level) {
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
  return 175;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
  return 350;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
  return 500;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
  return 1000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
  return 2000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
  return 2000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
  return 4000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
  return 4000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
  return 10000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
  return 14000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
  return 20000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
  return 25000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
  return 62500;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
  return 62500;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
  return 135000;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
 default:
  return 240000;
 }
}

static enum v4l2_mpeg_video_hevc_level
select_minimum_hevc_level(unsigned int width, unsigned int height)
{
 unsigned int luma_picture_size = width * height;
 enum v4l2_mpeg_video_hevc_level level;

 if (luma_picture_size <= 36864)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_1;
 else if (luma_picture_size <= 122880)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2;
 else if (luma_picture_size <= 245760)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1;
 else if (luma_picture_size <= 552960)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3;
 else if (luma_picture_size <= 983040)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1;
 else if (luma_picture_size <= 2228224)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_4;
 else if (luma_picture_size <= 8912896)
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_5;
 else
  level = V4L2_MPEG_VIDEO_HEVC_LEVEL_6;

 return level;
}

static unsigned int hevc_maximum_bitrate(enum v4l2_mpeg_video_hevc_level level)
{
 /*
 * See Rec. ITU-T H.265 v5 (02/2018), A.4.2 Profile-specific level
 * limits for the video profiles.
 */

 switch (level) {
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
  return 128;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
  return 1500;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
  return 3000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
  return 6000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
  return 10000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
  return 12000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
  return 20000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
  return 25000;
 default:
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
  return 40000;
 }
}

static unsigned int hevc_maximum_cpb_size(enum v4l2_mpeg_video_hevc_level level)
{
 switch (level) {
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
  return 350;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
  return 1500;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
  return 3000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
  return 6000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
  return 10000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
  return 12000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
  return 20000;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
  return 25000;
 default:
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
  return 40000;
 }
}

static const struct fw_info *
allegro_get_firmware_info(struct allegro_dev *dev,
     const struct firmware *fw,
     const struct firmware *fw_codec)
{
 int i;
 unsigned int id = fw->size;
 unsigned int id_codec = fw_codec->size;

 for (i = 0; i < ARRAY_SIZE(supported_firmware); i++)
  if (supported_firmware[i].id == id &&
      supported_firmware[i].id_codec == id_codec)
   return &supported_firmware[i];

 return NULL;
}

/*
 * Buffers that are used internally by the MCU.
 */


static int allegro_alloc_buffer(struct allegro_dev *dev,
    struct allegro_buffer *buffer, size_t size)
{
 buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size,
        &buffer->paddr, GFP_KERNEL);
 if (!buffer->vaddr)
  return -ENOMEM;
 buffer->size = size;

 return 0;
}

static void allegro_free_buffer(struct allegro_dev *dev,
    struct allegro_buffer *buffer)
{
 if (buffer->vaddr) {
  dma_free_coherent(&dev->plat_dev->dev, buffer->size,
      buffer->vaddr, buffer->paddr);
  buffer->vaddr = NULL;
  buffer->size = 0;
 }
}

/*
 * Mailbox interface to send messages to the MCU.
 */


static void allegro_mcu_interrupt(struct allegro_dev *dev);
static void allegro_handle_message(struct allegro_dev *dev,
       union mcu_msg_response *msg);

static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev,
           unsigned int base, size_t size)
{
 struct allegro_mbox *mbox;

 mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL);
 if (!mbox)
  return ERR_PTR(-ENOMEM);

 mbox->dev = dev;

 mbox->head = base;
 mbox->tail = base + 0x4;
 mbox->data = base + 0x8;
 mbox->size = size;
 mutex_init(&mbox->lock);

 regmap_write(dev->sram, mbox->head, 0);
 regmap_write(dev->sram, mbox->tail, 0);

 return mbox;
}

static int allegro_mbox_write(struct allegro_mbox *mbox,
         const u32 *src, size_t size)
{
 struct regmap *sram = mbox->dev->sram;
 unsigned int tail;
 size_t size_no_wrap;
 int err = 0;
 int stride = regmap_get_reg_stride(sram);

 if (!src)
  return -EINVAL;

 if (size > mbox->size)
  return -EINVAL;

 mutex_lock(&mbox->lock);
 regmap_read(sram, mbox->tail, &tail);
 if (tail > mbox->size) {
  err = -EIO;
  goto out;
 }
 size_no_wrap = min(size, mbox->size - (size_t)tail);
 regmap_bulk_write(sram, mbox->data + tail,
     src, size_no_wrap / stride);
 regmap_bulk_write(sram, mbox->data,
     src + (size_no_wrap / sizeof(*src)),
     (size - size_no_wrap) / stride);
 regmap_write(sram, mbox->tail, (tail + size) % mbox->size);

out:
 mutex_unlock(&mbox->lock);

 return err;
}

static ssize_t allegro_mbox_read(struct allegro_mbox *mbox,
     u32 *dst, size_t nbyte)
{
 struct {
  u16 length;
  u16 type;
 } __attribute__ ((__packed__)) *header;
 struct regmap *sram = mbox->dev->sram;
 unsigned int head;
 ssize_t size;
 size_t body_no_wrap;
 int stride = regmap_get_reg_stride(sram);

 regmap_read(sram, mbox->head, &head);
 if (head > mbox->size)
  return -EIO;

 /* Assume that the header does not wrap. */
 regmap_bulk_read(sram, mbox->data + head,
    dst, sizeof(*header) / stride);
 header = (void *)dst;
 size = header->length + sizeof(*header);
 if (size > mbox->size || size & 0x3)
  return -EIO;
 if (size > nbyte)
  return -EINVAL;

 /*
 * The message might wrap within the mailbox. If the message does not
 * wrap, the first read will read the entire message, otherwise the
 * first read will read message until the end of the mailbox and the
 * second read will read the remaining bytes from the beginning of the
 * mailbox.
 *
 * Skip the header, as was already read to get the size of the body.
 */

 body_no_wrap = min((size_t)header->length,
      (size_t)(mbox->size - (head + sizeof(*header))));
 regmap_bulk_read(sram, mbox->data + head + sizeof(*header),
    dst + (sizeof(*header) / sizeof(*dst)),
    body_no_wrap / stride);
 regmap_bulk_read(sram, mbox->data,
    dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst),
    (header->length - body_no_wrap) / stride);

 regmap_write(sram, mbox->head, (head + size) % mbox->size);

 return size;
}

/**
 * allegro_mbox_send() - Send a message via the mailbox
 * @mbox: the mailbox which is used to send the message
 * @msg: the message to send
 */

static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg)
{
 struct allegro_dev *dev = mbox->dev;
 ssize_t size;
 int err;
 u32 *tmp;

 tmp = kzalloc(mbox->size, GFP_KERNEL);
 if (!tmp) {
  err = -ENOMEM;
  goto out;
 }

 size = allegro_encode_mail(tmp, msg);

 err = allegro_mbox_write(mbox, tmp, size);
 kfree(tmp);
 if (err)
  goto out;

 allegro_mcu_interrupt(dev);

out:
 return err;
}

/**
 * allegro_mbox_notify() - Notify the mailbox about a new message
 * @mbox: The allegro_mbox to notify
 */

static void allegro_mbox_notify(struct allegro_mbox *mbox)
{
 struct allegro_dev *dev = mbox->dev;
 union mcu_msg_response *msg;
 ssize_t size;
 u32 *tmp;
 int err;

 msg = kmalloc(sizeof(*msg), GFP_KERNEL);
 if (!msg)
  return;

 msg->header.version = dev->fw_info->mailbox_version;

 tmp = kmalloc(mbox->size, GFP_KERNEL);
 if (!tmp)
  goto out;

 size = allegro_mbox_read(mbox, tmp, mbox->size);
 if (size < 0)
  goto out;

 err = allegro_decode_mail(msg, tmp);
 if (err)
  goto out;

 allegro_handle_message(dev, msg);

out:
 kfree(tmp);
 kfree(msg);
}

static int allegro_encoder_buffer_init(struct allegro_dev *dev,
           struct allegro_encoder_buffer *buffer)
{
 int err;
 struct regmap *settings = dev->settings;
 unsigned int supports_10_bit;
 unsigned int memory_depth;
 unsigned int num_cores;
 unsigned int color_depth;
 unsigned long clk_rate;

 /* We don't support the encoder buffer pre Firmware version 2019.2 */
 if (dev->fw_info->mailbox_version < MCU_MSG_VERSION_2019_2)
  return -ENODEV;

 if (!settings)
  return -EINVAL;

 err = regmap_read(settings, VCU_ENC_COLOR_DEPTH, &supports_10_bit);
 if (err < 0)
  return err;
 err = regmap_read(settings, VCU_MEMORY_DEPTH, &memory_depth);
 if (err < 0)
  return err;
 err = regmap_read(settings, VCU_NUM_CORE, &num_cores);
 if (err < 0)
  return err;

 clk_rate = clk_get_rate(dev->clk_core);
 if (clk_rate == 0)
  return -EINVAL;

 color_depth = supports_10_bit ? 10 : 8;
 /* The firmware expects the encoder buffer size in bits. */
 buffer->size = color_depth * 32 * memory_depth;
 buffer->color_depth = color_depth;
 buffer->num_cores = num_cores;
 buffer->clk_rate = clk_rate;

 v4l2_dbg(1, debug, &dev->v4l2_dev,
   "using %d bits encoder buffer with %d-bit color depth\n",
   buffer->size, color_depth);

 return 0;
}

static void allegro_mcu_send_init(struct allegro_dev *dev,
      dma_addr_t suballoc_dma, size_t suballoc_size)
{
 struct mcu_msg_init_request msg;

 memset(&msg, 0, sizeof(msg));

 msg.header.type = MCU_MSG_TYPE_INIT;
 msg.header.version = dev->fw_info->mailbox_version;

 msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma);
 msg.suballoc_size = to_mcu_size(dev, suballoc_size);

 if (dev->has_encoder_buffer) {
  msg.encoder_buffer_size = dev->encoder_buffer.size;
  msg.encoder_buffer_color_depth = dev->encoder_buffer.color_depth;
  msg.num_cores = dev->encoder_buffer.num_cores;
  msg.clk_rate = dev->encoder_buffer.clk_rate;
 } else {
  msg.encoder_buffer_size = -1;
  msg.encoder_buffer_color_depth = -1;
  msg.num_cores = -1;
  msg.clk_rate = -1;
 }

 allegro_mbox_send(dev->mbox_command, &msg);
}

static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat)
{
 switch (pixelformat) {
 case V4L2_PIX_FMT_NV12:
  /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */
  return 0x100 | 0x88;
 default:
  return -EINVAL;
 }
}

static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace)
{
 switch (colorspace) {
 case V4L2_COLORSPACE_REC709:
  return 2;
 case V4L2_COLORSPACE_SMPTE170M:
  return 3;
 case V4L2_COLORSPACE_SMPTE240M:
  return 4;
 case V4L2_COLORSPACE_SRGB:
  return 7;
 default:
  /* UNKNOWN */
  return 0;
 }
}

static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile)
{
 switch (profile) {
 case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
 default:
  return 66;
 }
}

static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level)
{
 switch (level) {
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
  return 10;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
  return 11;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
  return 12;
 case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
  return 13;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
  return 20;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
  return 21;
 case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
  return 22;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
  return 30;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
  return 31;
 case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
  return 32;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
  return 40;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
  return 41;
 case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
  return 42;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
  return 50;
 case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
 default:
  return 51;
 }
}

static u8 hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile)
{
 switch (profile) {
 default:
 case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
  return 1;
 case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
  return 2;
 case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
  return 3;
 }
}

static u16 hevc_level_to_mcu_level(enum v4l2_mpeg_video_hevc_level level)
{
 switch (level) {
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
  return 10;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
  return 20;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
  return 21;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
  return 30;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
  return 31;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
  return 40;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
  return 41;
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
  return 50;
 default:
 case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
  return 51;
 }
}

static u8 hevc_tier_to_mcu_tier(enum v4l2_mpeg_video_hevc_tier tier)
{
 switch (tier) {
 default:
 case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN:
  return 0;
 case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH:
  return 1;
 }
}

static u32
v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode)
{
 switch (mode) {
 case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
  return 2;
 case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
 default:
  return 1;
 }
}

static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate)
{
 unsigned int cpb_size_kbit;
 unsigned int bitrate_kbps;

 /*
 * The mcu expects the CPB size in units of a 90 kHz clock, but the
 * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores
 * the CPB size in kilobytes.
 */

 cpb_size_kbit = cpb_size * BITS_PER_BYTE;
 bitrate_kbps = bitrate / 1000;

 return (cpb_size_kbit * 90000) / bitrate_kbps;
}

static s16 get_qp_delta(int minuend, int subtrahend)
{
 if (minuend == subtrahend)
  return -1;
 else
  return minuend - subtrahend;
}

static u32 allegro_channel_get_entropy_mode(struct allegro_channel *channel)
{
#define ALLEGRO_ENTROPY_MODE_CAVLC 0
#define ALLEGRO_ENTROPY_MODE_CABAC 1

 /* HEVC always uses CABAC, but this has to be explicitly set */
 if (channel->codec == V4L2_PIX_FMT_HEVC)
  return ALLEGRO_ENTROPY_MODE_CABAC;

 return ALLEGRO_ENTROPY_MODE_CAVLC;
}

static int fill_create_channel_param(struct allegro_channel *channel,
         struct create_channel_param *param)
{
 int i_frame_qp = allegro_channel_get_i_frame_qp(channel);
 int p_frame_qp = allegro_channel_get_p_frame_qp(channel);
 int b_frame_qp = allegro_channel_get_b_frame_qp(channel);
 int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode);
 unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size);

 param->width = channel->width;
 param->height = channel->height;
 param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat);
 param->colorspace =
  v4l2_colorspace_to_mcu_colorspace(channel->colorspace);
 param->src_mode = 0x0;

 param->codec = channel->codec;
 if (channel->codec == V4L2_PIX_FMT_H264) {
  enum v4l2_mpeg_video_h264_profile profile;
  enum v4l2_mpeg_video_h264_level level;

  profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile);
  level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level);

  param->profile = v4l2_profile_to_mcu_profile(profile);
  param->constraint_set_flags = BIT(1);
  param->level = v4l2_level_to_mcu_level(level);
 } else {
  enum v4l2_mpeg_video_hevc_profile profile;
  enum v4l2_mpeg_video_hevc_level level;
  enum v4l2_mpeg_video_hevc_tier tier;

  profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
  level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
  tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);

  param->profile = hevc_profile_to_mcu_profile(profile);
  param->level = hevc_level_to_mcu_level(level);
  param->tier = hevc_tier_to_mcu_tier(tier);
 }

 param->log2_max_poc = LOG2_MAX_PIC_ORDER_CNT;
 param->log2_max_frame_num = channel->log2_max_frame_num;
 param->temporal_mvp_enable = channel->temporal_mvp_enable;

 param->dbf_ovr_en = channel->dbf_ovr_en;
 param->override_lf = channel->enable_deblocking_filter_override;
 param->enable_reordering = channel->enable_reordering;
 param->entropy_mode = allegro_channel_get_entropy_mode(channel);
 param->rdo_cost_mode = 1;
 param->custom_lda = 1;
 param->lf = 1;
 param->lf_x_tile = channel->enable_loop_filter_across_tiles;
 param->lf_x_slice = channel->enable_loop_filter_across_slices;

 param->src_bit_depth = 8;

 param->beta_offset = BETA_OFFSET_DIV_2;
 param->tc_offset = TC_OFFSET_DIV_2;
 param->num_slices = 1;
 param->me_range[0] = channel->b_hrz_me_range;
 param->me_range[1] = channel->b_vrt_me_range;
 param->me_range[2] = channel->p_hrz_me_range;
 param->me_range[3] = channel->p_vrt_me_range;
 param->max_cu_size = channel->max_cu_size;
 param->min_cu_size = channel->min_cu_size;
 param->max_tu_size = channel->max_tu_size;
 param->min_tu_size = channel->min_tu_size;
 param->max_transfo_depth_intra = channel->max_transfo_depth_intra;
 param->max_transfo_depth_inter = channel->max_transfo_depth_inter;

 param->encoder_buffer_enabled = v4l2_ctrl_g_ctrl(channel->encoder_buffer);
 param->encoder_buffer_offset = 0;

 param->rate_control_mode = channel->frame_rc_enable ?
  v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0;

 param->cpb_size = v4l2_cpb_size_to_mcu(cpb_size, channel->bitrate_peak);
 /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */
 param->initial_rem_delay = param->cpb_size;
 param->framerate = DIV_ROUND_UP(channel->framerate.numerator,
     channel->framerate.denominator);
 param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000;
 param->target_bitrate = channel->bitrate;
 param->max_bitrate = channel->bitrate_peak;
 param->initial_qp = i_frame_qp;
 param->min_qp = allegro_channel_get_min_qp(channel);
 param->max_qp = allegro_channel_get_max_qp(channel);
 param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp);
 param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp);
 param->golden_ref = 0;
 param->golden_delta = 2;
 param->golden_ref_frequency = 10;
 param->rate_control_option = 0x00000000;

 param->num_pixel = channel->width + channel->height;
 param->max_psnr = 4200;
 param->max_pixel_value = 255;

 param->gop_ctrl_mode = 0x00000002;
 param->freq_idr = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size);
 param->freq_lt = 0;
 param->gdr_mode = 0x00000000;
 param->gop_length = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size);
 param->subframe_latency = 0x00000000;

 param->lda_factors[0] = 51;
 param->lda_factors[1] = 90;
 param->lda_factors[2] = 151;
 param->lda_factors[3] = 151;
 param->lda_factors[4] = 151;
 param->lda_factors[5] = 151;

 param->max_num_merge_cand = 5;

 return 0;
}

static int allegro_mcu_send_create_channel(struct allegro_dev *dev,
        struct allegro_channel *channel)
{
 struct mcu_msg_create_channel msg;
 struct allegro_buffer *blob = &channel->config_blob;
 struct create_channel_param param;
 size_t size;

 memset(¶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 dst_handle)
{
 struct mcu_msg_put_stream_buffer msg;

 memset(&msg, 0, sizeof(msg));

 msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER;
 msg.header.version = dev->fw_info->mailbox_version;

 msg.channel_id = channel->mcu_channel_id;
 msg.dma_addr = to_codec_addr(dev, paddr);
 msg.mcu_addr = to_mcu_addr(dev, paddr);
 msg.size = size;
 msg.offset = ENCODER_STREAM_OFFSET;
 /* copied to mcu_msg_encode_frame_response */
 msg.dst_handle = dst_handle;

 allegro_mbox_send(dev->mbox_command, &msg);

 return 0;
}

static int allegro_mcu_send_encode_frame(struct allegro_dev *dev,
      struct allegro_channel *channel,
      dma_addr_t src_y, dma_addr_t src_uv,
      u64 src_handle)
{
 struct mcu_msg_encode_frame msg;
 bool use_encoder_buffer = v4l2_ctrl_g_ctrl(channel->encoder_buffer);

 memset(&msg, 0, sizeof(msg));

 msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME;
 msg.header.version = dev->fw_info->mailbox_version;

 msg.channel_id = channel->mcu_channel_id;
 msg.encoding_options = AL_OPT_FORCE_LOAD;
 if (use_encoder_buffer)
  msg.encoding_options |= AL_OPT_USE_L2;
 msg.pps_qp = 26; /* qp are relative to 26 */
 msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */
 /* src_handle is copied to mcu_msg_encode_frame_response */
 msg.src_handle = src_handle;
 msg.src_y = to_codec_addr(dev, src_y);
 msg.src_uv = to_codec_addr(dev, src_uv);
 msg.stride = channel->stride;

 allegro_mbox_send(dev->mbox_command, &msg);

 return 0;
}

static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev,
          unsigned long timeout_ms)
{
 unsigned long time_left;

 time_left = wait_for_completion_timeout(&dev->init_complete,
      msecs_to_jiffies(timeout_ms));
 if (time_left == 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) {
   kfree(buffer);
   goto err;
  }
  list_add(&buffer->head, list);
 }

 return 0;

err:
 list_for_each_entry_safe(buffer, tmp, list, head) {
  list_del(&buffer->head);
  allegro_free_buffer(dev, buffer);
  kfree(buffer);
 }
 return err;
}

static void destroy_buffers_internal(struct allegro_channel *channel,
         struct list_head *list)
{
 struct allegro_dev *dev = channel->dev;
 struct allegro_buffer *buffer, *tmp;

 list_for_each_entry_safe(buffer, tmp, list, head) {
  list_del(&buffer->head);
  allegro_free_buffer(dev, buffer);
  kfree(buffer);
 }
}

static void destroy_reference_buffers(struct allegro_channel *channel)
{
 return destroy_buffers_internal(channel, &channel->buffers_reference);
}

static void destroy_intermediate_buffers(struct allegro_channel *channel)
{
 return destroy_buffers_internal(channel,
     &channel->buffers_intermediate);
}

static int allocate_intermediate_buffers(struct allegro_channel *channel,
      size_t n, size_t size)
{
 return allocate_buffers_internal(channel,
      &channel->buffers_intermediate,
      n, size);
}

static int allocate_reference_buffers(struct allegro_channel *channel,
          size_t n, size_t size)
{
 return allocate_buffers_internal(channel,
      &channel->buffers_reference,
      n, PAGE_ALIGN(size));
}

static ssize_t allegro_h264_write_sps(struct allegro_channel *channel,
          void *dest, size_t n)
{
 struct allegro_dev *dev = channel->dev;
 struct nal_h264_sps *sps;
 ssize_t size;
 unsigned int size_mb = SIZE_MACROBLOCK;
 /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */
 unsigned int crop_unit_x = 2;
 unsigned int crop_unit_y = 2;
 enum v4l2_mpeg_video_h264_profile profile;
 enum v4l2_mpeg_video_h264_level level;
 unsigned int cpb_size;
 unsigned int cpb_size_scale;

 sps = kzalloc(sizeof(*sps), GFP_KERNEL);
 if (!sps)
  return -ENOMEM;

 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile);
 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level);

 sps->profile_idc = nal_h264_profile(profile);
 sps->constraint_set0_flag = 0;
 sps->constraint_set1_flag = 1;
 sps->constraint_set2_flag = 0;
 sps->constraint_set3_flag = 0;
 sps->constraint_set4_flag = 0;
 sps->constraint_set5_flag = 0;
 sps->level_idc = nal_h264_level(level);
 sps->seq_parameter_set_id = 0;
 sps->log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4;
 sps->pic_order_cnt_type = 0;
 sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4;
 sps->max_num_ref_frames = 3;
 sps->gaps_in_frame_num_value_allowed_flag = 0;
 sps->pic_width_in_mbs_minus1 =
  DIV_ROUND_UP(channel->width, size_mb) - 1;
 sps->pic_height_in_map_units_minus1 =
  DIV_ROUND_UP(channel->height, size_mb) - 1;
 sps->frame_mbs_only_flag = 1;
 sps->mb_adaptive_frame_field_flag = 0;
 sps->direct_8x8_inference_flag = 1;
 sps->frame_cropping_flag =
  (channel->width % size_mb) || (channel->height % size_mb);
 if (sps->frame_cropping_flag) {
  sps->crop_left = 0;
  sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x;
  sps->crop_top = 0;
  sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y;
 }
 sps->vui_parameters_present_flag = 1;
 sps->vui.aspect_ratio_info_present_flag = 0;
 sps->vui.overscan_info_present_flag = 0;

 sps->vui.video_signal_type_present_flag = 1;
 sps->vui.video_format = 5; /* unspecified */
 sps->vui.video_full_range_flag = nal_h264_full_range(channel->quantization);
 sps->vui.colour_description_present_flag = 1;
 sps->vui.colour_primaries = nal_h264_color_primaries(channel->colorspace);
 sps->vui.transfer_characteristics =
  nal_h264_transfer_characteristics(channel->colorspace, channel->xfer_func);
 sps->vui.matrix_coefficients =
  nal_h264_matrix_coeffs(channel->colorspace, channel->ycbcr_enc);

 sps->vui.chroma_loc_info_present_flag = 1;
 sps->vui.chroma_sample_loc_type_top_field = 0;
 sps->vui.chroma_sample_loc_type_bottom_field = 0;

 sps->vui.timing_info_present_flag = 1;
 sps->vui.num_units_in_tick = channel->framerate.denominator;
 sps->vui.time_scale = 2 * channel->framerate.numerator;

 sps->vui.fixed_frame_rate_flag = 1;
 sps->vui.nal_hrd_parameters_present_flag = 0;
 sps->vui.vcl_hrd_parameters_present_flag = 1;
 sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0;
 /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */
 sps->vui.vcl_hrd_parameters.bit_rate_scale =
  ffs(channel->bitrate_peak) - 6;
 sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] =
  channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1;
 /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */
 cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size);
 cpb_size_scale = ffs(cpb_size) - 4;
 sps->vui.vcl_hrd_parameters.cpb_size_scale = cpb_size_scale;
 sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] =
  (cpb_size * 1000) / (1 << (4 + cpb_size_scale)) - 1;
 sps->vui.vcl_hrd_parameters.cbr_flag[0] =
  !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable);
 sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31;
 sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31;
 sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31;
 sps->vui.vcl_hrd_parameters.time_offset_length = 0;
 sps->vui.low_delay_hrd_flag = 0;
 sps->vui.pic_struct_present_flag = 1;
 sps->vui.bitstream_restriction_flag = 0;

 size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps);

 kfree(sps);

 return size;
}

static ssize_t allegro_h264_write_pps(struct allegro_channel *channel,
          void *dest, size_t n)
{
 struct allegro_dev *dev = channel->dev;
 struct nal_h264_pps *pps;
 ssize_t size;

 pps = kzalloc(sizeof(*pps), GFP_KERNEL);
 if (!pps)
  return -ENOMEM;

 pps->pic_parameter_set_id = 0;
 pps->seq_parameter_set_id = 0;
 pps->entropy_coding_mode_flag = 0;
 pps->bottom_field_pic_order_in_frame_present_flag = 0;
 pps->num_slice_groups_minus1 = 0;
 pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1;
 pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1;
 pps->weighted_pred_flag = 0;
 pps->weighted_bipred_idc = 0;
 pps->pic_init_qp_minus26 = 0;
 pps->pic_init_qs_minus26 = 0;
 pps->chroma_qp_index_offset = 0;
 pps->deblocking_filter_control_present_flag = 1;
 pps->constrained_intra_pred_flag = 0;
 pps->redundant_pic_cnt_present_flag = 0;
 pps->transform_8x8_mode_flag = 0;
 pps->pic_scaling_matrix_present_flag = 0;
 pps->second_chroma_qp_index_offset = 0;

 size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps);

 kfree(pps);

 return size;
}

static void allegro_channel_eos_event(struct allegro_channel *channel)
{
 const struct v4l2_event eos_event = {
  .type = V4L2_EVENT_EOS
 };

 v4l2_event_queue_fh(&channel->fh, &eos_event);
}

static ssize_t allegro_hevc_write_vps(struct allegro_channel *channel,
          void *dest, size_t n)
{
 struct allegro_dev *dev = channel->dev;
 struct nal_hevc_vps *vps;
 struct nal_hevc_profile_tier_level *ptl;
 ssize_t size;
 unsigned int num_ref_frames = channel->num_ref_idx_l0;
 s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
 s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
 s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);

 vps = kzalloc(sizeof(*vps), GFP_KERNEL);
 if (!vps)
  return -ENOMEM;

 vps->base_layer_internal_flag = 1;
 vps->base_layer_available_flag = 1;
 vps->temporal_id_nesting_flag = 1;

 ptl = &vps->profile_tier_level;
 ptl->general_profile_idc = nal_hevc_profile(profile);
 ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
 ptl->general_tier_flag = nal_hevc_tier(tier);
 ptl->general_progressive_source_flag = 1;
 ptl->general_frame_only_constraint_flag = 1;
 ptl->general_level_idc = nal_hevc_level(level);

 vps->sub_layer_ordering_info_present_flag = 0;
 vps->max_dec_pic_buffering_minus1[0] = num_ref_frames;
 vps->max_num_reorder_pics[0] = num_ref_frames;

 size = nal_hevc_write_vps(&dev->plat_dev->dev, dest, n, vps);

 kfree(vps);

 return size;
}

static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel,
          void *dest, size_t n)
{
 struct allegro_dev *dev = channel->dev;
 struct nal_hevc_sps *sps;
 struct nal_hevc_profile_tier_level *ptl;
 struct nal_hevc_vui_parameters *vui;
 struct nal_hevc_hrd_parameters *hrd;
 ssize_t size;
 unsigned int cpb_size;
 unsigned int num_ref_frames = channel->num_ref_idx_l0;
 s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile);
 s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level);
 s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier);

 sps = kzalloc(sizeof(*sps), GFP_KERNEL);
 if (!sps)
  return -ENOMEM;

 sps->temporal_id_nesting_flag = 1;

 ptl = &sps->profile_tier_level;
 ptl->general_profile_idc = nal_hevc_profile(profile);
 ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
 ptl->general_tier_flag = nal_hevc_tier(tier);
 ptl->general_progressive_source_flag = 1;
 ptl->general_frame_only_constraint_flag = 1;
 ptl->general_level_idc = nal_hevc_level(level);

 sps->seq_parameter_set_id = 0;
 sps->chroma_format_idc = 1; /* Only 4:2:0 sampling supported */
 sps->pic_width_in_luma_samples = round_up(channel->width, 8);
 sps->pic_height_in_luma_samples = round_up(channel->height, 8);
 sps->conf_win_right_offset =
  sps->pic_width_in_luma_samples - channel->width;
 sps->conf_win_bottom_offset =
  sps->pic_height_in_luma_samples - channel->height;
 sps->conformance_window_flag =
  sps->conf_win_right_offset || sps->conf_win_bottom_offset;

 sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4;

 sps->sub_layer_ordering_info_present_flag = 1;
 sps->max_dec_pic_buffering_minus1[0] = num_ref_frames;
 sps->max_num_reorder_pics[0] = num_ref_frames;

 sps->log2_min_luma_coding_block_size_minus3 =
  channel->min_cu_size - 3;
 sps->log2_diff_max_min_luma_coding_block_size =
  channel->max_cu_size - channel->min_cu_size;
 sps->log2_min_luma_transform_block_size_minus2 =
  channel->min_tu_size - 2;
 sps->log2_diff_max_min_luma_transform_block_size =
  channel->max_tu_size - channel->min_tu_size;
 sps->max_transform_hierarchy_depth_intra =
  channel->max_transfo_depth_intra;
 sps->max_transform_hierarchy_depth_inter =
  channel->max_transfo_depth_inter;

 sps->sps_temporal_mvp_enabled_flag = channel->temporal_mvp_enable;
 sps->strong_intra_smoothing_enabled_flag = channel->max_cu_size > 4;

 sps->vui_parameters_present_flag = 1;
 vui = &sps->vui;

 vui->video_signal_type_present_flag = 1;
 vui->video_format = 5; /* unspecified */
 vui->video_full_range_flag = nal_hevc_full_range(channel->quantization);
 vui->colour_description_present_flag = 1;
 vui->colour_primaries = nal_hevc_color_primaries(channel->colorspace);
 vui->transfer_characteristics = nal_hevc_transfer_characteristics(channel->colorspace,
           channel->xfer_func);
 vui->matrix_coeffs = nal_hevc_matrix_coeffs(channel->colorspace, channel->ycbcr_enc);

 vui->chroma_loc_info_present_flag = 1;
 vui->chroma_sample_loc_type_top_field = 0;
 vui->chroma_sample_loc_type_bottom_field = 0;

 vui->vui_timing_info_present_flag = 1;
 vui->vui_num_units_in_tick = channel->framerate.denominator;
 vui->vui_time_scale = channel->framerate.numerator;

 vui->bitstream_restriction_flag = 1;
 vui->motion_vectors_over_pic_boundaries_flag = 1;
 vui->restricted_ref_pic_lists_flag = 1;
 vui->log2_max_mv_length_horizontal = 15;
 vui->log2_max_mv_length_vertical = 15;

 vui->vui_hrd_parameters_present_flag = 1;
 hrd = &vui->nal_hrd_parameters;
 hrd->vcl_hrd_parameters_present_flag = 1;

 hrd->initial_cpb_removal_delay_length_minus1 = 31;
 hrd->au_cpb_removal_delay_length_minus1 = 30;
 hrd->dpb_output_delay_length_minus1 = 30;

 hrd->bit_rate_scale = ffs(channel->bitrate_peak) - 6;
 hrd->vcl_hrd[0].bit_rate_value_minus1[0] =
  (channel->bitrate_peak >> (6 + hrd->bit_rate_scale)) - 1;

 cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size) * 1000;
 hrd->cpb_size_scale = ffs(cpb_size) - 4;
 hrd->vcl_hrd[0].cpb_size_value_minus1[0] = (cpb_size >> (4 + hrd->cpb_size_scale)) - 1;

 hrd->vcl_hrd[0].cbr_flag[0] = !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable);

 size = nal_hevc_write_sps(&dev->plat_dev->dev, dest, n, sps);

 kfree(sps);

 return size;
}

static ssize_t allegro_hevc_write_pps(struct allegro_channel *channel,
          struct mcu_msg_encode_frame_response *msg,
          void *dest, size_t n)
{
 struct allegro_dev *dev = channel->dev;
 struct nal_hevc_pps *pps;
 ssize_t size;
 int i;

 pps = kzalloc(sizeof(*pps), GFP_KERNEL);
 if (!pps)
  return -ENOMEM;

 pps->pps_pic_parameter_set_id = 0;
 pps->pps_seq_parameter_set_id = 0;

 if (msg->num_column > 1 || msg->num_row > 1) {
  pps->tiles_enabled_flag = 1;
  pps->num_tile_columns_minus1 = msg->num_column - 1;
  pps->num_tile_rows_minus1 = msg->num_row - 1;

  for (i = 0; i < msg->num_column; i++)
   pps->column_width_minus1[i] = msg->tile_width[i] - 1;

  for (i = 0; i < msg->num_row; i++)
   pps->row_height_minus1[i] = msg->tile_height[i] - 1;
 }

 pps->loop_filter_across_tiles_enabled_flag =
  channel->enable_loop_filter_across_tiles;
 pps->pps_loop_filter_across_slices_enabled_flag =
  channel->enable_loop_filter_across_slices;
 pps->deblocking_filter_control_present_flag = 1;
 pps->deblocking_filter_override_enabled_flag =
  channel->enable_deblocking_filter_override;
 pps->pps_beta_offset_div2 = BETA_OFFSET_DIV_2;
 pps->pps_tc_offset_div2 = TC_OFFSET_DIV_2;

 pps->lists_modification_present_flag = channel->enable_reordering;

 size = nal_hevc_write_pps(&dev->plat_dev->dev, dest, n, pps);

 kfree(pps);

 return size;
}

static u64 allegro_put_buffer(struct allegro_channel *channel,
         struct list_head *list,
         struct vb2_v4l2_buffer *buffer)
{
 struct v4l2_m2m_buffer *b = container_of(buffer,
       struct v4l2_m2m_buffer, vb);
 struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b);

 mutex_lock(&channel->shadow_list_lock);
 list_add_tail(&shadow->head, list);
 mutex_unlock(&channel->shadow_list_lock);

 return ptr_to_u64(buffer);
}

static struct vb2_v4l2_buffer *
allegro_get_buffer(struct allegro_channel *channel,
     struct list_head *list, u64 handle)
{
 struct allegro_m2m_buffer *shadow, *tmp;
 struct vb2_v4l2_buffer *buffer = NULL;

 mutex_lock(&channel->shadow_list_lock);
 list_for_each_entry_safe(shadow, tmp, list, head) {
  if (handle == ptr_to_u64(&shadow->buf.vb)) {
   buffer = &shadow->buf.vb;
   list_del_init(&shadow->head);
   break;
  }
 }
 mutex_unlock(&channel->shadow_list_lock);

 return buffer;
}

static void allegro_channel_finish_frame(struct allegro_channel *channel,
  struct mcu_msg_encode_frame_response *msg)
{
 struct allegro_dev *dev = channel->dev;
 struct vb2_v4l2_buffer *src_buf;
 struct vb2_v4l2_buffer *dst_buf;
 struct {
  u32 offset;
  u32 size;
 } *partition;
 enum vb2_buffer_state state = VB2_BUF_STATE_ERROR;
 char *curr;
 ssize_t len;
 ssize_t free;

 src_buf = allegro_get_buffer(channel, &channel->source_shadow_list,
         msg->src_handle);
 if (!src_buf)
  v4l2_warn(&dev->v4l2_dev,
     "channel %d: invalid source buffer\n",
     channel->mcu_channel_id);

 dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list,
         msg->dst_handle);
 if (!dst_buf)
  v4l2_warn(&dev->v4l2_dev,
     "channel %d: invalid stream buffer\n",
     channel->mcu_channel_id);

 if (!src_buf || !dst_buf)
  goto err;

 if (v4l2_m2m_is_last_draining_src_buf(channel->fh.m2m_ctx, src_buf)) {
  dst_buf->flags |= V4L2_BUF_FLAG_LAST;
  allegro_channel_eos_event(channel);
  v4l2_m2m_mark_stopped(channel->fh.m2m_ctx);
 }

 dst_buf->sequence = channel->csequence++;

 if (msg->error_code & AL_ERROR) {
  v4l2_err(&dev->v4l2_dev,
    "channel %d: failed to encode frame: %s (%x)\n",
    channel->mcu_channel_id,
    allegro_err_to_string(msg->error_code),
    msg->error_code);
  goto err;
 }

 if (msg->partition_table_size != 1) {
  v4l2_warn(&dev->v4l2_dev,
     "channel %d: only handling first partition table entry (%d entries)\n",
     channel->mcu_channel_id, msg->partition_table_size);
 }

 if (msg->partition_table_offset +
     msg->partition_table_size * sizeof(*partition) >
     vb2_plane_size(&dst_buf->vb2_buf, 0)) {
  v4l2_err(&dev->v4l2_dev,
    "channel %d: partition table outside of dst_buf\n",
    channel->mcu_channel_id);
  goto err;
 }

 partition =
     vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset;
 if (partition->offset + partition->size >
     vb2_plane_size(&dst_buf->vb2_buf, 0)) {
  v4l2_err(&dev->v4l2_dev,
    "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n",
    channel->mcu_channel_id, partition->offset,
    partition->size);
  goto err;
 }

 v4l2_dbg(2, debug, &dev->v4l2_dev,
   "channel %d: encoded frame of size %d is at offset 0x%x\n",
   channel->mcu_channel_id, partition->size, partition->offset);

 /*
 * The payload must include the data before the partition offset,
 * because we will put the sps and pps data there.
 */

 vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
         partition->offset + partition->size);

 curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
 free = partition->offset;

 if (channel->codec == V4L2_PIX_FMT_HEVC && msg->is_idr) {
  len = allegro_hevc_write_vps(channel, curr, free);
  if (len < 0) {
   v4l2_err(&dev->v4l2_dev,
     "not enough space for video parameter set: %zd left\n",
     free);
   goto err;
  }
  curr += len;
  free -= len;
  v4l2_dbg(1, debug, &dev->v4l2_dev,
    "channel %d: wrote %zd byte VPS nal unit\n",
    channel->mcu_channel_id, len);
 }

 if (msg->is_idr) {
  if (channel->codec == V4L2_PIX_FMT_H264)
   len = allegro_h264_write_sps(channel, curr, free);
  else
   len = allegro_hevc_write_sps(channel, curr, free);
  if (len < 0) {
   v4l2_err(&dev->v4l2_dev,
     "not enough space for sequence parameter set: %zd left\n",
     free);
   goto err;
  }
  curr += len;
  free -= len;
  v4l2_dbg(1, debug, &dev->v4l2_dev,
    "channel %d: wrote %zd byte SPS nal unit\n",
    channel->mcu_channel_id, len);
 }

 if (msg->slice_type == AL_ENC_SLICE_TYPE_I) {
  if (channel->codec == V4L2_PIX_FMT_H264)
   len = allegro_h264_write_pps(channel, curr, free);
  else
   len = allegro_hevc_write_pps(channel, msg, curr, free);
  if (len < 0) {
   v4l2_err(&dev->v4l2_dev,
     "not enough space for picture parameter set: %zd left\n",
     free);
   goto err;
  }
  curr += len;
  free -= len;
  v4l2_dbg(1, debug, &dev->v4l2_dev,
    "channel %d: wrote %zd byte PPS nal unit\n",
    channel->mcu_channel_id, len);
 }

 if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) {
  dst_buf->vb2_buf.planes[0].data_offset = free;
  free = 0;
 } else {
  if (channel->codec == V4L2_PIX_FMT_H264)
   len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free);
  else
   len = nal_hevc_write_filler(&dev->plat_dev->dev, curr, free);
  if (len < 0) {
   v4l2_err(&dev->v4l2_dev,
     "failed to write %zd filler data\n", free);
   goto err;
  }
  curr += len;
  free -= len;
  v4l2_dbg(2, debug, &dev->v4l2_dev,
    "channel %d: wrote %zd bytes filler nal unit\n",
    channel->mcu_channel_id, len);
 }

 if (free != 0) {
  v4l2_err(&dev->v4l2_dev,
    "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n",
    free);
  goto err;
 }

 state = VB2_BUF_STATE_DONE;

 v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
 if (msg->is_idr)
  dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
 else
  dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;

 v4l2_dbg(1, debug, &dev->v4l2_dev,
   "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n",
   channel->mcu_channel_id,
   dst_buf->sequence,
   msg->is_idr ? "IDR, " : "",
   msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" :
   msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown",
   msg->qp, partition->size);

err:
 if (src_buf)
  v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);

 if (dst_buf)
  v4l2_m2m_buf_done(dst_buf, state);
}

static int allegro_handle_init(struct allegro_dev *dev,
          struct mcu_msg_init_response *msg)
{
 complete(&dev->init_complete);

 return 0;
}

static int
allegro_handle_create_channel(struct allegro_dev *dev,
         struct mcu_msg_create_channel_response *msg)
{
 struct allegro_channel *channel;
 int err = 0;
 struct create_channel_param param;

 channel = allegro_find_channel_by_user_id(dev, msg->user_id);
 if (IS_ERR(channel)) {
  v4l2_warn(&dev->v4l2_dev,
     "received %s for unknown user %d\n",
     msg_type_name(msg->header.type),
     msg->user_id);
  return -EINVAL;
 }

 if (msg->error_code) {
  v4l2_err(&dev->v4l2_dev,
    "user %d: mcu failed to create channel: %s (%x)\n",
    channel->user_id,
    allegro_err_to_string(msg->error_code),
    msg->error_code);
  err = -EIO;
  goto out;
 }

 channel->mcu_channel_id = msg->channel_id;
 v4l2_dbg(1, debug, &dev->v4l2_dev,
   "user %d: channel has channel id %d\n",
   channel->user_id, channel->mcu_channel_id);

 err = allegro_decode_config_blob(¶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;

 /*
 * The firmware is initialized after the mailbox is setup. We further
 * check the AL5_ITC_CPU_IRQ_STA register, if the firmware actually
 * triggered the interrupt. Although this should not happen, make sure
 * that we ignore interrupts, if the mailbox is not initialized.
 */

 if (!dev->mbox_status)
  return IRQ_NONE;

 allegro_mbox_notify(dev->mbox_status);

 return IRQ_HANDLED;
}

static void allegro_copy_firmware(struct allegro_dev *dev,
      const u8 * const buf, size_t size)
{
 int err = 0;

 v4l2_dbg(1, debug, &dev->v4l2_dev,
   "copy mcu firmware (%zu B) to SRAM\n", size);
 err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4);
 if (err)
  v4l2_err(&dev->v4l2_dev,
    "failed to copy firmware: %d\n", err);
}

static void allegro_copy_fw_codec(struct allegro_dev *dev,
      const u8 * const buf, size_t size)
{
 int err;
 dma_addr_t icache_offset, dcache_offset;

 /*
 * The downstream allocates 600 KB for the codec firmware to have some
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=96 G=96

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.