diff options
author | Michael Tretter <m.tretter@pengutronix.de> | 2020-12-02 14:30:37 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2021-01-04 13:19:40 +0100 |
commit | d74d4e2359ec7985831192f9b5ee22ed5e55b81c (patch) | |
tree | 78edc9494750ab2f1496b212db37cbd34cd41435 /drivers/staging/media/allegro-dvt/allegro-core.c | |
parent | ce814ad4bb52bfc7c0472e6da0aa742ab88f4361 (diff) |
media: allegro: move driver out of staging
The stateful encoder API was finalized. Nothing is blocking the driver
from being moved out of staging.
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Diffstat (limited to 'drivers/staging/media/allegro-dvt/allegro-core.c')
-rw-r--r-- | drivers/staging/media/allegro-dvt/allegro-core.c | 3226 |
1 files changed, 0 insertions, 3226 deletions
diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c deleted file mode 100644 index 640451134072..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ /dev/null @@ -1,3226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> - * - * Allegro DVT video encoder driver - */ - -#include <linux/bits.h> -#include <linux/firmware.h> -#include <linux/gcd.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/log2.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/sizes.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mem2mem.h> -#include <media/videobuf2-dma-contig.h> -#include <media/videobuf2-v4l2.h> - -#include "allegro-mail.h" -#include "nal-h264.h" - -/* - * Support up to 4k video streams. The hardware actually supports higher - * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video - * Codec Unit v1.1) Chapter 3. - */ -#define ALLEGRO_WIDTH_MIN 128 -#define ALLEGRO_WIDTH_DEFAULT 1920 -#define ALLEGRO_WIDTH_MAX 3840 -#define ALLEGRO_HEIGHT_MIN 64 -#define ALLEGRO_HEIGHT_DEFAULT 1080 -#define ALLEGRO_HEIGHT_MAX 2160 - -#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 }) - -#define ALLEGRO_GOP_SIZE_DEFAULT 25 -#define ALLEGRO_GOP_SIZE_MAX 1000 - -/* - * MCU Control Registers - * - * The Zynq UltraScale+ Devices Register Reference documents the registers - * with an offset of 0x9000, which equals the size of the SRAM and one page - * gap. The driver handles SRAM and registers separately and, therefore, is - * oblivious of the offset. - */ -#define AL5_MCU_RESET 0x0000 -#define AL5_MCU_RESET_SOFT BIT(0) -#define AL5_MCU_RESET_REGS BIT(1) -#define AL5_MCU_RESET_MODE 0x0004 -#define AL5_MCU_RESET_MODE_SLEEP BIT(0) -#define AL5_MCU_RESET_MODE_HALT BIT(1) -#define AL5_MCU_STA 0x0008 -#define AL5_MCU_STA_SLEEP BIT(0) -#define AL5_MCU_WAKEUP 0x000c - -#define AL5_ICACHE_ADDR_OFFSET_MSB 0x0010 -#define AL5_ICACHE_ADDR_OFFSET_LSB 0x0014 -#define AL5_DCACHE_ADDR_OFFSET_MSB 0x0018 -#define AL5_DCACHE_ADDR_OFFSET_LSB 0x001c - -#define AL5_MCU_INTERRUPT 0x0100 -#define AL5_ITC_CPU_IRQ_MSK 0x0104 -#define AL5_ITC_CPU_IRQ_CLR 0x0108 -#define AL5_ITC_CPU_IRQ_STA 0x010C -#define AL5_ITC_CPU_IRQ_STA_TRIGGERED BIT(0) - -#define AXI_ADDR_OFFSET_IP 0x0208 - -/* - * The MCU accesses the system memory with a 2G offset compared to CPU - * physical addresses. - */ -#define MCU_CACHE_OFFSET SZ_2G - -/* - * The driver needs to reserve some space at the beginning of capture buffers, - * because it needs to write SPS/PPS NAL units. The encoder writes the actual - * frame data after the offset. - */ -#define ENCODER_STREAM_OFFSET SZ_64 - -#define SIZE_MACROBLOCK 16 - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -struct allegro_buffer { - void *vaddr; - dma_addr_t paddr; - size_t size; - struct list_head head; -}; - -struct allegro_dev; -struct allegro_channel; - -struct allegro_mbox { - struct allegro_dev *dev; - unsigned int head; - unsigned int tail; - unsigned int data; - size_t size; - /* protect mailbox from simultaneous accesses */ - struct mutex lock; -}; - -struct allegro_dev { - struct v4l2_device v4l2_dev; - struct video_device video_dev; - struct v4l2_m2m_dev *m2m_dev; - struct platform_device *plat_dev; - - /* mutex protecting vb2_queue structure */ - struct mutex lock; - - struct regmap *regmap; - struct regmap *sram; - - const struct fw_info *fw_info; - struct allegro_buffer firmware; - struct allegro_buffer suballocator; - - struct completion init_complete; - - /* The mailbox interface */ - struct allegro_mbox *mbox_command; - struct allegro_mbox *mbox_status; - - /* - * The downstream driver limits the users to 64 users, thus I can use - * a bitfield for the user_ids that are in use. See also user_id in - * struct allegro_channel. - */ - unsigned long channel_user_ids; - struct list_head channels; -}; - -static struct regmap_config allegro_regmap_config = { - .name = "regmap", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0xfff, - .cache_type = REGCACHE_NONE, -}; - -static struct regmap_config allegro_sram_config = { - .name = "sram", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x7fff, - .cache_type = REGCACHE_NONE, -}; - -enum allegro_state { - ALLEGRO_STATE_ENCODING, - ALLEGRO_STATE_DRAIN, - ALLEGRO_STATE_WAIT_FOR_BUFFER, - ALLEGRO_STATE_STOPPED, -}; - -#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) - -struct allegro_channel { - struct allegro_dev *dev; - struct v4l2_fh fh; - struct v4l2_ctrl_handler ctrl_handler; - - unsigned int width; - unsigned int height; - unsigned int stride; - struct v4l2_fract framerate; - - enum v4l2_colorspace colorspace; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - enum v4l2_xfer_func xfer_func; - - u32 pixelformat; - unsigned int sizeimage_raw; - unsigned int osequence; - - u32 codec; - enum v4l2_mpeg_video_h264_profile profile; - enum v4l2_mpeg_video_h264_level level; - unsigned int sizeimage_encoded; - unsigned int csequence; - - bool frame_rc_enable; - unsigned int bitrate; - unsigned int bitrate_peak; - unsigned int cpb_size; - unsigned int gop_size; - - struct allegro_buffer config_blob; - - unsigned int num_ref_idx_l0; - unsigned int num_ref_idx_l1; - - struct v4l2_ctrl *mpeg_video_h264_profile; - struct v4l2_ctrl *mpeg_video_h264_level; - struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; - struct v4l2_ctrl *mpeg_video_h264_max_qp; - struct v4l2_ctrl *mpeg_video_h264_min_qp; - struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; - struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; - struct v4l2_ctrl *mpeg_video_frame_rc_enable; - struct { /* video bitrate mode control cluster */ - struct v4l2_ctrl *mpeg_video_bitrate_mode; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_bitrate_peak; - }; - struct v4l2_ctrl *mpeg_video_cpb_size; - struct v4l2_ctrl *mpeg_video_gop_size; - - /* user_id is used to identify the channel during CREATE_CHANNEL */ - /* not sure, what to set here and if this is actually required */ - int user_id; - /* channel_id is set by the mcu and used by all later commands */ - int mcu_channel_id; - - struct list_head buffers_reference; - struct list_head buffers_intermediate; - - struct list_head source_shadow_list; - struct list_head stream_shadow_list; - /* protect shadow lists of buffers passed to firmware */ - struct mutex shadow_list_lock; - - struct list_head list; - struct completion completion; - - unsigned int error; - enum allegro_state state; -}; - -static inline int -allegro_set_state(struct allegro_channel *channel, enum allegro_state state) -{ - channel->state = state; - - return 0; -} - -static inline enum allegro_state -allegro_get_state(struct allegro_channel *channel) -{ - return channel->state; -} - -struct allegro_m2m_buffer { - struct v4l2_m2m_buffer buf; - struct list_head head; -}; - -#define to_allegro_m2m_buffer(__buf) \ - container_of(__buf, struct allegro_m2m_buffer, buf) - -struct fw_info { - unsigned int id; - unsigned int id_codec; - char *version; - unsigned int mailbox_cmd; - unsigned int mailbox_status; - size_t mailbox_size; - enum mcu_msg_version mailbox_version; - size_t suballocator_size; -}; - -static const struct fw_info supported_firmware[] = { - { - .id = 18296, - .id_codec = 96272, - .version = "v2018.2", - .mailbox_cmd = 0x7800, - .mailbox_status = 0x7c00, - .mailbox_size = 0x400 - 0x8, - .mailbox_version = MCU_MSG_VERSION_2018_2, - .suballocator_size = SZ_16M, - }, { - .id = 14680, - .id_codec = 126572, - .version = "v2019.2", - .mailbox_cmd = 0x7000, - .mailbox_status = 0x7800, - .mailbox_size = 0x800 - 0x8, - .mailbox_version = MCU_MSG_VERSION_2019_2, - .suballocator_size = SZ_32M, - }, -}; - -static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) -{ - if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) - v4l2_warn(&dev->v4l2_dev, - "address %pad is outside mcu window\n", &phys); - - return lower_32_bits(phys) | MCU_CACHE_OFFSET; -} - -static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) -{ - return lower_32_bits(size); -} - -static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) -{ - if (upper_32_bits(phys)) - v4l2_warn(&dev->v4l2_dev, - "address %pad cannot be used by codec\n", &phys); - - return lower_32_bits(phys); -} - -static inline u64 ptr_to_u64(const void *ptr) -{ - return (uintptr_t)ptr; -} - -/* Helper functions for channel and user operations */ - -static unsigned long allegro_next_user_id(struct allegro_dev *dev) -{ - if (dev->channel_user_ids == ~0UL) - return -EBUSY; - - return ffz(dev->channel_user_ids); -} - -static struct allegro_channel * -allegro_find_channel_by_user_id(struct allegro_dev *dev, - unsigned int user_id) -{ - struct allegro_channel *channel; - - list_for_each_entry(channel, &dev->channels, list) { - if (channel->user_id == user_id) - return channel; - } - - return ERR_PTR(-EINVAL); -} - -static struct allegro_channel * -allegro_find_channel_by_channel_id(struct allegro_dev *dev, - unsigned int channel_id) -{ - struct allegro_channel *channel; - - list_for_each_entry(channel, &dev->channels, list) { - if (channel->mcu_channel_id == channel_id) - return channel; - } - - return ERR_PTR(-EINVAL); -} - -static inline bool channel_exists(struct allegro_channel *channel) -{ - return channel->mcu_channel_id != -1; -} - -#define AL_ERROR 0x80 -#define AL_ERR_INIT_FAILED 0x81 -#define AL_ERR_NO_FRAME_DECODED 0x82 -#define AL_ERR_RESOLUTION_CHANGE 0x85 -#define AL_ERR_NO_MEMORY 0x87 -#define AL_ERR_STREAM_OVERFLOW 0x88 -#define AL_ERR_TOO_MANY_SLICES 0x89 -#define AL_ERR_BUF_NOT_READY 0x8c -#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d -#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e -#define AL_ERR_NOT_ENOUGH_CORES 0x8f -#define AL_ERR_REQUEST_MALFORMED 0x90 -#define AL_ERR_CMD_NOT_ALLOWED 0x91 -#define AL_ERR_INVALID_CMD_VALUE 0x92 - -static inline const char *allegro_err_to_string(unsigned int err) -{ - switch (err) { - case AL_ERR_INIT_FAILED: - return "initialization failed"; - case AL_ERR_NO_FRAME_DECODED: - return "no frame decoded"; - case AL_ERR_RESOLUTION_CHANGE: - return "resolution change"; - case AL_ERR_NO_MEMORY: - return "out of memory"; - case AL_ERR_STREAM_OVERFLOW: - return "stream buffer overflow"; - case AL_ERR_TOO_MANY_SLICES: - return "too many slices"; - case AL_ERR_BUF_NOT_READY: - return "buffer not ready"; - case AL_ERR_NO_CHANNEL_AVAILABLE: - return "no channel available"; - case AL_ERR_RESOURCE_UNAVAILABLE: - return "resource unavailable"; - case AL_ERR_NOT_ENOUGH_CORES: - return "not enough cores"; - case AL_ERR_REQUEST_MALFORMED: - return "request malformed"; - case AL_ERR_CMD_NOT_ALLOWED: - return "command not allowed"; - case AL_ERR_INVALID_CMD_VALUE: - return "invalid command value"; - case AL_ERROR: - default: - return "unknown error"; - } -} - -static unsigned int estimate_stream_size(unsigned int width, - unsigned int height) -{ - unsigned int offset = ENCODER_STREAM_OFFSET; - unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) * - DIV_ROUND_UP(height, SIZE_MACROBLOCK); - unsigned int pcm_size = SZ_256; - unsigned int partition_table = SZ_256; - - return round_up(offset + num_blocks * pcm_size + partition_table, 32); -} - -static enum v4l2_mpeg_video_h264_level -select_minimum_h264_level(unsigned int width, unsigned int height) -{ - unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK); - unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK); - unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb; - enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - - /* - * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and - * also specify limits regarding bit rate and CBP size. Only approximate - * the levels using the frame size. - * - * Level 5.1 allows up to 4k video resolution. - */ - if (frame_size_in_mb <= 99) - level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; - else if (frame_size_in_mb <= 396) - level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1; - else if (frame_size_in_mb <= 792) - level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1; - else if (frame_size_in_mb <= 1620) - level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2; - else if (frame_size_in_mb <= 3600) - level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1; - else if (frame_size_in_mb <= 5120) - level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2; - else if (frame_size_in_mb <= 8192) - level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - else if (frame_size_in_mb <= 8704) - level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; - else if (frame_size_in_mb <= 22080) - level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0; - else - level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; - - return level; -} - -static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 64000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 128000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 192000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 384000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 768000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 2000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 4000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 4000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 10000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 14000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 20000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 20000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 50000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 50000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 135000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 240000000; - } -} - -static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 175; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 350; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 500; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 1000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 2000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 2000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 4000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 4000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 10000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 14000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 20000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 25000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 62500; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 62500; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 135000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 240000; - } -} - -static const struct fw_info * -allegro_get_firmware_info(struct allegro_dev *dev, - const struct firmware *fw, - const struct firmware *fw_codec) -{ - int i; - unsigned int id = fw->size; - unsigned int id_codec = fw_codec->size; - - for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) - if (supported_firmware[i].id == id && - supported_firmware[i].id_codec == id_codec) - return &supported_firmware[i]; - - return NULL; -} - -/* - * Buffers that are used internally by the MCU. - */ - -static int allegro_alloc_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer, size_t size) -{ - buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, - &buffer->paddr, GFP_KERNEL); - if (!buffer->vaddr) - return -ENOMEM; - buffer->size = size; - - return 0; -} - -static void allegro_free_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer) -{ - if (buffer->vaddr) { - dma_free_coherent(&dev->plat_dev->dev, buffer->size, - buffer->vaddr, buffer->paddr); - buffer->vaddr = NULL; - buffer->size = 0; - } -} - -/* - * Mailbox interface to send messages to the MCU. - */ - -static void allegro_mcu_interrupt(struct allegro_dev *dev); -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg); - -static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, - unsigned int base, size_t size) -{ - struct allegro_mbox *mbox; - - mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); - if (!mbox) - return ERR_PTR(-ENOMEM); - - mbox->dev = dev; - - mbox->head = base; - mbox->tail = base + 0x4; - mbox->data = base + 0x8; - mbox->size = size; - mutex_init(&mbox->lock); - - regmap_write(dev->sram, mbox->head, 0); - regmap_write(dev->sram, mbox->tail, 0); - - return mbox; -} - -static int allegro_mbox_write(struct allegro_mbox *mbox, - const u32 *src, size_t size) -{ - struct regmap *sram = mbox->dev->sram; - unsigned int tail; - size_t size_no_wrap; - int err = 0; - int stride = regmap_get_reg_stride(sram); - - if (!src) - return -EINVAL; - - if (size > mbox->size) - return -EINVAL; - - mutex_lock(&mbox->lock); - regmap_read(sram, mbox->tail, &tail); - if (tail > mbox->size) { - err = -EIO; - goto out; - } - size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(sram, mbox->data + tail, - src, size_no_wrap / stride); - regmap_bulk_write(sram, mbox->data, - src + (size_no_wrap / sizeof(*src)), - (size - size_no_wrap) / stride); - regmap_write(sram, mbox->tail, (tail + size) % mbox->size); - -out: - mutex_unlock(&mbox->lock); - - return err; -} - -static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, - u32 *dst, size_t nbyte) -{ - struct { - u16 length; - u16 type; - } __attribute__ ((__packed__)) *header; - struct regmap *sram = mbox->dev->sram; - unsigned int head; - ssize_t size; - size_t body_no_wrap; - int stride = regmap_get_reg_stride(sram); - - regmap_read(sram, mbox->head, &head); - if (head > mbox->size) - return -EIO; - - /* Assume that the header does not wrap. */ - regmap_bulk_read(sram, mbox->data + head, - dst, sizeof(*header) / stride); - header = (void *)dst; - size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) - return -EIO; - if (size > nbyte) - return -EINVAL; - - /* - * The message might wrap within the mailbox. If the message does not - * wrap, the first read will read the entire message, otherwise the - * first read will read message until the end of the mailbox and the - * second read will read the remaining bytes from the beginning of the - * mailbox. - * - * Skip the header, as was already read to get the size of the body. - */ - body_no_wrap = min((size_t)header->length, - (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(sram, mbox->data + head + sizeof(*header), - dst + (sizeof(*header) / sizeof(*dst)), - body_no_wrap / stride); - regmap_bulk_read(sram, mbox->data, - dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), - (header->length - body_no_wrap) / stride); - - regmap_write(sram, mbox->head, (head + size) % mbox->size); - - return size; -} - -/** - * allegro_mbox_send() - Send a message via the mailbox - * @mbox: the mailbox which is used to send the message - * @msg: the message to send - */ -static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) -{ - struct allegro_dev *dev = mbox->dev; - ssize_t size; - int err; - u32 *tmp; - - tmp = kzalloc(mbox->size, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto out; - } - - size = allegro_encode_mail(tmp, msg); - - err = allegro_mbox_write(mbox, tmp, size); - kfree(tmp); - if (err) - goto out; - - allegro_mcu_interrupt(dev); - -out: - return err; -} - -/** - * allegro_mbox_notify() - Notify the mailbox about a new message - * @mbox: The allegro_mbox to notify - */ -static void allegro_mbox_notify(struct allegro_mbox *mbox) -{ - struct allegro_dev *dev = mbox->dev; - union mcu_msg_response *msg; - ssize_t size; - u32 *tmp; - int err; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return; - - msg->header.version = dev->fw_info->mailbox_version; - - tmp = kmalloc(mbox->size, GFP_KERNEL); - if (!tmp) - goto out; - - size = allegro_mbox_read(mbox, tmp, mbox->size); - if (size < 0) - goto out; - - err = allegro_decode_mail(msg, tmp); - if (err) - goto out; - - allegro_handle_message(dev, msg); - -out: - kfree(tmp); - kfree(msg); -} - -static void allegro_mcu_send_init(struct allegro_dev *dev, - dma_addr_t suballoc_dma, size_t suballoc_size) -{ - struct mcu_msg_init_request msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.version = dev->fw_info->mailbox_version; - - msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); - msg.suballoc_size = to_mcu_size(dev, suballoc_size); - - /* disable L2 cache */ - msg.l2_cache[0] = -1; - msg.l2_cache[1] = -1; - msg.l2_cache[2] = -1; - - allegro_mbox_send(dev->mbox_command, &msg); -} - -static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_NV12: - /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ - return 0x100 | 0x88; - default: - return -EINVAL; - } -} - -static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) -{ - switch (colorspace) { - case V4L2_COLORSPACE_REC709: - return 2; - case V4L2_COLORSPACE_SMPTE170M: - return 3; - case V4L2_COLORSPACE_SMPTE240M: - return 4; - case V4L2_COLORSPACE_SRGB: - return 7; - default: - /* UNKNOWN */ - return 0; - } -} - -static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) -{ - switch (profile) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - default: - return 66; - } -} - -static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 10; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 20; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 30; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 40; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 51; - } -} - -static u32 -v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) -{ - switch (mode) { - case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: - return 2; - case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: - default: - return 1; - } -} - -static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate) -{ - unsigned int cpb_size_kbit; - unsigned int bitrate_kbps; - - /* - * The mcu expects the CPB size in units of a 90 kHz clock, but the - * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores - * the CPB size in kilobytes. - */ - cpb_size_kbit = cpb_size * BITS_PER_BYTE; - bitrate_kbps = bitrate / 1000; - - return (cpb_size_kbit * 90000) / bitrate_kbps; -} - -static s16 get_qp_delta(int minuend, int subtrahend) -{ - if (minuend == subtrahend) - return -1; - else - return minuend - subtrahend; -} - -static int fill_create_channel_param(struct allegro_channel *channel, - struct create_channel_param *param) -{ - int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); - int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); - int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); - int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); - - param->width = channel->width; - param->height = channel->height; - param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); - param->colorspace = - v4l2_colorspace_to_mcu_colorspace(channel->colorspace); - param->src_mode = 0x0; - param->profile = v4l2_profile_to_mcu_profile(channel->profile); - param->constraint_set_flags = BIT(1); - param->codec = channel->codec; - param->level = v4l2_level_to_mcu_level(channel->level); - param->tier = 0; - - param->log2_max_poc = 10; - param->log2_max_frame_num = 4; - param->temporal_mvp_enable = 1; - - param->dbf_ovr_en = 1; - param->rdo_cost_mode = 1; - param->custom_lda = 1; - param->lf = 1; - param->lf_x_tile = 1; - param->lf_x_slice = 1; - - param->src_bit_depth = 8; - - param->beta_offset = -1; - param->tc_offset = -1; - param->num_slices = 1; - param->me_range[0] = 8; - param->me_range[1] = 8; - param->me_range[2] = 16; - param->me_range[3] = 16; - param->max_cu_size = ilog2(SIZE_MACROBLOCK); - param->min_cu_size = ilog2(8); - param->max_tu_size = 2; - param->min_tu_size = 2; - param->max_transfo_depth_intra = 1; - param->max_transfo_depth_inter = 1; - - param->prefetch_auto = 0; - param->prefetch_mem_offset = 0; - param->prefetch_mem_size = 0; - - param->rate_control_mode = channel->frame_rc_enable ? - v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; - - param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, - channel->bitrate_peak); - /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ - param->initial_rem_delay = param->cpb_size; - param->framerate = DIV_ROUND_UP(channel->framerate.numerator, - channel->framerate.denominator); - param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; - param->target_bitrate = channel->bitrate; - param->max_bitrate = channel->bitrate_peak; - param->initial_qp = i_frame_qp; - param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); - param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); - param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); - param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); - param->golden_ref = 0; - param->golden_delta = 2; - param->golden_ref_frequency = 10; - param->rate_control_option = 0x00000000; - - param->num_pixel = channel->width + channel->height; - param->max_psnr = 4200; - param->max_pixel_value = 255; - - param->gop_ctrl_mode = 0x00000002; - param->freq_idr = channel->gop_size; - param->freq_lt = 0; - param->gdr_mode = 0x00000000; - param->gop_length = channel->gop_size; - param->subframe_latency = 0x00000000; - - param->lda_factors[0] = 51; - param->lda_factors[1] = 90; - param->lda_factors[2] = 151; - param->lda_factors[3] = 151; - param->lda_factors[4] = 151; - param->lda_factors[5] = 151; - - param->max_num_merge_cand = 5; - - return 0; -} - -static int allegro_mcu_send_create_channel(struct allegro_dev *dev, - struct allegro_channel *channel) -{ - struct mcu_msg_create_channel msg; - struct allegro_buffer *blob = &channel->config_blob; - struct create_channel_param param; - size_t size; - - memset(¶m, 0, sizeof(param)); - fill_create_channel_param(channel, ¶m); - allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); - param.version = dev->fw_info->mailbox_version; - size = allegro_encode_config_blob(blob->vaddr, ¶m); - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; - msg.header.version = dev->fw_info->mailbox_version; - - msg.user_id = channel->user_id; - - msg.blob = blob->vaddr; - msg.blob_size = size; - msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, - struct allegro_channel *channel) -{ - struct mcu_msg_destroy_channel msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, - struct allegro_channel *channel, - dma_addr_t paddr, - unsigned long size, - u64 stream_id) -{ - struct mcu_msg_put_stream_buffer msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - msg.dma_addr = to_codec_addr(dev, paddr); - msg.mcu_addr = to_mcu_addr(dev, paddr); - msg.size = size; - msg.offset = ENCODER_STREAM_OFFSET; - /* copied to mcu_msg_encode_frame_response */ - msg.stream_id = stream_id; - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, - struct allegro_channel *channel, - dma_addr_t src_y, dma_addr_t src_uv, - u64 src_handle) -{ - struct mcu_msg_encode_frame msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - msg.encoding_options = AL_OPT_FORCE_LOAD; - msg.pps_qp = 26; /* qp are relative to 26 */ - msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ - /* src_handle is copied to mcu_msg_encode_frame_response */ - msg.src_handle = src_handle; - msg.src_y = to_codec_addr(dev, src_y); - msg.src_uv = to_codec_addr(dev, src_uv); - msg.stride = channel->stride; - msg.ep2 = 0x0; - msg.ep2_v = to_mcu_addr(dev, msg.ep2); - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, - unsigned long timeout_ms) -{ - unsigned long tmo; - - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) - return -ETIMEDOUT; - - reinit_completion(&dev->init_complete); - return 0; -} - -static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, - enum mcu_msg_type type) -{ - struct allegro_dev *dev = channel->dev; - struct mcu_msg_push_buffers_internal *msg; - struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = 0; - size_t size; - struct allegro_buffer *al_buffer; - struct list_head *list; - int err; - - switch (type) { - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - list = &channel->buffers_reference; - break; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - list = &channel->buffers_intermediate; - break; - default: - return -EINVAL; - } - - list_for_each_entry(al_buffer, list, head) - num_buffers++; - size = struct_size(msg, buffer, num_buffers); - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->header.type = type; - msg->header.version = dev->fw_info->mailbox_version; - - msg->channel_id = channel->mcu_channel_id; - msg->num_buffers = num_buffers; - - buffer = msg->buffer; - list_for_each_entry(al_buffer, list, head) { - buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); - buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); - buffer->size = to_mcu_size(dev, al_buffer->size); - buffer++; - } - - err = allegro_mbox_send(dev->mbox_command, msg); - - kfree(msg); - return err; -} - -static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allocate_buffers_internal(struct allegro_channel *channel, - struct list_head *list, - size_t n, size_t size) -{ - struct allegro_dev *dev = channel->dev; - unsigned int i; - int err; - struct allegro_buffer *buffer, *tmp; - - for (i = 0; i < n; i++) { - buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - err = -ENOMEM; - goto err; - } - INIT_LIST_HEAD(&buffer->head); - - err = allegro_alloc_buffer(dev, buffer, size); - if (err) - goto err; - list_add(&buffer->head, list); - } - - return 0; - -err: - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } - return err; -} - -static void destroy_buffers_internal(struct allegro_channel *channel, - struct list_head *list) -{ - struct allegro_dev *dev = channel->dev; - struct allegro_buffer *buffer, *tmp; - - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } -} - -static void destroy_reference_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, &channel->buffers_reference); -} - -static void destroy_intermediate_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, - &channel->buffers_intermediate); -} - -static int allocate_intermediate_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_intermediate, - n, size); -} - -static int allocate_reference_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_reference, - n, PAGE_ALIGN(size)); -} - -static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, - void *dest, size_t n) -{ - struct allegro_dev *dev = channel->dev; - struct nal_h264_sps *sps; - ssize_t size; - unsigned int size_mb = SIZE_MACROBLOCK; - /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ - unsigned int crop_unit_x = 2; - unsigned int crop_unit_y = 2; - - sps = kzalloc(sizeof(*sps), GFP_KERNEL); - if (!sps) - return -ENOMEM; - - sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); - sps->constraint_set0_flag = 0; - sps->constraint_set1_flag = 1; - sps->constraint_set2_flag = 0; - sps->constraint_set3_flag = 0; - sps->constraint_set4_flag = 0; - sps->constraint_set5_flag = 0; - sps->level_idc = nal_h264_level_from_v4l2(channel->level); - sps->seq_parameter_set_id = 0; - sps->log2_max_frame_num_minus4 = 0; - sps->pic_order_cnt_type = 0; - sps->log2_max_pic_order_cnt_lsb_minus4 = 6; - sps->max_num_ref_frames = 3; - sps->gaps_in_frame_num_value_allowed_flag = 0; - sps->pic_width_in_mbs_minus1 = - DIV_ROUND_UP(channel->width, size_mb) - 1; - sps->pic_height_in_map_units_minus1 = - DIV_ROUND_UP(channel->height, size_mb) - 1; - sps->frame_mbs_only_flag = 1; - sps->mb_adaptive_frame_field_flag = 0; - sps->direct_8x8_inference_flag = 1; - sps->frame_cropping_flag = - (channel->width % size_mb) || (channel->height % size_mb); - if (sps->frame_cropping_flag) { - sps->crop_left = 0; - sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; - sps->crop_top = 0; - sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; - } - sps->vui_parameters_present_flag = 1; - sps->vui.aspect_ratio_info_present_flag = 0; - sps->vui.overscan_info_present_flag = 0; - sps->vui.video_signal_type_present_flag = 1; - sps->vui.video_format = 1; - sps->vui.video_full_range_flag = 0; - sps->vui.colour_description_present_flag = 1; - sps->vui.colour_primaries = 5; - sps->vui.transfer_characteristics = 5; - sps->vui.matrix_coefficients = 5; - sps->vui.chroma_loc_info_present_flag = 1; - sps->vui.chroma_sample_loc_type_top_field = 0; - sps->vui.chroma_sample_loc_type_bottom_field = 0; - - sps->vui.timing_info_present_flag = 1; - sps->vui.num_units_in_tick = channel->framerate.denominator; - sps->vui.time_scale = 2 * channel->framerate.numerator; - - sps->vui.fixed_frame_rate_flag = 1; - sps->vui.nal_hrd_parameters_present_flag = 0; - sps->vui.vcl_hrd_parameters_present_flag = 1; - sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; - sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; - sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ - sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = - channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ - sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = - (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; - sps->vui.vcl_hrd_parameters.cbr_flag[0] = - !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); - sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.time_offset_length = 0; - sps->vui.low_delay_hrd_flag = 0; - sps->vui.pic_struct_present_flag = 1; - sps->vui.bitstream_restriction_flag = 0; - - size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps); - - kfree(sps); - - return size; -} - -static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, - void *dest, size_t n) -{ - struct allegro_dev *dev = channel->dev; - struct nal_h264_pps *pps; - ssize_t size; - - pps = kzalloc(sizeof(*pps), GFP_KERNEL); - if (!pps) - return -ENOMEM; - - pps->pic_parameter_set_id = 0; - pps->seq_parameter_set_id = 0; - pps->entropy_coding_mode_flag = 0; - pps->bottom_field_pic_order_in_frame_present_flag = 0; - pps->num_slice_groups_minus1 = 0; - pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; - pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; - pps->weighted_pred_flag = 0; - pps->weighted_bipred_idc = 0; - pps->pic_init_qp_minus26 = 0; - pps->pic_init_qs_minus26 = 0; - pps->chroma_qp_index_offset = 0; - pps->deblocking_filter_control_present_flag = 1; - pps->constrained_intra_pred_flag = 0; - pps->redundant_pic_cnt_present_flag = 0; - pps->transform_8x8_mode_flag = 0; - pps->pic_scaling_matrix_present_flag = 0; - pps->second_chroma_qp_index_offset = 0; - - size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps); - - kfree(pps); - - return size; -} - -static bool allegro_channel_is_at_eos(struct allegro_channel *channel) -{ - bool is_at_eos = false; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_STOPPED: - is_at_eos = true; - break; - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - mutex_lock(&channel->shadow_list_lock); - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && - list_empty(&channel->source_shadow_list)) - is_at_eos = true; - mutex_unlock(&channel->shadow_list_lock); - break; - default: - break; - } - - return is_at_eos; -} - -static void allegro_channel_buf_done(struct allegro_channel *channel, - struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) -{ - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - if (allegro_channel_is_at_eos(channel)) { - buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&channel->fh, &eos_event); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - } - - v4l2_m2m_buf_done(buf, state); -} - -static u64 allegro_put_buffer(struct allegro_channel *channel, - struct list_head *list, - struct vb2_v4l2_buffer *buffer) -{ - struct v4l2_m2m_buffer *b = container_of(buffer, - struct v4l2_m2m_buffer, vb); - struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); - - mutex_lock(&channel->shadow_list_lock); - list_add_tail(&shadow->head, list); - mutex_unlock(&channel->shadow_list_lock); - - return ptr_to_u64(buffer); -} - -static struct vb2_v4l2_buffer * -allegro_get_buffer(struct allegro_channel *channel, - struct list_head *list, u64 handle) -{ - struct allegro_m2m_buffer *shadow, *tmp; - struct vb2_v4l2_buffer *buffer = NULL; - - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, list, head) { - if (handle == ptr_to_u64(&shadow->buf.vb)) { - buffer = &shadow->buf.vb; - list_del_init(&shadow->head); - break; - } - } - mutex_unlock(&channel->shadow_list_lock); - - return buffer; -} - -static void allegro_channel_finish_frame(struct allegro_channel *channel, - struct mcu_msg_encode_frame_response *msg) -{ - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *src_buf; - struct vb2_v4l2_buffer *dst_buf; - struct { - u32 offset; - u32 size; - } *partition; - enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; - char *curr; - ssize_t len; - ssize_t free; - - src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, - msg->src_handle); - if (!src_buf) - v4l2_warn(&dev->v4l2_dev, - "channel %d: invalid source buffer\n", - channel->mcu_channel_id); - - dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, - msg->stream_id); - if (!dst_buf) - v4l2_warn(&dev->v4l2_dev, - "channel %d: invalid stream buffer\n", - channel->mcu_channel_id); - - if (!src_buf || !dst_buf) - goto err; - - dst_buf->sequence = channel->csequence++; - - if (msg->error_code & AL_ERROR) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to encode frame: %s (%x)\n", - channel->mcu_channel_id, - allegro_err_to_string(msg->error_code), - msg->error_code); - goto err; - } - - if (msg->partition_table_size != 1) { - v4l2_warn(&dev->v4l2_dev, - "channel %d: only handling first partition table entry (%d entries)\n", - channel->mcu_channel_id, msg->partition_table_size); - } - - if (msg->partition_table_offset + - msg->partition_table_size * sizeof(*partition) > - vb2_plane_size(&dst_buf->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, - "channel %d: partition table outside of dst_buf\n", - channel->mcu_channel_id); - goto err; - } - - partition = - vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset; - if (partition->offset + partition->size > - vb2_plane_size(&dst_buf->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, - "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n", - channel->mcu_channel_id, partition->offset, - partition->size); - goto err; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: encoded frame of size %d is at offset 0x%x\n", - channel->mcu_channel_id, partition->size, partition->offset); - - /* - * The payload must include the data before the partition offset, - * because we will put the sps and pps data there. - */ - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - partition->offset + partition->size); - - curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - free = partition->offset; - if (msg->is_idr) { - len = allegro_h264_write_sps(channel, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "not enough space for sequence parameter set: %zd left\n", - free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: wrote %zd byte SPS nal unit\n", - channel->mcu_channel_id, len); - } - - if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { - len = allegro_h264_write_pps(channel, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "not enough space for picture parameter set: %zd left\n", - free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: wrote %zd byte PPS nal unit\n", - channel->mcu_channel_id, len); - } - - if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { - dst_buf->vb2_buf.planes[0].data_offset = free; - free = 0; - } else { - len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to write %zd filler data\n", free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: wrote %zd bytes filler nal unit\n", - channel->mcu_channel_id, len); - } - - if (free != 0) { - v4l2_err(&dev->v4l2_dev, - "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n", - free); - goto err; - } - - state = VB2_BUF_STATE_DONE; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - if (msg->is_idr) - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", - channel->mcu_channel_id, - dst_buf->sequence, - msg->is_idr ? "IDR, " : "", - msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : - msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", - msg->qp, partition->size); - -err: - if (src_buf) - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - if (dst_buf) - allegro_channel_buf_done(channel, dst_buf, state); -} - -static int allegro_handle_init(struct allegro_dev *dev, - struct mcu_msg_init_response *msg) -{ - complete(&dev->init_complete); - - return 0; -} - -static int -allegro_handle_create_channel(struct allegro_dev *dev, - struct mcu_msg_create_channel_response *msg) -{ - struct allegro_channel *channel; - int err = 0; - struct create_channel_param param; - - channel = allegro_find_channel_by_user_id(dev, msg->user_id); - if (IS_ERR(channel)) { - v4l2_warn(&dev->v4l2_dev, - "received %s for unknown user %d\n", - msg_type_name(msg->header.type), - msg->user_id); - return -EINVAL; - } - - if (msg->error_code) { - v4l2_err(&dev->v4l2_dev, - "user %d: mcu failed to create channel: %s (%x)\n", - channel->user_id, - allegro_err_to_string(msg->error_code), - msg->error_code); - err = -EIO; - goto out; - } - - channel->mcu_channel_id = msg->channel_id; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "user %d: channel has channel id %d\n", - channel->user_id, channel->mcu_channel_id); - - err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); - allegro_free_buffer(channel->dev, &channel->config_blob); - if (err) - goto out; - - channel->num_ref_idx_l0 = param.num_ref_idx_l0; - channel->num_ref_idx_l1 = param.num_ref_idx_l1; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: intermediate buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->int_buffers_count, msg->int_buffers_size); - err = allocate_intermediate_buffers(channel, msg->int_buffers_count, - msg->int_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate intermediate buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_intermediate(channel); - if (err) - goto out; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: reference buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->rec_buffers_count, msg->rec_buffers_size); - err = allocate_reference_buffers(channel, msg->rec_buffers_count, - msg->rec_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate reference buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_reference(channel); - if (err) - goto out; - -out: - channel->error = err; - complete(&channel->completion); - - /* Handled successfully, error is passed via channel->error */ - return 0; -} - -static int -allegro_handle_destroy_channel(struct allegro_dev *dev, - struct mcu_msg_destroy_channel_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "user %d: vcu destroyed channel %d\n", - channel->user_id, channel->mcu_channel_id); - complete(&channel->completion); - - return 0; -} - -static int -allegro_handle_encode_frame(struct allegro_dev *dev, - struct mcu_msg_encode_frame_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - allegro_channel_finish_frame(channel, msg); - - return 0; -} - -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg) -{ - switch (msg->header.type) { - case MCU_MSG_TYPE_INIT: - allegro_handle_init(dev, &msg->init); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - allegro_handle_create_channel(dev, &msg->create_channel); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - allegro_handle_destroy_channel(dev, &msg->destroy_channel); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - allegro_handle_encode_frame(dev, &msg->encode_frame); - break; - default: - v4l2_warn(&dev->v4l2_dev, - "%s: unknown message %s\n", - __func__, msg_type_name(msg->header.type)); - break; - } -} - -static irqreturn_t allegro_hardirq(int irq, void *data) -{ - struct allegro_dev *dev = data; - unsigned int status; - - regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); - if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) - return IRQ_NONE; - - regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); - - return IRQ_WAKE_THREAD; -} - -static irqreturn_t allegro_irq_thread(int irq, void *data) -{ - struct allegro_dev *dev = data; - - allegro_mbox_notify(dev->mbox_status); - - return IRQ_HANDLED; -} - -static void allegro_copy_firmware(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err = 0; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy mcu firmware (%zu B) to SRAM\n", size); - err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); - if (err) - v4l2_err(&dev->v4l2_dev, - "failed to copy firmware: %d\n", err); -} - -static void allegro_copy_fw_codec(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err; - dma_addr_t icache_offset, dcache_offset; - - /* - * The downstream allocates 600 KB for the codec firmware to have some - * extra space for "possible extensions." My tests were fine with - * allocating just enough memory for the actual firmware, but I am not - * sure that the firmware really does not use the remaining space. - */ - err = allegro_alloc_buffer(dev, &dev->firmware, size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for firmware\n", size); - return; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy codec firmware (%zd B) to phys %pad\n", - size, &dev->firmware.paddr); - memcpy(dev->firmware.vaddr, buf, size); - - regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, - upper_32_bits(dev->firmware.paddr)); - - icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "icache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(icache_offset), lower_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, - upper_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, - lower_32_bits(icache_offset)); - - dcache_offset = - (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "dcache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, - upper_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, - lower_32_bits(dcache_offset)); -} - -static void allegro_free_fw_codec(struct allegro_dev *dev) -{ - allegro_free_buffer(dev, &dev->firmware); -} - -/* - * Control functions for the MCU - */ - -static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); -} - -static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); -} - -static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status != AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} - -static int allegro_mcu_start(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - int err; - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); - if (err) - return err; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status == AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - return 0; -} - -static int allegro_mcu_reset(struct allegro_dev *dev) -{ - int err; - - /* - * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu - * does not go to sleep after the reset. - */ - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - err = regmap_write(dev->regmap, - AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); - if (err < 0) - return err; - - err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); - if (err < 0) - return err; - - return allegro_mcu_wait_for_sleep(dev); -} - -static void allegro_mcu_interrupt(struct allegro_dev *dev) -{ - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); -} - -static void allegro_destroy_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - - if (channel_exists(channel)) { - reinit_completion(&channel->completion); - allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - v4l2_warn(&dev->v4l2_dev, - "channel %d: timeout while destroying\n", - channel->mcu_channel_id); - - channel->mcu_channel_id = -1; - } - - destroy_intermediate_buffers(channel); - destroy_reference_buffers(channel); - - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); - - if (channel->user_id != -1) { - clear_bit(channel->user_id, &dev->channel_user_ids); - channel->user_id = -1; - } -} - -/* - * Create the MCU channel - * - * After the channel has been created, the picture size, format, colorspace - * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be - * changed anymore. - * - * The channel can be created only once. The MCU will accept source buffers - * and stream buffers only after a channel has been created. - */ -static int allegro_create_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - enum v4l2_mpeg_video_h264_level min_level; - - if (channel_exists(channel)) { - v4l2_warn(&dev->v4l2_dev, - "channel already exists\n"); - return 0; - } - - channel->user_id = allegro_next_user_id(dev); - if (channel->user_id < 0) { - v4l2_err(&dev->v4l2_dev, - "no free channels available\n"); - return -EBUSY; - } - set_bit(channel->user_id, &dev->channel_user_ids); - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "user %d: creating channel (%4.4s, %dx%d@%d)\n", - channel->user_id, - (char *)&channel->codec, channel->width, channel->height, - DIV_ROUND_UP(channel->framerate.numerator, - channel->framerate.denominator)); - - min_level = select_minimum_h264_level(channel->width, channel->height); - if (channel->level < min_level) { - v4l2_warn(&dev->v4l2_dev, - "user %d: selected Level %s too low: increasing to Level %s\n", - channel->user_id, - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); - channel->level = min_level; - } - - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); - - reinit_completion(&channel->completion); - allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - channel->error = -ETIMEDOUT; - if (channel->error) - goto err; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: accepting buffers\n", - channel->mcu_channel_id); - - return 0; - -err: - allegro_destroy_channel(channel); - - return channel->error; -} - -static void allegro_set_default_params(struct allegro_channel *channel) -{ - channel->width = ALLEGRO_WIDTH_DEFAULT; - channel->height = ALLEGRO_HEIGHT_DEFAULT; - channel->stride = round_up(channel->width, 32); - channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; - - channel->colorspace = V4L2_COLORSPACE_REC709; - channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - channel->quantization = V4L2_QUANTIZATION_DEFAULT; - channel->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - channel->pixelformat = V4L2_PIX_FMT_NV12; - channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; - - channel->codec = V4L2_PIX_FMT_H264; - channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - channel->bitrate = maximum_bitrate(channel->level); - channel->bitrate_peak = maximum_bitrate(channel->level); - channel->cpb_size = maximum_cpb_size(channel->level); - channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; -} - -static int allegro_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct allegro_channel *channel = vb2_get_drv_priv(vq); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: queue setup[%s]: nplanes = %d\n", - V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture", - *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes); - - if (*nplanes != 0) { - if (V4L2_TYPE_IS_OUTPUT(vq->type)) { - if (sizes[0] < channel->sizeimage_raw) - return -EINVAL; - } else { - if (sizes[0] < channel->sizeimage_encoded) - return -EINVAL; - } - } else { - *nplanes = 1; - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - sizes[0] = channel->sizeimage_raw; - else - sizes[0] = channel->sizeimage_encoded; - } - - return 0; -} - -static int allegro_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); - struct allegro_dev *dev = channel->dev; - - if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && - V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) - return -EBUSY; - - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - v4l2_err(&dev->v4l2_dev, - "channel %d: unsupported field\n", - channel->mcu_channel_id); - return -EINVAL; - } - } - - return 0; -} - -static void allegro_buf_queue(struct vb2_buffer *vb) -{ - struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); - return; - } - - v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf); -} - -static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct allegro_channel *channel = vb2_get_drv_priv(q); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: start streaming\n", - V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - channel->osequence = 0; - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - channel->csequence = 0; - } - - return 0; -} - -static void allegro_stop_streaming(struct vb2_queue *q) -{ - struct allegro_channel *channel = vb2_get_drv_priv(q); - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *buffer; - struct allegro_m2m_buffer *shadow, *tmp; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: stop streaming\n", - V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, - &channel->source_shadow_list, head) { - list_del(&shadow->head); - v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); - } - mutex_unlock(&channel->shadow_list_lock); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) - v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, - &channel->stream_shadow_list, head) { - list_del(&shadow->head); - v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); - } - mutex_unlock(&channel->shadow_list_lock); - - allegro_destroy_channel(channel); - while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) - v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } -} - -static const struct vb2_ops allegro_queue_ops = { - .queue_setup = allegro_queue_setup, - .buf_prepare = allegro_buf_prepare, - .buf_queue = allegro_buf_queue, - .start_streaming = allegro_start_streaming, - .stop_streaming = allegro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int allegro_queue_init(void *priv, - struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int err; - struct allegro_channel *channel = priv; - - src_vq->dev = &channel->dev->plat_dev->dev; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->drv_priv = channel; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->ops = &allegro_queue_ops; - src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - src_vq->lock = &channel->dev->lock; - err = vb2_queue_init(src_vq); - if (err) - return err; - - dst_vq->dev = &channel->dev->plat_dev->dev; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->drv_priv = channel; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->ops = &allegro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - dst_vq->lock = &channel->dev->lock; - err = vb2_queue_init(dst_vq); - if (err) - return err; - - return 0; -} - -static int allegro_clamp_qp(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *next_ctrl; - - if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_p_frame_qp; - else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_b_frame_qp; - else - return 0; - - /* Modify range automatically updates the value */ - __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); - - return allegro_clamp_qp(channel, next_ctrl); -} - -static int allegro_clamp_bitrate(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; - struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; - - if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - ctrl_bitrate_peak->val < ctrl_bitrate->val) - ctrl_bitrate_peak->val = ctrl_bitrate->val; - - return 0; -} - -static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - allegro_clamp_bitrate(channel, ctrl); - break; - } - - return 0; -} - -static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - channel->level = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - channel->frame_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - channel->bitrate = channel->mpeg_video_bitrate->val; - channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val; - v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, - ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - break; - case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: - channel->cpb_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - channel->gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: - allegro_clamp_qp(channel, ctrl); - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops allegro_ctrl_ops = { - .try_ctrl = allegro_try_ctrl, - .s_ctrl = allegro_s_ctrl, -}; - -static int allegro_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - struct allegro_channel *channel = NULL; - struct v4l2_ctrl_handler *handler; - u64 mask; - int ret; - - channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - v4l2_fh_init(&channel->fh, vdev); - - init_completion(&channel->completion); - INIT_LIST_HEAD(&channel->source_shadow_list); - INIT_LIST_HEAD(&channel->stream_shadow_list); - mutex_init(&channel->shadow_list_lock); - - channel->dev = dev; - - allegro_set_default_params(channel); - - handler = &channel->ctrl_handler; - v4l2_ctrl_handler_init(handler, 0); - channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); - mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B; - channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, - V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - channel->mpeg_video_h264_i_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_h264_max_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, - 0, 51, 1, 51); - channel->mpeg_video_h264_min_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, - 0, 51, 1, 0); - channel->mpeg_video_h264_p_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_h264_b_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_frame_rc_enable = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, - false, 0x1, - true, false); - channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate); - channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate_peak); - channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, - 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->cpb_size); - channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0, ALLEGRO_GOP_SIZE_MAX, - 1, channel->gop_size); - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, - 1, 32, - 1, 1); - if (handler->error != 0) { - ret = handler->error; - goto error; - } - - channel->fh.ctrl_handler = handler; - - v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); - - channel->mcu_channel_id = -1; - channel->user_id = -1; - - INIT_LIST_HEAD(&channel->buffers_reference); - INIT_LIST_HEAD(&channel->buffers_intermediate); - - channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, - allegro_queue_init); - - if (IS_ERR(channel->fh.m2m_ctx)) { - ret = PTR_ERR(channel->fh.m2m_ctx); - goto error; - } - - list_add(&channel->list, &dev->channels); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); - - return 0; - -error: - v4l2_ctrl_handler_free(handler); - kfree(channel); - return ret; -} - -static int allegro_release(struct file *file) -{ - struct allegro_channel *channel = fh_to_channel(file->private_data); - - v4l2_m2m_ctx_release(channel->fh.m2m_ctx); - - list_del(&channel->list); - - v4l2_ctrl_handler_free(&channel->ctrl_handler); - - v4l2_fh_del(&channel->fh); - v4l2_fh_exit(&channel->fh); - - kfree(channel); - - return 0; -} - -static int allegro_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&dev->plat_dev->dev)); - - return 0; -} - -static int allegro_enum_fmt_vid(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - f->pixelformat = V4L2_PIX_FMT_NV12; - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - f->pixelformat = V4L2_PIX_FMT_H264; - break; - default: - return -EINVAL; - } - return 0; -} - -static int allegro_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - - f->fmt.pix.colorspace = channel->colorspace; - f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; - f->fmt.pix.quantization = channel->quantization; - f->fmt.pix.xfer_func = channel->xfer_func; - - f->fmt.pix.pixelformat = channel->codec; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = channel->sizeimage_encoded; - - return 0; -} - -static int allegro_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - f->fmt.pix.field = V4L2_FIELD_NONE; - - f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, - ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); - f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, - ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); - - return 0; -} - -static int allegro_g_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - - f->fmt.pix.field = V4L2_FIELD_NONE; - - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - - f->fmt.pix.colorspace = channel->colorspace; - f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; - f->fmt.pix.quantization = channel->quantization; - f->fmt.pix.xfer_func = channel->xfer_func; - - f->fmt.pix.pixelformat = channel->pixelformat; - f->fmt.pix.bytesperline = channel->stride; - f->fmt.pix.sizeimage = channel->sizeimage_raw; - - return 0; -} - -static int allegro_try_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - f->fmt.pix.field = V4L2_FIELD_NONE; - - /* - * The firmware of the Allegro codec handles the padding internally - * and expects the visual frame size when configuring a channel. - * Therefore, unlike other encoder drivers, this driver does not round - * up the width and height to macroblock alignment and does not - * implement the selection api. - */ - f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, - ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); - f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, - ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32); - f->fmt.pix.sizeimage = - f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; - - return 0; -} - -static int allegro_s_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - err = allegro_try_fmt_vid_out(file, fh, f); - if (err) - return err; - - channel->width = f->fmt.pix.width; - channel->height = f->fmt.pix.height; - channel->stride = f->fmt.pix.bytesperline; - channel->sizeimage_raw = f->fmt.pix.sizeimage; - - channel->colorspace = f->fmt.pix.colorspace; - channel->ycbcr_enc = f->fmt.pix.ycbcr_enc; - channel->quantization = f->fmt.pix.quantization; - channel->xfer_func = f->fmt.pix.xfer_func; - - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - return 0; -} - -static int allegro_channel_cmd_stop(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *dst_buf; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_ENCODING: - allegro_set_state(channel, ALLEGRO_STATE_DRAIN); - break; - default: - return 0; - } - - /* If there are output buffers, they must be encoded */ - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: continue encoding src buffers\n", - channel->mcu_channel_id); - return 0; - } - - /* If there are capture buffers, use it to signal EOS */ - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - if (dst_buf) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: signaling EOS\n", - channel->mcu_channel_id); - allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); - return 0; - } - - /* - * If there are no capture buffers, we need to wait for the next - * buffer to signal EOS. - */ - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", - channel->mcu_channel_id); - allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); - - return 0; -} - -static int allegro_channel_cmd_start(struct allegro_channel *channel) -{ - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_STOPPED: - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - break; - default: - return 0; - } - - return 0; -} - -static int allegro_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *cmd) -{ - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); - if (err) - return err; - - switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: - err = allegro_channel_cmd_stop(channel); - break; - case V4L2_ENC_CMD_START: - err = allegro_channel_cmd_start(channel); - break; - default: - err = -EINVAL; - break; - } - - return err; -} - -static int allegro_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - switch (fsize->pixel_format) { - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_NV12: - break; - default: - return -EINVAL; - } - - if (fsize->index) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN; - fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN; - fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int allegro_ioctl_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct v4l2_fh *fh = file->private_data; - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - err = allegro_create_channel(channel); - if (err) - return err; - } - - return v4l2_m2m_streamon(file, fh->m2m_ctx, type); -} - -static int allegro_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct allegro_channel *channel = fh_to_channel(fh); - struct v4l2_fract *timeperframe; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - timeperframe = &a->parm.output.timeperframe; - timeperframe->numerator = channel->framerate.denominator; - timeperframe->denominator = channel->framerate.numerator; - - return 0; -} - -static int allegro_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct allegro_channel *channel = fh_to_channel(fh); - struct v4l2_fract *timeperframe; - int div; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - timeperframe = &a->parm.output.timeperframe; - - if (timeperframe->numerator == 0 || timeperframe->denominator == 0) - return allegro_g_parm(file, fh, a); - - div = gcd(timeperframe->denominator, timeperframe->numerator); - channel->framerate.numerator = timeperframe->denominator / div; - channel->framerate.denominator = timeperframe->numerator / div; - - return 0; -} - -static int allegro_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 0, NULL); - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static const struct v4l2_ioctl_ops allegro_ioctl_ops = { - .vidioc_querycap = allegro_querycap, - .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid, - .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, - .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap, - .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, - .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, - - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_streamon = allegro_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = allegro_encoder_cmd, - .vidioc_enum_framesizes = allegro_enum_framesizes, - - .vidioc_g_parm = allegro_g_parm, - .vidioc_s_parm = allegro_s_parm, - - .vidioc_subscribe_event = allegro_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations allegro_fops = { - .owner = THIS_MODULE, - .open = allegro_open, - .release = allegro_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int allegro_register_device(struct allegro_dev *dev) -{ - struct video_device *video_dev = &dev->video_dev; - - strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); - video_dev->fops = &allegro_fops; - video_dev->ioctl_ops = &allegro_ioctl_ops; - video_dev->release = video_device_release_empty; - video_dev->lock = &dev->lock; - video_dev->v4l2_dev = &dev->v4l2_dev; - video_dev->vfl_dir = VFL_DIR_M2M; - video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - video_set_drvdata(video_dev, dev); - - return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); -} - -static void allegro_device_run(void *priv) -{ - struct allegro_channel *channel = priv; - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *src_buf; - struct vb2_v4l2_buffer *dst_buf; - dma_addr_t src_y; - dma_addr_t src_uv; - dma_addr_t dst_addr; - unsigned long dst_size; - u64 src_handle; - u64 dst_handle; - - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); - dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, - dst_buf); - allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, - dst_handle); - - src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); - src_buf->sequence = channel->osequence++; - src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_uv = src_y + (channel->stride * channel->height); - src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, - src_buf); - allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); - - v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); -} - -static const struct v4l2_m2m_ops allegro_m2m_ops = { - .device_run = allegro_device_run, -}; - -static int allegro_mcu_hw_init(struct allegro_dev *dev, - const struct fw_info *info) -{ - int err; - - dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, - info->mailbox_size); - dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, - info->mailbox_size); - if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { - v4l2_err(&dev->v4l2_dev, - "failed to initialize mailboxes\n"); - return -EIO; - } - - allegro_mcu_enable_interrupts(dev); - - /* The mcu sends INIT after reset. */ - allegro_mcu_start(dev); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu did not send INIT after reset\n"); - err = -EIO; - goto err_disable_interrupts; - } - - err = allegro_alloc_buffer(dev, &dev->suballocator, - info->suballocator_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for suballocator\n", - info->suballocator_size); - goto err_reset_mcu; - } - - allegro_mcu_send_init(dev, dev->suballocator.paddr, - dev->suballocator.size); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu failed to configure sub-allocator\n"); - err = -EIO; - goto err_free_suballocator; - } - - return 0; - -err_free_suballocator: - allegro_free_buffer(dev, &dev->suballocator); -err_reset_mcu: - allegro_mcu_reset(dev); -err_disable_interrupts: - allegro_mcu_disable_interrupts(dev); - - return err; -} - -static int allegro_mcu_hw_deinit(struct allegro_dev *dev) -{ - int err; - - err = allegro_mcu_reset(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "mcu failed to enter sleep state\n"); - - err = allegro_mcu_disable_interrupts(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "failed to disable interrupts\n"); - - allegro_free_buffer(dev, &dev->suballocator); - - return 0; -} - -static void allegro_fw_callback(const struct firmware *fw, void *context) -{ - struct allegro_dev *dev = context; - const char *fw_codec_name = "al5e.fw"; - const struct firmware *fw_codec; - int err; - - if (!fw) - return; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting codec firmware '%s'\n", fw_codec_name); - err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); - if (err) - goto err_release_firmware; - - dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!dev->fw_info) { - v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); - goto err_release_firmware_codec; - } - - v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", dev->fw_info->version); - - /* Ensure that the mcu is sleeping at the reset vector */ - err = allegro_mcu_reset(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); - goto err_release_firmware_codec; - } - - allegro_copy_firmware(dev, fw->data, fw->size); - allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - - err = allegro_mcu_hw_init(dev, dev->fw_info); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); - goto err_free_fw_codec; - } - - dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); - goto err_mcu_hw_deinit; - } - - err = allegro_register_device(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); - goto err_m2m_release; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "allegro codec registered as /dev/video%d\n", - dev->video_dev.num); - - release_firmware(fw_codec); - release_firmware(fw); - - return; - -err_m2m_release: - v4l2_m2m_release(dev->m2m_dev); - dev->m2m_dev = NULL; -err_mcu_hw_deinit: - allegro_mcu_hw_deinit(dev); -err_free_fw_codec: - allegro_free_fw_codec(dev); -err_release_firmware_codec: - release_firmware(fw_codec); -err_release_firmware: - release_firmware(fw); -} - -static int allegro_firmware_request_nowait(struct allegro_dev *dev) -{ - const char *fw = "al5e_b.fw"; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting firmware '%s'\n", fw); - return request_firmware_nowait(THIS_MODULE, true, fw, - &dev->plat_dev->dev, GFP_KERNEL, dev, - allegro_fw_callback); -} - -static int allegro_probe(struct platform_device *pdev) -{ - struct allegro_dev *dev; - struct resource *res, *sram_res; - int ret; - int irq; - void __iomem *regs, *sram_regs; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - dev->plat_dev = pdev; - init_completion(&dev->init_complete); - INIT_LIST_HEAD(&dev->channels); - - mutex_init(&dev->lock); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - if (!res) { - dev_err(&pdev->dev, - "regs resource missing from device tree\n"); - return -EINVAL; - } - regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - return -ENOMEM; - } - dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &allegro_regmap_config); - if (IS_ERR(dev->regmap)) { - dev_err(&pdev->dev, "failed to init regmap\n"); - return PTR_ERR(dev->regmap); - } - - sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); - if (!sram_res) { - dev_err(&pdev->dev, - "sram resource missing from device tree\n"); - return -EINVAL; - } - sram_regs = devm_ioremap(&pdev->dev, - sram_res->start, - resource_size(sram_res)); - if (!sram_regs) { - dev_err(&pdev->dev, "failed to map sram\n"); - return -ENOMEM; - } - dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, - &allegro_sram_config); - if (IS_ERR(dev->sram)) { - dev_err(&pdev->dev, "failed to init sram\n"); - return PTR_ERR(dev->sram); - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - ret = devm_request_threaded_irq(&pdev->dev, irq, - allegro_hardirq, - allegro_irq_thread, - IRQF_SHARED, dev_name(&pdev->dev), dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - platform_set_drvdata(pdev, dev); - - ret = allegro_firmware_request_nowait(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to request firmware: %d\n", ret); - return ret; - } - - return 0; -} - -static int allegro_remove(struct platform_device *pdev) -{ - struct allegro_dev *dev = platform_get_drvdata(pdev); - - video_unregister_device(&dev->video_dev); - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - allegro_mcu_hw_deinit(dev); - allegro_free_fw_codec(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - return 0; -} - -static const struct of_device_id allegro_dt_ids[] = { - { .compatible = "allegro,al5e-1.1" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, allegro_dt_ids); - -static struct platform_driver allegro_driver = { - .probe = allegro_probe, - .remove = allegro_remove, - .driver = { - .name = "allegro", - .of_match_table = of_match_ptr(allegro_dt_ids), - }, -}; - -module_platform_driver(allegro_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Tretter <kernel@pengutronix.de>"); -MODULE_DESCRIPTION("Allegro DVT encoder driver"); |