root/drivers/media/platform/allegro-dvt/allegro-core.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;
        struct mutex channels_lock;
};

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,
};

struct allegro_channel {
        struct kref ref;
        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 struct allegro_channel *file_to_channel(struct file *filp)
{
        return container_of(file_to_v4l2_fh(filp), struct allegro_channel, fh);
}

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_ref_get_channel_by_user_id(struct allegro_dev *dev,
                                   unsigned int user_id)
{
        struct allegro_channel *channel;

        guard(mutex)(&dev->channels_lock);

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

        return ERR_PTR(-EINVAL);
}

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

        guard(mutex)(&dev->channels_lock);

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

        return ERR_PTR(-EINVAL);
}

static void allegro_free_channel(struct kref *ref)
{
        struct allegro_channel *channel = container_of(ref, struct allegro_channel, ref);

        kfree(channel);
}

static int allegro_ref_put_channel(struct allegro_channel *channel)
{
        return kref_put(&channel->ref, allegro_free_channel);
}

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 unsigned int allegro_mbox_get_available(struct allegro_mbox *mbox)
{
        struct regmap *sram = mbox->dev->sram;
        unsigned int head, tail;

        regmap_read(sram, mbox->head, &head);
        regmap_read(sram, mbox->tail, &tail);

        if (tail >= head)
                return tail - head;
        else
                return mbox->size - (head - tail);
}

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 available, head;
        ssize_t size;
        size_t body_no_wrap;
        int stride = regmap_get_reg_stride(sram);

        available = allegro_mbox_get_available(mbox);
        if (available < sizeof(*header))
                return -EAGAIN;

        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;
        if (size > available)
                return -EAGAIN;

        /*
         * 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 int allegro_mbox_notify(struct allegro_mbox *mbox)
{
        struct allegro_dev *dev = mbox->dev;
        union mcu_msg_response *msg;
        u32 *tmp;
        int err;

        msg = kmalloc_obj(*msg);
        if (!msg)
                return -ENOMEM;

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

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

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

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

        allegro_handle_message(dev, msg);

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

        return err;
}

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

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

        if (!settings)
                return -EINVAL;

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

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

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

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

        return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        /*
         * The mcu expects the CPB size in units of a 90 kHz clock, but the
         * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores
         * the CPB size in kilobytes.
         */
        cpb_size_kbit = cpb_size * BITS_PER_BYTE;
        bitrate_kbps = bitrate / 1000;

        return (cpb_size_kbit * 90000) / bitrate_kbps;
}

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

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

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

        return ALLEGRO_ENTROPY_MODE_CAVLC;
}

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

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

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

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

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

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

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

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

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

        param->src_bit_depth = 8;

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

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

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

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

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

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

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

        param->max_num_merge_cand = 5;

        return 0;
}

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

        memset(&param, 0, sizeof(param));
        fill_create_channel_param(channel, &param);
        allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param));
        param.version = dev->fw_info->mailbox_version;
        size = allegro_encode_config_blob(blob->vaddr, &param);

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

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

        msg.user_id = channel->user_id;

        msg.blob = blob->vaddr;
        msg.blob_size = size;
        msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr);

        allegro_mbox_send(dev->mbox_command, &msg);

        return 0;
}

static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev,
                                            struct allegro_channel *channel)
{
        struct mcu_msg_destroy_channel msg;

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

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

        msg.channel_id = channel->mcu_channel_id;

        allegro_mbox_send(dev->mbox_command, &msg);

        return 0;
}

static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev,
                                              struct allegro_channel *channel,
                                              dma_addr_t paddr,
                                              unsigned long size,
                                              u64 dst_handle)
{
        struct mcu_msg_put_stream_buffer msg;

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

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

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

        allegro_mbox_send(dev->mbox_command, &msg);

        return 0;
}

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

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

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

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

        allegro_mbox_send(dev->mbox_command, &msg);

        return 0;
}

static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev,
                                             unsigned long timeout_ms)
{
        unsigned long 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_obj(*buffer);
                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_obj(*sps);
        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_obj(*pps);
        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_obj(*vps);
        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_obj(*sps);
        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_obj(*pps);
        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);
        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_ref_get_channel_by_user_id(dev, msg->user_id);
        if (IS_ERR(channel)) {
                v4l2_warn(&dev->v4l2_dev,
                          "received %s for unknown user %d\n",
                          msg_type_name(msg->header.type),
                          msg->user_id);
                return -EINVAL;
        }

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

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

        err = allegro_decode_config_blob(&param, msg, channel->config_blob.vaddr);
        allegro_free_buffer(channel->dev, &channel->config_blob);
        if (err)
                goto out;

        channel->num_ref_idx_l0 = param.num_ref_idx_l0;
        channel->num_ref_idx_l1 = param.num_ref_idx_l1;

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "channel %d: intermediate buffers: %d x %d bytes\n",
                 channel->mcu_channel_id,
                 msg->int_buffers_count, msg->int_buffers_size);
        err = allocate_intermediate_buffers(channel, msg->int_buffers_count,
                                            msg->int_buffers_size);
        if (err) {
                v4l2_err(&dev->v4l2_dev,
                         "channel %d: failed to allocate intermediate buffers\n",
                         channel->mcu_channel_id);
                goto out;
        }
        err = allegro_mcu_push_buffer_intermediate(channel);
        if (err)
                goto out;

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "channel %d: reference buffers: %d x %d bytes\n",
                 channel->mcu_channel_id,
                 msg->rec_buffers_count, msg->rec_buffers_size);
        err = allocate_reference_buffers(channel, msg->rec_buffers_count,
                                         msg->rec_buffers_size);
        if (err) {
                v4l2_err(&dev->v4l2_dev,
                         "channel %d: failed to allocate reference buffers\n",
                         channel->mcu_channel_id);
                goto out;
        }
        err = allegro_mcu_push_buffer_reference(channel);
        if (err)
                goto out;

out:
        channel->error = err;
        complete(&channel->completion);
        allegro_ref_put_channel(channel);

        /* 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_ref_get_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);
        allegro_ref_put_channel(channel);

        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_ref_get_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);
        allegro_ref_put_channel(channel);

        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;

        while (allegro_mbox_get_available(dev->mbox_status) > 0) {
                if (allegro_mbox_notify(dev->mbox_status))
                        break;
        }

        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 time_left;

        if (channel_exists(channel)) {
                reinit_completion(&channel->completion);
                allegro_mcu_send_destroy_channel(dev, channel);
                time_left = wait_for_completion_timeout(&channel->completion,
                                                        msecs_to_jiffies(5000));
                if (time_left == 0)
                        v4l2_warn(&dev->v4l2_dev,
                                  "channel %d: timeout while destroying\n",
                                  channel->mcu_channel_id);

                channel->mcu_channel_id = -1;
        }

        destroy_intermediate_buffers(channel);
        destroy_reference_buffers(channel);

        v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_level, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false);

        v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_level, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, false);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, false);

        v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate, false);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false);
        v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false);
        v4l2_ctrl_grab(channel->mpeg_video_gop_size, false);

        v4l2_ctrl_grab(channel->encoder_buffer, false);

        if (channel->user_id != -1) {
                clear_bit(channel->user_id, &dev->channel_user_ids);
                channel->user_id = -1;
        }
}

/*
 * Create the MCU channel
 *
 * After the channel has been created, the picture size, format, colorspace
 * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be
 * changed anymore.
 *
 * The channel can be created only once. The MCU will accept source buffers
 * and stream buffers only after a channel has been created.
 */
static int allegro_create_channel(struct allegro_channel *channel)
{
        struct allegro_dev *dev = channel->dev;
        unsigned long time_left;

        if (channel_exists(channel)) {
                v4l2_warn(&dev->v4l2_dev,
                          "channel already exists\n");
                return 0;
        }

        channel->user_id = allegro_next_user_id(dev);
        if (channel->user_id < 0) {
                v4l2_err(&dev->v4l2_dev,
                         "no free channels available\n");
                return -EBUSY;
        }
        set_bit(channel->user_id, &dev->channel_user_ids);

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "user %d: creating channel (%4.4s, %dx%d@%d)\n",
                 channel->user_id,
                 (char *)&channel->codec, channel->width, channel->height,
                 DIV_ROUND_UP(channel->framerate.numerator,
                              channel->framerate.denominator));

        v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_level, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true);

        v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_level, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, true);
        v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, true);

        v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate, true);
        v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true);
        v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true);
        v4l2_ctrl_grab(channel->mpeg_video_gop_size, true);

        v4l2_ctrl_grab(channel->encoder_buffer, true);

        reinit_completion(&channel->completion);
        allegro_mcu_send_create_channel(dev, channel);
        time_left = wait_for_completion_timeout(&channel->completion,
                                                msecs_to_jiffies(5000));
        if (time_left == 0) {
                v4l2_warn(&dev->v4l2_dev,
                          "user %d: timeout while creating channel\n",
                          channel->user_id);

                channel->error = -ETIMEDOUT;
        }

        if (channel->error)
                goto err;

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "channel %d: accepting buffers\n",
                 channel->mcu_channel_id);

        return 0;

err:
        allegro_destroy_channel(channel);

        return channel->error;
}

/**
 * allegro_channel_adjust() - Adjust channel parameters to current format
 * @channel: the channel to adjust
 *
 * Various parameters of a channel and their limits depend on the currently
 * set format. Adjust the parameters after a format change in one go.
 */
static void allegro_channel_adjust(struct allegro_channel *channel)
{
        struct allegro_dev *dev = channel->dev;
        u32 codec = channel->codec;
        struct v4l2_ctrl *ctrl;
        s64 min;
        s64 max;

        channel->sizeimage_encoded =
                estimate_stream_size(channel->width, channel->height);

        if (codec == V4L2_PIX_FMT_H264) {
                ctrl = channel->mpeg_video_h264_level;
                min = select_minimum_h264_level(channel->width, channel->height);
        } else {
                ctrl = channel->mpeg_video_hevc_level;
                min = select_minimum_hevc_level(channel->width, channel->height);
        }
        if (ctrl->minimum > min)
                v4l2_dbg(1, debug, &dev->v4l2_dev,
                         "%s.minimum: %lld -> %lld\n",
                         v4l2_ctrl_get_name(ctrl->id), ctrl->minimum, min);
        v4l2_ctrl_lock(ctrl);
        __v4l2_ctrl_modify_range(ctrl, min, ctrl->maximum,
                                 ctrl->step, ctrl->default_value);
        v4l2_ctrl_unlock(ctrl);

        ctrl = channel->mpeg_video_bitrate;
        if (codec == V4L2_PIX_FMT_H264)
                max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level));
        else
                max = hevc_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level));
        if (ctrl->maximum < max)
                v4l2_dbg(1, debug, &dev->v4l2_dev,
                         "%s: maximum: %lld -> %lld\n",
                         v4l2_ctrl_get_name(ctrl->id), ctrl->maximum, max);
        v4l2_ctrl_lock(ctrl);
        __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max,
                                 ctrl->step, ctrl->default_value);
        v4l2_ctrl_unlock(ctrl);

        ctrl = channel->mpeg_video_bitrate_peak;
        v4l2_ctrl_lock(ctrl);
        __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max,
                                 ctrl->step, ctrl->default_value);
        v4l2_ctrl_unlock(ctrl);

        v4l2_ctrl_activate(channel->mpeg_video_h264_profile,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_level,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_i_frame_qp,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_max_qp,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_min_qp,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_p_frame_qp,
                           codec == V4L2_PIX_FMT_H264);
        v4l2_ctrl_activate(channel->mpeg_video_h264_b_frame_qp,
                           codec == V4L2_PIX_FMT_H264);

        v4l2_ctrl_activate(channel->mpeg_video_hevc_profile,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_level,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_tier,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_i_frame_qp,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_max_qp,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_min_qp,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_p_frame_qp,
                           codec == V4L2_PIX_FMT_HEVC);
        v4l2_ctrl_activate(channel->mpeg_video_hevc_b_frame_qp,
                           codec == V4L2_PIX_FMT_HEVC);

        if (codec == V4L2_PIX_FMT_H264)
                channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM;
        channel->temporal_mvp_enable = true;
        channel->dbf_ovr_en = (codec == V4L2_PIX_FMT_H264);
        channel->enable_deblocking_filter_override = (codec == V4L2_PIX_FMT_HEVC);
        channel->enable_reordering = (codec == V4L2_PIX_FMT_HEVC);
        channel->enable_loop_filter_across_tiles = true;
        channel->enable_loop_filter_across_slices = true;

        if (codec == V4L2_PIX_FMT_H264) {
                channel->b_hrz_me_range = 8;
                channel->b_vrt_me_range = 8;
                channel->p_hrz_me_range = 16;
                channel->p_vrt_me_range = 16;
                channel->max_cu_size = ilog2(16);
                channel->min_cu_size = ilog2(8);
                channel->max_tu_size = ilog2(4);
                channel->min_tu_size = ilog2(4);
        } else {
                channel->b_hrz_me_range = 16;
                channel->b_vrt_me_range = 16;
                channel->p_hrz_me_range = 32;
                channel->p_vrt_me_range = 32;
                channel->max_cu_size = ilog2(32);
                channel->min_cu_size = ilog2(8);
                channel->max_tu_size = ilog2(32);
                channel->min_tu_size = ilog2(4);
        }
        channel->max_transfo_depth_intra = 1;
        channel->max_transfo_depth_inter = 1;
}

static void allegro_set_default_params(struct allegro_channel *channel)
{
        channel->width = ALLEGRO_WIDTH_DEFAULT;
        channel->height = ALLEGRO_HEIGHT_DEFAULT;
        channel->stride = round_up(channel->width, 32);
        channel->framerate = ALLEGRO_FRAMERATE_DEFAULT;

        channel->colorspace = V4L2_COLORSPACE_REC709;
        channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
        channel->quantization = V4L2_QUANTIZATION_DEFAULT;
        channel->xfer_func = V4L2_XFER_FUNC_DEFAULT;

        channel->pixelformat = V4L2_PIX_FMT_NV12;
        channel->sizeimage_raw = channel->stride * channel->height * 3 / 2;

        channel->codec = V4L2_PIX_FMT_H264;
}

static int allegro_queue_setup(struct vb2_queue *vq,
                               unsigned int *nbuffers, unsigned int *nplanes,
                               unsigned int sizes[],
                               struct device *alloc_devs[])
{
        struct allegro_channel *channel = vb2_get_drv_priv(vq);
        struct allegro_dev *dev = channel->dev;

        v4l2_dbg(2, debug, &dev->v4l2_dev,
                 "%s: queue setup[%s]: nplanes = %d\n",
                 V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture",
                 *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes);

        if (*nplanes != 0) {
                if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
                        if (sizes[0] < channel->sizeimage_raw)
                                return -EINVAL;
                } else {
                        if (sizes[0] < channel->sizeimage_encoded)
                                return -EINVAL;
                }
        } else {
                *nplanes = 1;
                if (V4L2_TYPE_IS_OUTPUT(vq->type))
                        sizes[0] = channel->sizeimage_raw;
                else
                        sizes[0] = channel->sizeimage_encoded;
        }

        return 0;
}

static int allegro_buf_prepare(struct vb2_buffer *vb)
{
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
        struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
        struct allegro_dev *dev = channel->dev;

        if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
                if (vbuf->field == V4L2_FIELD_ANY)
                        vbuf->field = V4L2_FIELD_NONE;
                if (vbuf->field != V4L2_FIELD_NONE) {
                        v4l2_err(&dev->v4l2_dev,
                                 "channel %d: unsupported field\n",
                                 channel->mcu_channel_id);
                        return -EINVAL;
                }
        }

        return 0;
}

static void allegro_buf_queue(struct vb2_buffer *vb)
{
        struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
        struct vb2_queue *q = vb->vb2_queue;

        if (V4L2_TYPE_IS_CAPTURE(q->type) &&
            vb2_is_streaming(q) &&
            v4l2_m2m_dst_buf_is_last(channel->fh.m2m_ctx)) {
                unsigned int i;

                for (i = 0; i < vb->num_planes; i++)
                        vb2_set_plane_payload(vb, i, 0);

                vbuf->field = V4L2_FIELD_NONE;
                vbuf->sequence = channel->csequence++;

                v4l2_m2m_last_buffer_done(channel->fh.m2m_ctx, vbuf);
                allegro_channel_eos_event(channel);
                return;
        }

        v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf);
}

static int allegro_start_streaming(struct vb2_queue *q, unsigned int count)
{
        struct allegro_channel *channel = vb2_get_drv_priv(q);
        struct allegro_dev *dev = channel->dev;

        v4l2_dbg(2, debug, &dev->v4l2_dev,
                 "%s: start streaming\n",
                 V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");

        v4l2_m2m_update_start_streaming_state(channel->fh.m2m_ctx, q);

        if (V4L2_TYPE_IS_OUTPUT(q->type))
                channel->osequence = 0;
        else
                channel->csequence = 0;

        return 0;
}

static void allegro_stop_streaming(struct vb2_queue *q)
{
        struct allegro_channel *channel = vb2_get_drv_priv(q);
        struct allegro_dev *dev = channel->dev;
        struct vb2_v4l2_buffer *buffer;
        struct allegro_m2m_buffer *shadow, *tmp;

        v4l2_dbg(2, debug, &dev->v4l2_dev,
                 "%s: stop streaming\n",
                 V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");

        if (V4L2_TYPE_IS_OUTPUT(q->type)) {
                mutex_lock(&channel->shadow_list_lock);
                list_for_each_entry_safe(shadow, tmp,
                                         &channel->source_shadow_list, head) {
                        list_del(&shadow->head);
                        v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
                }
                mutex_unlock(&channel->shadow_list_lock);

                while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx)))
                        v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
        } else {
                mutex_lock(&channel->shadow_list_lock);
                list_for_each_entry_safe(shadow, tmp,
                                         &channel->stream_shadow_list, head) {
                        list_del(&shadow->head);
                        v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
                }
                mutex_unlock(&channel->shadow_list_lock);

                allegro_destroy_channel(channel);
                while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx)))
                        v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
        }

        v4l2_m2m_update_stop_streaming_state(channel->fh.m2m_ctx, q);

        if (V4L2_TYPE_IS_OUTPUT(q->type) &&
            v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
                allegro_channel_eos_event(channel);
}

static const struct vb2_ops allegro_queue_ops = {
        .queue_setup = allegro_queue_setup,
        .buf_prepare = allegro_buf_prepare,
        .buf_queue = allegro_buf_queue,
        .start_streaming = allegro_start_streaming,
        .stop_streaming = allegro_stop_streaming,
};

static int allegro_queue_init(void *priv,
                              struct vb2_queue *src_vq,
                              struct vb2_queue *dst_vq)
{
        int err;
        struct allegro_channel *channel = priv;

        src_vq->dev = &channel->dev->plat_dev->dev;
        src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
        src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->drv_priv = channel;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->ops = &allegro_queue_ops;
        src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
        src_vq->lock = &channel->dev->lock;
        err = vb2_queue_init(src_vq);
        if (err)
                return err;

        dst_vq->dev = &channel->dev->plat_dev->dev;
        dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->drv_priv = channel;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->ops = &allegro_queue_ops;
        dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
        dst_vq->lock = &channel->dev->lock;
        err = vb2_queue_init(dst_vq);
        if (err)
                return err;

        return 0;
}

static int allegro_clamp_qp(struct allegro_channel *channel,
                            struct v4l2_ctrl *ctrl)
{
        struct v4l2_ctrl *next_ctrl;

        if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP)
                next_ctrl = channel->mpeg_video_h264_p_frame_qp;
        else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP)
                next_ctrl = channel->mpeg_video_h264_b_frame_qp;
        else
                return 0;

        /* Modify range automatically updates the value */
        __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val);

        return allegro_clamp_qp(channel, next_ctrl);
}

static int allegro_clamp_bitrate(struct allegro_channel *channel,
                                 struct v4l2_ctrl *ctrl)
{
        struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate;
        struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak;

        if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
            ctrl_bitrate_peak->val < ctrl_bitrate->val)
                ctrl_bitrate_peak->val = ctrl_bitrate->val;

        return 0;
}

static int allegro_try_ctrl(struct v4l2_ctrl *ctrl)
{
        struct allegro_channel *channel = container_of(ctrl->handler,
                                                       struct allegro_channel,
                                                       ctrl_handler);

        switch (ctrl->id) {
        case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
                allegro_clamp_bitrate(channel, ctrl);
                break;
        case V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER:
                if (!channel->dev->has_encoder_buffer)
                        ctrl->val = 0;
                break;
        }

        return 0;
}

static int allegro_s_ctrl(struct v4l2_ctrl *ctrl)
{
        struct allegro_channel *channel = container_of(ctrl->handler,
                                                       struct allegro_channel,
                                                       ctrl_handler);
        struct allegro_dev *dev = channel->dev;

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val);

        switch (ctrl->id) {
        case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
                channel->frame_rc_enable = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
                channel->bitrate = channel->mpeg_video_bitrate->val;
                channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val;
                v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak,
                                   ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
                break;
        case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
        case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
        case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
                allegro_clamp_qp(channel, ctrl);
                break;
        }

        return 0;
}

static const struct v4l2_ctrl_ops allegro_ctrl_ops = {
        .try_ctrl = allegro_try_ctrl,
        .s_ctrl = allegro_s_ctrl,
};

static const struct v4l2_ctrl_config allegro_encoder_buffer_ctrl_config = {
        .id = V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER,
        .name = "Encoder Buffer Enable",
        .type = V4L2_CTRL_TYPE_BOOLEAN,
        .min = 0,
        .max = 1,
        .step = 1,
        .def = 1,
};

static int allegro_open(struct file *file)
{
        struct video_device *vdev = video_devdata(file);
        struct allegro_dev *dev = video_get_drvdata(vdev);
        struct allegro_channel *channel = NULL;
        struct v4l2_ctrl_handler *handler;
        u64 mask;
        int ret;
        unsigned int bitrate_max;
        unsigned int bitrate_def;
        unsigned int cpb_size_max;
        unsigned int cpb_size_def;

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

        kref_init(&channel->ref);

        v4l2_fh_init(&channel->fh, vdev);

        init_completion(&channel->completion);
        INIT_LIST_HEAD(&channel->source_shadow_list);
        INIT_LIST_HEAD(&channel->stream_shadow_list);
        mutex_init(&channel->shadow_list_lock);

        channel->dev = dev;

        allegro_set_default_params(channel);

        handler = &channel->ctrl_handler;
        v4l2_ctrl_handler_init(handler, 0);
        channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_H264_PROFILE,
                        V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0,
                        V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
        mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B;
        channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_H264_LEVEL,
                        V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask,
                        V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
        channel->mpeg_video_h264_i_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
                                  0, 51, 1, 30);
        channel->mpeg_video_h264_max_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
                                  0, 51, 1, 51);
        channel->mpeg_video_h264_min_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
                                  0, 51, 1, 0);
        channel->mpeg_video_h264_p_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
                                  0, 51, 1, 30);
        channel->mpeg_video_h264_b_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
                                  0, 51, 1, 30);

        channel->mpeg_video_hevc_profile =
                v4l2_ctrl_new_std_menu(handler,
                                       &allegro_ctrl_ops,
                                       V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
                                       V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0x0,
                                       V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
        channel->mpeg_video_hevc_level =
                v4l2_ctrl_new_std_menu(handler,
                                       &allegro_ctrl_ops,
                                       V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
                                       V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0x0,
                                       V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
        channel->mpeg_video_hevc_tier =
                v4l2_ctrl_new_std_menu(handler,
                                       &allegro_ctrl_ops,
                                       V4L2_CID_MPEG_VIDEO_HEVC_TIER,
                                       V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, 0x0,
                                       V4L2_MPEG_VIDEO_HEVC_TIER_MAIN);
        channel->mpeg_video_hevc_i_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
                                  0, 51, 1, 30);
        channel->mpeg_video_hevc_max_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
                                  0, 51, 1, 51);
        channel->mpeg_video_hevc_min_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
                                  0, 51, 1, 0);
        channel->mpeg_video_hevc_p_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
                                  0, 51, 1, 30);
        channel->mpeg_video_hevc_b_frame_qp =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP,
                                  0, 51, 1, 30);

        channel->mpeg_video_frame_rc_enable =
                v4l2_ctrl_new_std(handler,
                                  &allegro_ctrl_ops,
                                  V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
                                  false, 0x1,
                                  true, false);
        channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
                        V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
                        V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);

        if (channel->codec == V4L2_PIX_FMT_H264) {
                bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
                bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
                cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
                cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
        } else {
                bitrate_max = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
                bitrate_def = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
                cpb_size_max = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
                cpb_size_def = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1);
        }
        channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_BITRATE,
                        0, bitrate_max, 1, bitrate_def);
        channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
                        0, bitrate_max, 1, bitrate_def);
        channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
                        0, cpb_size_max, 1, cpb_size_def);
        channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler,
                        &allegro_ctrl_ops,
                        V4L2_CID_MPEG_VIDEO_GOP_SIZE,
                        0, ALLEGRO_GOP_SIZE_MAX,
                        1, ALLEGRO_GOP_SIZE_DEFAULT);
        channel->encoder_buffer = v4l2_ctrl_new_custom(handler,
                        &allegro_encoder_buffer_ctrl_config, NULL);
        v4l2_ctrl_new_std(handler,
                          &allegro_ctrl_ops,
                          V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
                          1, 32,
                          1, 1);
        if (handler->error != 0) {
                ret = handler->error;
                goto error;
        }

        channel->fh.ctrl_handler = handler;

        v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode);

        v4l2_ctrl_handler_setup(handler);

        channel->mcu_channel_id = -1;
        channel->user_id = -1;

        INIT_LIST_HEAD(&channel->buffers_reference);
        INIT_LIST_HEAD(&channel->buffers_intermediate);

        channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel,
                                                allegro_queue_init);

        if (IS_ERR(channel->fh.m2m_ctx)) {
                ret = PTR_ERR(channel->fh.m2m_ctx);
                goto error;
        }

        scoped_guard(mutex, &dev->channels_lock) {
                list_add(&channel->list, &dev->channels);
        }

        v4l2_fh_add(&channel->fh, file);

        allegro_channel_adjust(channel);

        return 0;

error:
        v4l2_ctrl_handler_free(handler);
        kfree(channel);
        return ret;
}

static int allegro_release(struct file *file)
{
        struct allegro_channel *channel = file_to_channel(file);
        struct allegro_dev *dev = channel->dev;

        v4l2_m2m_ctx_release(channel->fh.m2m_ctx);

        scoped_guard(mutex, &dev->channels_lock) {
                list_del(&channel->list);
        }

        v4l2_ctrl_handler_free(&channel->ctrl_handler);

        v4l2_fh_del(&channel->fh, file);
        v4l2_fh_exit(&channel->fh);

        allegro_ref_put_channel(channel);

        return 0;
}

static int allegro_querycap(struct file *file, void *fh,
                            struct v4l2_capability *cap)
{
        strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
        strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card));

        return 0;
}

static int allegro_enum_fmt_vid(struct file *file, void *fh,
                                struct v4l2_fmtdesc *f)
{
        switch (f->type) {
        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
                if (f->index >= 1)
                        return -EINVAL;
                f->pixelformat = V4L2_PIX_FMT_NV12;
                break;
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
                if (f->index >= 2)
                        return -EINVAL;
                if (f->index == 0)
                        f->pixelformat = V4L2_PIX_FMT_H264;
                if (f->index == 1)
                        f->pixelformat = V4L2_PIX_FMT_HEVC;
                break;
        default:
                return -EINVAL;
        }
        return 0;
}

static int allegro_g_fmt_vid_cap(struct file *file, void *fh,
                                 struct v4l2_format *f)
{
        struct allegro_channel *channel = file_to_channel(file);

        f->fmt.pix.field = V4L2_FIELD_NONE;
        f->fmt.pix.width = channel->width;
        f->fmt.pix.height = channel->height;

        f->fmt.pix.colorspace = channel->colorspace;
        f->fmt.pix.ycbcr_enc = channel->ycbcr_enc;
        f->fmt.pix.quantization = channel->quantization;
        f->fmt.pix.xfer_func = channel->xfer_func;

        f->fmt.pix.pixelformat = channel->codec;
        f->fmt.pix.bytesperline = 0;
        f->fmt.pix.sizeimage = channel->sizeimage_encoded;

        return 0;
}

static int allegro_try_fmt_vid_cap(struct file *file, void *fh,
                                   struct v4l2_format *f)
{
        f->fmt.pix.field = V4L2_FIELD_NONE;

        f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width,
                                   ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX);
        f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height,
                                    ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX);

        if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_HEVC &&
            f->fmt.pix.pixelformat != V4L2_PIX_FMT_H264)
                f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;

        f->fmt.pix.bytesperline = 0;
        f->fmt.pix.sizeimage =
                estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height);

        return 0;
}

static int allegro_s_fmt_vid_cap(struct file *file, void *fh,
                                 struct v4l2_format *f)
{
        struct allegro_channel *channel = file_to_channel(file);
        struct vb2_queue *vq;
        int err;

        err = allegro_try_fmt_vid_cap(file, fh, f);
        if (err)
                return err;

        vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type);
        if (vb2_is_busy(vq))
                return -EBUSY;

        channel->codec = f->fmt.pix.pixelformat;

        allegro_channel_adjust(channel);

        return 0;
}

static int allegro_g_fmt_vid_out(struct file *file, void *fh,
                                 struct v4l2_format *f)
{
        struct allegro_channel *channel = file_to_channel(file);

        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 = file_to_channel(file);
        int err;

        err = allegro_try_fmt_vid_out(file, fh, f);
        if (err)
                return err;

        channel->width = f->fmt.pix.width;
        channel->height = f->fmt.pix.height;
        channel->stride = f->fmt.pix.bytesperline;
        channel->sizeimage_raw = f->fmt.pix.sizeimage;

        channel->colorspace = f->fmt.pix.colorspace;
        channel->ycbcr_enc = f->fmt.pix.ycbcr_enc;
        channel->quantization = f->fmt.pix.quantization;
        channel->xfer_func = f->fmt.pix.xfer_func;

        allegro_channel_adjust(channel);

        return 0;
}

static int allegro_channel_cmd_stop(struct allegro_channel *channel)
{
        if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
                allegro_channel_eos_event(channel);

        return 0;
}

static int allegro_channel_cmd_start(struct allegro_channel *channel)
{
        if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx))
                vb2_clear_last_buffer_dequeued(&channel->fh.m2m_ctx->cap_q_ctx.q);

        return 0;
}

static int allegro_encoder_cmd(struct file *file, void *fh,
                               struct v4l2_encoder_cmd *cmd)
{
        struct allegro_channel *channel = file_to_channel(file);
        int err;

        err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
        if (err)
                return err;

        err = v4l2_m2m_ioctl_encoder_cmd(file, fh, cmd);
        if (err)
                return err;

        if (cmd->cmd == V4L2_ENC_CMD_STOP)
                err = allegro_channel_cmd_stop(channel);

        if (cmd->cmd == V4L2_ENC_CMD_START)
                err = allegro_channel_cmd_start(channel);

        return err;
}

static int allegro_enum_framesizes(struct file *file, void *fh,
                                   struct v4l2_frmsizeenum *fsize)
{
        switch (fsize->pixel_format) {
        case V4L2_PIX_FMT_HEVC:
        case V4L2_PIX_FMT_H264:
        case V4L2_PIX_FMT_NV12:
                break;
        default:
                return -EINVAL;
        }

        if (fsize->index)
                return -EINVAL;

        fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
        fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN;
        fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX;
        fsize->stepwise.step_width = 1;
        fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN;
        fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX;
        fsize->stepwise.step_height = 1;

        return 0;
}

static int allegro_ioctl_streamon(struct file *file, void *priv,
                                  enum v4l2_buf_type type)
{
        struct allegro_channel *channel = file_to_channel(file);
        int err;

        if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
                err = allegro_create_channel(channel);
                if (err)
                        return err;
        }

        return v4l2_m2m_streamon(file, channel->fh.m2m_ctx, type);
}

static int allegro_g_parm(struct file *file, void *fh,
                          struct v4l2_streamparm *a)
{
        struct allegro_channel *channel = file_to_channel(file);
        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 = file_to_channel(file);
        struct v4l2_fract *timeperframe;
        int div;

        if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
                return -EINVAL;

        a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
        timeperframe = &a->parm.output.timeperframe;

        if (timeperframe->numerator == 0 || timeperframe->denominator == 0)
                return allegro_g_parm(file, fh, a);

        div = gcd(timeperframe->denominator, timeperframe->numerator);
        channel->framerate.numerator = timeperframe->denominator / div;
        channel->framerate.denominator = timeperframe->numerator / div;

        return 0;
}

static int allegro_subscribe_event(struct v4l2_fh *fh,
                                   const struct v4l2_event_subscription *sub)
{
        switch (sub->type) {
        case V4L2_EVENT_EOS:
                return v4l2_event_subscribe(fh, sub, 0, NULL);
        default:
                return v4l2_ctrl_subscribe_event(fh, sub);
        }
}

static const struct v4l2_ioctl_ops allegro_ioctl_ops = {
        .vidioc_querycap = allegro_querycap,
        .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid,
        .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid,
        .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap = allegro_s_fmt_vid_cap,
        .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out,
        .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out,
        .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out,

        .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
        .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,

        .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
        .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
        .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
        .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
        .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,

        .vidioc_streamon = allegro_ioctl_streamon,
        .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,

        .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
        .vidioc_encoder_cmd = allegro_encoder_cmd,
        .vidioc_enum_framesizes = allegro_enum_framesizes,

        .vidioc_g_parm          = allegro_g_parm,
        .vidioc_s_parm          = allegro_s_parm,

        .vidioc_subscribe_event = allegro_subscribe_event,
        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};

static const struct v4l2_file_operations allegro_fops = {
        .owner = THIS_MODULE,
        .open = allegro_open,
        .release = allegro_release,
        .poll = v4l2_m2m_fop_poll,
        .unlocked_ioctl = video_ioctl2,
        .mmap = v4l2_m2m_fop_mmap,
};

static int allegro_register_device(struct allegro_dev *dev)
{
        struct video_device *video_dev = &dev->video_dev;

        strscpy(video_dev->name, "allegro", sizeof(video_dev->name));
        video_dev->fops = &allegro_fops;
        video_dev->ioctl_ops = &allegro_ioctl_ops;
        video_dev->release = video_device_release_empty;
        video_dev->lock = &dev->lock;
        video_dev->v4l2_dev = &dev->v4l2_dev;
        video_dev->vfl_dir = VFL_DIR_M2M;
        video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
        video_set_drvdata(video_dev, dev);

        return video_register_device(video_dev, VFL_TYPE_VIDEO, 0);
}

static void allegro_device_run(void *priv)
{
        struct allegro_channel *channel = priv;
        struct allegro_dev *dev = channel->dev;
        struct vb2_v4l2_buffer *src_buf;
        struct vb2_v4l2_buffer *dst_buf;
        dma_addr_t src_y;
        dma_addr_t src_uv;
        dma_addr_t dst_addr;
        unsigned long dst_size;
        u64 src_handle;
        u64 dst_handle;

        dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx);
        dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
        dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0);
        dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list,
                                        dst_buf);
        allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size,
                                           dst_handle);

        src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx);
        src_buf->sequence = channel->osequence++;
        src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
        src_uv = src_y + (channel->stride * channel->height);
        src_handle = allegro_put_buffer(channel, &channel->source_shadow_list,
                                        src_buf);
        allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle);

        v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx);
}

static const struct v4l2_m2m_ops allegro_m2m_ops = {
        .device_run = allegro_device_run,
};

static int allegro_mcu_hw_init(struct allegro_dev *dev,
                               const struct fw_info *info)
{
        int err;

        dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd,
                                              info->mailbox_size);
        dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status,
                                             info->mailbox_size);
        if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) {
                v4l2_err(&dev->v4l2_dev,
                         "failed to initialize mailboxes\n");
                return -EIO;
        }

        err = allegro_encoder_buffer_init(dev, &dev->encoder_buffer);
        dev->has_encoder_buffer = (err == 0);
        if (!dev->has_encoder_buffer)
                v4l2_info(&dev->v4l2_dev, "encoder buffer not available\n");

        allegro_mcu_enable_interrupts(dev);

        /* The mcu sends INIT after reset. */
        allegro_mcu_start(dev);
        err = allegro_mcu_wait_for_init_timeout(dev, 5000);
        if (err < 0) {
                v4l2_err(&dev->v4l2_dev,
                         "mcu did not send INIT after reset\n");
                err = -EIO;
                goto err_disable_interrupts;
        }

        err = allegro_alloc_buffer(dev, &dev->suballocator,
                                   info->suballocator_size);
        if (err) {
                v4l2_err(&dev->v4l2_dev,
                         "failed to allocate %zu bytes for suballocator\n",
                         info->suballocator_size);
                goto err_reset_mcu;
        }

        allegro_mcu_send_init(dev, dev->suballocator.paddr,
                              dev->suballocator.size);
        err = allegro_mcu_wait_for_init_timeout(dev, 5000);
        if (err < 0) {
                v4l2_err(&dev->v4l2_dev,
                         "mcu failed to configure sub-allocator\n");
                err = -EIO;
                goto err_free_suballocator;
        }

        return 0;

err_free_suballocator:
        allegro_free_buffer(dev, &dev->suballocator);
err_reset_mcu:
        allegro_mcu_reset(dev);
err_disable_interrupts:
        allegro_mcu_disable_interrupts(dev);

        return err;
}

static int allegro_mcu_hw_deinit(struct allegro_dev *dev)
{
        int err;

        err = allegro_mcu_reset(dev);
        if (err)
                v4l2_warn(&dev->v4l2_dev,
                          "mcu failed to enter sleep state\n");

        err = allegro_mcu_disable_interrupts(dev);
        if (err)
                v4l2_warn(&dev->v4l2_dev,
                          "failed to disable interrupts\n");

        allegro_free_buffer(dev, &dev->suballocator);

        return 0;
}

static void allegro_fw_callback(const struct firmware *fw, void *context)
{
        struct allegro_dev *dev = context;
        const char *fw_codec_name = "al5e.fw";
        const struct firmware *fw_codec;
        int err;

        if (!fw)
                return;

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "requesting codec firmware '%s'\n", fw_codec_name);
        err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev);
        if (err)
                goto err_release_firmware;

        dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec);
        if (!dev->fw_info) {
                v4l2_err(&dev->v4l2_dev, "firmware is not supported\n");
                goto err_release_firmware_codec;
        }

        v4l2_info(&dev->v4l2_dev,
                  "using mcu firmware version '%s'\n", dev->fw_info->version);

        pm_runtime_enable(&dev->plat_dev->dev);
        err = pm_runtime_resume_and_get(&dev->plat_dev->dev);
        if (err)
                goto err_release_firmware_codec;

        /* Ensure that the mcu is sleeping at the reset vector */
        err = allegro_mcu_reset(dev);
        if (err) {
                v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n");
                goto err_suspend;
        }

        allegro_copy_firmware(dev, fw->data, fw->size);
        allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size);

        err = allegro_mcu_hw_init(dev, dev->fw_info);
        if (err) {
                v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n");
                goto err_free_fw_codec;
        }

        dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops);
        if (IS_ERR(dev->m2m_dev)) {
                v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n");
                goto err_mcu_hw_deinit;
        }

        err = allegro_register_device(dev);
        if (err) {
                v4l2_err(&dev->v4l2_dev, "failed to register video device\n");
                goto err_m2m_release;
        }

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "allegro codec registered as /dev/video%d\n",
                 dev->video_dev.num);

        dev->initialized = true;

        release_firmware(fw_codec);
        release_firmware(fw);

        return;

err_m2m_release:
        v4l2_m2m_release(dev->m2m_dev);
        dev->m2m_dev = NULL;
err_mcu_hw_deinit:
        allegro_mcu_hw_deinit(dev);
err_free_fw_codec:
        allegro_free_fw_codec(dev);
err_suspend:
        pm_runtime_put(&dev->plat_dev->dev);
        pm_runtime_disable(&dev->plat_dev->dev);
err_release_firmware_codec:
        release_firmware(fw_codec);
err_release_firmware:
        release_firmware(fw);
}

static int allegro_firmware_request_nowait(struct allegro_dev *dev)
{
        const char *fw = "al5e_b.fw";

        v4l2_dbg(1, debug, &dev->v4l2_dev,
                 "requesting firmware '%s'\n", fw);
        return request_firmware_nowait(THIS_MODULE, true, fw,
                                       &dev->plat_dev->dev, GFP_KERNEL, dev,
                                       allegro_fw_callback);
}

static int allegro_probe(struct platform_device *pdev)
{
        struct allegro_dev *dev;
        struct resource *res, *sram_res;
        int ret;
        int irq;
        void __iomem *regs, *sram_regs;

        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return -ENOMEM;
        dev->plat_dev = pdev;
        init_completion(&dev->init_complete);
        INIT_LIST_HEAD(&dev->channels);
        mutex_init(&dev->channels_lock);

        mutex_init(&dev->lock);

        dev->initialized = false;

        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
        if (!res) {
                dev_err(&pdev->dev,
                        "regs resource missing from device tree\n");
                return -EINVAL;
        }
        regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
        if (!regs) {
                dev_err(&pdev->dev, "failed to map registers\n");
                return -ENOMEM;
        }
        dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
                                            &allegro_regmap_config);
        if (IS_ERR(dev->regmap)) {
                dev_err(&pdev->dev, "failed to init regmap\n");
                return PTR_ERR(dev->regmap);
        }

        sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
        if (!sram_res) {
                dev_err(&pdev->dev,
                        "sram resource missing from device tree\n");
                return -EINVAL;
        }
        sram_regs = devm_ioremap(&pdev->dev,
                                 sram_res->start,
                                 resource_size(sram_res));
        if (!sram_regs) {
                dev_err(&pdev->dev, "failed to map sram\n");
                return -ENOMEM;
        }
        dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs,
                                          &allegro_sram_config);
        if (IS_ERR(dev->sram)) {
                dev_err(&pdev->dev, "failed to init sram\n");
                return PTR_ERR(dev->sram);
        }

        dev->settings = syscon_regmap_lookup_by_compatible("xlnx,vcu-settings");
        if (IS_ERR(dev->settings))
                dev_warn(&pdev->dev, "failed to open settings\n");

        dev->clk_core = devm_clk_get(&pdev->dev, "core_clk");
        if (IS_ERR(dev->clk_core))
                return PTR_ERR(dev->clk_core);

        dev->clk_mcu = devm_clk_get(&pdev->dev, "mcu_clk");
        if (IS_ERR(dev->clk_mcu))
                return PTR_ERR(dev->clk_mcu);

        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
                return irq;
        ret = devm_request_threaded_irq(&pdev->dev, irq,
                                        allegro_hardirq,
                                        allegro_irq_thread,
                                        IRQF_SHARED, dev_name(&pdev->dev), dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
                return ret;
        }

        ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
        if (ret)
                return ret;

        platform_set_drvdata(pdev, dev);

        ret = allegro_firmware_request_nowait(dev);
        if (ret < 0) {
                v4l2_err(&dev->v4l2_dev,
                         "failed to request firmware: %d\n", ret);
                v4l2_device_unregister(&dev->v4l2_dev);
                return ret;
        }

        return 0;
}

static void allegro_remove(struct platform_device *pdev)
{
        struct allegro_dev *dev = platform_get_drvdata(pdev);

        if (dev->initialized) {
                video_unregister_device(&dev->video_dev);
                if (dev->m2m_dev)
                        v4l2_m2m_release(dev->m2m_dev);
                allegro_mcu_hw_deinit(dev);
                allegro_free_fw_codec(dev);
        }

        pm_runtime_put(&dev->plat_dev->dev);
        pm_runtime_disable(&dev->plat_dev->dev);

        v4l2_device_unregister(&dev->v4l2_dev);
}

static int allegro_runtime_resume(struct device *device)
{
        struct allegro_dev *dev = dev_get_drvdata(device);
        struct regmap *settings = dev->settings;
        unsigned int clk_mcu;
        unsigned int clk_core;
        int err;

        if (!settings)
                return -EINVAL;

#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000)

        err = regmap_read(settings, VCU_CORE_CLK, &clk_core);
        if (err < 0)
                return err;
        err = clk_set_rate(dev->clk_core, MHZ_TO_HZ(clk_core));
        if (err < 0)
                return err;
        err = clk_prepare_enable(dev->clk_core);
        if (err)
                return err;

        err = regmap_read(settings, VCU_MCU_CLK, &clk_mcu);
        if (err < 0)
                goto disable_clk_core;
        err = clk_set_rate(dev->clk_mcu, MHZ_TO_HZ(clk_mcu));
        if (err < 0)
                goto disable_clk_core;
        err = clk_prepare_enable(dev->clk_mcu);
        if (err)
                goto disable_clk_core;

#undef MHZ_TO_HZ

        return 0;

disable_clk_core:
        clk_disable_unprepare(dev->clk_core);

        return err;
}

static int allegro_runtime_suspend(struct device *device)
{
        struct allegro_dev *dev = dev_get_drvdata(device);

        clk_disable_unprepare(dev->clk_mcu);
        clk_disable_unprepare(dev->clk_core);

        return 0;
}

static const struct of_device_id allegro_dt_ids[] = {
        { .compatible = "allegro,al5e-1.1" },
        { /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, allegro_dt_ids);

static const struct dev_pm_ops allegro_pm_ops = {
        .runtime_resume = allegro_runtime_resume,
        .runtime_suspend = allegro_runtime_suspend,
};

static struct platform_driver allegro_driver = {
        .probe = allegro_probe,
        .remove = allegro_remove,
        .driver = {
                .name = "allegro",
                .of_match_table = allegro_dt_ids,
                .pm = &allegro_pm_ops,
        },
};

module_platform_driver(allegro_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Tretter <kernel@pengutronix.de>");
MODULE_DESCRIPTION("Allegro DVT encoder driver");