diff options
Diffstat (limited to 'drivers/media/platform')
88 files changed, 11068 insertions, 1231 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 35a18d388f3f..fd1831e97b22 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -199,6 +199,21 @@ menuconfig V4L_MEM2MEM_DRIVERS if V4L_MEM2MEM_DRIVERS +config VIDEO_ALLEGRO_DVT + tristate "Allegro DVT Video IP Core" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_ZYNQMP || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + Support for the encoder video IP core by Allegro DVT. This core is + found for example on the Xilinx ZynqMP SoC in the EV family and is + called VCU in the reference manual. + + To compile this driver as a module, choose M here: the module + will be called allegro. + config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) @@ -530,10 +545,9 @@ config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST - depends on INTERCONNECT || !INTERCONNECT select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM if ARCH_QCOM - select VIDEOBUF2_DMA_SG + select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help This is a V4L2 driver for Qualcomm Venus video accelerator diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 1d63aa956bcd..9d4d6370908d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,6 +3,7 @@ # Makefile for the video capture/playback device drivers. # +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o obj-$(CONFIG_VIDEO_CADENCE) += cadence/ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile new file mode 100644 index 000000000000..66108a303774 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +allegro-objs := allegro-core.o allegro-mail.o +allegro-objs += nal-rbsp.o nal-h264.o nal-hevc.o + +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c new file mode 100644 index 000000000000..887b492e4ad1 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -0,0 +1,3773 @@ +// 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" +#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 + +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, +}; + +#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) + +struct allegro_channel { + struct allegro_dev *dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + + unsigned int width; + unsigned int height; + unsigned int stride; + struct v4l2_fract framerate; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + u32 pixelformat; + unsigned int sizeimage_raw; + unsigned int osequence; + + u32 codec; + unsigned int sizeimage_encoded; + unsigned int csequence; + + bool frame_rc_enable; + unsigned int bitrate; + unsigned int bitrate_peak; + + struct allegro_buffer config_blob; + + unsigned int log2_max_frame_num; + bool temporal_mvp_enable; + + bool enable_loop_filter_across_tiles; + bool enable_loop_filter_across_slices; + bool enable_deblocking_filter_override; + bool enable_reordering; + bool dbf_ovr_en; + + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + + /* Maximum range for motion estimation */ + int b_hrz_me_range; + int b_vrt_me_range; + int p_hrz_me_range; + int p_vrt_me_range; + /* Size limits of coding unit */ + int min_cu_size; + int max_cu_size; + /* Size limits of transform unit */ + int min_tu_size; + int max_tu_size; + int max_transfo_depth_intra; + int max_transfo_depth_inter; + + struct v4l2_ctrl *mpeg_video_h264_profile; + struct v4l2_ctrl *mpeg_video_h264_level; + struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_max_qp; + struct v4l2_ctrl *mpeg_video_h264_min_qp; + struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; + + struct v4l2_ctrl *mpeg_video_hevc_profile; + struct v4l2_ctrl *mpeg_video_hevc_level; + struct v4l2_ctrl *mpeg_video_hevc_tier; + struct v4l2_ctrl *mpeg_video_hevc_i_frame_qp; + struct v4l2_ctrl *mpeg_video_hevc_max_qp; + struct v4l2_ctrl *mpeg_video_hevc_min_qp; + struct v4l2_ctrl *mpeg_video_hevc_p_frame_qp; + struct v4l2_ctrl *mpeg_video_hevc_b_frame_qp; + + struct v4l2_ctrl *mpeg_video_frame_rc_enable; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + }; + struct v4l2_ctrl *mpeg_video_cpb_size; + struct v4l2_ctrl *mpeg_video_gop_size; + + /* user_id is used to identify the channel during CREATE_CHANNEL */ + /* not sure, what to set here and if this is actually required */ + int user_id; + /* channel_id is set by the mcu and used by all later commands */ + int mcu_channel_id; + + struct list_head buffers_reference; + struct list_head buffers_intermediate; + + struct list_head source_shadow_list; + struct list_head stream_shadow_list; + /* protect shadow lists of buffers passed to firmware */ + struct mutex shadow_list_lock; + + struct list_head list; + struct completion completion; + + unsigned int error; +}; + +static inline int +allegro_channel_get_i_frame_qp(struct allegro_channel *channel) +{ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_i_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); +} + +static inline int +allegro_channel_get_p_frame_qp(struct allegro_channel *channel) +{ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_p_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); +} + +static inline int +allegro_channel_get_b_frame_qp(struct allegro_channel *channel) +{ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_b_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); +} + +static inline int +allegro_channel_get_min_qp(struct allegro_channel *channel) +{ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_min_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); +} + +static inline int +allegro_channel_get_max_qp(struct allegro_channel *channel) +{ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_max_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); +} + +struct allegro_m2m_buffer { + struct v4l2_m2m_buffer buf; + struct list_head head; +}; + +#define to_allegro_m2m_buffer(__buf) \ + container_of(__buf, struct allegro_m2m_buffer, buf) + +struct fw_info { + unsigned int id; + unsigned int id_codec; + char *version; + unsigned int mailbox_cmd; + unsigned int mailbox_status; + size_t mailbox_size; + enum mcu_msg_version mailbox_version; + size_t suballocator_size; +}; + +static const struct fw_info supported_firmware[] = { + { + .id = 18296, + .id_codec = 96272, + .version = "v2018.2", + .mailbox_cmd = 0x7800, + .mailbox_status = 0x7c00, + .mailbox_size = 0x400 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2018_2, + .suballocator_size = SZ_16M, + }, { + .id = 14680, + .id_codec = 126572, + .version = "v2019.2", + .mailbox_cmd = 0x7000, + .mailbox_status = 0x7800, + .mailbox_size = 0x800 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2019_2, + .suballocator_size = SZ_32M, + }, +}; + +static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) + v4l2_warn(&dev->v4l2_dev, + "address %pad is outside mcu window\n", &phys); + + return lower_32_bits(phys) | MCU_CACHE_OFFSET; +} + +static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) +{ + return lower_32_bits(size); +} + +static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys)) + v4l2_warn(&dev->v4l2_dev, + "address %pad cannot be used by codec\n", &phys); + + return lower_32_bits(phys); +} + +static inline u64 ptr_to_u64(const void *ptr) +{ + return (uintptr_t)ptr; +} + +/* Helper functions for channel and user operations */ + +static unsigned long allegro_next_user_id(struct allegro_dev *dev) +{ + if (dev->channel_user_ids == ~0UL) + return -EBUSY; + + return ffz(dev->channel_user_ids); +} + +static struct allegro_channel * +allegro_find_channel_by_user_id(struct allegro_dev *dev, + unsigned int user_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->user_id == user_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static struct allegro_channel * +allegro_find_channel_by_channel_id(struct allegro_dev *dev, + unsigned int channel_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->mcu_channel_id == channel_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static inline bool channel_exists(struct allegro_channel *channel) +{ + return channel->mcu_channel_id != -1; +} + +#define AL_ERROR 0x80 +#define AL_ERR_INIT_FAILED 0x81 +#define AL_ERR_NO_FRAME_DECODED 0x82 +#define AL_ERR_RESOLUTION_CHANGE 0x85 +#define AL_ERR_NO_MEMORY 0x87 +#define AL_ERR_STREAM_OVERFLOW 0x88 +#define AL_ERR_TOO_MANY_SLICES 0x89 +#define AL_ERR_BUF_NOT_READY 0x8c +#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d +#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e +#define AL_ERR_NOT_ENOUGH_CORES 0x8f +#define AL_ERR_REQUEST_MALFORMED 0x90 +#define AL_ERR_CMD_NOT_ALLOWED 0x91 +#define AL_ERR_INVALID_CMD_VALUE 0x92 + +static inline const char *allegro_err_to_string(unsigned int err) +{ + switch (err) { + case AL_ERR_INIT_FAILED: + return "initialization failed"; + case AL_ERR_NO_FRAME_DECODED: + return "no frame decoded"; + case AL_ERR_RESOLUTION_CHANGE: + return "resolution change"; + case AL_ERR_NO_MEMORY: + return "out of memory"; + case AL_ERR_STREAM_OVERFLOW: + return "stream buffer overflow"; + case AL_ERR_TOO_MANY_SLICES: + return "too many slices"; + case AL_ERR_BUF_NOT_READY: + return "buffer not ready"; + case AL_ERR_NO_CHANNEL_AVAILABLE: + return "no channel available"; + case AL_ERR_RESOURCE_UNAVAILABLE: + return "resource unavailable"; + case AL_ERR_NOT_ENOUGH_CORES: + return "not enough cores"; + case AL_ERR_REQUEST_MALFORMED: + return "request malformed"; + case AL_ERR_CMD_NOT_ALLOWED: + return "command not allowed"; + case AL_ERR_INVALID_CMD_VALUE: + return "invalid command value"; + case AL_ERROR: + default: + return "unknown error"; + } +} + +static unsigned int estimate_stream_size(unsigned int width, + unsigned int height) +{ + unsigned int offset = ENCODER_STREAM_OFFSET; + unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) * + DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int pcm_size = SZ_256; + unsigned int partition_table = SZ_256; + + return round_up(offset + num_blocks * pcm_size + partition_table, 32); +} + +static enum v4l2_mpeg_video_h264_level +select_minimum_h264_level(unsigned int width, unsigned int height) +{ + unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK); + unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb; + enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + + /* + * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and + * also specify limits regarding bit rate and CBP size. Only approximate + * the levels using the frame size. + * + * Level 5.1 allows up to 4k video resolution. + */ + if (frame_size_in_mb <= 99) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + else if (frame_size_in_mb <= 396) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + else if (frame_size_in_mb <= 792) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + else if (frame_size_in_mb <= 1620) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + else if (frame_size_in_mb <= 3600) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + else if (frame_size_in_mb <= 5120) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + else if (frame_size_in_mb <= 8192) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else if (frame_size_in_mb <= 8704) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + else if (frame_size_in_mb <= 22080) + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + else + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + + return level; +} + +static unsigned int h264_maximum_bitrate(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 64000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 128000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 192000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 384000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 768000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000000; + } +} + +static unsigned int h264_maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 175; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 350; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 500; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 1000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 25000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000; + } +} + +static enum v4l2_mpeg_video_hevc_level +select_minimum_hevc_level(unsigned int width, unsigned int height) +{ + unsigned int luma_picture_size = width * height; + enum v4l2_mpeg_video_hevc_level level; + + if (luma_picture_size <= 36864) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_1; + else if (luma_picture_size <= 122880) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2; + else if (luma_picture_size <= 245760) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1; + else if (luma_picture_size <= 552960) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3; + else if (luma_picture_size <= 983040) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1; + else if (luma_picture_size <= 2228224) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_4; + else if (luma_picture_size <= 8912896) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_5; + else + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_6; + + return level; +} + +static unsigned int hevc_maximum_bitrate(enum v4l2_mpeg_video_hevc_level level) +{ + /* + * See Rec. ITU-T H.265 v5 (02/2018), A.4.2 Profile-specific level + * limits for the video profiles. + */ + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return 128; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return 1500; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return 3000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return 6000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return 10000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return 12000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return 20000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return 25000; + default: + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return 40000; + } +} + +static unsigned int hevc_maximum_cpb_size(enum v4l2_mpeg_video_hevc_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return 350; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return 1500; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return 3000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return 6000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return 10000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return 12000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return 20000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return 25000; + default: + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return 40000; + } +} + +static const struct fw_info * +allegro_get_firmware_info(struct allegro_dev *dev, + const struct firmware *fw, + const struct firmware *fw_codec) +{ + int i; + unsigned int id = fw->size; + unsigned int id_codec = fw_codec->size; + + for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) + if (supported_firmware[i].id == id && + supported_firmware[i].id_codec == id_codec) + return &supported_firmware[i]; + + return NULL; +} + +/* + * Buffers that are used internally by the MCU. + */ + +static int allegro_alloc_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer, size_t size) +{ + buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, + &buffer->paddr, GFP_KERNEL); + if (!buffer->vaddr) + return -ENOMEM; + buffer->size = size; + + return 0; +} + +static void allegro_free_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer) +{ + if (buffer->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buffer->size, + buffer->vaddr, buffer->paddr); + buffer->vaddr = NULL; + buffer->size = 0; + } +} + +/* + * Mailbox interface to send messages to the MCU. + */ + +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) +{ + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; + + mbox->head = base; + mbox->tail = base + 0x4; + mbox->data = base + 0x8; + mbox->size = size; + mutex_init(&mbox->lock); + + regmap_write(dev->sram, mbox->head, 0); + regmap_write(dev->sram, mbox->tail, 0); + + return mbox; +} + +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) +{ + struct regmap *sram = mbox->dev->sram; + unsigned int tail; + size_t size_no_wrap; + int err = 0; + int stride = regmap_get_reg_stride(sram); + + if (!src) + return -EINVAL; + + if (size > mbox->size) + return -EINVAL; + + mutex_lock(&mbox->lock); + regmap_read(sram, mbox->tail, &tail); + if (tail > mbox->size) { + err = -EIO; + goto out; + } + size_no_wrap = min(size, mbox->size - (size_t)tail); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); + +out: + mutex_unlock(&mbox->lock); + + return err; +} + +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) +{ + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; + unsigned int head; + ssize_t size; + size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); + + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) + return -EIO; + + /* Assume that the header does not wrap. */ + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; + size = header->length + sizeof(*header); + if (size > mbox->size || size & 0x3) + return -EIO; + if (size > nbyte) + return -EINVAL; + + /* + * The message might wrap within the mailbox. If the message does not + * wrap, the first read will read the entire message, otherwise the + * first read will read message until the end of the mailbox and the + * second read will read the remaining bytes from the beginning of the + * mailbox. + * + * Skip the header, as was already read to get the size of the body. + */ + body_no_wrap = min((size_t)header->length, + (size_t)(mbox->size - (head + sizeof(*header)))); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); + + regmap_write(sram, mbox->head, (head + size) % mbox->size); + + return size; +} + +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) +{ + struct allegro_dev *dev = mbox->dev; + ssize_t size; + int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); + + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + u32 *tmp; + int err; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + msg->header.version = dev->fw_info->mailbox_version; + + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); + if (size < 0) + goto out; + + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(tmp); + kfree(msg); +} + +static 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 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->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(cpb_size, channel->bitrate_peak); + /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ + param->initial_rem_delay = param->cpb_size; + param->framerate = DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator); + param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; + param->target_bitrate = channel->bitrate; + param->max_bitrate = channel->bitrate_peak; + param->initial_qp = i_frame_qp; + param->min_qp = allegro_channel_get_min_qp(channel); + param->max_qp = allegro_channel_get_max_qp(channel); + param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); + param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); + param->golden_ref = 0; + param->golden_delta = 2; + param->golden_ref_frequency = 10; + param->rate_control_option = 0x00000000; + + param->num_pixel = channel->width + channel->height; + param->max_psnr = 4200; + param->max_pixel_value = 255; + + param->gop_ctrl_mode = 0x00000002; + param->freq_idr = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size); + param->freq_lt = 0; + param->gdr_mode = 0x00000000; + param->gop_length = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size); + param->subframe_latency = 0x00000000; + + param->lda_factors[0] = 51; + param->lda_factors[1] = 90; + param->lda_factors[2] = 151; + param->lda_factors[3] = 151; + param->lda_factors[4] = 151; + param->lda_factors[5] = 151; + + param->max_num_merge_cand = 5; + + return 0; +} + +static int allegro_mcu_send_create_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_create_channel msg; + struct allegro_buffer *blob = &channel->config_blob; + struct create_channel_param param; + size_t size; + + memset(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; + size = allegro_encode_config_blob(blob->vaddr, ¶m); + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; + + msg.user_id = channel->user_id; + + msg.blob = blob->vaddr; + msg.blob_size = size; + msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_destroy_channel msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t paddr, + unsigned long size, + u64 dst_handle) +{ + struct mcu_msg_put_stream_buffer msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + msg.dma_addr = to_codec_addr(dev, paddr); + msg.mcu_addr = to_mcu_addr(dev, paddr); + msg.size = size; + msg.offset = ENCODER_STREAM_OFFSET; + /* copied to mcu_msg_encode_frame_response */ + msg.dst_handle = dst_handle; + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t src_y, dma_addr_t src_uv, + u64 src_handle) +{ + struct mcu_msg_encode_frame msg; + + 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; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + unsigned int cpb_size; + unsigned int cpb_size_scale; + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); + level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); + + sps->profile_idc = nal_h264_profile_from_v4l2(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(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 = 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; + /* 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 */ + cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); + cpb_size_scale = ffs(cpb_size) - 4; + sps->vui.vcl_hrd_parameters.cpb_size_scale = cpb_size_scale; + sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = + (cpb_size * 1000) / (1 << (4 + cpb_size_scale)) - 1; + sps->vui.vcl_hrd_parameters.cbr_flag[0] = + !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); + sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.time_offset_length = 0; + sps->vui.low_delay_hrd_flag = 0; + sps->vui.pic_struct_present_flag = 1; + sps->vui.bitstream_restriction_flag = 0; + + size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps); + + kfree(sps); + + return size; +} + +static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_pps *pps; + ssize_t size; + + pps = kzalloc(sizeof(*pps), GFP_KERNEL); + if (!pps) + return -ENOMEM; + + pps->pic_parameter_set_id = 0; + pps->seq_parameter_set_id = 0; + pps->entropy_coding_mode_flag = 0; + pps->bottom_field_pic_order_in_frame_present_flag = 0; + pps->num_slice_groups_minus1 = 0; + pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; + pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; + pps->weighted_pred_flag = 0; + pps->weighted_bipred_idc = 0; + pps->pic_init_qp_minus26 = 0; + pps->pic_init_qs_minus26 = 0; + pps->chroma_qp_index_offset = 0; + pps->deblocking_filter_control_present_flag = 1; + pps->constrained_intra_pred_flag = 0; + pps->redundant_pic_cnt_present_flag = 0; + pps->transform_8x8_mode_flag = 0; + pps->pic_scaling_matrix_present_flag = 0; + pps->second_chroma_qp_index_offset = 0; + + size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps); + + kfree(pps); + + return size; +} + +static void allegro_channel_eos_event(struct allegro_channel *channel) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + v4l2_event_queue_fh(&channel->fh, &eos_event); +} + +static ssize_t allegro_hevc_write_vps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_hevc_vps *vps; + struct nal_hevc_profile_tier_level *ptl; + ssize_t size; + unsigned int num_ref_frames = channel->num_ref_idx_l0; + s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile); + s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level); + s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier); + + vps = kzalloc(sizeof(*vps), GFP_KERNEL); + if (!vps) + return -ENOMEM; + + vps->base_layer_internal_flag = 1; + vps->base_layer_available_flag = 1; + vps->temporal_id_nesting_flag = 1; + + ptl = &vps->profile_tier_level; + ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; + ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_progressive_source_flag = 1; + ptl->general_frame_only_constraint_flag = 1; + ptl->general_level_idc = nal_hevc_level_from_v4l2(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; + 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); + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->temporal_id_nesting_flag = 1; + + ptl = &sps->profile_tier_level; + ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; + ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_progressive_source_flag = 1; + ptl->general_frame_only_constraint_flag = 1; + ptl->general_level_idc = nal_hevc_level_from_v4l2(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; + + size = nal_hevc_write_sps(&dev->plat_dev->dev, dest, n, sps); + + kfree(sps); + + return size; +} + +static ssize_t allegro_hevc_write_pps(struct allegro_channel *channel, + struct mcu_msg_encode_frame_response *msg, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_hevc_pps *pps; + ssize_t size; + int i; + + pps = kzalloc(sizeof(*pps), GFP_KERNEL); + if (!pps) + return -ENOMEM; + + pps->pps_pic_parameter_set_id = 0; + pps->pps_seq_parameter_set_id = 0; + + if (msg->num_column > 1 || msg->num_row > 1) { + pps->tiles_enabled_flag = 1; + pps->num_tile_columns_minus1 = msg->num_column - 1; + pps->num_tile_rows_minus1 = msg->num_row - 1; + + for (i = 0; i < msg->num_column; i++) + pps->column_width_minus1[i] = msg->tile_width[i] - 1; + + for (i = 0; i < msg->num_row; i++) + pps->row_height_minus1[i] = msg->tile_height[i] - 1; + } + + pps->loop_filter_across_tiles_enabled_flag = + channel->enable_loop_filter_across_tiles; + pps->pps_loop_filter_across_slices_enabled_flag = + channel->enable_loop_filter_across_slices; + pps->deblocking_filter_control_present_flag = 1; + pps->deblocking_filter_override_enabled_flag = + channel->enable_deblocking_filter_override; + pps->pps_beta_offset_div2 = BETA_OFFSET_DIV_2; + pps->pps_tc_offset_div2 = TC_OFFSET_DIV_2; + + pps->lists_modification_present_flag = channel->enable_reordering; + + size = nal_hevc_write_pps(&dev->plat_dev->dev, dest, n, pps); + + kfree(pps); + + return size; +} + +static u64 allegro_put_buffer(struct allegro_channel *channel, + struct list_head *list, + struct vb2_v4l2_buffer *buffer) +{ + struct v4l2_m2m_buffer *b = container_of(buffer, + struct v4l2_m2m_buffer, vb); + struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); + + mutex_lock(&channel->shadow_list_lock); + list_add_tail(&shadow->head, list); + mutex_unlock(&channel->shadow_list_lock); + + return ptr_to_u64(buffer); +} + +static struct vb2_v4l2_buffer * +allegro_get_buffer(struct allegro_channel *channel, + struct list_head *list, u64 handle) +{ + struct allegro_m2m_buffer *shadow, *tmp; + struct vb2_v4l2_buffer *buffer = NULL; + + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, list, head) { + if (handle == ptr_to_u64(&shadow->buf.vb)) { + buffer = &shadow->buf.vb; + list_del_init(&shadow->head); + break; + } + } + mutex_unlock(&channel->shadow_list_lock); + + return buffer; +} + +static void allegro_channel_finish_frame(struct allegro_channel *channel, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct { + u32 offset; + u32 size; + } *partition; + enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; + char *curr; + ssize_t len; + ssize_t free; + + src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, + msg->src_handle); + if (!src_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid source buffer\n", + channel->mcu_channel_id); + + dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, + msg->dst_handle); + if (!dst_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid stream buffer\n", + channel->mcu_channel_id); + + if (!src_buf || !dst_buf) + goto err; + + if (v4l2_m2m_is_last_draining_src_buf(channel->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + allegro_channel_eos_event(channel); + v4l2_m2m_mark_stopped(channel->fh.m2m_ctx); + } + + dst_buf->sequence = channel->csequence++; + + if (msg->error_code & AL_ERROR) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to encode frame: %s (%x)\n", + channel->mcu_channel_id, + allegro_err_to_string(msg->error_code), + msg->error_code); + goto err; + } + + if (msg->partition_table_size != 1) { + v4l2_warn(&dev->v4l2_dev, + "channel %d: only handling first partition table entry (%d entries)\n", + channel->mcu_channel_id, msg->partition_table_size); + } + + if (msg->partition_table_offset + + msg->partition_table_size * sizeof(*partition) > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: partition table outside of dst_buf\n", + channel->mcu_channel_id); + goto err; + } + + partition = + vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset; + if (partition->offset + partition->size > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n", + channel->mcu_channel_id, partition->offset, + partition->size); + goto err; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: encoded frame of size %d is at offset 0x%x\n", + channel->mcu_channel_id, partition->size, partition->offset); + + /* + * The payload must include the data before the partition offset, + * because we will put the sps and pps data there. + */ + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + partition->offset + partition->size); + + curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + free = partition->offset; + + if (channel->codec == V4L2_PIX_FMT_HEVC && msg->is_idr) { + len = allegro_hevc_write_vps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for video parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte VPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->is_idr) { + if (channel->codec == V4L2_PIX_FMT_H264) + len = allegro_h264_write_sps(channel, curr, free); + else + len = allegro_hevc_write_sps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for sequence parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte SPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { + if (channel->codec == V4L2_PIX_FMT_H264) + len = allegro_h264_write_pps(channel, curr, free); + else + len = allegro_hevc_write_pps(channel, msg, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for picture parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte PPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { + dst_buf->vb2_buf.planes[0].data_offset = free; + free = 0; + } else { + if (channel->codec == V4L2_PIX_FMT_H264) + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + else + len = nal_hevc_write_filler(&dev->plat_dev->dev, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to write %zd filler data\n", free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: wrote %zd bytes filler nal unit\n", + channel->mcu_channel_id, len); + } + + if (free != 0) { + v4l2_err(&dev->v4l2_dev, + "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n", + free); + goto err; + } + + state = VB2_BUF_STATE_DONE; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + if (msg->is_idr) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", + channel->mcu_channel_id, + dst_buf->sequence, + msg->is_idr ? "IDR, " : "", + msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : + msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", + msg->qp, partition->size); + +err: + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + if (dst_buf) + v4l2_m2m_buf_done(dst_buf, state); +} + +static int allegro_handle_init(struct allegro_dev *dev, + struct mcu_msg_init_response *msg) +{ + complete(&dev->init_complete); + + return 0; +} + +static int +allegro_handle_create_channel(struct allegro_dev *dev, + struct mcu_msg_create_channel_response *msg) +{ + struct allegro_channel *channel; + int err = 0; + struct create_channel_param param; + + channel = allegro_find_channel_by_user_id(dev, msg->user_id); + if (IS_ERR(channel)) { + v4l2_warn(&dev->v4l2_dev, + "received %s for unknown user %d\n", + msg_type_name(msg->header.type), + msg->user_id); + return -EINVAL; + } + + if (msg->error_code) { + v4l2_err(&dev->v4l2_dev, + "user %d: mcu failed to create channel: %s (%x)\n", + channel->user_id, + allegro_err_to_string(msg->error_code), + msg->error_code); + err = -EIO; + goto out; + } + + channel->mcu_channel_id = msg->channel_id; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: channel has channel id %d\n", + channel->user_id, channel->mcu_channel_id); + + err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: intermediate buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->int_buffers_count, msg->int_buffers_size); + err = allocate_intermediate_buffers(channel, msg->int_buffers_count, + msg->int_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate intermediate buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_intermediate(channel); + if (err) + goto out; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: reference buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->rec_buffers_count, msg->rec_buffers_size); + err = allocate_reference_buffers(channel, msg->rec_buffers_count, + msg->rec_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate reference buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_reference(channel); + if (err) + goto out; + +out: + channel->error = err; + complete(&channel->completion); + + /* Handled successfully, error is passed via channel->error */ + return 0; +} + +static int +allegro_handle_destroy_channel(struct allegro_dev *dev, + struct mcu_msg_destroy_channel_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "user %d: vcu destroyed channel %d\n", + channel->user_id, channel->mcu_channel_id); + complete(&channel->completion); + + return 0; +} + +static int +allegro_handle_encode_frame(struct allegro_dev *dev, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + allegro_channel_finish_frame(channel, msg); + + return 0; +} + +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) +{ + switch (msg->header.type) { + case MCU_MSG_TYPE_INIT: + allegro_handle_init(dev, &msg->init); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_handle_create_channel(dev, &msg->create_channel); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_handle_destroy_channel(dev, &msg->destroy_channel); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_handle_encode_frame(dev, &msg->encode_frame); + break; + default: + v4l2_warn(&dev->v4l2_dev, + "%s: unknown message %s\n", + __func__, msg_type_name(msg->header.type)); + break; + } +} + +static irqreturn_t allegro_hardirq(int irq, void *data) +{ + struct allegro_dev *dev = data; + unsigned int status; + + regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); + if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) + return IRQ_NONE; + + regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t allegro_irq_thread(int irq, void *data) +{ + struct allegro_dev *dev = data; + + allegro_mbox_notify(dev->mbox_status); + + return IRQ_HANDLED; +} + +static void allegro_copy_firmware(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err = 0; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy mcu firmware (%zu B) to SRAM\n", size); + err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); + if (err) + v4l2_err(&dev->v4l2_dev, + "failed to copy firmware: %d\n", err); +} + +static void allegro_copy_fw_codec(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err; + dma_addr_t icache_offset, dcache_offset; + + /* + * The downstream allocates 600 KB for the codec firmware to have some + * extra space for "possible extensions." My tests were fine with + * allocating just enough memory for the actual firmware, but I am not + * sure that the firmware really does not use the remaining space. + */ + err = allegro_alloc_buffer(dev, &dev->firmware, size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for firmware\n", size); + return; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy codec firmware (%zd B) to phys %pad\n", + size, &dev->firmware.paddr); + memcpy(dev->firmware.vaddr, buf, size); + + regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, + upper_32_bits(dev->firmware.paddr)); + + icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "icache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(icache_offset), lower_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, + upper_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, + lower_32_bits(icache_offset)); + + dcache_offset = + (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "dcache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, + upper_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, + lower_32_bits(dcache_offset)); +} + +static void allegro_free_fw_codec(struct allegro_dev *dev) +{ + allegro_free_buffer(dev, &dev->firmware); +} + +/* + * Control functions for the MCU + */ + +static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); +} + +static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); +} + +static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status != AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static int allegro_mcu_start(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + int err; + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); + if (err) + return err; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status == AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + return 0; +} + +static int allegro_mcu_reset(struct allegro_dev *dev) +{ + int err; + + /* + * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu + * does not go to sleep after the reset. + */ + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + err = regmap_write(dev->regmap, + AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); + if (err < 0) + return err; + + err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); + if (err < 0) + return err; + + return allegro_mcu_wait_for_sleep(dev); +} + +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + +static void allegro_destroy_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + + if (channel_exists(channel)) { + reinit_completion(&channel->completion); + allegro_mcu_send_destroy_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + v4l2_warn(&dev->v4l2_dev, + "channel %d: timeout while destroying\n", + channel->mcu_channel_id); + + channel->mcu_channel_id = -1; + } + + destroy_intermediate_buffers(channel); + destroy_reference_buffers(channel); + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + + v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_level, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, false); + + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); + + if (channel->user_id != -1) { + clear_bit(channel->user_id, &dev->channel_user_ids); + channel->user_id = -1; + } +} + +/* + * Create the MCU channel + * + * After the channel has been created, the picture size, format, colorspace + * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be + * changed anymore. + * + * The channel can be created only once. The MCU will accept source buffers + * and stream buffers only after a channel has been created. + */ +static int allegro_create_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + + if (channel_exists(channel)) { + v4l2_warn(&dev->v4l2_dev, + "channel already exists\n"); + return 0; + } + + channel->user_id = allegro_next_user_id(dev); + if (channel->user_id < 0) { + v4l2_err(&dev->v4l2_dev, + "no free channels available\n"); + return -EBUSY; + } + set_bit(channel->user_id, &dev->channel_user_ids); + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: creating channel (%4.4s, %dx%d@%d)\n", + channel->user_id, + (char *)&channel->codec, channel->width, channel->height, + DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator)); + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); + + v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_level, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, true); + + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); + + reinit_completion(&channel->completion); + allegro_mcu_send_create_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + channel->error = -ETIMEDOUT; + if (channel->error) + goto err; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: accepting buffers\n", + channel->mcu_channel_id); + + return 0; + +err: + allegro_destroy_channel(channel); + + return channel->error; +} + +/** + * allegro_channel_adjust() - Adjust channel parameters to current format + * @channel: the channel to adjust + * + * Various parameters of a channel and their limits depend on the currently + * set format. Adjust the parameters after a format change in one go. + */ +static void allegro_channel_adjust(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + u32 codec = channel->codec; + struct v4l2_ctrl *ctrl; + s64 min; + s64 max; + + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + if (codec == V4L2_PIX_FMT_H264) { + ctrl = channel->mpeg_video_h264_level; + min = select_minimum_h264_level(channel->width, channel->height); + } else { + ctrl = channel->mpeg_video_hevc_level; + min = select_minimum_hevc_level(channel->width, channel->height); + } + if (ctrl->minimum > min) + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s.minimum: %lld -> %lld\n", + v4l2_ctrl_get_name(ctrl->id), ctrl->minimum, min); + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, min, ctrl->maximum, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); + + ctrl = channel->mpeg_video_bitrate; + if (codec == V4L2_PIX_FMT_H264) + max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); + else + max = hevc_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level)); + if (ctrl->maximum < max) + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: maximum: %lld -> %lld\n", + v4l2_ctrl_get_name(ctrl->id), ctrl->maximum, max); + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); + + ctrl = channel->mpeg_video_bitrate_peak; + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); + + v4l2_ctrl_activate(channel->mpeg_video_h264_profile, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_level, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_i_frame_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_max_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_min_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_p_frame_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_b_frame_qp, + codec == V4L2_PIX_FMT_H264); + + v4l2_ctrl_activate(channel->mpeg_video_hevc_profile, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_level, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_tier, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_i_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_max_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_min_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_p_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_b_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + + if (codec == V4L2_PIX_FMT_H264) + channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM; + channel->temporal_mvp_enable = true; + channel->dbf_ovr_en = (codec == V4L2_PIX_FMT_H264); + channel->enable_deblocking_filter_override = (codec == V4L2_PIX_FMT_HEVC); + channel->enable_reordering = (codec == V4L2_PIX_FMT_HEVC); + channel->enable_loop_filter_across_tiles = true; + channel->enable_loop_filter_across_slices = true; + + if (codec == V4L2_PIX_FMT_H264) { + channel->b_hrz_me_range = 8; + channel->b_vrt_me_range = 8; + channel->p_hrz_me_range = 16; + channel->p_vrt_me_range = 16; + channel->max_cu_size = ilog2(16); + channel->min_cu_size = ilog2(8); + channel->max_tu_size = ilog2(4); + channel->min_tu_size = ilog2(4); + } else { + channel->b_hrz_me_range = 16; + channel->b_vrt_me_range = 16; + channel->p_hrz_me_range = 32; + channel->p_vrt_me_range = 32; + channel->max_cu_size = ilog2(32); + channel->min_cu_size = ilog2(8); + channel->max_tu_size = ilog2(32); + channel->min_tu_size = ilog2(4); + } + channel->max_transfo_depth_intra = 1; + channel->max_transfo_depth_inter = 1; +} + +static void allegro_set_default_params(struct allegro_channel *channel) +{ + channel->width = ALLEGRO_WIDTH_DEFAULT; + channel->height = ALLEGRO_HEIGHT_DEFAULT; + channel->stride = round_up(channel->width, 32); + channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; + + channel->colorspace = V4L2_COLORSPACE_REC709; + channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + channel->quantization = V4L2_QUANTIZATION_DEFAULT; + channel->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + channel->pixelformat = V4L2_PIX_FMT_NV12; + channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; + + channel->codec = V4L2_PIX_FMT_H264; +} + +static int allegro_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vq); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: queue setup[%s]: nplanes = %d\n", + V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture", + *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes); + + if (*nplanes != 0) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (sizes[0] < channel->sizeimage_raw) + return -EINVAL; + } else { + if (sizes[0] < channel->sizeimage_encoded) + return -EINVAL; + } + } else { + *nplanes = 1; + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + sizes[0] = channel->sizeimage_raw; + else + sizes[0] = channel->sizeimage_encoded; + } + + return 0; +} + +static int allegro_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct allegro_dev *dev = channel->dev; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + v4l2_err(&dev->v4l2_dev, + "channel %d: unsupported field\n", + channel->mcu_channel_id); + return -EINVAL; + } + } + + return 0; +} + +static void allegro_buf_queue(struct vb2_buffer *vb) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *q = vb->vb2_queue; + + if (V4L2_TYPE_IS_CAPTURE(q->type) && + vb2_is_streaming(q) && + v4l2_m2m_dst_buf_is_last(channel->fh.m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; + + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = channel->csequence++; + + v4l2_m2m_last_buffer_done(channel->fh.m2m_ctx, vbuf); + allegro_channel_eos_event(channel); + return; + } + + v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf); +} + +static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: start streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + v4l2_m2m_update_start_streaming_state(channel->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + channel->osequence = 0; + else + channel->csequence = 0; + + return 0; +} + +static void allegro_stop_streaming(struct vb2_queue *q) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *buffer; + struct allegro_m2m_buffer *shadow, *tmp; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: stop streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->source_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + + while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } else { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->stream_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + + allegro_destroy_channel(channel); + while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } + + v4l2_m2m_update_stop_streaming_state(channel->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + allegro_channel_eos_event(channel); +} + +static const struct vb2_ops allegro_queue_ops = { + .queue_setup = allegro_queue_setup, + .buf_prepare = allegro_buf_prepare, + .buf_queue = allegro_buf_queue, + .start_streaming = allegro_start_streaming, + .stop_streaming = allegro_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int allegro_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int err; + struct allegro_channel *channel = priv; + + src_vq->dev = &channel->dev->plat_dev->dev; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = channel; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &allegro_queue_ops; + src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + src_vq->lock = &channel->dev->lock; + err = vb2_queue_init(src_vq); + if (err) + return err; + + dst_vq->dev = &channel->dev->plat_dev->dev; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = channel; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &allegro_queue_ops; + dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + dst_vq->lock = &channel->dev->lock; + err = vb2_queue_init(dst_vq); + if (err) + return err; + + return 0; +} + +static int allegro_clamp_qp(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *next_ctrl; + + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_p_frame_qp; + else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_b_frame_qp; + else + return 0; + + /* Modify range automatically updates the value */ + __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); + + return allegro_clamp_qp(channel, next_ctrl); +} + +static int allegro_clamp_bitrate(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; + struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + ctrl_bitrate_peak->val < ctrl_bitrate->val) + ctrl_bitrate_peak->val = ctrl_bitrate->val; + + return 0; +} + +static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + allegro_clamp_bitrate(channel, ctrl); + break; + } + + 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 int allegro_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + struct allegro_channel *channel = NULL; + struct v4l2_ctrl_handler *handler; + u64 mask; + int ret; + unsigned int bitrate_max; + unsigned int bitrate_def; + unsigned int cpb_size_max; + unsigned int cpb_size_def; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + v4l2_fh_init(&channel->fh, vdev); + + init_completion(&channel->completion); + INIT_LIST_HEAD(&channel->source_shadow_list); + INIT_LIST_HEAD(&channel->stream_shadow_list); + mutex_init(&channel->shadow_list_lock); + + channel->dev = dev; + + allegro_set_default_params(channel); + + handler = &channel->ctrl_handler; + v4l2_ctrl_handler_init(handler, 0); + channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B; + channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + channel->mpeg_video_h264_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_h264_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_h264_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + 0, 51, 1, 30); + + channel->mpeg_video_hevc_profile = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0x0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + channel->mpeg_video_hevc_level = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0x0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + channel->mpeg_video_hevc_tier = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_TIER, + V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, 0x0, + V4L2_MPEG_VIDEO_HEVC_TIER_MAIN); + channel->mpeg_video_hevc_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_hevc_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_hevc_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_hevc_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_hevc_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + 0, 51, 1, 30); + + channel->mpeg_video_frame_rc_enable = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + false, 0x1, + true, false); + channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + if (channel->codec == V4L2_PIX_FMT_H264) { + bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + } else { + bitrate_max = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + bitrate_def = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + cpb_size_max = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + cpb_size_def = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + } + channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, bitrate_max, 1, bitrate_def); + channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, bitrate_max, 1, bitrate_def); + channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 0, cpb_size_max, 1, cpb_size_def); + channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, ALLEGRO_GOP_SIZE_MAX, + 1, ALLEGRO_GOP_SIZE_DEFAULT); + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 32, + 1, 1); + if (handler->error != 0) { + ret = handler->error; + goto error; + } + + channel->fh.ctrl_handler = handler; + + v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); + + v4l2_ctrl_handler_setup(handler); + + channel->mcu_channel_id = -1; + channel->user_id = -1; + + INIT_LIST_HEAD(&channel->buffers_reference); + INIT_LIST_HEAD(&channel->buffers_intermediate); + + channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, + allegro_queue_init); + + if (IS_ERR(channel->fh.m2m_ctx)) { + ret = PTR_ERR(channel->fh.m2m_ctx); + goto error; + } + + list_add(&channel->list, &dev->channels); + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + + allegro_channel_adjust(channel); + + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; +} + +static int allegro_release(struct file *file) +{ + struct allegro_channel *channel = fh_to_channel(file->private_data); + + v4l2_m2m_ctx_release(channel->fh.m2m_ctx); + + list_del(&channel->list); + + v4l2_ctrl_handler_free(&channel->ctrl_handler); + + v4l2_fh_del(&channel->fh); + v4l2_fh_exit(&channel->fh); + + kfree(channel); + + return 0; +} + +static int allegro_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + 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) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (f->index >= 1) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (f->index >= 2) + return -EINVAL; + if (f->index == 0) + f->pixelformat = V4L2_PIX_FMT_H264; + if (f->index == 1) + f->pixelformat = V4L2_PIX_FMT_HEVC; + break; + default: + return -EINVAL; + } + return 0; +} + +static int allegro_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->codec; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = channel->sizeimage_encoded; + + return 0; +} + +static int allegro_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_HEVC && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_H264) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); + + return 0; +} + +static int allegro_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct vb2_queue *vq; + int err; + + err = allegro_try_fmt_vid_cap(file, fh, f); + if (err) + return err; + + vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + if (vb2_is_busy(vq)) + return -EBUSY; + + channel->codec = f->fmt.pix.pixelformat; + + allegro_channel_adjust(channel); + + return 0; +} + +static int allegro_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->pixelformat; + f->fmt.pix.bytesperline = channel->stride; + f->fmt.pix.sizeimage = channel->sizeimage_raw; + + return 0; +} + +static int allegro_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + /* + * The firmware of the Allegro codec handles the padding internally + * and expects the visual frame size when configuring a channel. + * Therefore, unlike other encoder drivers, this driver does not round + * up the width and height to macroblock alignment and does not + * implement the selection api. + */ + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32); + f->fmt.pix.sizeimage = + f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; + + return 0; +} + +static int allegro_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = allegro_try_fmt_vid_out(file, fh, f); + if (err) + return err; + + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; + channel->stride = f->fmt.pix.bytesperline; + channel->sizeimage_raw = f->fmt.pix.sizeimage; + + channel->colorspace = f->fmt.pix.colorspace; + channel->ycbcr_enc = f->fmt.pix.ycbcr_enc; + channel->quantization = f->fmt.pix.quantization; + channel->xfer_func = f->fmt.pix.xfer_func; + + allegro_channel_adjust(channel); + + return 0; +} + +static int allegro_channel_cmd_stop(struct allegro_channel *channel) +{ + if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + allegro_channel_eos_event(channel); + + return 0; +} + +static int allegro_channel_cmd_start(struct allegro_channel *channel) +{ + if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(&channel->fh.m2m_ctx->cap_q_ctx.q); + + return 0; +} + +static int allegro_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *cmd) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); + if (err) + return err; + + err = v4l2_m2m_ioctl_encoder_cmd(file, fh, cmd); + if (err) + return err; + + if (cmd->cmd == V4L2_ENC_CMD_STOP) + err = allegro_channel_cmd_stop(channel); + + if (cmd->cmd == V4L2_ENC_CMD_START) + err = allegro_channel_cmd_start(channel); + + return err; +} + +static int allegro_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_HEVC: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_NV12: + break; + default: + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN; + fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN; + fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int allegro_ioctl_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct v4l2_fh *fh = file->private_data; + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + err = allegro_create_channel(channel); + if (err) + return err; + } + + return v4l2_m2m_streamon(file, fh->m2m_ctx, type); +} + +static int allegro_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + timeperframe->numerator = channel->framerate.denominator; + timeperframe->denominator = channel->framerate.numerator; + + return 0; +} + +static int allegro_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + int div; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) + return allegro_g_parm(file, fh, a); + + div = gcd(timeperframe->denominator, timeperframe->numerator); + channel->framerate.numerator = timeperframe->denominator / div; + channel->framerate.denominator = timeperframe->numerator / div; + + return 0; +} + +static int allegro_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops allegro_ioctl_ops = { + .vidioc_querycap = allegro_querycap, + .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = allegro_s_fmt_vid_cap, + .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = allegro_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = allegro_encoder_cmd, + .vidioc_enum_framesizes = allegro_enum_framesizes, + + .vidioc_g_parm = allegro_g_parm, + .vidioc_s_parm = allegro_s_parm, + + .vidioc_subscribe_event = allegro_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations allegro_fops = { + .owner = THIS_MODULE, + .open = allegro_open, + .release = allegro_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int allegro_register_device(struct allegro_dev *dev) +{ + struct video_device *video_dev = &dev->video_dev; + + strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); + video_dev->fops = &allegro_fops; + video_dev->ioctl_ops = &allegro_ioctl_ops; + video_dev->release = video_device_release_empty; + video_dev->lock = &dev->lock; + video_dev->v4l2_dev = &dev->v4l2_dev; + video_dev->vfl_dir = VFL_DIR_M2M; + video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(video_dev, dev); + + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); +} + +static void allegro_device_run(void *priv) +{ + struct allegro_channel *channel = priv; + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + dma_addr_t src_y; + dma_addr_t src_uv; + dma_addr_t dst_addr; + unsigned long dst_size; + u64 src_handle; + u64 dst_handle; + + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); + dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, + dst_buf); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, + dst_handle); + + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + src_buf->sequence = channel->osequence++; + src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_uv = src_y + (channel->stride * channel->height); + src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, + src_buf); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); +} + +static const struct v4l2_m2m_ops allegro_m2m_ops = { + .device_run = allegro_device_run, +}; + +static int allegro_mcu_hw_init(struct allegro_dev *dev, + const struct fw_info *info) +{ + int err; + + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } + + 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"); diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c new file mode 100644 index 000000000000..7e08c5050f2e --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Helper functions for handling messages that are send via mailbox to the + * Allegro VCU firmware. + */ + +#include <linux/bitfield.h> +#include <linux/export.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/videodev2.h> + +#include "allegro-mail.h" + +const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} +EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } + + return i * sizeof(*dst); +} + +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + u32 pixelformat = param->codec; + + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_HEVC: + return 2; + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + case V4L2_PIX_FMT_HEVC: + return 1; + case V4L2_PIX_FMT_H264: + default: + return 0; + } + } +} + +ssize_t +allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + unsigned int i = 0; + unsigned int j = 0; + u32 val; + unsigned int codec = settings_get_mcu_codec(param); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->layer_id; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | + FIELD_PREP(GENMASK(15, 0), param->width); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->videomode; + dst[i++] = param->format; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->colorspace; + dst[i++] = param->src_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->src_bit_depth; + dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | + FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | + FIELD_PREP(GENMASK(7, 0), param->profile); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | + FIELD_PREP(GENMASK(15, 0), param->level); + + val = 0; + val |= param->temporal_mvp_enable ? BIT(20) : 0; + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num); + if (version >= MCU_MSG_VERSION_2019_2) + val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc - 1); + else + val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->enable_reordering ? BIT(0) : 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + val |= param->override_lf ? BIT(12) : 0; + dst[i++] = val; + + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } + + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; + + return i * sizeof(*dst); +} + +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } + + return 0; +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = msg->num_buffers; + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->dst_handle); + dst[i++] = upper_32_bits(msg->dst_handle); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + dst[i++] = msg->reserved; + dst[i++] = msg->encoding_options; + dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | + FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + } + + dst[i++] = lower_32_bits(msg->user_param); + dst[i++] = upper_32_bits(msg->user_param); + dst[i++] = lower_32_bits(msg->src_handle); + dst[i++] = upper_32_bits(msg->src_handle); + dst[i++] = msg->request_options; + dst[i++] = msg->src_y; + dst[i++] = msg->src_uv; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->is_10_bit; + dst[i++] = msg->stride; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->format; + dst[i++] = msg->ep2; + dst[i++] = lower_32_bits(msg->ep2_v); + dst[i++] = upper_32_bits(msg->ep2_v); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) +{ + unsigned int i = 0; + + msg->reserved0 = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + msg->channel_id = src[i++]; + msg->user_id = src[i++]; + /* + * Version >= MCU_MSG_VERSION_2019_2 is handled in + * allegro_decode_config_blob(). + */ + if (version < MCU_MSG_VERSION_2019_2) { + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + } + msg->int_buffers_count = src[i++]; + msg->int_buffers_size = src[i++]; + msg->rec_buffers_count = src[i++]; + msg->rec_buffers_size = src[i++]; + msg->reserved = src[i++]; + msg->error_code = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->channel_id = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + unsigned int j; + + msg->channel_id = src[i++]; + + msg->dst_handle = src[i++]; + msg->dst_handle |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (header->type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h new file mode 100644 index 000000000000..2c7bc509eac3 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.h @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Allegro VCU firmware mailbox mail definitions + */ + +#ifndef ALLEGRO_MAIL_H +#define ALLEGRO_MAIL_H + +#include <linux/kernel.h> + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, +}; + +const char *msg_type_name(enum mcu_msg_type type); + +struct mcu_msg_header { + enum mcu_msg_type type; + enum mcu_msg_version version; +}; + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +}; + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +}; + +struct create_channel_param { + enum mcu_msg_version version; + u32 layer_id; + u16 width; + u16 height; + u32 videomode; + u32 format; + u32 colorspace; + u32 src_mode; + u32 src_bit_depth; + u8 profile; + u16 constraint_set_flags; + u32 codec; + u16 level; + u16 tier; + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 enable_reordering; + u32 dbf_ovr_en; + u32 override_lf; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 custom_lda; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_idr; + u32 freq_lt; + u32 gdr_mode; + u16 gop_length; + u8 num_b; + u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; + + u32 subframe_latency; + u32 lda_control_mode; + u32 unknown41; + + u32 lda_factors[6]; + + u32 max_num_merge_cand; +}; + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; + u32 ep1_addr; +}; + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +}; + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +}; + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + size_t num_buffers; + struct mcu_msg_push_buffers_internal_buffer buffer[]; +}; + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 dst_handle; +}; + +struct mcu_msg_encode_frame { + struct mcu_msg_header header; + u32 channel_id; + u32 reserved; + + u32 encoding_options; +#define AL_OPT_USE_QP_TABLE BIT(0) +#define AL_OPT_FORCE_LOAD BIT(1) +#define AL_OPT_USE_L2 BIT(2) +#define AL_OPT_DISABLE_INTRA BIT(3) +#define AL_OPT_DEPENDENT_SLICES BIT(4) + + s16 pps_qp; + u16 padding; + u64 user_param; + u64 src_handle; + + u32 request_options; +#define AL_OPT_SCENE_CHANGE BIT(0) +#define AL_OPT_RESTART_GOP BIT(1) +#define AL_OPT_USE_LONG_TERM BIT(2) +#define AL_OPT_UPDATE_PARAMS BIT(3) + + /* u32 scene_change_delay (optional) */ + /* rate control param (optional) */ + /* gop param (optional) */ + /* dynamic resolution params (optional) */ + u32 src_y; + u32 src_uv; + u32 is_10_bit; + u32 stride; + u32 format; + u32 ep2; + u64 ep2_v; +}; + +struct mcu_msg_encode_frame_response { + struct mcu_msg_header header; + u32 channel_id; + u64 dst_handle; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; +}; + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + +#endif diff --git a/drivers/media/platform/allegro-dvt/nal-h264.c b/drivers/media/platform/allegro-dvt/nal-h264.c new file mode 100644 index 000000000000..94dd9266d850 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.c @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs + * + * The conversion is defined in "ITU-T Rec. H.264 (04/2017) Advanced video + * coding for generic audiovisual services". Decoder drivers may use the + * parser to parse RBSP from encoded streams and configure the hardware, if + * the hardware is not able to parse RBSP itself. Encoder drivers may use the + * generator to generate the RBSP for SPS/PPS nal units and add them to the + * encoded stream if the hardware does not generate the units. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/v4l2-controls.h> + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/log2.h> + +#include "nal-h264.h" +#include "nal-rbsp.h" + +/* + * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax + * element categories, and NAL unit type classes + */ +enum nal_unit_type { + SEQUENCE_PARAMETER_SET = 7, + PICTURE_PARAMETER_SET = 8, + FILLER_DATA = 12, +}; + +/** + * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile + * @profile: the profile as &enum v4l2_mpeg_video_h264_profile + * + * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified + * in Rec. ITU-T H.264 (04/2017) A.2. + * + * Return: the profile_idc for the passed level + */ +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return 88; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + default: + return -EINVAL; + } +} + +/** + * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level + * @level: the level as &enum v4l2_mpeg_video_h264_level + * + * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in + * Rec. ITU-T H.264 (04/2017) A.3.2. + * + * Return: the level_idc for the passed level + */ +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 9; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return 51; + default: + return -EINVAL; + } +} + +static void nal_h264_write_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + + rbsp->pos += i * 8; +} + +static void nal_h264_read_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { + rbsp->error = -EINVAL; + return; + } + + rbsp->pos += i * 8; +} + +static void nal_h264_write_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i; + + /* Keep 1 byte extra for terminating the NAL unit */ + i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; + memset(p, 0xff, i); + rbsp->pos += i * 8; +} + +static void nal_h264_read_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + + while (*p == 0xff) { + if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p++; + rbsp->pos += 8; + } +} + +static void nal_h264_rbsp_hrd_parameters(struct rbsp *rbsp, + struct nal_h264_hrd_parameters *hrd) +{ + unsigned int i; + + if (!hrd) { + rbsp->error = -EINVAL; + return; + } + + rbsp_uev(rbsp, &hrd->cpb_cnt_minus1); + rbsp_bits(rbsp, 4, &hrd->bit_rate_scale); + rbsp_bits(rbsp, 4, &hrd->cpb_size_scale); + + for (i = 0; i <= hrd->cpb_cnt_minus1; i++) { + rbsp_uev(rbsp, &hrd->bit_rate_value_minus1[i]); + rbsp_uev(rbsp, &hrd->cpb_size_value_minus1[i]); + rbsp_bit(rbsp, &hrd->cbr_flag[i]); + } + + rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->time_offset_length); +} + +static void nal_h264_rbsp_vui_parameters(struct rbsp *rbsp, + struct nal_h264_vui_parameters *vui) +{ + if (!vui) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bit(rbsp, &vui->aspect_ratio_info_present_flag); + if (vui->aspect_ratio_info_present_flag) { + rbsp_bits(rbsp, 8, &vui->aspect_ratio_idc); + if (vui->aspect_ratio_idc == 255) { + rbsp_bits(rbsp, 16, &vui->sar_width); + rbsp_bits(rbsp, 16, &vui->sar_height); + } + } + + rbsp_bit(rbsp, &vui->overscan_info_present_flag); + if (vui->overscan_info_present_flag) + rbsp_bit(rbsp, &vui->overscan_appropriate_flag); + + rbsp_bit(rbsp, &vui->video_signal_type_present_flag); + if (vui->video_signal_type_present_flag) { + rbsp_bits(rbsp, 3, &vui->video_format); + rbsp_bit(rbsp, &vui->video_full_range_flag); + + rbsp_bit(rbsp, &vui->colour_description_present_flag); + if (vui->colour_description_present_flag) { + rbsp_bits(rbsp, 8, &vui->colour_primaries); + rbsp_bits(rbsp, 8, &vui->transfer_characteristics); + rbsp_bits(rbsp, 8, &vui->matrix_coefficients); + } + } + + rbsp_bit(rbsp, &vui->chroma_loc_info_present_flag); + if (vui->chroma_loc_info_present_flag) { + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_top_field); + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_bottom_field); + } + + rbsp_bit(rbsp, &vui->timing_info_present_flag); + if (vui->timing_info_present_flag) { + rbsp_bits(rbsp, 32, &vui->num_units_in_tick); + rbsp_bits(rbsp, 32, &vui->time_scale); + rbsp_bit(rbsp, &vui->fixed_frame_rate_flag); + } + + rbsp_bit(rbsp, &vui->nal_hrd_parameters_present_flag); + if (vui->nal_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters); + + rbsp_bit(rbsp, &vui->vcl_hrd_parameters_present_flag); + if (vui->vcl_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->vcl_hrd_parameters); + + if (vui->nal_hrd_parameters_present_flag || + vui->vcl_hrd_parameters_present_flag) + rbsp_bit(rbsp, &vui->low_delay_hrd_flag); + + rbsp_bit(rbsp, &vui->pic_struct_present_flag); + + rbsp_bit(rbsp, &vui->bitstream_restriction_flag); + if (vui->bitstream_restriction_flag) { + rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag); + rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom); + rbsp_uev(rbsp, &vui->max_bits_per_mb_denom); + rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal); + rbsp_uev(rbsp, &vui->log21_max_mv_length_vertical); + rbsp_uev(rbsp, &vui->max_num_reorder_frames); + rbsp_uev(rbsp, &vui->max_dec_frame_buffering); + } +} + +static void nal_h264_rbsp_sps(struct rbsp *rbsp, struct nal_h264_sps *sps) +{ + unsigned int i; + + if (!sps) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bits(rbsp, 8, &sps->profile_idc); + rbsp_bit(rbsp, &sps->constraint_set0_flag); + rbsp_bit(rbsp, &sps->constraint_set1_flag); + rbsp_bit(rbsp, &sps->constraint_set2_flag); + rbsp_bit(rbsp, &sps->constraint_set3_flag); + rbsp_bit(rbsp, &sps->constraint_set4_flag); + rbsp_bit(rbsp, &sps->constraint_set5_flag); + rbsp_bits(rbsp, 2, &sps->reserved_zero_2bits); + rbsp_bits(rbsp, 8, &sps->level_idc); + + rbsp_uev(rbsp, &sps->seq_parameter_set_id); + + if (sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244 || + sps->profile_idc == 44 || sps->profile_idc == 83 || + sps->profile_idc == 86 || sps->profile_idc == 118 || + sps->profile_idc == 128 || sps->profile_idc == 138 || + sps->profile_idc == 139 || sps->profile_idc == 134 || + sps->profile_idc == 135) { + rbsp_uev(rbsp, &sps->chroma_format_idc); + + if (sps->chroma_format_idc == 3) + rbsp_bit(rbsp, &sps->separate_colour_plane_flag); + rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); + rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); + rbsp_bit(rbsp, &sps->qpprime_y_zero_transform_bypass_flag); + rbsp_bit(rbsp, &sps->seq_scaling_matrix_present_flag); + if (sps->seq_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + } + + rbsp_uev(rbsp, &sps->log2_max_frame_num_minus4); + + rbsp_uev(rbsp, &sps->pic_order_cnt_type); + switch (sps->pic_order_cnt_type) { + case 0: + rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); + break; + case 1: + rbsp_bit(rbsp, &sps->delta_pic_order_always_zero_flag); + rbsp_sev(rbsp, &sps->offset_for_non_ref_pic); + rbsp_sev(rbsp, &sps->offset_for_top_to_bottom_field); + + rbsp_uev(rbsp, &sps->num_ref_frames_in_pic_order_cnt_cycle); + for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + rbsp_sev(rbsp, &sps->offset_for_ref_frame[i]); + break; + default: + rbsp->error = -EINVAL; + break; + } + + rbsp_uev(rbsp, &sps->max_num_ref_frames); + rbsp_bit(rbsp, &sps->gaps_in_frame_num_value_allowed_flag); + rbsp_uev(rbsp, &sps->pic_width_in_mbs_minus1); + rbsp_uev(rbsp, &sps->pic_height_in_map_units_minus1); + + rbsp_bit(rbsp, &sps->frame_mbs_only_flag); + if (!sps->frame_mbs_only_flag) + rbsp_bit(rbsp, &sps->mb_adaptive_frame_field_flag); + + rbsp_bit(rbsp, &sps->direct_8x8_inference_flag); + + rbsp_bit(rbsp, &sps->frame_cropping_flag); + if (sps->frame_cropping_flag) { + rbsp_uev(rbsp, &sps->crop_left); + rbsp_uev(rbsp, &sps->crop_right); + rbsp_uev(rbsp, &sps->crop_top); + rbsp_uev(rbsp, &sps->crop_bottom); + } + + rbsp_bit(rbsp, &sps->vui_parameters_present_flag); + if (sps->vui_parameters_present_flag) + nal_h264_rbsp_vui_parameters(rbsp, &sps->vui); +} + +static void nal_h264_rbsp_pps(struct rbsp *rbsp, struct nal_h264_pps *pps) +{ + int i; + + rbsp_uev(rbsp, &pps->pic_parameter_set_id); + rbsp_uev(rbsp, &pps->seq_parameter_set_id); + rbsp_bit(rbsp, &pps->entropy_coding_mode_flag); + rbsp_bit(rbsp, &pps->bottom_field_pic_order_in_frame_present_flag); + rbsp_uev(rbsp, &pps->num_slice_groups_minus1); + if (pps->num_slice_groups_minus1 > 0) { + rbsp_uev(rbsp, &pps->slice_group_map_type); + switch (pps->slice_group_map_type) { + case 0: + for (i = 0; i < pps->num_slice_groups_minus1; i++) + rbsp_uev(rbsp, &pps->run_length_minus1[i]); + break; + case 2: + for (i = 0; i < pps->num_slice_groups_minus1; i++) { + rbsp_uev(rbsp, &pps->top_left[i]); + rbsp_uev(rbsp, &pps->bottom_right[i]); + } + break; + case 3: case 4: case 5: + rbsp_bit(rbsp, &pps->slice_group_change_direction_flag); + rbsp_uev(rbsp, &pps->slice_group_change_rate_minus1); + break; + case 6: + rbsp_uev(rbsp, &pps->pic_size_in_map_units_minus1); + for (i = 0; i < pps->pic_size_in_map_units_minus1; i++) + rbsp_bits(rbsp, + order_base_2(pps->num_slice_groups_minus1 + 1), + &pps->slice_group_id[i]); + break; + default: + break; + } + } + rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); + rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); + rbsp_bit(rbsp, &pps->weighted_pred_flag); + rbsp_bits(rbsp, 2, &pps->weighted_bipred_idc); + rbsp_sev(rbsp, &pps->pic_init_qp_minus26); + rbsp_sev(rbsp, &pps->pic_init_qs_minus26); + rbsp_sev(rbsp, &pps->chroma_qp_index_offset); + rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); + rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); + rbsp_bit(rbsp, &pps->redundant_pic_cnt_present_flag); + if (/* more_rbsp_data() */ false) { + rbsp_bit(rbsp, &pps->transform_8x8_mode_flag); + rbsp_bit(rbsp, &pps->pic_scaling_matrix_present_flag); + if (pps->pic_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + rbsp_sev(rbsp, &pps->second_chroma_qp_index_offset); + } +} + +/** + * nal_h264_write_sps() - Write SPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @sps: &struct nal_h264_sps to convert to RBSP + * + * Convert @sps to RBSP data and write it into @dest. + * + * The size of the SPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the SPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = SEQUENCE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_sps); + +/** + * nal_h264_read_sps() - Read SPS NAL unit from RBSP format + * @dev: device pointer + * @sps: the &struct nal_h264_sps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @sps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != SEQUENCE_PARAMETER_SET) + return -EINVAL; + + nal_h264_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_sps); + +/** + * nal_h264_write_pps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @pps: &struct nal_h264_pps to convert to RBSP + * + * Convert @pps to RBSP data and write it into @dest. + * + * The size of the PPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the PPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = PICTURE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_pps); + +/** + * nal_h264_read_pps() - Read PPS NAL unit from RBSP format + * @dev: device pointer + * @pps: the &struct nal_h264_pps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @pps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n) +{ + struct rbsp rbsp; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp.pos += 8; + + nal_h264_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_pps); + +/** + * nal_h264_write_filler() - Write filler data RBSP + * @dev: device pointer + * @dest: buffer to fill with filler data + * @n: size of the buffer to fill with filler data + * + * Write a filler data RBSP to @dest with a size of @n bytes and return the + * number of written filler data bytes. + * + * Use this function to generate dummy data in an RBSP data stream that can be + * safely ignored by h264 decoders. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = FILLER_DATA; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_write_filler_data(&rbsp); + + rbsp_trailing_bits(&rbsp); + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_filler); + +/** + * nal_h264_read_filler() - Read filler data RBSP + * @dev: device pointer + * @src: buffer with RBSP data that is read + * @n: maximum size of src that shall be read + * + * Read a filler data RBSP from @src up to a maximum size of @n bytes and + * return the size of the filler data in bytes including the marker. + * + * This function is used to parse filler data and skip the respective bytes in + * the RBSP data. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error) + return rbsp.error; + if (forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != FILLER_DATA) + return -EINVAL; + + nal_h264_read_filler_data(&rbsp); + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_filler); diff --git a/drivers/media/platform/allegro-dvt/nal-h264.h b/drivers/media/platform/allegro-dvt/nal-h264.h new file mode 100644 index 000000000000..2ba7cbced7a5 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_H264_H__ +#define __NAL_H264_H__ + +#include <linux/kernel.h> +#include <linux/types.h> + +/** + * struct nal_h264_hdr_parameters - HDR parameters + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. + */ +struct nal_h264_hrd_parameters { + unsigned int cpb_cnt_minus1; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + struct { + int bit_rate_value_minus1[16]; + int cpb_size_value_minus1[16]; + unsigned int cbr_flag[16]; + }; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + unsigned int time_offset_length; +}; + +/** + * struct nal_h264_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.264 (04/2017) E.1.1 VUI parameters syntax. + */ +struct nal_h264_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int fixed_frame_rate_flag; + }; + unsigned int nal_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters nal_hrd_parameters; + unsigned int vcl_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters vcl_hrd_parameters; + unsigned int low_delay_hrd_flag; + unsigned int pic_struct_present_flag; + unsigned int bitstream_restriction_flag; + struct { + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log21_max_mv_length_vertical; + unsigned int max_num_reorder_frames; + unsigned int max_dec_frame_buffering; + }; +}; + +/** + * struct nal_h264_sps - Sequence parameter set + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. + */ +struct nal_h264_sps { + unsigned int profile_idc; + unsigned int constraint_set0_flag; + unsigned int constraint_set1_flag; + unsigned int constraint_set2_flag; + unsigned int constraint_set3_flag; + unsigned int constraint_set4_flag; + unsigned int constraint_set5_flag; + unsigned int reserved_zero_2bits; + unsigned int level_idc; + unsigned int seq_parameter_set_id; + struct { + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int qpprime_y_zero_transform_bypass_flag; + unsigned int seq_scaling_matrix_present_flag; + }; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + union { + unsigned int log2_max_pic_order_cnt_lsb_minus4; + struct { + unsigned int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_pic_order_cnt_cycle; + int offset_for_ref_frame[255]; + }; + }; + unsigned int max_num_ref_frames; + unsigned int gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + unsigned int frame_mbs_only_flag; + unsigned int mb_adaptive_frame_field_flag; + unsigned int direct_8x8_inference_flag; + unsigned int frame_cropping_flag; + struct { + unsigned int crop_left; + unsigned int crop_right; + unsigned int crop_top; + unsigned int crop_bottom; + }; + unsigned int vui_parameters_present_flag; + struct nal_h264_vui_parameters vui; +}; + +/** + * struct nal_h264_pps - Picture parameter set + * + * C struct representation of the picture parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. + */ +struct nal_h264_pps { + unsigned int pic_parameter_set_id; + unsigned int seq_parameter_set_id; + unsigned int entropy_coding_mode_flag; + unsigned int bottom_field_pic_order_in_frame_present_flag; + unsigned int num_slice_groups_minus1; + unsigned int slice_group_map_type; + union { + unsigned int run_length_minus1[8]; + struct { + unsigned int top_left[8]; + unsigned int bottom_right[8]; + }; + struct { + unsigned int slice_group_change_direction_flag; + unsigned int slice_group_change_rate_minus1; + }; + struct { + unsigned int pic_size_in_map_units_minus1; + unsigned int slice_group_id[8]; + }; + }; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + unsigned int deblocking_filter_control_present_flag; + unsigned int constrained_intra_pred_flag; + unsigned int redundant_pic_cnt_present_flag; + struct { + unsigned int transform_8x8_mode_flag; + unsigned int pic_scaling_matrix_present_flag; + int second_chroma_qp_index_offset; + }; +}; + +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); + +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps); +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n); +void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); + +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps); +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n); +void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); + +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_H264_H__ */ diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.c b/drivers/media/platform/allegro-dvt/nal-hevc.c new file mode 100644 index 000000000000..5db540c69bfe --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-hevc.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + * + * The conversion is defined in "ITU-T Rec. H.265 (02/2018) high efficiency + * video coding". Decoder drivers may use the parser to parse RBSP from + * encoded streams and configure the hardware, if the hardware is not able to + * parse RBSP itself. Encoder drivers may use the generator to generate the + * RBSP for VPS/SPS/PPS nal units and add them to the encoded stream if the + * hardware does not generate the units. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/v4l2-controls.h> + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/log2.h> + +#include "nal-hevc.h" +#include "nal-rbsp.h" + +/* + * See Rec. ITU-T H.265 (02/2018) Table 7-1 – NAL unit type codes and NAL unit + * type classes + */ +enum nal_unit_type { + VPS_NUT = 32, + SPS_NUT = 33, + PPS_NUT = 34, + FD_NUT = 38, +}; + +int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + return 1; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return 2; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return 3; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_profile_from_v4l2); + +int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier) +{ + switch (tier) { + case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN: + return 0; + case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH: + return 1; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_tier_from_v4l2); + +int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level) +{ + /* + * T-Rec-H.265 p. 280: general_level_idc and sub_layer_level_idc[ i ] + * shall be set equal to a value of 30 times the level number + * specified in Table A.6. + */ + int factor = 30 / 10; + + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return factor * 10; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return factor * 20; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return factor * 21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return factor * 30; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return factor * 31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return factor * 40; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return factor * 41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return factor * 50; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return factor * 51; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + return factor * 52; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: + return factor * 60; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: + return factor * 61; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: + return factor * 62; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_level_from_v4l2); + +static void nal_hevc_write_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + + rbsp->pos += i * 8; +} + +static void nal_hevc_read_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { + rbsp->error = -EINVAL; + return; + } + + rbsp->pos += i * 8; +} + +static void nal_hevc_write_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i; + + /* Keep 1 byte extra for terminating the NAL unit */ + i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; + memset(p, 0xff, i); + rbsp->pos += i * 8; +} + +static void nal_hevc_read_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + + while (*p == 0xff) { + if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p++; + rbsp->pos += 8; + } +} + +static void nal_hevc_rbsp_profile_tier_level(struct rbsp *rbsp, + struct nal_hevc_profile_tier_level *ptl) +{ + unsigned int i; + unsigned int max_num_sub_layers_minus_1 = 0; + + rbsp_bits(rbsp, 2, &ptl->general_profile_space); + rbsp_bit(rbsp, &ptl->general_tier_flag); + rbsp_bits(rbsp, 5, &ptl->general_profile_idc); + for (i = 0; i < 32; i++) + rbsp_bit(rbsp, &ptl->general_profile_compatibility_flag[i]); + rbsp_bit(rbsp, &ptl->general_progressive_source_flag); + rbsp_bit(rbsp, &ptl->general_interlaced_source_flag); + rbsp_bit(rbsp, &ptl->general_non_packed_constraint_flag); + rbsp_bit(rbsp, &ptl->general_frame_only_constraint_flag); + if (ptl->general_profile_idc == 4 || + ptl->general_profile_compatibility_flag[4] || + ptl->general_profile_idc == 5 || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_idc == 6 || + ptl->general_profile_compatibility_flag[6] || + ptl->general_profile_idc == 7 || + ptl->general_profile_compatibility_flag[7] || + ptl->general_profile_idc == 8 || + ptl->general_profile_compatibility_flag[8] || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[9] || + ptl->general_profile_idc == 10 || + ptl->general_profile_compatibility_flag[10]) { + rbsp_bit(rbsp, &ptl->general_max_12bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_10bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_8bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_422chroma_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_420chroma_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_monochrome_constraint_flag); + rbsp_bit(rbsp, &ptl->general_intra_constraint_flag); + rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag); + rbsp_bit(rbsp, &ptl->general_lower_bit_rate_constraint_flag); + if (ptl->general_profile_idc == 5 || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[9] || + ptl->general_profile_idc == 10 || + ptl->general_profile_compatibility_flag[10]) { + rbsp_bit(rbsp, &ptl->general_max_14bit_constraint_flag); + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_33bits); + rbsp_bits(rbsp, 33 - 32, &ptl->general_reserved_zero_33bits); + } else { + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_34bits); + rbsp_bits(rbsp, 34 - 2, &ptl->general_reserved_zero_34bits); + } + } else if (ptl->general_profile_idc == 2 || + ptl->general_profile_compatibility_flag[2]) { + rbsp_bits(rbsp, 7, &ptl->general_reserved_zero_7bits); + rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag); + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_35bits); + rbsp_bits(rbsp, 35 - 32, &ptl->general_reserved_zero_35bits); + } else { + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_43bits); + rbsp_bits(rbsp, 43 - 32, &ptl->general_reserved_zero_43bits); + } + if ((ptl->general_profile_idc >= 1 && ptl->general_profile_idc <= 5) || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[1] || + ptl->general_profile_compatibility_flag[2] || + ptl->general_profile_compatibility_flag[3] || + ptl->general_profile_compatibility_flag[4] || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_compatibility_flag[9]) + rbsp_bit(rbsp, &ptl->general_inbld_flag); + else + rbsp_bit(rbsp, &ptl->general_reserved_zero_bit); + rbsp_bits(rbsp, 8, &ptl->general_level_idc); + if (max_num_sub_layers_minus_1 > 0) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_vps(struct rbsp *rbsp, struct nal_hevc_vps *vps) +{ + unsigned int i, j; + unsigned int reserved_0xffff_16bits = 0xffff; + + rbsp_bits(rbsp, 4, &vps->video_parameter_set_id); + rbsp_bit(rbsp, &vps->base_layer_internal_flag); + rbsp_bit(rbsp, &vps->base_layer_available_flag); + rbsp_bits(rbsp, 6, &vps->max_layers_minus1); + rbsp_bits(rbsp, 3, &vps->max_sub_layers_minus1); + rbsp_bits(rbsp, 1, &vps->temporal_id_nesting_flag); + rbsp_bits(rbsp, 16, &reserved_0xffff_16bits); + nal_hevc_rbsp_profile_tier_level(rbsp, &vps->profile_tier_level); + rbsp_bit(rbsp, &vps->sub_layer_ordering_info_present_flag); + for (i = vps->sub_layer_ordering_info_present_flag ? 0 : vps->max_sub_layers_minus1; + i <= vps->max_sub_layers_minus1; i++) { + rbsp_uev(rbsp, &vps->max_dec_pic_buffering_minus1[i]); + rbsp_uev(rbsp, &vps->max_num_reorder_pics[i]); + rbsp_uev(rbsp, &vps->max_latency_increase_plus1[i]); + } + rbsp_bits(rbsp, 6, &vps->max_layer_id); + rbsp_uev(rbsp, &vps->num_layer_sets_minus1); + for (i = 0; i <= vps->num_layer_sets_minus1; i++) + for (j = 0; j <= vps->max_layer_id; j++) + rbsp_bit(rbsp, &vps->layer_id_included_flag[i][j]); + rbsp_bit(rbsp, &vps->timing_info_present_flag); + if (vps->timing_info_present_flag) + rbsp_unsupported(rbsp); + rbsp_bit(rbsp, &vps->extension_flag); + if (vps->extension_flag) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_sps(struct rbsp *rbsp, struct nal_hevc_sps *sps) +{ + unsigned int i; + + rbsp_bits(rbsp, 4, &sps->video_parameter_set_id); + rbsp_bits(rbsp, 3, &sps->max_sub_layers_minus1); + rbsp_bit(rbsp, &sps->temporal_id_nesting_flag); + nal_hevc_rbsp_profile_tier_level(rbsp, &sps->profile_tier_level); + rbsp_uev(rbsp, &sps->seq_parameter_set_id); + + rbsp_uev(rbsp, &sps->chroma_format_idc); + if (sps->chroma_format_idc == 3) + rbsp_bit(rbsp, &sps->separate_colour_plane_flag); + rbsp_uev(rbsp, &sps->pic_width_in_luma_samples); + rbsp_uev(rbsp, &sps->pic_height_in_luma_samples); + rbsp_bit(rbsp, &sps->conformance_window_flag); + if (sps->conformance_window_flag) { + rbsp_uev(rbsp, &sps->conf_win_left_offset); + rbsp_uev(rbsp, &sps->conf_win_right_offset); + rbsp_uev(rbsp, &sps->conf_win_top_offset); + rbsp_uev(rbsp, &sps->conf_win_bottom_offset); + } + rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); + rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); + + rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); + + rbsp_bit(rbsp, &sps->sub_layer_ordering_info_present_flag); + for (i = (sps->sub_layer_ordering_info_present_flag ? 0 : sps->max_sub_layers_minus1); + i <= sps->max_sub_layers_minus1; i++) { + rbsp_uev(rbsp, &sps->max_dec_pic_buffering_minus1[i]); + rbsp_uev(rbsp, &sps->max_num_reorder_pics[i]); + rbsp_uev(rbsp, &sps->max_latency_increase_plus1[i]); + } + rbsp_uev(rbsp, &sps->log2_min_luma_coding_block_size_minus3); + rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_coding_block_size); + rbsp_uev(rbsp, &sps->log2_min_luma_transform_block_size_minus2); + rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_transform_block_size); + rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_inter); + rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_intra); + + rbsp_bit(rbsp, &sps->scaling_list_enabled_flag); + if (sps->scaling_list_enabled_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->amp_enabled_flag); + rbsp_bit(rbsp, &sps->sample_adaptive_offset_enabled_flag); + rbsp_bit(rbsp, &sps->pcm_enabled_flag); + if (sps->pcm_enabled_flag) { + rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_luma_minus1); + rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_chroma_minus1); + rbsp_uev(rbsp, &sps->log2_min_pcm_luma_coding_block_size_minus3); + rbsp_uev(rbsp, &sps->log2_diff_max_min_pcm_luma_coding_block_size); + rbsp_bit(rbsp, &sps->pcm_loop_filter_disabled_flag); + } + + rbsp_uev(rbsp, &sps->num_short_term_ref_pic_sets); + if (sps->num_short_term_ref_pic_sets > 0) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->long_term_ref_pics_present_flag); + if (sps->long_term_ref_pics_present_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->sps_temporal_mvp_enabled_flag); + rbsp_bit(rbsp, &sps->strong_intra_smoothing_enabled_flag); + rbsp_bit(rbsp, &sps->vui_parameters_present_flag); + if (sps->vui_parameters_present_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->extension_present_flag); + if (sps->extension_present_flag) { + rbsp_bit(rbsp, &sps->sps_range_extension_flag); + rbsp_bit(rbsp, &sps->sps_multilayer_extension_flag); + rbsp_bit(rbsp, &sps->sps_3d_extension_flag); + rbsp_bit(rbsp, &sps->sps_scc_extension_flag); + rbsp_bits(rbsp, 5, &sps->sps_extension_4bits); + } + if (sps->sps_range_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_multilayer_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_3d_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_scc_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_extension_4bits) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_pps(struct rbsp *rbsp, struct nal_hevc_pps *pps) +{ + unsigned int i; + + rbsp_uev(rbsp, &pps->pps_pic_parameter_set_id); + rbsp_uev(rbsp, &pps->pps_seq_parameter_set_id); + rbsp_bit(rbsp, &pps->dependent_slice_segments_enabled_flag); + rbsp_bit(rbsp, &pps->output_flag_present_flag); + rbsp_bits(rbsp, 3, &pps->num_extra_slice_header_bits); + rbsp_bit(rbsp, &pps->sign_data_hiding_enabled_flag); + rbsp_bit(rbsp, &pps->cabac_init_present_flag); + rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); + rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); + rbsp_sev(rbsp, &pps->init_qp_minus26); + rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); + rbsp_bit(rbsp, &pps->transform_skip_enabled_flag); + rbsp_bit(rbsp, &pps->cu_qp_delta_enabled_flag); + if (pps->cu_qp_delta_enabled_flag) + rbsp_uev(rbsp, &pps->diff_cu_qp_delta_depth); + rbsp_sev(rbsp, &pps->pps_cb_qp_offset); + rbsp_sev(rbsp, &pps->pps_cr_qp_offset); + rbsp_bit(rbsp, &pps->pps_slice_chroma_qp_offsets_present_flag); + rbsp_bit(rbsp, &pps->weighted_pred_flag); + rbsp_bit(rbsp, &pps->weighted_bipred_flag); + rbsp_bit(rbsp, &pps->transquant_bypass_enabled_flag); + rbsp_bit(rbsp, &pps->tiles_enabled_flag); + rbsp_bit(rbsp, &pps->entropy_coding_sync_enabled_flag); + if (pps->tiles_enabled_flag) { + rbsp_uev(rbsp, &pps->num_tile_columns_minus1); + rbsp_uev(rbsp, &pps->num_tile_rows_minus1); + rbsp_bit(rbsp, &pps->uniform_spacing_flag); + if (!pps->uniform_spacing_flag) { + for (i = 0; i < pps->num_tile_columns_minus1; i++) + rbsp_uev(rbsp, &pps->column_width_minus1[i]); + for (i = 0; i < pps->num_tile_rows_minus1; i++) + rbsp_uev(rbsp, &pps->row_height_minus1[i]); + } + rbsp_bit(rbsp, &pps->loop_filter_across_tiles_enabled_flag); + } + rbsp_bit(rbsp, &pps->pps_loop_filter_across_slices_enabled_flag); + rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); + if (pps->deblocking_filter_control_present_flag) { + rbsp_bit(rbsp, &pps->deblocking_filter_override_enabled_flag); + rbsp_bit(rbsp, &pps->pps_deblocking_filter_disabled_flag); + if (!pps->pps_deblocking_filter_disabled_flag) { + rbsp_sev(rbsp, &pps->pps_beta_offset_div2); + rbsp_sev(rbsp, &pps->pps_tc_offset_div2); + } + } + rbsp_bit(rbsp, &pps->pps_scaling_list_data_present_flag); + if (pps->pps_scaling_list_data_present_flag) + rbsp_unsupported(rbsp); + rbsp_bit(rbsp, &pps->lists_modification_present_flag); + rbsp_uev(rbsp, &pps->log2_parallel_merge_level_minus2); + rbsp_bit(rbsp, &pps->slice_segment_header_extension_present_flag); + rbsp_bit(rbsp, &pps->pps_extension_present_flag); + if (pps->pps_extension_present_flag) { + rbsp_bit(rbsp, &pps->pps_range_extension_flag); + rbsp_bit(rbsp, &pps->pps_multilayer_extension_flag); + rbsp_bit(rbsp, &pps->pps_3d_extension_flag); + rbsp_bit(rbsp, &pps->pps_scc_extension_flag); + rbsp_bits(rbsp, 4, &pps->pps_extension_4bits); + } + if (pps->pps_range_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_multilayer_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_3d_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_scc_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_extension_4bits) + rbsp_unsupported(rbsp); +} + +/** + * nal_hevc_write_vps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @vps: &struct nal_hevc_vps to convert to RBSP + * + * Convert @vps to RBSP data and write it into @dest. + * + * The size of the VPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the VPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_vps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_vps *vps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = VPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_vps(&rbsp, vps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_vps); + +/** + * nal_hevc_read_vps() - Read VPS NAL unit from RBSP format + * @dev: device pointer + * @vps: the &struct nal_hevc_vps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @vps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_vps(const struct device *dev, + struct nal_hevc_vps *vps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_unit_type != VPS_NUT) + return -EINVAL; + + nal_hevc_rbsp_vps(&rbsp, vps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_vps); + +/** + * nal_hevc_write_sps() - Write SPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @sps: &struct nal_hevc_sps to convert to RBSP + * + * Convert @sps to RBSP data and write it into @dest. + * + * The size of the SPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the SPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_sps *sps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = SPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_sps); + +/** + * nal_hevc_read_sps() - Read SPS NAL unit from RBSP format + * @dev: device pointer + * @sps: the &struct nal_hevc_sps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @sps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_sps(const struct device *dev, + struct nal_hevc_sps *sps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_unit_type != SPS_NUT) + return -EINVAL; + + nal_hevc_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_sps); + +/** + * nal_hevc_write_pps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @pps: &struct nal_hevc_pps to convert to RBSP + * + * Convert @pps to RBSP data and write it into @dest. + * + * The size of the PPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the PPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_pps *pps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = PPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_pps); + +/** + * nal_hevc_read_pps() - Read PPS NAL unit from RBSP format + * @dev: device pointer + * @pps: the &struct nal_hevc_pps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @pps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_pps(const struct device *dev, + struct nal_hevc_pps *pps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_pps); + +/** + * nal_hevc_write_filler() - Write filler data RBSP + * @dev: device pointer + * @dest: buffer to fill with filler data + * @n: size of the buffer to fill with filler data + * + * Write a filler data RBSP to @dest with a size of @n bytes and return the + * number of written filler data bytes. + * + * Use this function to generate dummy data in an RBSP data stream that can be + * safely ignored by hevc decoders. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.265 + * (02/2018) 7.3.2.8 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = FD_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_write_filler_data(&rbsp); + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_filler); + +/** + * nal_hevc_read_filler() - Read filler data RBSP + * @dev: device pointer + * @src: buffer with RBSP data that is read + * @n: maximum size of src that shall be read + * + * Read a filler data RBSP from @src up to a maximum size of @n bytes and + * return the size of the filler data in bytes including the marker. + * + * This function is used to parse filler data and skip the respective bytes in + * the RBSP data. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.265 + * (02/2018) 7.3.2.8 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error) + return rbsp.error; + if (forbidden_zero_bit != 0 || + nal_unit_type != FD_NUT) + return -EINVAL; + + nal_hevc_read_filler_data(&rbsp); + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_filler); diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h new file mode 100644 index 000000000000..fc994d4242d8 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-hevc.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_HEVC_H__ +#define __NAL_HEVC_H__ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <media/v4l2-ctrls.h> + +struct nal_hevc_profile_tier_level { + unsigned int general_profile_space; + unsigned int general_tier_flag; + unsigned int general_profile_idc; + unsigned int general_profile_compatibility_flag[32]; + unsigned int general_progressive_source_flag; + unsigned int general_interlaced_source_flag; + unsigned int general_non_packed_constraint_flag; + unsigned int general_frame_only_constraint_flag; + union { + struct { + unsigned int general_max_12bit_constraint_flag; + unsigned int general_max_10bit_constraint_flag; + unsigned int general_max_8bit_constraint_flag; + unsigned int general_max_422chroma_constraint_flag; + unsigned int general_max_420chroma_constraint_flag; + unsigned int general_max_monochrome_constraint_flag; + unsigned int general_intra_constraint_flag; + unsigned int general_one_picture_only_constraint_flag; + unsigned int general_lower_bit_rate_constraint_flag; + union { + struct { + unsigned int general_max_14bit_constraint_flag; + unsigned int general_reserved_zero_33bits; + }; + unsigned int general_reserved_zero_34bits; + }; + }; + struct { + unsigned int general_reserved_zero_7bits; + /* unsigned int general_one_picture_only_constraint_flag; */ + unsigned int general_reserved_zero_35bits; + }; + unsigned int general_reserved_zero_43bits; + }; + union { + unsigned int general_inbld_flag; + unsigned int general_reserved_zero_bit; + }; + unsigned int general_level_idc; +}; + +/** + * struct nal_hevc_vps - Video parameter set + * + * C struct representation of the video parameter set NAL unit as defined by + * Rec. ITU-T H.265 (02/2018) 7.3.2.1 Video parameter set RBSP syntax + */ +struct nal_hevc_vps { + unsigned int video_parameter_set_id; + unsigned int base_layer_internal_flag; + unsigned int base_layer_available_flag; + unsigned int max_layers_minus1; + unsigned int max_sub_layers_minus1; + unsigned int temporal_id_nesting_flag; + struct nal_hevc_profile_tier_level profile_tier_level; + unsigned int sub_layer_ordering_info_present_flag; + struct { + unsigned int max_dec_pic_buffering_minus1[7]; + unsigned int max_num_reorder_pics[7]; + unsigned int max_latency_increase_plus1[7]; + }; + unsigned int max_layer_id; + unsigned int num_layer_sets_minus1; + unsigned int layer_id_included_flag[1024][64]; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int poc_proportional_to_timing_flag; + unsigned int num_ticks_poc_diff_one_minus1; + unsigned int num_hrd_parameters; + struct { + unsigned int hrd_layer_set_idx[0]; + unsigned int cprms_present_flag[0]; + }; + /* hrd_parameters( cprms_present_flag[ i ], max_sub_layers_minus1 ) */ + }; + unsigned int extension_flag; + unsigned int extension_data_flag; +}; + +struct nal_hevc_sub_layer_hrd_parameters { + unsigned int bit_rate_value_minus1[1]; + unsigned int cpb_size_value_minus1[1]; + unsigned int cbr_flag[1]; +}; + +struct nal_hevc_hrd_parameters { + unsigned int nal_hrd_parameters_present_flag; + unsigned int vcl_hrd_parameters_present_flag; + struct { + unsigned int sub_pic_hrd_params_present_flag; + struct { + unsigned int tick_divisor_minus2; + unsigned int du_cpb_removal_delay_increment_length_minus1; + unsigned int sub_pic_cpb_params_in_pic_timing_sei_flag; + unsigned int dpb_output_delay_du_length_minus1; + }; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + unsigned int cpb_size_du_scale; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int au_cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + }; + struct { + unsigned int fixed_pic_rate_general_flag[1]; + unsigned int fixed_pic_rate_within_cvs_flag[1]; + unsigned int elemental_duration_in_tc_minus1[1]; + unsigned int low_delay_hrd_flag[1]; + unsigned int cpb_cnt_minus1[1]; + struct nal_hevc_sub_layer_hrd_parameters nal_hrd[1]; + struct nal_hevc_sub_layer_hrd_parameters vcl_hrd[1]; + }; +}; + +/** + * struct nal_hevc_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.265 (02/2018) E.2.1 VUI parameters syntax. + */ +struct nal_hevc_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coeffs; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int neutral_chroma_indication_flag; + unsigned int field_seq_flag; + unsigned int frame_field_info_present_flag; + unsigned int default_display_window_flag; + struct { + unsigned int def_disp_win_left_offset; + unsigned int def_disp_win_right_offset; + unsigned int def_disp_win_top_offset; + unsigned int def_disp_win_bottom_offset; + }; + unsigned int vui_timing_info_present_flag; + struct { + unsigned int vui_num_units_in_tick; + unsigned int vui_time_scale; + unsigned int vui_poc_proportional_to_timing_flag; + unsigned int vui_num_ticks_poc_diff_one_minus1; + unsigned int vui_hrd_parameters_present_flag; + struct nal_hevc_hrd_parameters nal_hrd_parameters; + }; + unsigned int bitstream_restriction_flag; + struct { + unsigned int tiles_fixed_structure_flag; + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int restricted_ref_pic_lists_flag; + unsigned int min_spatial_segmentation_idc; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_min_cu_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log2_max_mv_length_vertical; + }; +}; + +/** + * struct nal_hevc_sps - Sequence parameter set + * + * C struct representation of the video parameter set NAL unit as defined by + * Rec. ITU-T H.265 (02/2018) 7.3.2.2 Sequence parameter set RBSP syntax + */ +struct nal_hevc_sps { + unsigned int video_parameter_set_id; + unsigned int max_sub_layers_minus1; + unsigned int temporal_id_nesting_flag; + struct nal_hevc_profile_tier_level profile_tier_level; + unsigned int seq_parameter_set_id; + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int pic_width_in_luma_samples; + unsigned int pic_height_in_luma_samples; + unsigned int conformance_window_flag; + struct { + unsigned int conf_win_left_offset; + unsigned int conf_win_right_offset; + unsigned int conf_win_top_offset; + unsigned int conf_win_bottom_offset; + }; + + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int log2_max_pic_order_cnt_lsb_minus4; + unsigned int sub_layer_ordering_info_present_flag; + struct { + unsigned int max_dec_pic_buffering_minus1[7]; + unsigned int max_num_reorder_pics[7]; + unsigned int max_latency_increase_plus1[7]; + }; + unsigned int log2_min_luma_coding_block_size_minus3; + unsigned int log2_diff_max_min_luma_coding_block_size; + unsigned int log2_min_luma_transform_block_size_minus2; + unsigned int log2_diff_max_min_luma_transform_block_size; + unsigned int max_transform_hierarchy_depth_inter; + unsigned int max_transform_hierarchy_depth_intra; + + unsigned int scaling_list_enabled_flag; + unsigned int scaling_list_data_present_flag; + unsigned int amp_enabled_flag; + unsigned int sample_adaptive_offset_enabled_flag; + unsigned int pcm_enabled_flag; + struct { + unsigned int pcm_sample_bit_depth_luma_minus1; + unsigned int pcm_sample_bit_depth_chroma_minus1; + unsigned int log2_min_pcm_luma_coding_block_size_minus3; + unsigned int log2_diff_max_min_pcm_luma_coding_block_size; + unsigned int pcm_loop_filter_disabled_flag; + }; + + unsigned int num_short_term_ref_pic_sets; + unsigned int long_term_ref_pics_present_flag; + unsigned int sps_temporal_mvp_enabled_flag; + unsigned int strong_intra_smoothing_enabled_flag; + unsigned int vui_parameters_present_flag; + struct nal_hevc_vui_parameters vui; + unsigned int extension_present_flag; + struct { + unsigned int sps_range_extension_flag; + unsigned int sps_multilayer_extension_flag; + unsigned int sps_3d_extension_flag; + unsigned int sps_scc_extension_flag; + unsigned int sps_extension_4bits; + }; +}; + +struct nal_hevc_pps { + unsigned int pps_pic_parameter_set_id; + unsigned int pps_seq_parameter_set_id; + unsigned int dependent_slice_segments_enabled_flag; + unsigned int output_flag_present_flag; + unsigned int num_extra_slice_header_bits; + unsigned int sign_data_hiding_enabled_flag; + unsigned int cabac_init_present_flag; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + int init_qp_minus26; + unsigned int constrained_intra_pred_flag; + unsigned int transform_skip_enabled_flag; + unsigned int cu_qp_delta_enabled_flag; + unsigned int diff_cu_qp_delta_depth; + int pps_cb_qp_offset; + int pps_cr_qp_offset; + unsigned int pps_slice_chroma_qp_offsets_present_flag; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_flag; + unsigned int transquant_bypass_enabled_flag; + unsigned int tiles_enabled_flag; + unsigned int entropy_coding_sync_enabled_flag; + struct { + unsigned int num_tile_columns_minus1; + unsigned int num_tile_rows_minus1; + unsigned int uniform_spacing_flag; + struct { + unsigned int column_width_minus1[1]; + unsigned int row_height_minus1[1]; + }; + unsigned int loop_filter_across_tiles_enabled_flag; + }; + unsigned int pps_loop_filter_across_slices_enabled_flag; + unsigned int deblocking_filter_control_present_flag; + struct { + unsigned int deblocking_filter_override_enabled_flag; + unsigned int pps_deblocking_filter_disabled_flag; + struct { + int pps_beta_offset_div2; + int pps_tc_offset_div2; + }; + }; + unsigned int pps_scaling_list_data_present_flag; + unsigned int lists_modification_present_flag; + unsigned int log2_parallel_merge_level_minus2; + unsigned int slice_segment_header_extension_present_flag; + unsigned int pps_extension_present_flag; + struct { + unsigned int pps_range_extension_flag; + unsigned int pps_multilayer_extension_flag; + unsigned int pps_3d_extension_flag; + unsigned int pps_scc_extension_flag; + unsigned int pps_extension_4bits; + }; +}; + +int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile); +int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier); +int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level); + +int nal_range_from_v4l2(enum v4l2_quantization quantization); +int nal_color_primaries_from_v4l2(enum v4l2_colorspace colorspace); +int nal_transfer_characteristics_from_v4l2(enum v4l2_colorspace colorspace, + enum v4l2_xfer_func xfer_func); +int nal_matrix_coeffs_from_v4l2(enum v4l2_colorspace colorspace, + enum v4l2_ycbcr_encoding ycbcr_encoding); + +ssize_t nal_hevc_write_vps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_vps *vps); +ssize_t nal_hevc_read_vps(const struct device *dev, + struct nal_hevc_vps *vps, void *src, size_t n); + +ssize_t nal_hevc_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_sps *sps); +ssize_t nal_hevc_read_sps(const struct device *dev, + struct nal_hevc_sps *sps, void *src, size_t n); + +ssize_t nal_hevc_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_pps *pps); +ssize_t nal_hevc_read_pps(const struct device *dev, + struct nal_hevc_pps *pps, void *src, size_t n); + +ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_HEVC_H__ */ diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.c b/drivers/media/platform/allegro-dvt/nal-rbsp.c new file mode 100644 index 000000000000..d911322d0efa --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Helper functions to generate a raw byte sequence payload from values. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/v4l2-controls.h> + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/log2.h> + +#include "nal-rbsp.h" + +void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_rbsp_ops *ops) +{ + if (!rbsp) + return; + + rbsp->data = addr; + rbsp->size = size; + rbsp->pos = 0; + rbsp->ops = ops; + rbsp->error = 0; +} + +void rbsp_unsupported(struct rbsp *rbsp) +{ + rbsp->error = -EINVAL; +} + +static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); + +/* + * When reading or writing, the emulation_prevention_three_byte is detected + * only when the 2 one bits need to be inserted. Therefore, we are not + * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the + * next byte. + */ +#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) + +static int add_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + rbsp->num_consecutive_zeros = 0; + rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); + + return 0; +} + +static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + unsigned int tmp = 0; + + rbsp->num_consecutive_zeros = 0; + rbsp_read_bits(rbsp, 8, &tmp); + if (tmp != EMULATION_PREVENTION_THREE_BYTE) + return -EINVAL; + + return 0; +} + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift; + int ofs; + int bit; + int err; + + if (rbsp->num_consecutive_zeros == 22) { + err = discard_emulation_prevention_three_byte(rbsp); + if (err) + return err; + } + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + bit = (rbsp->data[ofs] >> shift) & 1; + + rbsp->pos++; + + if (bit == 1 || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) + rbsp->num_consecutive_zeros = 0; + else + rbsp->num_consecutive_zeros++; + + return bit; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) +{ + int shift; + int ofs; + + if (rbsp->num_consecutive_zeros == 22) + add_emulation_prevention_three_byte(rbsp); + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->data[ofs] &= ~(1 << shift); + rbsp->data[ofs] |= value << shift; + + rbsp->pos++; + + if (value || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { + rbsp->num_consecutive_zeros = 0; + } else { + rbsp->num_consecutive_zeros++; + } + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + int i; + int bit; + unsigned int tmp = 0; + + if (n > 8 * sizeof(*value)) + return -EINVAL; + + for (i = n; i > 0; i--) { + bit = rbsp_read_bit(rbsp); + if (bit < 0) + return bit; + tmp |= bit << (i - 1); + } + + if (value) + *value = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) +{ + int ret; + + if (n > 8 * sizeof(value)) + return -EINVAL; + + while (n--) { + ret = rbsp_write_bit(rbsp, (value >> n) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (value) + *value = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) +{ + int ret; + int leading_zero_bits; + + if (!value) + return -EINVAL; + + leading_zero_bits = ilog2(*value + 1); + + ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); + if (ret) + return ret; + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *value) +{ + int ret; + unsigned int tmp; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (value) { + if (tmp & 1) + *value = (tmp + 1) / 2; + else + *value = -(tmp / 2); + } + + return 0; +} + +static int rbsp_write_sev(struct rbsp *rbsp, int *value) +{ + unsigned int tmp; + + if (!value) + return -EINVAL; + + if (*value > 0) + tmp = (2 * (*value)) | 1; + else + tmp = -2 * (*value); + + return rbsp_write_uev(rbsp, &tmp); +} + +static int __rbsp_write_bit(struct rbsp *rbsp, int *value) +{ + return rbsp_write_bit(rbsp, *value); +} + +static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + return rbsp_write_bits(rbsp, n, *value); +} + +struct nal_rbsp_ops write = { + .rbsp_bit = __rbsp_write_bit, + .rbsp_bits = __rbsp_write_bits, + .rbsp_uev = rbsp_write_uev, + .rbsp_sev = rbsp_write_sev, +}; + +static int __rbsp_read_bit(struct rbsp *rbsp, int *value) +{ + int tmp = rbsp_read_bit(rbsp); + + if (tmp < 0) + return tmp; + *value = tmp; + + return 0; +} + +struct nal_rbsp_ops read = { + .rbsp_bit = __rbsp_read_bit, + .rbsp_bits = rbsp_read_bits, + .rbsp_uev = rbsp_read_uev, + .rbsp_sev = rbsp_read_sev, +}; + +void rbsp_bit(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); +} + +void rbsp_bits(struct rbsp *rbsp, int n, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); +} + +void rbsp_uev(struct rbsp *rbsp, unsigned int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); +} + +void rbsp_sev(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); +} + +void rbsp_trailing_bits(struct rbsp *rbsp) +{ + unsigned int rbsp_stop_one_bit = 1; + unsigned int rbsp_alignment_zero_bit = 0; + + rbsp_bit(rbsp, &rbsp_stop_one_bit); + rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, + &rbsp_alignment_zero_bit); +} diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.h b/drivers/media/platform/allegro-dvt/nal-rbsp.h new file mode 100644 index 000000000000..c72f49fed8d3 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter <kernel@pengutronix.de> + */ + +#ifndef __NAL_RBSP_H__ +#define __NAL_RBSP_H__ + +#include <linux/kernel.h> +#include <linux/types.h> + +struct rbsp; + +struct nal_rbsp_ops { + int (*rbsp_bit)(struct rbsp *rbsp, int *val); + int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); + int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); + int (*rbsp_sev)(struct rbsp *rbsp, int *val); +}; + +/** + * struct rbsp - State object for handling a raw byte sequence payload + * @data: pointer to the data of the rbsp + * @size: maximum size of the data of the rbsp + * @pos: current bit position inside the rbsp + * @num_consecutive_zeros: number of zeros before @pos + * @ops: per datatype functions for interacting with the rbsp + * @error: an error occurred while handling the rbsp + * + * This struct is passed around the various parsing functions and tracks the + * current position within the raw byte sequence payload. + * + * The @ops field allows to separate the operation, i.e., reading/writing a + * value from/to that rbsp, from the structure of the NAL unit. This allows to + * have a single function for iterating the NAL unit, while @ops has function + * pointers for handling each type in the rbsp. + */ +struct rbsp { + u8 *data; + size_t size; + unsigned int pos; + unsigned int num_consecutive_zeros; + struct nal_rbsp_ops *ops; + int error; +}; + +extern struct nal_rbsp_ops write; +extern struct nal_rbsp_ops read; + +void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_rbsp_ops *ops); +void rbsp_unsupported(struct rbsp *rbsp); + +void rbsp_bit(struct rbsp *rbsp, int *value); +void rbsp_bits(struct rbsp *rbsp, int n, int *value); +void rbsp_uev(struct rbsp *rbsp, unsigned int *value); +void rbsp_sev(struct rbsp *rbsp, int *value); + +void rbsp_trailing_bits(struct rbsp *rbsp); + +#endif /* __NAL_RBSP_H__ */ diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 0fb9f9ba1219..6cdc77dda0e4 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2365,7 +2365,7 @@ vpfe_get_pdata(struct vpfe_device *vpfe) pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev( &vpfe->notifier, of_fwnode_handle(rem), - sizeof(struct v4l2_async_subdev)); + struct v4l2_async_subdev); of_node_put(rem); if (IS_ERR(pdata->asd[i])) goto cleanup; diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index c46a79eace98..f2c4dadd6a0e 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1551,12 +1551,12 @@ static int aspeed_video_setup_video(struct aspeed_video *video) V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, V4L2_JPEG_CHROMA_SUBSAMPLING_444); - if (video->ctrl_handler.error) { + rc = video->ctrl_handler.error; + if (rc) { v4l2_ctrl_handler_free(&video->ctrl_handler); v4l2_device_unregister(v4l2_dev); - dev_err(video->dev, "Failed to init controls: %d\n", - video->ctrl_handler.error); + dev_err(video->dev, "Failed to init controls: %d\n", rc); return rc; } diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index 24b784b893d6..fab8eca58d93 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -41,6 +41,7 @@ struct isc_buffer { struct isc_subdev_entity { struct v4l2_subdev *sd; struct v4l2_async_subdev *asd; + struct device_node *epn; struct v4l2_async_notifier notifier; u32 pfe_cfg0; diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index d74aa73f26be..0514be6153df 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -70,7 +70,6 @@ struct frame_buffer { struct isi_graph_entity { struct device_node *node; - struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; }; @@ -1136,45 +1135,26 @@ static const struct v4l2_async_notifier_operations isi_graph_notify_ops = { .complete = isi_graph_notify_complete, }; -static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) -{ - struct device_node *ep = NULL; - struct device_node *remote; - - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; - - remote = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!remote) - return -EINVAL; - - /* Remote node to connect */ - isi->entity.node = remote; - isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - isi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; -} - static int isi_graph_init(struct atmel_isi *isi) { + struct v4l2_async_subdev *asd; + struct device_node *ep; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = isi_graph_parse(isi, isi->dev->of_node); - if (ret < 0) { - dev_err(isi->dev, "Graph parsing failed\n"); - return ret; - } + ep = of_graph_get_next_endpoint(isi->dev->of_node, NULL); + if (!ep) + return -EINVAL; v4l2_async_notifier_init(&isi->notifier); - ret = v4l2_async_notifier_add_subdev(&isi->notifier, &isi->entity.asd); - if (ret) { - of_node_put(isi->entity.node); - return ret; - } + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isi->notifier, + of_fwnode_handle(ep), + struct v4l2_async_subdev); + of_node_put(ep); + + if (IS_ERR(asd)) + return PTR_ERR(asd); isi->notifier.ops = &isi_graph_notify_ops; diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index a3304f49e499..0b78fecfd2a8 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -57,7 +57,7 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; + struct device_node *epn = NULL; struct isc_subdev_entity *subdev_entity; unsigned int flags; int ret; @@ -71,17 +71,9 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) if (!epn) return 0; - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "Remote device at %pOF not found\n", - epn); - continue; - } - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - of_node_put(rem); ret = -EINVAL; dev_err(dev, "Could not parse the endpoint\n"); break; @@ -90,21 +82,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - of_node_put(rem); - ret = -ENOMEM; - break; - } - - /* asd will be freed by the subsystem once it's added to the - * notifier list - */ - subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd), - GFP_KERNEL); - if (!subdev_entity->asd) { - of_node_put(rem); ret = -ENOMEM; break; } + subdev_entity->epn = epn; flags = v4l2_epn.bus.parallel.flags; @@ -121,12 +102,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | ISC_PFE_CFG0_CCIR656; - subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - subdev_entity->asd->match.fwnode = of_fwnode_handle(rem); list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); + return ret; } @@ -228,13 +207,20 @@ static int atmel_isc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + struct v4l2_async_subdev *asd; + v4l2_async_notifier_init(&subdev_entity->notifier); - ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier, - subdev_entity->asd); - if (ret) { - fwnode_handle_put(subdev_entity->asd->match.fwnode); - kfree(subdev_entity->asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &subdev_entity->notifier, + of_fwnode_handle(subdev_entity->epn), + struct v4l2_async_subdev); + + of_node_put(subdev_entity->epn); + subdev_entity->epn = NULL; + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto cleanup_subdev; } diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index be9ec59774d6..c68a3eac62cd 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -81,7 +81,6 @@ struct csi2rx_priv { struct media_pad pads[CSI2RX_PAD_MAX]; /* Remote source */ - struct v4l2_async_subdev asd; struct v4l2_subdev *source_subdev; int source_pad; }; @@ -362,6 +361,7 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) { struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; + struct v4l2_async_subdev *asd; struct fwnode_handle *fwh; struct device_node *ep; int ret; @@ -395,17 +395,14 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) return -EINVAL; } - csi2rx->asd.match.fwnode = fwnode_graph_get_remote_port_parent(fwh); - csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - of_node_put(ep); - v4l2_async_notifier_init(&csi2rx->notifier); - ret = v4l2_async_notifier_add_subdev(&csi2rx->notifier, &csi2rx->asd); - if (ret) { - fwnode_handle_put(csi2rx->asd.match.fwnode); - return ret; - } + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi2rx->notifier, + fwh, + struct v4l2_async_subdev); + of_node_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); csi2rx->notifier.ops = &csi2rx_notifier_ops; diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index fe9468b180e6..5f0aeb744e81 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -628,7 +628,7 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) if (err) { v4l2_err(&vpbe_dev->v4l2_dev, "unable to initialize the OSD device"); - err = -ENOMEM; + ret = -ENOMEM; goto fail_dev_unregister; } } diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index 5e67994e62cc..f1ce10828b8e 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -41,7 +41,7 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME); #define VPIF_CH2_MAX_MODES 15 #define VPIF_CH3_MAX_MODES 2 -spinlock_t vpif_lock; +DEFINE_SPINLOCK(vpif_lock); EXPORT_SYMBOL_GPL(vpif_lock); void __iomem *vpif_base; @@ -437,7 +437,6 @@ static int vpif_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get(&pdev->dev); - spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); /* diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 72a0e94e2e21..8d2e165bf7de 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1584,7 +1584,7 @@ vpif_capture_get_pdata(struct platform_device *pdev) pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev( &vpif_obj.notifier, of_fwnode_handle(rem), - sizeof(struct v4l2_async_subdev)); + struct v4l2_async_subdev); if (IS_ERR(pdata->asd[i])) goto err_cleanup; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 46afc029138f..e5f61d9b221d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1117,23 +1117,6 @@ static void free_vpif_objs(void) kfree(vpif_obj.dev[i]); } -static int vpif_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) -{ - int i; - - for (i = 0; i < vpif_obj.config->subdev_count; i++) - if (!strcmp(vpif_obj.config->subdevinfo[i].name, - subdev->name)) { - vpif_obj.sd[i] = subdev; - vpif_obj.sd[i]->grp_id = 1 << i; - return 0; - } - - return -EINVAL; -} - static int vpif_probe_complete(void) { struct common_obj *common; @@ -1230,16 +1213,6 @@ probe_out: return err; } -static int vpif_async_complete(struct v4l2_async_notifier *notifier) -{ - return vpif_probe_complete(); -} - -static const struct v4l2_async_notifier_operations vpif_async_ops = { - .bound = vpif_async_bound, - .complete = vpif_async_complete, -}; - /* * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects @@ -1294,52 +1267,28 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } - v4l2_async_notifier_init(&vpif_obj.notifier); - - if (!vpif_obj.config->asd_sizes) { - i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id); - for (i = 0; i < subdev_count; i++) { - vpif_obj.sd[i] = - v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata[i]. - board_info, - NULL); - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; - goto probe_subdev_out; - } - - if (vpif_obj.sd[i]) - vpif_obj.sd[i]->grp_id = 1 << i; - } - err = vpif_probe_complete(); - if (err) { + i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id); + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata[i].board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + err = -ENODEV; goto probe_subdev_out; } - } else { - for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) { - err = v4l2_async_notifier_add_subdev( - &vpif_obj.notifier, vpif_obj.config->asd[i]); - if (err) - goto probe_cleanup; - } - vpif_obj.notifier.ops = &vpif_async_ops; - err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, - &vpif_obj.notifier); - if (err) { - vpif_err("Error registering async notifier\n"); - err = -EINVAL; - goto probe_cleanup; - } + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; } + err = vpif_probe_complete(); + if (err) + goto probe_subdev_out; return 0; -probe_cleanup: - v4l2_async_notifier_cleanup(&vpif_obj.notifier); probe_subdev_out: kfree(vpif_obj.sd); vpif_unregister: @@ -1358,11 +1307,6 @@ static int vpif_remove(struct platform_device *device) struct channel_obj *ch; int i; - if (vpif_obj.config->asd_sizes) { - v4l2_async_notifier_unregister(&vpif_obj.notifier); - v4l2_async_notifier_cleanup(&vpif_obj.notifier); - } - v4l2_device_unregister(&vpif_obj.v4l2_dev); kfree(vpif_obj.sd); diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index f731a65eefd6..f98062e79167 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -118,7 +118,6 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; struct v4l2_subdev **sd; - struct v4l2_async_notifier notifier; struct vpif_display_config *config; }; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e636c33e847b..8e1e892085ec 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -401,6 +401,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, int index = fmd->num_sensors; struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *np; + struct v4l2_async_subdev *asd; struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; int ret; @@ -418,10 +419,10 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, pd->mux_id = (endpoint.base.port - 1) & 0x1; rem = of_graph_get_remote_port_parent(ep); - of_node_put(ep); if (rem == NULL) { v4l2_info(&fmd->v4l2_dev, "Remote device at %pOF not found\n", ep); + of_node_put(ep); return 0; } @@ -450,6 +451,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, * checking parent's node name. */ np = of_get_parent(rem); + of_node_put(rem); if (of_node_name_eq(np, "i2c-isp")) pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; @@ -458,20 +460,20 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, of_node_put(np); if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { - of_node_put(rem); + of_node_put(ep); return -EINVAL; } - fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - fmd->sensor[index].asd.match.fwnode = of_fwnode_handle(rem); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &fmd->subdev_notifier, of_fwnode_handle(ep), + struct v4l2_async_subdev); - ret = v4l2_async_notifier_add_subdev(&fmd->subdev_notifier, - &fmd->sensor[index].asd); - if (ret) { - of_node_put(rem); - return ret; - } + of_node_put(ep); + + if (IS_ERR(asd)) + return PTR_ERR(asd); + fmd->sensor[index].asd = asd; fmd->num_sensors++; return 0; @@ -1381,7 +1383,8 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, /* Find platform data for this sensor subdev */ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) - if (fmd->sensor[i].asd.match.fwnode == + if (fmd->sensor[i].asd && + fmd->sensor[i].asd->match.fwnode == of_fwnode_handle(subdev->dev->of_node)) si = &fmd->sensor[i]; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 9447fafe23c6..a3876d668ea6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -83,7 +83,7 @@ struct fimc_camclk_info { */ struct fimc_sensor_info { struct fimc_source_info pdata; - struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asd; struct v4l2_subdev *subdev; struct fimc_dev *host; }; diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 00f623d62c96..9c94a8b58b7c 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -489,6 +489,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, int ret; struct cafe_camera *cam; struct mcam_camera *mcam; + struct v4l2_async_subdev *asd; /* * Start putting together one of our big camera structures. @@ -546,9 +547,16 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_pdown; - mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C; - mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter); - mcam->asd.match.i2c.address = ov7670_info.addr; + v4l2_async_notifier_init(&mcam->notifier); + + asd = v4l2_async_notifier_add_i2c_subdev(&mcam->notifier, + i2c_adapter_id(cam->i2c_adapter), + ov7670_info.addr, + struct v4l2_async_subdev); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out_smbus_shutdown; + } ret = mccic_register(mcam); if (ret) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index c012fd2e1d29..141bf5d97a04 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -931,6 +931,7 @@ static int mclk_enable(struct clk_hw *hw) mclk_div = 2; } + pm_runtime_get_sync(cam->dev); clk_enable(cam->clk[0]); mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); mcam_ctlr_power_up(cam); @@ -944,6 +945,7 @@ static void mclk_disable(struct clk_hw *hw) mcam_ctlr_power_down(cam); clk_disable(cam->clk[0]); + pm_runtime_put(cam->dev); } static unsigned long mclk_recalc_rate(struct clk_hw *hw, @@ -1866,16 +1868,6 @@ int mccic_register(struct mcam_camera *cam) cam->pix_format = mcam_def_pix_format; cam->mbus_code = mcam_def_mbus_code; - /* - * Register sensor notifier. - */ - v4l2_async_notifier_init(&cam->notifier); - ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd); - if (ret) { - cam_warn(cam, "failed to add subdev to a notifier"); - goto out; - } - cam->notifier.ops = &mccic_notify_ops; ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier); if (ret < 0) { diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index b55545822fd2..f324d808d737 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -151,7 +151,6 @@ struct mcam_camera { */ struct video_device vdev; struct v4l2_async_notifier notifier; - struct v4l2_async_subdev asd; struct v4l2_subdev *sensor; /* Videobuf2 stuff */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 032fdddbbecc..f2f09cea751d 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -180,6 +180,7 @@ static int mmpcam_probe(struct platform_device *pdev) struct resource *res; struct fwnode_handle *ep; struct mmp_camera_platform_data *pdata; + struct v4l2_async_subdev *asd; int ret; cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); @@ -238,10 +239,15 @@ static int mmpcam_probe(struct platform_device *pdev) if (!ep) return -ENODEV; - mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep); + v4l2_async_notifier_init(&mcam->notifier); + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&mcam->notifier, ep, + struct v4l2_async_subdev); fwnode_handle_put(ep); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out; + } /* * Register the device with the core. @@ -278,7 +284,6 @@ static int mmpcam_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; out: - fwnode_handle_put(mcam->asd.match.fwnode); mccic_shutdown(mcam); return ret; diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/meson/ge2d/ge2d.c index f526501bd473..153612ca96fc 100644 --- a/drivers/media/platform/meson/ge2d/ge2d.c +++ b/drivers/media/platform/meson/ge2d/ge2d.c @@ -988,6 +988,7 @@ static int ge2d_probe(struct platform_device *pdev) vfd = video_device_alloc(); if (!vfd) { v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; goto unreg_v4l2_dev; } diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 724c7333b6e5..ace4528cdc5e 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -199,7 +199,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->ycbcr_enc = ctx->ycbcr_enc; pix_mp->quantization = ctx->quant; } - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); max_w = variant->pix_max->target_rot_dis_w; max_h = variant->pix_max->target_rot_dis_h; @@ -247,8 +246,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->plane_fmt[i].bytesperline = bpl; if (pix_mp->plane_fmt[i].sizeimage < sizeimage) pix_mp->plane_fmt[i].sizeimage = sizeimage; - memset(pix_mp->plane_fmt[i].reserved, 0, - sizeof(pix_mp->plane_fmt[i].reserved)); mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)", ctx->id, i, bpl, pix_mp->plane_fmt[i].sizeimage, sizeimage); } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index c768a587a944..56d86e59421e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -657,7 +657,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - int i; pix_fmt_mp->field = V4L2_FIELD_NONE; @@ -715,12 +714,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, } } - for (i = 0; i < pix_fmt_mp->num_planes; i++) - memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix_fmt_mp->plane_fmt[0].reserved)); - pix_fmt_mp->flags = 0; - memset(&pix_fmt_mp->reserved, 0x0, sizeof(pix_fmt_mp->reserved)); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 21de1431cfcb..8c917969c2f1 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -121,7 +121,6 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, return -EINVAL; f->pixelformat = formats[f->index].fourcc; - memset(f->reserved, 0, sizeof(f->reserved)); return 0; } @@ -252,7 +251,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - int i; pix_fmt_mp->field = V4L2_FIELD_NONE; @@ -320,13 +318,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, } } - for (i = 0; i < pix_fmt_mp->num_planes; i++) - memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix_fmt_mp->plane_fmt[0].reserved)); - pix_fmt_mp->flags = 0; - memset(&pix_fmt_mp->reserved, 0x0, - sizeof(pix_fmt_mp->reserved)); return 0; } @@ -532,8 +524,6 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, for (i = 0; i < pix->num_planes; i++) { pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; - memset(&(pix->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix->plane_fmt[i].reserved)); } pix->flags = 0; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index dfb42e19bf81..be3842e6ca47 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -303,7 +303,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]); goto err_res; } - mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_SYS]); + mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_SYS, dev->reg_base[VENC_SYS]); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { @@ -332,7 +332,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]); goto err_res; } - mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_LT_SYS]); + mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_LT_SYS, dev->reg_base[VENC_LT_SYS]); dev->enc_lt_irq = platform_get_irq(pdev, 1); irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c index a3c7a380c930..70580c2525ba 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -27,13 +27,13 @@ int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command, if (!ret) { status = -1; /* timeout */ - mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!", - ctx->id, ctx->type, command, timeout_ms, - ctx->int_cond, ctx->int_type); + mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout time=%ums out %d %d!", + ctx->id, ctx->type, command, timeout_ms, + ctx->int_cond, ctx->int_type); } else if (-ERESTARTSYS == ret) { - mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", - ctx->id, ctx->type, command, ctx->int_cond, - ctx->int_type); + mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", + ctx->id, ctx->type, command, ctx->int_cond, + ctx->int_type); status = -1; } diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index 5ea153a68522..d9880210b2ab 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -890,7 +890,8 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); if (vsi->show_frame & BIT(2)) { - if (vpu_dec_start(&inst->vpu, NULL, 0)) { + ret = vpu_dec_start(&inst->vpu, NULL, 0); + if (ret) { mtk_vcodec_err(inst, "vpu trig decoder failed"); goto DECODE_ERROR; } diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index b1fc4518e275..a6bb7d9bf75f 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2126,21 +2126,6 @@ static void isp_parse_of_csi1_endpoint(struct device *dev, buscfg->bus.ccp2.crc = 1; } -static int isp_alloc_isd(struct isp_async_subdev **isd, - struct isp_bus_cfg **buscfg) -{ - struct isp_async_subdev *__isd; - - __isd = kzalloc(sizeof(*__isd), GFP_KERNEL); - if (!__isd) - return -ENOMEM; - - *isd = __isd; - *buscfg = &__isd->bus; - - return 0; -} - static struct { u32 phy; u32 csi2_if; @@ -2156,7 +2141,6 @@ static int isp_parse_of_endpoints(struct isp_device *isp) { struct fwnode_handle *ep; struct isp_async_subdev *isd = NULL; - struct isp_bus_cfg *buscfg; unsigned int i; ep = fwnode_graph_get_endpoint_by_id( @@ -2174,20 +2158,13 @@ static int isp_parse_of_endpoints(struct isp_device *isp) ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (!ret) { - ret = isp_alloc_isd(&isd, &buscfg); - if (ret) - return ret; - } - - if (!ret) { - isp_parse_of_parallel_endpoint(isp->dev, &vep, buscfg); - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, &isd->asd); + isd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, struct isp_async_subdev); + if (!IS_ERR(isd)) + isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus); } fwnode_handle_put(ep); - if (ret) - kfree(isd); } for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) { @@ -2206,15 +2183,8 @@ static int isp_parse_of_endpoints(struct isp_device *isp) dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i, to_of_node(ep)); - ret = isp_alloc_isd(&isd, &buscfg); - if (ret) - return ret; - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (!ret) { - buscfg->interface = isp_bus_interfaces[i].csi2_if; - isp_parse_of_csi2_endpoint(isp->dev, &vep, buscfg); - } else if (ret == -ENXIO) { + if (ret == -ENXIO) { vep = (struct v4l2_fwnode_endpoint) { .bus_type = V4L2_MBUS_CSI1 }; ret = v4l2_fwnode_endpoint_parse(ep, &vep); @@ -2224,21 +2194,33 @@ static int isp_parse_of_endpoints(struct isp_device *isp) { .bus_type = V4L2_MBUS_CCP2 }; ret = v4l2_fwnode_endpoint_parse(ep, &vep); } - if (!ret) { - buscfg->interface = - isp_bus_interfaces[i].csi1_if; - isp_parse_of_csi1_endpoint(isp->dev, &vep, - buscfg); - } } - if (!ret) - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, &isd->asd); + if (!ret) { + isd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, struct isp_async_subdev); + + if (!IS_ERR(isd)) { + switch (vep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + isd->bus.interface = + isp_bus_interfaces[i].csi2_if; + isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus); + break; + case V4L2_MBUS_CSI1: + case V4L2_MBUS_CCP2: + isd->bus.interface = + isp_bus_interfaces[i].csi1_if; + isp_parse_of_csi1_endpoint(isp->dev, &vep, + &isd->bus); + break; + default: + break; + } + } + } fwnode_handle_put(ep); - if (ret) - kfree(isd); } return 0; diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index b664ce7558a1..14077797f5e1 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -31,7 +31,6 @@ #include <linux/dma/pxa-dma.h> #include <media/v4l2-async.h> -#include <media/v4l2-clk.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -656,8 +655,6 @@ struct pxa_camera_dev { const struct pxa_camera_format_xlate *current_fmt; struct v4l2_pix_format current_pix; - struct v4l2_async_subdev asd; - /* * PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than @@ -677,7 +674,6 @@ struct pxa_camera_dev { unsigned long ciclk; unsigned long mclk; u32 mclk_divisor; - struct v4l2_clk *mclk_clk; u16 width_flags; /* max 10 bits */ struct list_head capture; @@ -1386,6 +1382,9 @@ static int pxac_vb2_prepare(struct vb2_buffer *vb) struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); int ret = 0; +#ifdef DEBUG + int i; +#endif switch (pcdev->channels) { case 1: @@ -2030,9 +2029,6 @@ static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static const struct v4l2_clk_ops pxa_camera_mclk_ops = { -}; - static const struct video_device pxa_camera_videodev_template = { .name = "pxa-camera", .minor = -1, @@ -2140,11 +2136,6 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, pxa_camera_destroy_formats(pcdev); - if (pcdev->mclk_clk) { - v4l2_clk_unregister(pcdev->mclk_clk); - pcdev->mclk_clk = NULL; - } - video_unregister_device(&pcdev->vdev); pcdev->sensor = NULL; @@ -2199,11 +2190,11 @@ static int pxa_camera_resume(struct device *dev) } static int pxa_camera_pdata_from_dt(struct device *dev, - struct pxa_camera_dev *pcdev, - struct v4l2_async_subdev *asd) + struct pxa_camera_dev *pcdev) { u32 mclk_rate; - struct device_node *remote, *np = dev->of_node; + struct v4l2_async_subdev *asd; + struct device_node *np = dev->of_node; struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; int err = of_property_read_u32(np, "clock-frequency", &mclk_rate); @@ -2255,13 +2246,12 @@ static int pxa_camera_pdata_from_dt(struct device *dev, if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - remote = of_graph_get_remote_port_parent(np); - if (remote) - asd->match.fwnode = of_fwnode_handle(remote); - else - dev_notice(dev, "no remote for %pOF\n", np); - + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &pcdev->notifier, + of_fwnode_handle(np), + struct v4l2_async_subdev); + if (IS_ERR(asd)) + err = PTR_ERR(asd); out: of_node_put(np); @@ -2278,7 +2268,6 @@ static int pxa_camera_probe(struct platform_device *pdev) .src_maxburst = 8, .direction = DMA_DEV_TO_MEM, }; - char clk_name[V4L2_CLK_NAME_SIZE]; int irq; int err = 0, i; @@ -2297,18 +2286,23 @@ static int pxa_camera_probe(struct platform_device *pdev) if (IS_ERR(pcdev->clk)) return PTR_ERR(pcdev->clk); + v4l2_async_notifier_init(&pcdev->notifier); pcdev->res = res; - pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { + struct v4l2_async_subdev *asd; + pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; - pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; - pcdev->asd.match.i2c.adapter_id = - pcdev->pdata->sensor_i2c_adapter_id; - pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; + asd = v4l2_async_notifier_add_i2c_subdev( + &pcdev->notifier, + pcdev->pdata->sensor_i2c_adapter_id, + pcdev->pdata->sensor_i2c_address, + struct v4l2_async_subdev); + if (IS_ERR(asd)) + err = PTR_ERR(asd); } else if (pdev->dev.of_node) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); } else { return -ENODEV; } @@ -2400,43 +2394,18 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_deactivate; - v4l2_async_notifier_init(&pcdev->notifier); - - err = v4l2_async_notifier_add_subdev(&pcdev->notifier, &pcdev->asd); - if (err) { - fwnode_handle_put(pcdev->asd.match.fwnode); - goto exit_free_v4l2dev; - } - - pcdev->notifier.ops = &pxa_camera_sensor_ops; - - if (!of_have_populated_dt()) - pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; - err = pxa_camera_init_videobuf2(pcdev); if (err) goto exit_notifier_cleanup; - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->asd.match.i2c.adapter_id, - pcdev->asd.match.i2c.address); - - pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); - if (IS_ERR(pcdev->mclk_clk)) { - err = PTR_ERR(pcdev->mclk_clk); - goto exit_notifier_cleanup; - } - + pcdev->notifier.ops = &pxa_camera_sensor_ops; err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); if (err) - goto exit_free_clk; + goto exit_notifier_cleanup; return 0; -exit_free_clk: - v4l2_clk_unregister(pcdev->mclk_clk); exit_notifier_cleanup: v4l2_async_notifier_cleanup(&pcdev->notifier); -exit_free_v4l2dev: v4l2_device_unregister(&pcdev->v4l2_dev); exit_deactivate: pxa_camera_deactivate(pcdev); @@ -2463,11 +2432,6 @@ static int pxa_camera_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&pcdev->notifier); v4l2_async_notifier_cleanup(&pcdev->notifier); - if (pcdev->mclk_clk) { - v4l2_clk_unregister(pcdev->mclk_clk); - pcdev->mclk_clk = NULL; - } - v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index bd9334af1c73..97cea7c4d769 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -579,7 +579,7 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) break; } - if (k < f->index) + if (k == -1 || k < f->index) /* * All the unique pixel formats matching the arguments * have been enumerated (k >= 0 and f->index > 0), or @@ -961,6 +961,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, video->nformats = ARRAY_SIZE(formats_rdi_8x96); } } else { + ret = -EINVAL; goto error_video_register; } diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8fefce57bc49..7c0f669f8aa6 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -655,7 +655,6 @@ static int camss_of_parse_ports(struct camss *camss) for_each_endpoint_of_node(dev->of_node, node) { struct camss_async_subdev *csd; - struct v4l2_async_subdev *asd; if (!of_device_is_available(node)) continue; @@ -667,17 +666,15 @@ static int camss_of_parse_ports(struct camss *camss) goto err_cleanup; } - asd = v4l2_async_notifier_add_fwnode_subdev( + csd = v4l2_async_notifier_add_fwnode_subdev( &camss->notifier, of_fwnode_handle(remote), - sizeof(*csd)); + struct camss_async_subdev); of_node_put(remote); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + if (IS_ERR(csd)) { + ret = PTR_ERR(csd); goto err_cleanup; } - csd = container_of(asd, struct camss_async_subdev, asd); - ret = camss_of_parse_endpoint_node(dev, node, csd); if (ret < 0) goto err_cleanup; diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index dfc636865709..91ee6be10292 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,9 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o pm_helpers.o dbgfs.o + hfi_parser.o pm_helpers.o dbgfs.o \ + hfi_platform.o hfi_platform_v4.o \ + hfi_platform_v6.o hfi_plat_bufs_v6.o \ venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 7233a7311757..f9896c121fd8 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -7,6 +7,7 @@ #include <linux/interconnect.h> #include <linux/ioctl.h> #include <linux/delay.h> +#include <linux/devcoredump.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> @@ -22,6 +23,33 @@ #include "firmware.h" #include "pm_helpers.h" +static void venus_coredump(struct venus_core *core) +{ + struct device *dev; + phys_addr_t mem_phys; + size_t mem_size; + void *mem_va; + void *data; + + dev = core->dev; + mem_phys = core->fw.mem_phys; + mem_size = core->fw.mem_size; + + mem_va = memremap(mem_phys, mem_size, MEMREMAP_WC); + if (!mem_va) + return; + + data = vmalloc(mem_size); + if (!data) { + memunmap(mem_va); + return; + } + + memcpy(data, mem_va, mem_size); + memunmap(mem_va); + dev_coredumpv(dev, data, mem_size, GFP_KERNEL); +} + static void venus_event_notify(struct venus_core *core, u32 event) { struct venus_inst *inst; @@ -67,6 +95,8 @@ static void venus_sys_error_handler(struct work_struct *work) venus_shutdown(core); + venus_coredump(core); + pm_runtime_put_sync(core->dev); while (core->pmdomains[0] && pm_runtime_active(core->pmdomains[0])) @@ -488,17 +518,6 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; -static const struct codec_freq_data sdm845_codec_freq_data[] = { - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, -}; - static const struct bw_tbl sdm845_bw_table_enc[] = { { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ @@ -520,8 +539,6 @@ static const struct venus_resources sdm845_res = { .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), .bw_tbl_dec = sdm845_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "core", "bus" }, @@ -543,8 +560,6 @@ static const struct venus_resources sdm845_res_v2 = { .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), .bw_tbl_dec = sdm845_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, @@ -594,8 +609,6 @@ static const struct venus_resources sc7180_res = { .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), .bw_tbl_dec = sc7180_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index f03ed427accd..a252ed32cc14 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -14,6 +14,7 @@ #include "dbgfs.h" #include "hfi.h" +#include "hfi_platform.h" #define VDBGL "VenusLow : " #define VDBGM "VenusMed : " @@ -36,13 +37,6 @@ struct reg_val { u32 value; }; -struct codec_freq_data { - u32 pixfmt; - u32 session_type; - unsigned long vpp_freq; - unsigned long vsp_freq; -}; - struct bw_tbl { u32 mbs_per_sec; u32 avg; @@ -61,8 +55,6 @@ struct venus_resources { unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; - const struct codec_freq_data *codec_freq_data; - unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; @@ -91,30 +83,6 @@ struct venus_format { u32 flags; }; -#define MAX_PLANES 4 -#define MAX_FMT_ENTRIES 32 -#define MAX_CAP_ENTRIES 32 -#define MAX_ALLOC_MODE_ENTRIES 16 -#define MAX_CODEC_NUM 32 - -struct raw_formats { - u32 buftype; - u32 fmt; -}; - -struct venus_caps { - u32 codec; - u32 domain; - bool cap_bufs_mode_dynamic; - unsigned int num_caps; - struct hfi_capability caps[MAX_CAP_ENTRIES]; - unsigned int num_pl; - struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; - unsigned int num_fmts; - struct raw_formats fmts[MAX_FMT_ENTRIES]; - bool valid; /* used only for Venus v1xx */ -}; - /** * struct venus_core - holds core parameters valid for all instances * @@ -123,7 +91,6 @@ struct venus_caps { * @clks: an array of struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers - * @pd_dl_venus: pmdomain device-link for venus domain * @pmdomains: an array of pmdomains struct device pointers * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances @@ -145,7 +112,6 @@ struct venus_caps { * @enc_codecs: encoders supported by this core * @dec_codecs: decoders supported by this core * @max_sessions_supported: holds the maximum number of sessions - * @core_caps: core capabilities * @priv: a private filed for HFI operations * @ops: the core HFI operations * @work: a delayed work for handling system fatal error @@ -161,7 +127,6 @@ struct venus_core { struct icc_path *cpucfg_path; struct opp_table *opp_table; bool has_opp_table; - struct device_link *pd_dl_venus; struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct device_link *opp_dl_venus; struct device *opp_pmdomain; @@ -177,6 +142,8 @@ struct venus_core { struct device *dev; struct iommu_domain *iommu_domain; size_t mapped_mem_size; + phys_addr_t mem_phys; + size_t mem_size; } fw; struct mutex lock; struct list_head instances; @@ -191,15 +158,10 @@ struct venus_core { unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; -#define ENC_ROTATION_CAPABILITY 0x1 -#define ENC_SCALING_CAPABILITY 0x2 -#define ENC_DEINTERLACE_CAPABILITY 0x4 -#define DEC_MULTI_STREAM_CAPABILITY 0x8 - unsigned int core_caps; void *priv; const struct hfi_ops *ops; struct delayed_work work; - struct venus_caps caps[MAX_CODEC_NUM]; + struct hfi_plat_caps caps[MAX_CODEC_NUM]; unsigned int codecs_count; unsigned int core0_usage_count; unsigned int core1_usage_count; @@ -230,10 +192,28 @@ struct venc_controls { u32 h264_b_qp; u32 h264_min_qp; u32 h264_max_qp; + u32 h264_i_min_qp; + u32 h264_i_max_qp; + u32 h264_p_min_qp; + u32 h264_p_max_qp; + u32 h264_b_min_qp; + u32 h264_b_max_qp; u32 h264_loop_filter_mode; s32 h264_loop_filter_alpha; s32 h264_loop_filter_beta; + u32 hevc_i_qp; + u32 hevc_p_qp; + u32 hevc_b_qp; + u32 hevc_min_qp; + u32 hevc_max_qp; + u32 hevc_i_min_qp; + u32 hevc_i_max_qp; + u32 hevc_p_min_qp; + u32 hevc_p_max_qp; + u32 hevc_b_min_qp; + u32 hevc_b_max_qp; + u32 vp8_min_qp; u32 vp8_max_qp; @@ -256,6 +236,8 @@ struct venc_controls { u32 hevc; u32 vp9; } level; + + u32 base_priority_id; }; struct venus_buffer { @@ -271,7 +253,8 @@ struct venus_buffer { struct clock_data { u32 core_id; unsigned long freq; - const struct codec_freq_data *codec_freq_data; + unsigned long vpp_freq; + unsigned long vsp_freq; }; #define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb) @@ -285,7 +268,6 @@ enum venus_dec_state { VENUS_DEC_STATE_DRAIN = 5, VENUS_DEC_STATE_DECODING = 6, VENUS_DEC_STATE_DRC = 7, - VENUS_DEC_STATE_DRC_FLUSH_DONE = 8, }; struct venus_ts_metadata { @@ -350,7 +332,7 @@ struct venus_ts_metadata { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @last_buf: last capture buffer for dynamic-resoluton-change + * @next_buf_last: a flag to mark next queued capture buffer as last */ struct venus_inst { struct list_head list; @@ -413,7 +395,9 @@ struct venus_inst { union hfi_get_property hprop; unsigned int core_acquired: 1; unsigned int bit_depth; - struct vb2_buffer *last_buf; + unsigned int pic_struct; + bool next_buf_last; + bool drain_active; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) @@ -433,7 +417,7 @@ static inline void *to_hfi_priv(struct venus_core *core) return core->priv; } -static inline struct venus_caps * +static inline struct hfi_plat_caps * venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain) { unsigned int c; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index d03e2dd5808c..89defc21ea81 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -201,6 +201,9 @@ int venus_boot(struct venus_core *core) return -EINVAL; } + core->fw.mem_size = mem_size; + core->fw.mem_phys = mem_phys; + if (core->use_tz) ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); else diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 50439eb1ffea..76ece2ff8d39 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -7,7 +7,7 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/kernel.h> -#include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dma-contig.h> #include <media/v4l2-mem2mem.h> #include <asm/div64.h> @@ -15,6 +15,8 @@ #include "helpers.h" #include "hfi_helper.h" #include "pm_helpers.h" +#include "hfi_platform.h" +#include "hfi_parser.h" struct intbuf { struct list_head list; @@ -480,7 +482,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) static bool is_dynamic_bufmode(struct venus_inst *inst) { struct venus_core *core = inst->core; - struct venus_caps *caps; + struct hfi_plat_caps *caps; /* * v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports @@ -552,6 +554,51 @@ static u32 to_hfi_raw_fmt(u32 v4l2_fmt) return 0; } +static int platform_get_bufreq(struct venus_inst *inst, u32 buftype, + struct hfi_buffer_requirements *req) +{ + enum hfi_version version = inst->core->res->hfi_version; + const struct hfi_platform *hfi_plat; + struct hfi_plat_buffers_params params; + bool is_dec = inst->session_type == VIDC_SESSION_TYPE_DEC; + struct venc_controls *enc_ctr = &inst->controls.enc; + + hfi_plat = hfi_platform_get(version); + + if (!hfi_plat || !hfi_plat->bufreq) + return -EINVAL; + + params.version = version; + params.num_vpp_pipes = hfi_platform_num_vpp_pipes(version); + + if (is_dec) { + params.width = inst->width; + params.height = inst->height; + params.codec = inst->fmt_out->pixfmt; + params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_cap->pixfmt); + params.dec.max_mbs_per_frame = mbs_per_frame_max(inst); + params.dec.buffer_size_limit = 0; + params.dec.is_secondary_output = + inst->opb_buftype == HFI_BUFFER_OUTPUT2; + params.dec.is_interlaced = + inst->pic_struct != HFI_INTERLACE_FRAME_PROGRESSIVE ? + true : false; + } else { + params.width = inst->out_width; + params.height = inst->out_height; + params.codec = inst->fmt_cap->pixfmt; + params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_out->pixfmt); + params.enc.work_mode = VIDC_WORK_MODE_2; + params.enc.rc_type = HFI_RATE_CONTROL_OFF; + if (enc_ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) + params.enc.rc_type = HFI_RATE_CONTROL_CQ; + params.enc.num_b_frames = enc_ctr->num_b_frames; + params.enc.is_tenbit = inst->bit_depth == VIDC_BITDEPTH_10; + } + + return hfi_plat->bufreq(¶ms, inst->session_type, buftype, req); +} + int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { @@ -563,6 +610,10 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, if (req) memset(req, 0, sizeof(*req)); + ret = platform_get_bufreq(inst, type, req); + if (!ret) + return 0; + ret = hfi_session_get_property(inst, ptype, &hprop); if (ret) return ret; @@ -986,6 +1037,8 @@ u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height) if (compressed) { sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + if (width < 1280 || height < 720) + sz *= 8; return ALIGN(sz, SZ_4K); } @@ -1040,36 +1093,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) } EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); -int venus_helper_init_codec_freq_data(struct venus_inst *inst) -{ - const struct codec_freq_data *data; - unsigned int i, data_size; - u32 pixfmt; - int ret = 0; - - if (!IS_V4(inst->core)) - return 0; - - data = inst->core->res->codec_freq_data; - data_size = inst->core->res->codec_freq_data_size; - pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ? - inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; - - for (i = 0; i < data_size; i++) { - if (data[i].pixfmt == pixfmt && - data[i].session_type == inst->session_type) { - inst->clk_data.codec_freq_data = &data[i]; - break; - } - } - - if (!inst->clk_data.codec_freq_data) - ret = -EINVAL; - - return ret; -} -EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data); - int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs) @@ -1284,14 +1307,9 @@ int venus_helper_vb2_buf_init(struct vb2_buffer *vb) struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct venus_buffer *buf = to_venus_buffer(vbuf); - struct sg_table *sgt; - - sgt = vb2_dma_sg_plane_desc(vb, 0); - if (!sgt) - return -EFAULT; buf->size = vb2_plane_size(vb, 0); - buf->dma_addr = sg_dma_address(sgt->sgl); + buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) list_add_tail(&buf->reg_list, &inst->registeredbufs); @@ -1343,28 +1361,29 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; int ret; - mutex_lock(&inst->lock); - v4l2_m2m_buf_queue(m2m_ctx, vbuf); + /* Skip processing queued capture buffers after LAST flag */ + if (inst->session_type == VIDC_SESSION_TYPE_DEC && + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + inst->codec_state == VENUS_DEC_STATE_DRC) + return; + cache_payload(inst, vb); if (inst->session_type == VIDC_SESSION_TYPE_ENC && !(inst->streamon_out && inst->streamon_cap)) - goto unlock; + return; if (vb2_start_streaming_called(vb->vb2_queue)) { ret = is_buf_refed(inst, vbuf); if (ret) - goto unlock; + return; ret = session_process_buf(inst, vbuf); if (ret) return_buf_error(inst, vbuf); } - -unlock: - mutex_unlock(&inst->lock); } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue); @@ -1529,6 +1548,29 @@ void venus_helper_m2m_job_abort(void *priv) } EXPORT_SYMBOL_GPL(venus_helper_m2m_job_abort); +int venus_helper_session_init(struct venus_inst *inst) +{ + enum hfi_version version = inst->core->res->hfi_version; + u32 session_type = inst->session_type; + u32 codec; + int ret; + + codec = inst->session_type == VIDC_SESSION_TYPE_DEC ? + inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; + + ret = hfi_session_init(inst, codec); + if (ret) + return ret; + + inst->clk_data.vpp_freq = hfi_platform_get_codec_vpp_freq(version, codec, + session_type); + inst->clk_data.vsp_freq = hfi_platform_get_codec_vsp_freq(version, codec, + session_type); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_session_init); + void venus_helper_init_instance(struct venus_inst *inst) { if (inst->session_type == VIDC_SESSION_TYPE_DEC) { @@ -1539,7 +1581,7 @@ void venus_helper_init_instance(struct venus_inst *inst) } EXPORT_SYMBOL_GPL(venus_helper_init_instance); -static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt) +static bool find_fmt_from_caps(struct hfi_plat_caps *caps, u32 buftype, u32 fmt) { unsigned int i; @@ -1556,7 +1598,7 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc) { struct venus_core *core = inst->core; - struct venus_caps *caps; + struct hfi_plat_caps *caps; u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt); bool found, found_ubwc; @@ -1620,3 +1662,21 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, return -EINVAL; } EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); + +int venus_helper_set_stride(struct venus_inst *inst, + unsigned int width, unsigned int height) +{ + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO; + + struct hfi_uncompressed_plane_actual_info plane_actual_info; + + plane_actual_info.buffer_type = HFI_BUFFER_INPUT; + plane_actual_info.num_planes = 2; + plane_actual_info.plane_format[0].actual_stride = width; + plane_actual_info.plane_format[0].actual_plane_buffer_height = height; + plane_actual_info.plane_format[1].actual_stride = width; + plane_actual_info.plane_format[1].actual_plane_buffer_height = height / 2; + + return hfi_session_set_property(inst, ptype, &plane_actual_info); +} +EXPORT_SYMBOL_GPL(venus_helper_set_stride); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index a4a0562bc83f..351093845499 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,7 +33,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); -int venus_helper_init_codec_freq_data(struct venus_inst *inst); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs); @@ -48,6 +47,7 @@ unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +int venus_helper_session_init(struct venus_inst *inst); int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc); int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); @@ -63,4 +63,6 @@ void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, struct vb2_v4l2_buffer *vbuf); int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level); int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level); +int venus_helper_set_stride(struct venus_inst *inst, unsigned int aligned_width, + unsigned int aligned_height); #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 638ed5cfe05e..0f2482367e06 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -175,6 +175,8 @@ static int wait_session_msg(struct venus_inst *inst) int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) { struct venus_core *core = inst->core; + bool max; + int ret; if (!ops) return -EINVAL; @@ -184,11 +186,19 @@ int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) inst->ops = ops; mutex_lock(&core->lock); - list_add_tail(&inst->list, &core->instances); - atomic_inc(&core->insts_count); + + max = atomic_add_unless(&core->insts_count, 1, + core->max_sessions_supported); + if (!max) { + ret = -EAGAIN; + } else { + list_add_tail(&inst->list, &core->instances); + ret = 0; + } + mutex_unlock(&core->lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(hfi_session_create); @@ -211,7 +221,7 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) mutex_unlock(&core->lock); if (inst->state != INST_UNINIT) - return -EINVAL; + return -EALREADY; inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 7022368c1e63..4f7565834469 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1205,6 +1205,18 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); break; } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { + struct hfi_uncompressed_plane_actual_info *in = pdata; + struct hfi_uncompressed_plane_actual_info *info = prop_data; + + info->buffer_type = in->buffer_type; + info->num_planes = in->num_planes; + info->plane_format[0] = in->plane_format[0]; + if (in->num_planes > 1) + info->plane_format[1] = in->plane_format[1]; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); + break; + } case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 60ee2479f7a6..6b524c7cde5f 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -364,6 +364,13 @@ #define HFI_HEVC_TIER_MAIN 0x1 #define HFI_HEVC_TIER_HIGH0 0x2 +#define HFI_VPX_PROFILE_MAIN 0x00000001 + +#define HFI_VPX_LEVEL_VERSION_0 0x00000001 +#define HFI_VPX_LEVEL_VERSION_1 0x00000002 +#define HFI_VPX_LEVEL_VERSION_2 0x00000004 +#define HFI_VPX_LEVEL_VERSION_3 0x00000008 + /* VP9 Profile 0, 8-bit */ #define HFI_VP9_PROFILE_P0 0x00000001 /* VP9 Profile 2, 10-bit */ @@ -571,7 +578,18 @@ struct hfi_bitrate { #define HFI_CAPABILITY_LCU_SIZE 0x14 #define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 #define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 +#define HFI_CAPABILITY_I_FRAME_QP 0x20 +#define HFI_CAPABILITY_P_FRAME_QP 0x21 +#define HFI_CAPABILITY_B_FRAME_QP 0x22 +#define HFI_CAPABILITY_RATE_CONTROL_MODES 0x23 +#define HFI_CAPABILITY_BLUR_WIDTH 0x24 +#define HFI_CAPABILITY_BLUR_HEIGHT 0x25 +#define HFI_CAPABILITY_SLICE_BYTE 0x27 +#define HFI_CAPABILITY_SLICE_MB 0x28 #define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b +#define HFI_CAPABILITY_MAX_WORKMODES 0x2c +#define HFI_CAPABILITY_ROTATION 0x2f +#define HFI_CAPABILITY_COLOR_SPACE_CONVERSION 0x30 struct hfi_capability { u32 capability_type; @@ -908,13 +926,13 @@ struct hfi_uncompressed_plane_actual { struct hfi_uncompressed_plane_actual_info { u32 buffer_type; u32 num_planes; - struct hfi_uncompressed_plane_actual plane_format[1]; + struct hfi_uncompressed_plane_actual plane_format[2]; }; struct hfi_uncompressed_plane_actual_constraints_info { u32 buffer_type; u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_format[1]; + struct hfi_uncompressed_plane_constraints plane_format[2]; }; struct hfi_codec_supported { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 363ee2a65453..7263c0c32695 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -11,12 +11,12 @@ #include "hfi_helper.h" #include "hfi_parser.h" -typedef void (*func)(struct venus_caps *cap, const void *data, +typedef void (*func)(struct hfi_plat_caps *cap, const void *data, unsigned int size); static void init_codecs(struct venus_core *core) { - struct venus_caps *caps = core->caps, *cap; + struct hfi_plat_caps *caps = core->caps, *cap; unsigned long bit; for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) { @@ -34,11 +34,11 @@ static void init_codecs(struct venus_core *core) } } -static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, +static void for_each_codec(struct hfi_plat_caps *caps, unsigned int caps_num, u32 codecs, u32 domain, func cb, void *data, unsigned int size) { - struct venus_caps *cap; + struct hfi_plat_caps *cap; unsigned int i; for (i = 0; i < caps_num; i++) { @@ -51,7 +51,7 @@ static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, } static void -fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) +fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const u32 *type = data; @@ -81,7 +81,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) } } -static void fill_profile_level(struct venus_caps *cap, const void *data, +static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const struct hfi_profile_level *pl = data; @@ -107,7 +107,7 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) } static void -fill_caps(struct venus_caps *cap, const void *data, unsigned int num) +fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const struct hfi_capability *caps = data; @@ -132,7 +132,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) fill_caps, caps_arr, num_caps); } -static void fill_raw_fmts(struct venus_caps *cap, const void *fmts, +static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, unsigned int num_fmts) { const struct raw_formats *formats = fmts; @@ -211,7 +211,7 @@ static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) { - struct venus_caps *caps, *cap; + struct hfi_plat_caps *caps, *cap; unsigned int i; u32 dom; @@ -228,11 +228,49 @@ static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) } } +static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) +{ + const struct hfi_platform *plat; + const struct hfi_plat_caps *caps = NULL; + u32 enc_codecs, dec_codecs, count = 0; + unsigned int entries; + + if (inst) + return 0; + + plat = hfi_platform_get(core->res->hfi_version); + if (!plat) + return -EINVAL; + + if (plat->codecs) + plat->codecs(&enc_codecs, &dec_codecs, &count); + + if (plat->capabilities) + caps = plat->capabilities(&entries); + + if (!caps || !entries || !count) + return -EINVAL; + + core->enc_codecs = enc_codecs; + core->dec_codecs = dec_codecs; + core->codecs_count = count; + core->max_sessions_supported = MAX_SESSIONS; + memset(core->caps, 0, sizeof(*caps) * MAX_CODEC_NUM); + memcpy(core->caps, caps, sizeof(*caps) * entries); + + return 0; +} + u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, u32 size) { unsigned int words_count = size >> 2; u32 *word = buf, *data, codecs = 0, domain = 0; + int ret; + + ret = hfi_platform_parser(core, inst); + if (!ret) + return HFI_ERR_NONE; if (size % 4) return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; @@ -276,6 +314,9 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, words_count--; } + if (!core->max_sessions_supported) + core->max_sessions_supported = MAX_SESSIONS; + parser_fini(inst, codecs, domain); return HFI_ERR_NONE; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 264e6dd2415f..5751d0140700 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -16,7 +16,7 @@ static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which) { struct venus_core *core = inst->core; struct hfi_capability *cap = NULL; - struct venus_caps *caps; + struct hfi_plat_caps *caps; unsigned int i; caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); @@ -112,4 +112,9 @@ static inline u32 core_num_max(struct venus_inst *inst) return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES); } +static inline u32 mbs_per_frame_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_MBS_PER_FRAME); +} + #endif diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs.h b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h new file mode 100644 index 000000000000..52a51a3b964a --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __HFI_PLATFORM_BUFFERS_H__ +#define __HFI_PLATFORM_BUFFERS_H__ + +#include <linux/types.h> +#include "hfi_helper.h" + +struct hfi_plat_buffers_params { + u32 width; + u32 height; + u32 codec; + u32 hfi_color_fmt; + enum hfi_version version; + u32 num_vpp_pipes; + union { + struct { + u32 max_mbs_per_frame; + u32 buffer_size_limit; + bool is_secondary_output; + bool is_interlaced; + } dec; + struct { + u32 work_mode; + u32 rc_type; + u32 num_b_frames; + bool is_tenbit; + } enc; + }; +}; + +int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq); + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c new file mode 100644 index 000000000000..d43d1a53e72d --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -0,0 +1,1317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include <linux/kernel.h> +#include <linux/sizes.h> +#include <linux/videodev2.h> + +#include "hfi.h" +#include "hfi_plat_bufs.h" +#include "helpers.h" + +#define MIN_INPUT_BUFFERS 4 +#define MIN_ENC_OUTPUT_BUFFERS 4 + +#define NV12_UBWC_Y_TILE_WIDTH 32 +#define NV12_UBWC_Y_TILE_HEIGHT 8 +#define NV12_UBWC_UV_TILE_WIDTH 16 +#define NV12_UBWC_UV_TILE_HEIGHT 8 +#define TP10_UBWC_Y_TILE_WIDTH 48 +#define TP10_UBWC_Y_TILE_HEIGHT 4 +#define METADATA_STRIDE_MULTIPLE 64 +#define METADATA_HEIGHT_MULTIPLE 16 +#define HFI_DMA_ALIGNMENT 256 + +#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640 +#define MAX_FE_NBR_DATA_CB_LINE_BUFFER_SIZE 320 +#define MAX_FE_NBR_DATA_CR_LINE_BUFFER_SIZE 320 + +#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE (128 / 8) +#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8) +#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8) + +#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE (64 * 2 * 3) +#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE (32 * 2 * 3) +#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE (16 * 2 * 3) + +#define MAX_TILE_COLUMNS 32 /* 8K/256 */ + +#define NUM_HW_PIC_BUF 10 +#define BIN_BUFFER_THRESHOLD (1280 * 736) +#define H264D_MAX_SLICE 1800 +/* sizeof(h264d_buftab_t) aligned to 256 */ +#define SIZE_H264D_BUFTAB_T 256 +/* sizeof(h264d_hw_pic_t) aligned to 32 */ +#define SIZE_H264D_HW_PIC_T BIT(11) +#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4) +#define SIZE_H264D_VPP_CMD_PER_BUF 512 + +/* Line Buffer definitions, One for Luma and 1/2 for each Chroma */ +#define SIZE_H264D_LB_FE_TOP_DATA(width, height) \ + (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN((width), 16) * 3) + +#define SIZE_H264D_LB_FE_TOP_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_FE_LEFT_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4)) + +#define SIZE_H264D_LB_SE_TOP_CTRL(width, height) \ + (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_SE_LEFT_CTRL(width, height) \ + (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4)) + +#define SIZE_H264D_LB_PE_TOP_DATA(width, height) \ + (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_VSP_TOP(width, height) (((((width) + 15) >> 4) << 7)) + +#define SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height) \ + (ALIGN((height), 16) * 32) + +#define SIZE_H264D_QP(width, height) \ + ((((width) + 63) >> 6) * (((height) + 63) >> 6) * 128) + +#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf)) + +#define H264_CABAC_HDR_RATIO_HD_TOT 1 +#define H264_CABAC_RES_RATIO_HD_TOT 3 + +/* + * Some content need more bin buffer, but limit buffer + * size for high resolution + */ +#define NUM_SLIST_BUF_H264 (256 + 32) +#define SIZE_SLIST_BUF_H264 512 +#define LCU_MAX_SIZE_PELS 64 +#define LCU_MIN_SIZE_PELS 16 + +#define H265D_MAX_SLICE 600 +#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T +#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32)) +#define SIZE_H265D_VPP_CMD_PER_BUF 256 + +#define SIZE_H265D_LB_FE_TOP_DATA(width, height) \ + (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * (ALIGN(width, 64) + 8) * 2) + +#define SIZE_H265D_LB_FE_TOP_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_FE_LEFT_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_SE_TOP_CTRL(width, height) \ + ((LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * (((width) + 15) >> 4)) + +static inline u32 size_h265d_lb_se_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 16 - 1) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 32 - 1) / 8) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 64 - 1) / 8) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_H265D_LB_PE_TOP_DATA(width, height) \ + (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_VSP_TOP(width, height) ((((width) + 63) >> 6) * 128) + +#define SIZE_H265D_LB_VSP_LEFT(width, height) ((((height) + 63) >> 6) * 128) + +#define SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height) \ + SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height) + +#define SIZE_H265D_QP(width, height) SIZE_H264D_QP(width, height) + +#define H265_CABAC_HDR_RATIO_HD_TOT 2 +#define H265_CABAC_RES_RATIO_HD_TOT 2 + +/* + * Some content need more bin buffer, but limit buffer size + * for high resolution + */ +#define SIZE_SLIST_BUF_H265 BIT(10) +#define NUM_SLIST_BUF_H265 (80 + 20) +#define H265_NUM_TILE_COL 32 +#define H265_NUM_TILE_ROW 128 +#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1) + +static inline u32 size_vpxd_lb_fe_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 31) >> 5) * MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 63) >> 6) * MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_VPXD_LB_FE_TOP_CTRL(width, height) \ + (((ALIGN(width, 64) + 8) * 10 * 2)) /* small line */ +#define SIZE_VPXD_LB_SE_TOP_CTRL(width, height) \ + ((((width) + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE) + +static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 15) >> 4) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 31) >> 5) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 63) >> 6) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height) \ + ALIGN((ALIGN(height, 16) / (4 / 2)) * 64, 32) +#define SIZE_VP8D_LB_FE_TOP_DATA(width, height) \ + ((ALIGN(width, 16) + 8) * 10 * 2) +#define SIZE_VP9D_LB_FE_TOP_DATA(width, height) \ + ((ALIGN(ALIGN(width, 16), 64) + 8) * 10 * 2) +#define SIZE_VP8D_LB_PE_TOP_DATA(width, height) \ + ((ALIGN(width, 16) >> 4) * 64) +#define SIZE_VP9D_LB_PE_TOP_DATA(width, height) \ + ((ALIGN(ALIGN(width, 16), 64) >> 6) * 176) +#define SIZE_VP8D_LB_VSP_TOP(width, height) \ + (((ALIGN(width, 16) >> 4) * 64 / 2) + 256) +#define SIZE_VP9D_LB_VSP_TOP(width, height) \ + (((ALIGN(ALIGN(width, 16), 64) >> 6) * 64 * 8) + 256) + +#define HFI_IRIS2_VP9D_COMV_SIZE \ + ((((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8)) + +#define VPX_DECODER_FRAME_CONCURENCY_LVL 2 +#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM 1 +#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN 2 +#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM 3 +#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN 2 + +#define VP8_NUM_FRAME_INFO_BUF (5 + 1) +#define VP9_NUM_FRAME_INFO_BUF (8 + 2 + 1 + 8) +#define VP8_NUM_PROBABILITY_TABLE_BUF VP8_NUM_FRAME_INFO_BUF +#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4) +#define VP8_PROB_TABLE_SIZE 3840 +#define VP9_PROB_TABLE_SIZE 3840 + +#define VP9_UDC_HEADER_BUF_SIZE (3 * 128) +#define MAX_SUPERFRAME_HEADER_LEN 34 +#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, 32) + +#define QMATRIX_SIZE (sizeof(u32) * 128 + 256) +#define MP2D_QPDUMP_SIZE 115200 +#define HFI_IRIS2_ENC_PERSIST_SIZE 102400 +#define HFI_MAX_COL_FRAME 6 +#define HFI_VENUS_VENC_TRE_WB_BUFF_SIZE (65 << 4) /* in Bytes */ +#define HFI_VENUS_VENC_DB_LINE_BUFF_PER_MB 512 +#define HFI_VENUS_VPPSG_MAX_REGISTERS 2048 +#define HFI_VENUS_WIDTH_ALIGNMENT 128 +#define HFI_VENUS_WIDTH_TEN_BIT_ALIGNMENT 192 +#define HFI_VENUS_HEIGHT_ALIGNMENT 32 + +#define SYSTEM_LAL_TILE10 192 +#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4)) +#define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4)) +#define MB_SIZE_IN_PIXEL (16 * 16) +#define HDR10PLUS_PAYLOAD_SIZE 1024 +#define HDR10_HIST_EXTRADATA_SIZE 4096 + +static u32 size_vpss_lb(u32 width, u32 height, u32 num_vpp_pipes) +{ + u32 vpss_4tap_top_buffer_size, vpss_div2_top_buffer_size; + u32 vpss_4tap_left_buffer_size, vpss_div2_left_buffer_size; + u32 opb_wr_top_line_luma_buf_size, opb_wr_top_line_chroma_buf_size; + u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size; + u32 macrotiling_size; + u32 size = 0; + + vpss_4tap_top_buffer_size = 0; + vpss_div2_top_buffer_size = 0; + vpss_4tap_left_buffer_size = 0; + vpss_div2_left_buffer_size = 0; + + macrotiling_size = 32; + opb_wr_top_line_luma_buf_size = + ALIGN(width, macrotiling_size) / macrotiling_size * 256; + opb_wr_top_line_luma_buf_size = + ALIGN(opb_wr_top_line_luma_buf_size, HFI_DMA_ALIGNMENT) + + (MAX_TILE_COLUMNS - 1) * 256; + opb_wr_top_line_luma_buf_size = + max(opb_wr_top_line_luma_buf_size, (32 * ALIGN(height, 16))); + opb_wr_top_line_chroma_buf_size = opb_wr_top_line_luma_buf_size; + opb_lb_wr_llb_y_buffer_size = ALIGN((ALIGN(height, 16) / 2) * 64, 32); + opb_lb_wr_llb_uv_buffer_size = opb_lb_wr_llb_y_buffer_size; + size = num_vpp_pipes * + 2 * (vpss_4tap_top_buffer_size + vpss_div2_top_buffer_size) + + 2 * (vpss_4tap_left_buffer_size + vpss_div2_left_buffer_size) + + opb_wr_top_line_luma_buf_size + + opb_wr_top_line_chroma_buf_size + + opb_lb_wr_llb_uv_buffer_size + + opb_lb_wr_llb_y_buffer_size; + + return size; +} + +static u32 size_h264d_hw_bin_buffer(u32 width, u32 height) +{ + u32 size_yuv, size_bin_hdr, size_bin_res; + u32 size = 0; + u32 product; + + product = width * height; + size_yuv = (product <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1); + + size_bin_hdr = size_yuv * H264_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * H264_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT); + size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT); + size = size_bin_hdr + size_bin_res; + + return size; +} + +static u32 h264d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size = 0; + + if (!is_interlaced) + size = size_h264d_hw_bin_buffer(aligned_width, aligned_height); + + return size; +} + +static u32 size_h265d_hw_bin_buffer(u32 width, u32 height) +{ + u32 size_yuv, size_bin_hdr, size_bin_res; + u32 size = 0; + u32 product; + + product = width * height; + size_yuv = (product <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1); + size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * H265_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT); + size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT); + size = size_bin_hdr + size_bin_res; + + return size; +} + +static u32 h265d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size = 0; + + if (!is_interlaced) + size = size_h265d_hw_bin_buffer(aligned_width, aligned_height); + + return size; +} + +static u32 vpxd_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size_yuv = aligned_width * aligned_height * 3 / 2; + u32 size = 0; + + if (!is_interlaced) { + u32 binbuffer1_size, binbufer2_size; + + binbuffer1_size = max_t(u32, size_yuv, + ((BIN_BUFFER_THRESHOLD * 3) >> 1)); + binbuffer1_size *= VPX_DECODER_FRAME_CONCURENCY_LVL * + VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM / + VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN; + binbufer2_size = max_t(u32, size_yuv, + ((BIN_BUFFER_THRESHOLD * 3) >> 1)); + binbufer2_size *= VPX_DECODER_FRAME_CONCURENCY_LVL * + VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM / + VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN; + size = ALIGN(binbuffer1_size + binbufer2_size, + HFI_DMA_ALIGNMENT); + } + + return size; +} + +static u32 mpeg2d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + return 0; +} + +static u32 calculate_enc_output_frame_size(u32 width, u32 height, u32 rc_type) +{ + u32 aligned_width, aligned_height; + u32 mbs_per_frame; + u32 frame_size; + + /* + * Encoder output size calculation: 32 Align width/height + * For resolution < 720p : YUVsize * 4 + * For resolution > 720p & <= 4K : YUVsize / 2 + * For resolution > 4k : YUVsize / 4 + * Initially frame_size = YUVsize * 2; + */ + aligned_width = ALIGN(width, 32); + aligned_height = ALIGN(height, 32); + mbs_per_frame = (ALIGN(aligned_height, 16) * + ALIGN(aligned_width, 16)) / 256; + frame_size = width * height * 3; + + if (mbs_per_frame < NUM_MBS_720P) + frame_size = frame_size << 1; + else if (mbs_per_frame <= NUM_MBS_4K) + frame_size = frame_size >> 2; + else + frame_size = frame_size >> 3; + + if (rc_type == HFI_RATE_CONTROL_OFF || rc_type == HFI_RATE_CONTROL_CQ) + frame_size = frame_size << 1; + + /* + * In case of opaque color format bitdepth will be known + * with first ETB, buffers allocated already with 8 bit + * won't be sufficient for 10 bit + * calculate size considering 10-bit by default + * For 10-bit cases size = size * 1.25 + */ + frame_size *= 5; + frame_size /= 4; + + return ALIGN(frame_size, SZ_4K); +} + +static u32 calculate_enc_scratch_size(u32 width, u32 height, u32 work_mode, + u32 lcu_size, u32 num_vpp_pipes, + u32 rc_type) +{ + u32 aligned_width, aligned_height, bitstream_size; + u32 total_bitbin_buffers, size_single_pipe, bitbin_size; + u32 sao_bin_buffer_size, padded_bin_size, size; + + aligned_width = ALIGN(width, lcu_size); + aligned_height = ALIGN(height, lcu_size); + bitstream_size = + calculate_enc_output_frame_size(width, height, rc_type); + + bitstream_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT); + + if (work_mode == VIDC_WORK_MODE_2) { + total_bitbin_buffers = 3; + bitbin_size = bitstream_size * 17 / 10; + bitbin_size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT); + } else { + total_bitbin_buffers = 1; + bitstream_size = aligned_width * aligned_height * 3; + bitbin_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT); + } + + if (num_vpp_pipes > 2) + size_single_pipe = bitbin_size / 2; + else + size_single_pipe = bitbin_size; + + size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + sao_bin_buffer_size = + (64 * (((width + 32) * (height + 32)) >> 10)) + 384; + padded_bin_size = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + size_single_pipe = sao_bin_buffer_size + padded_bin_size; + size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + bitbin_size = size_single_pipe * num_vpp_pipes; + size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT) * + total_bitbin_buffers + 512; + + return size; +} + +static u32 h264e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 16, + num_vpp_pipes, rc_type); +} + +static u32 h265e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 32, + num_vpp_pipes, rc_type); +} + +static u32 vp8e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 16, + num_vpp_pipes, rc_type); +} + +static u32 hfi_iris2_h264d_comv_size(u32 width, u32 height, + u32 yuv_buf_min_count) +{ + u32 frame_width_in_mbs = ((width + 15) >> 4); + u32 frame_height_in_mbs = ((height + 15) >> 4); + u32 col_mv_aligned_width = (frame_width_in_mbs << 6); + u32 col_zero_aligned_width = (frame_width_in_mbs << 2); + u32 col_zero_size = 0, size_colloc = 0, comv_size = 0; + + col_mv_aligned_width = ALIGN(col_mv_aligned_width, 16); + col_zero_aligned_width = ALIGN(col_zero_aligned_width, 16); + col_zero_size = + col_zero_aligned_width * ((frame_height_in_mbs + 1) >> 1); + col_zero_size = ALIGN(col_zero_size, 64); + col_zero_size <<= 1; + col_zero_size = ALIGN(col_zero_size, 512); + size_colloc = col_mv_aligned_width * ((frame_height_in_mbs + 1) >> 1); + size_colloc = ALIGN(size_colloc, 64); + size_colloc <<= 1; + size_colloc = ALIGN(size_colloc, 512); + size_colloc += (col_zero_size + SIZE_H264D_BUFTAB_T * 2); + comv_size = size_colloc * yuv_buf_min_count; + comv_size += 512; + + return comv_size; +} + +static u32 size_h264d_bse_cmd_buf(u32 height) +{ + u32 aligned_height = ALIGN(height, 32); + + return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), + H264D_MAX_SLICE) * SIZE_H264D_BSE_CMD_PER_BUF; +} + +static u32 size_h264d_vpp_cmd_buf(u32 height) +{ + u32 aligned_height = ALIGN(height, 32); + + return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), + H264D_MAX_SLICE) * SIZE_H264D_VPP_CMD_PER_BUF; +} + +static u32 hfi_iris2_h264d_non_comv_size(u32 width, u32 height, + u32 num_vpp_pipes) +{ + u32 size_bse, size_vpp, size; + + size_bse = size_h264d_bse_cmd_buf(height); + size_vpp = size_h264d_vpp_cmd_buf(height); + size = + ALIGN(size_bse, HFI_DMA_ALIGNMENT) + + ALIGN(size_vpp, HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H264D_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_SE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H264D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) * 2 + + ALIGN(SIZE_H264D_QP(width, height), HFI_DMA_ALIGNMENT); + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 size_h265d_bse_cmd_buf(u32 width, u32 height) +{ + u32 size; + + size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + NUM_HW_PIC_BUF; + size = min_t(u32, size, H265D_MAX_SLICE + 1); + size = 2 * size * SIZE_H265D_BSE_CMD_PER_BUF; + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 size_h265d_vpp_cmd_buf(u32 width, u32 height) +{ + u32 size; + + size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + NUM_HW_PIC_BUF; + size = min_t(u32, size, H265D_MAX_SLICE + 1); + size = ALIGN(size, 4); + size = 2 * size * SIZE_H265D_VPP_CMD_PER_BUF; + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 hfi_iris2_h265d_comv_size(u32 width, u32 height, + u32 yuv_buf_count_min) +{ + u32 size; + + size = ALIGN(((((width + 15) >> 4) * ((height + 15) >> 4)) << 8), 512); + size *= yuv_buf_count_min; + size += 512; + + return size; +} + +static u32 hfi_iris2_h265d_non_comv_size(u32 width, u32 height, + u32 num_vpp_pipes) +{ + u32 size_bse, size_vpp, size; + + size_bse = size_h265d_bse_cmd_buf(width, height); + size_vpp = size_h265d_vpp_cmd_buf(width, height); + size = + ALIGN(size_bse, HFI_DMA_ALIGNMENT) + + ALIGN(size_vpp, HFI_DMA_ALIGNMENT) + + ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, HFI_DMA_ALIGNMENT) + + ALIGN(2 * sizeof(u16) * + (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_h265d_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H265D_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_VSP_LEFT(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + * 4 + + ALIGN(SIZE_H265D_QP(width, height), HFI_DMA_ALIGNMENT); + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 hfi_iris2_vp8d_comv_size(u32 width, u32 height, + u32 yuv_min_buf_count) +{ + return (((width + 15) >> 4) * ((height + 15) >> 4) * 8 * 2); +} + +static u32 h264d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0; + + co_mv_size = hfi_iris2_h264d_comv_size(width, height, min_buf_count); + nonco_mv_size = hfi_iris2_h264d_non_comv_size(width, height, + num_vpp_pipes); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + return co_mv_size + nonco_mv_size + vpss_lb_size; +} + +static u32 h265d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0; + + co_mv_size = hfi_iris2_h265d_comv_size(width, height, min_buf_count); + nonco_mv_size = hfi_iris2_h265d_non_comv_size(width, height, + num_vpp_pipes); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + return co_mv_size + nonco_mv_size + vpss_lb_size + + HDR10_HIST_EXTRADATA_SIZE; +} + +static u32 vp8d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0, size; + + size = hfi_iris2_vp8d_comv_size(width, height, 0); + size += ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size; + + return size; +} + +static u32 vp9d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0; + u32 size; + + size = + ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP9D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP9D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP9D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size + HDR10_HIST_EXTRADATA_SIZE; + + return size; +} + +static u32 mpeg2d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0; + u32 size; + + size = + ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size; + + return size; +} + +static u32 +calculate_enc_scratch1_size(u32 width, u32 height, u32 lcu_size, u32 num_ref, + bool ten_bit, u32 num_vpp_pipes, bool is_h265) +{ + u32 line_buf_ctrl_size, line_buf_data_size, leftline_buf_ctrl_size; + u32 line_buf_sde_size, sps_pps_slice_hdr, topline_buf_ctrl_size_FE; + u32 leftline_buf_ctrl_size_FE, line_buf_recon_pix_size; + u32 leftline_buf_recon_pix_size, lambda_lut_size, override_buffer_size; + u32 col_mv_buf_size, vpp_reg_buffer_size, ir_buffer_size; + u32 vpss_line_buf, leftline_buf_meta_recony, h265e_colrcbuf_size; + u32 h265e_framerc_bufsize, h265e_lcubitcnt_bufsize; + u32 h265e_lcubitmap_bufsize, se_stats_bufsize; + u32 bse_reg_buffer_size, bse_slice_cmd_buffer_size, slice_info_bufsize; + u32 line_buf_ctrl_size_buffid2, slice_cmd_buffer_size; + u32 width_lcu_num, height_lcu_num, width_coded, height_coded; + u32 frame_num_lcu, linebuf_meta_recon_uv, topline_bufsize_fe_1stg_sao; + u32 size, bit_depth, num_lcu_mb; + u32 vpss_line_buffer_size_1; + + width_lcu_num = (width + lcu_size - 1) / lcu_size; + height_lcu_num = (height + lcu_size - 1) / lcu_size; + frame_num_lcu = width_lcu_num * height_lcu_num; + width_coded = width_lcu_num * lcu_size; + height_coded = height_lcu_num * lcu_size; + num_lcu_mb = (height_coded / lcu_size) * + ((width_coded + lcu_size * 8) / lcu_size); + slice_info_bufsize = 256 + (frame_num_lcu << 4); + slice_info_bufsize = ALIGN(slice_info_bufsize, HFI_DMA_ALIGNMENT); + line_buf_ctrl_size = ALIGN(width_coded, HFI_DMA_ALIGNMENT); + line_buf_ctrl_size_buffid2 = ALIGN(width_coded, HFI_DMA_ALIGNMENT); + + bit_depth = ten_bit ? 10 : 8; + line_buf_data_size = + (((((bit_depth * width_coded + 1024) + + (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 1) + + (((((bit_depth * width_coded + 1024) >> 1) + + (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 2)); + + leftline_buf_ctrl_size = is_h265 ? + ((height_coded + 32) / 32 * 4 * 16) : + ((height_coded + 15) / 16 * 5 * 16); + + if (num_vpp_pipes > 1) { + leftline_buf_ctrl_size += 512; + leftline_buf_ctrl_size = + ALIGN(leftline_buf_ctrl_size, 512) * num_vpp_pipes; + } + + leftline_buf_ctrl_size = + ALIGN(leftline_buf_ctrl_size, HFI_DMA_ALIGNMENT); + leftline_buf_recon_pix_size = (((ten_bit + 1) * 2 * + (height_coded) + HFI_DMA_ALIGNMENT) + + (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) & + (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1; + + topline_buf_ctrl_size_FE = is_h265 ? (64 * (width_coded >> 5)) : + (HFI_DMA_ALIGNMENT + 16 * (width_coded >> 4)); + topline_buf_ctrl_size_FE = + ALIGN(topline_buf_ctrl_size_FE, HFI_DMA_ALIGNMENT); + leftline_buf_ctrl_size_FE = + (((HFI_DMA_ALIGNMENT + 64 * (height_coded >> 4)) + + (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) & + (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1) * + num_vpp_pipes; + leftline_buf_meta_recony = (HFI_DMA_ALIGNMENT + 64 * + ((height_coded) / (8 * (ten_bit ? 4 : 8)))); + leftline_buf_meta_recony = + ALIGN(leftline_buf_meta_recony, HFI_DMA_ALIGNMENT); + leftline_buf_meta_recony = leftline_buf_meta_recony * num_vpp_pipes; + linebuf_meta_recon_uv = (HFI_DMA_ALIGNMENT + 64 * + ((height_coded) / (4 * (ten_bit ? 4 : 8)))); + linebuf_meta_recon_uv = ALIGN(linebuf_meta_recon_uv, HFI_DMA_ALIGNMENT); + linebuf_meta_recon_uv = linebuf_meta_recon_uv * num_vpp_pipes; + line_buf_recon_pix_size = ((ten_bit ? 3 : 2) * width_coded); + line_buf_recon_pix_size = + ALIGN(line_buf_recon_pix_size, HFI_DMA_ALIGNMENT); + slice_cmd_buffer_size = ALIGN(20480, HFI_DMA_ALIGNMENT); + sps_pps_slice_hdr = 2048 + 4096; + col_mv_buf_size = is_h265 ? (16 * ((frame_num_lcu << 2) + 32)) : + (3 * 16 * (width_lcu_num * height_lcu_num + 32)); + col_mv_buf_size = + ALIGN(col_mv_buf_size, HFI_DMA_ALIGNMENT) * (num_ref + 1); + h265e_colrcbuf_size = + (((width_lcu_num + 7) >> 3) * 16 * 2 * height_lcu_num); + if (num_vpp_pipes > 1) + h265e_colrcbuf_size = + ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) * + num_vpp_pipes; + + h265e_colrcbuf_size = ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) * + HFI_MAX_COL_FRAME; + h265e_framerc_bufsize = (is_h265) ? (256 + 16 * + (14 + (((height_coded >> 5) + 7) >> 3))) : + (256 + 16 * (14 + (((height_coded >> 4) + 7) >> 3))); + h265e_framerc_bufsize *= 6; /* multiply by max numtilescol */ + if (num_vpp_pipes > 1) + h265e_framerc_bufsize = + ALIGN(h265e_framerc_bufsize, HFI_DMA_ALIGNMENT) * + num_vpp_pipes; + + h265e_framerc_bufsize = ALIGN(h265e_framerc_bufsize, 512) * + HFI_MAX_COL_FRAME; + h265e_lcubitcnt_bufsize = 256 + 4 * frame_num_lcu; + h265e_lcubitcnt_bufsize = + ALIGN(h265e_lcubitcnt_bufsize, HFI_DMA_ALIGNMENT); + h265e_lcubitmap_bufsize = 256 + (frame_num_lcu >> 3); + h265e_lcubitmap_bufsize = + ALIGN(h265e_lcubitmap_bufsize, HFI_DMA_ALIGNMENT); + line_buf_sde_size = 256 + 16 * (width_coded >> 4); + line_buf_sde_size = ALIGN(line_buf_sde_size, HFI_DMA_ALIGNMENT); + if ((width_coded * height_coded) > (4096 * 2160)) + se_stats_bufsize = 0; + else if ((width_coded * height_coded) > (1920 * 1088)) + se_stats_bufsize = (40 * 4 * frame_num_lcu + 256 + 256); + else + se_stats_bufsize = (1024 * frame_num_lcu + 256 + 256); + + se_stats_bufsize = ALIGN(se_stats_bufsize, HFI_DMA_ALIGNMENT) * 2; + bse_slice_cmd_buffer_size = (((8192 << 2) + 7) & (~7)) * 6; + bse_reg_buffer_size = (((512 << 3) + 7) & (~7)) * 4; + vpp_reg_buffer_size = + (((HFI_VENUS_VPPSG_MAX_REGISTERS << 3) + 31) & (~31)) * 10; + lambda_lut_size = 256 * 11; + override_buffer_size = 16 * ((num_lcu_mb + 7) >> 3); + override_buffer_size = + ALIGN(override_buffer_size, HFI_DMA_ALIGNMENT) * 2; + ir_buffer_size = (((frame_num_lcu << 1) + 7) & (~7)) * 3; + vpss_line_buffer_size_1 = (((8192 >> 2) << 5) * num_vpp_pipes) + 64; + vpss_line_buf = + (((((max(width_coded, height_coded) + 3) >> 2) << 5) + 256) * + 16) + vpss_line_buffer_size_1; + topline_bufsize_fe_1stg_sao = 16 * (width_coded >> 5); + topline_bufsize_fe_1stg_sao = + ALIGN(topline_bufsize_fe_1stg_sao, HFI_DMA_ALIGNMENT); + + size = + line_buf_ctrl_size + line_buf_data_size + + line_buf_ctrl_size_buffid2 + leftline_buf_ctrl_size + + vpss_line_buf + col_mv_buf_size + topline_buf_ctrl_size_FE + + leftline_buf_ctrl_size_FE + line_buf_recon_pix_size + + leftline_buf_recon_pix_size + + leftline_buf_meta_recony + linebuf_meta_recon_uv + + h265e_colrcbuf_size + h265e_framerc_bufsize + + h265e_lcubitcnt_bufsize + h265e_lcubitmap_bufsize + + line_buf_sde_size + + topline_bufsize_fe_1stg_sao + override_buffer_size + + bse_reg_buffer_size + vpp_reg_buffer_size + sps_pps_slice_hdr + + slice_cmd_buffer_size + bse_slice_cmd_buffer_size + + ir_buffer_size + slice_info_bufsize + lambda_lut_size + + se_stats_bufsize + 1024; + + return size; +} + +static u32 h264e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit, + num_vpp_pipes, false); +} + +static u32 h265e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 32, num_ref, ten_bit, + num_vpp_pipes, true); +} + +static u32 vp8e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit, + 1, false); +} + +static u32 ubwc_metadata_plane_stride(u32 width, u32 metadata_stride_multi, + u32 tile_width_pels) +{ + return ALIGN(((width + (tile_width_pels - 1)) / tile_width_pels), + metadata_stride_multi); +} + +static u32 ubwc_metadata_plane_bufheight(u32 height, u32 metadata_height_multi, + u32 tile_height_pels) +{ + return ALIGN(((height + (tile_height_pels - 1)) / tile_height_pels), + metadata_height_multi); +} + +static u32 ubwc_metadata_plane_buffer_size(u32 metadata_stride, + u32 metadata_buf_height) +{ + return ALIGN(metadata_stride * metadata_buf_height, SZ_4K); +} + +static u32 enc_scratch2_size(u32 width, u32 height, u32 num_ref, bool ten_bit) +{ + u32 aligned_width, aligned_height, chroma_height, ref_buf_height; + u32 luma_size, chroma_size; + u32 metadata_stride, meta_buf_height, meta_size_y, meta_size_c; + u32 ref_luma_stride_bytes, ref_chroma_height_bytes; + u32 ref_buf_size, ref_stride; + u32 size; + + if (!ten_bit) { + aligned_height = ALIGN(height, HFI_VENUS_HEIGHT_ALIGNMENT); + chroma_height = height >> 1; + chroma_height = ALIGN(chroma_height, + HFI_VENUS_HEIGHT_ALIGNMENT); + aligned_width = ALIGN(width, HFI_VENUS_WIDTH_ALIGNMENT); + metadata_stride = + ubwc_metadata_plane_stride(width, 64, + NV12_UBWC_Y_TILE_WIDTH); + meta_buf_height = + ubwc_metadata_plane_bufheight(height, 16, + NV12_UBWC_Y_TILE_HEIGHT); + meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + size = (aligned_height + chroma_height) * aligned_width + + meta_size_y + meta_size_c; + size = (size * (num_ref + 3)) + 4096; + } else { + ref_buf_height = (height + (HFI_VENUS_HEIGHT_ALIGNMENT - 1)) + & (~(HFI_VENUS_HEIGHT_ALIGNMENT - 1)); + ref_luma_stride_bytes = + ((width + SYSTEM_LAL_TILE10 - 1) / SYSTEM_LAL_TILE10) * + SYSTEM_LAL_TILE10; + ref_stride = 4 * (ref_luma_stride_bytes / 3); + ref_stride = (ref_stride + (128 - 1)) & (~(128 - 1)); + luma_size = ref_buf_height * ref_stride; + ref_chroma_height_bytes = (((height + 1) >> 1) + + (32 - 1)) & (~(32 - 1)); + chroma_size = ref_stride * ref_chroma_height_bytes; + luma_size = (luma_size + (SZ_4K - 1)) & (~(SZ_4K - 1)); + chroma_size = (chroma_size + (SZ_4K - 1)) & (~(SZ_4K - 1)); + ref_buf_size = luma_size + chroma_size; + metadata_stride = + ubwc_metadata_plane_stride(width, + METADATA_STRIDE_MULTIPLE, + TP10_UBWC_Y_TILE_WIDTH); + meta_buf_height = + ubwc_metadata_plane_bufheight(height, + METADATA_HEIGHT_MULTIPLE, + TP10_UBWC_Y_TILE_HEIGHT); + meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + size = ref_buf_size + meta_size_y + meta_size_c; + size = (size * (num_ref + 3)) + 4096; + } + + return size; +} + +static u32 enc_persist_size(void) +{ + return HFI_IRIS2_ENC_PERSIST_SIZE; +} + +static u32 h264d_persist1_size(void) +{ + return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264), + HFI_DMA_ALIGNMENT); +} + +static u32 h265d_persist1_size(void) +{ + return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_TILE + * sizeof(u32)), HFI_DMA_ALIGNMENT); +} + +static u32 vp8d_persist1_size(void) +{ + return ALIGN(VP8_NUM_PROBABILITY_TABLE_BUF * VP8_PROB_TABLE_SIZE, + HFI_DMA_ALIGNMENT); +} + +static u32 vp9d_persist1_size(void) +{ + return + ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, + HFI_DMA_ALIGNMENT) + + ALIGN(HFI_IRIS2_VP9D_COMV_SIZE, HFI_DMA_ALIGNMENT) + + ALIGN(MAX_SUPERFRAME_HEADER_LEN, HFI_DMA_ALIGNMENT) + + ALIGN(VP9_UDC_HEADER_BUF_SIZE, HFI_DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, + HFI_DMA_ALIGNMENT); +} + +static u32 mpeg2d_persist1_size(void) +{ + return QMATRIX_SIZE + MP2D_QPDUMP_SIZE; +} + +struct dec_bufsize_ops { + u32 (*scratch)(u32 width, u32 height, bool is_interlaced); + u32 (*scratch1)(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes); + u32 (*persist1)(void); +}; + +struct enc_bufsize_ops { + u32 (*scratch)(u32 width, u32 height, u32 work_mode, u32 num_vpp_pipes, + u32 rc_type); + u32 (*scratch1)(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes); + u32 (*scratch2)(u32 width, u32 height, u32 num_ref, bool ten_bit); + u32 (*persist)(void); +}; + +static struct dec_bufsize_ops dec_h264_ops = { + .scratch = h264d_scratch_size, + .scratch1 = h264d_scratch1_size, + .persist1 = h264d_persist1_size, +}; + +static struct dec_bufsize_ops dec_h265_ops = { + .scratch = h265d_scratch_size, + .scratch1 = h265d_scratch1_size, + .persist1 = h265d_persist1_size, +}; + +static struct dec_bufsize_ops dec_vp8_ops = { + .scratch = vpxd_scratch_size, + .scratch1 = vp8d_scratch1_size, + .persist1 = vp8d_persist1_size, +}; + +static struct dec_bufsize_ops dec_vp9_ops = { + .scratch = vpxd_scratch_size, + .scratch1 = vp9d_scratch1_size, + .persist1 = vp9d_persist1_size, +}; + +static struct dec_bufsize_ops dec_mpeg2_ops = { + .scratch = mpeg2d_scratch_size, + .scratch1 = mpeg2d_scratch1_size, + .persist1 = mpeg2d_persist1_size, +}; + +static struct enc_bufsize_ops enc_h264_ops = { + .scratch = h264e_scratch_size, + .scratch1 = h264e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static struct enc_bufsize_ops enc_h265_ops = { + .scratch = h265e_scratch_size, + .scratch1 = h265e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static struct enc_bufsize_ops enc_vp8_ops = { + .scratch = vp8e_scratch_size, + .scratch1 = vp8e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static u32 +calculate_dec_input_frame_size(u32 width, u32 height, u32 codec, + u32 max_mbs_per_frame, u32 buffer_size_limit) +{ + u32 frame_size, num_mbs; + u32 div_factor = 1; + u32 base_res_mbs = NUM_MBS_4K; + + /* + * Decoder input size calculation: + * If clip is 8k buffer size is calculated for 8k : 8k mbs/4 + * For 8k cases we expect width/height to be set always. + * In all other cases size is calculated for 4k: + * 4k mbs for VP8/VP9 and 4k/2 for remaining codecs + */ + num_mbs = (ALIGN(height, 16) * ALIGN(width, 16)) / 256; + if (num_mbs > NUM_MBS_4K) { + div_factor = 4; + base_res_mbs = max_mbs_per_frame; + } else { + base_res_mbs = NUM_MBS_4K; + if (codec == V4L2_PIX_FMT_VP9) + div_factor = 1; + else + div_factor = 2; + } + + frame_size = base_res_mbs * MB_SIZE_IN_PIXEL * 3 / 2 / div_factor; + + /* multiply by 10/8 (1.25) to get size for 10 bit case */ + if (codec == V4L2_PIX_FMT_VP9 || codec == V4L2_PIX_FMT_HEVC) + frame_size = frame_size + (frame_size >> 2); + + if (buffer_size_limit && buffer_size_limit < frame_size) + frame_size = buffer_size_limit; + + return ALIGN(frame_size, SZ_4K); +} + +static int output_buffer_count(u32 session_type, u32 codec) +{ + u32 output_min_count; + + if (session_type == VIDC_SESSION_TYPE_DEC) { + switch (codec) { + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_VP8: + output_min_count = 6; + break; + case V4L2_PIX_FMT_VP9: + output_min_count = 9; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + default: + output_min_count = 8; + break; + } + } else { + output_min_count = MIN_ENC_OUTPUT_BUFFERS; + } + + return output_min_count; +} + +static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, + struct hfi_buffer_requirements *bufreq) +{ + enum hfi_version version = params->version; + u32 codec = params->codec; + u32 width = params->width, height = params->height, out_min_count; + struct dec_bufsize_ops *dec_ops; + bool is_secondary_output = params->dec.is_secondary_output; + bool is_interlaced = params->dec.is_interlaced; + u32 max_mbs_per_frame = params->dec.max_mbs_per_frame; + u32 buffer_size_limit = params->dec.buffer_size_limit; + u32 num_vpp_pipes = params->num_vpp_pipes; + + switch (codec) { + case V4L2_PIX_FMT_H264: + dec_ops = &dec_h264_ops; + break; + case V4L2_PIX_FMT_HEVC: + dec_ops = &dec_h265_ops; + break; + case V4L2_PIX_FMT_VP8: + dec_ops = &dec_vp8_ops; + break; + case V4L2_PIX_FMT_VP9: + dec_ops = &dec_vp9_ops; + break; + case V4L2_PIX_FMT_MPEG2: + dec_ops = &dec_mpeg2_ops; + break; + default: + return -EINVAL; + } + + out_min_count = output_buffer_count(VIDC_SESSION_TYPE_DEC, codec); + + bufreq->type = buftype; + bufreq->region_size = 0; + bufreq->count_min = 1; + bufreq->count_actual = 1; + bufreq->hold_count = 1; + bufreq->contiguous = 1; + bufreq->alignment = 256; + + if (buftype == HFI_BUFFER_INPUT) { + bufreq->count_min = MIN_INPUT_BUFFERS; + bufreq->size = + calculate_dec_input_frame_size(width, height, codec, + max_mbs_per_frame, + buffer_size_limit); + } else if (buftype == HFI_BUFFER_OUTPUT || + buftype == HFI_BUFFER_OUTPUT2) { + bufreq->count_min = out_min_count; + bufreq->size = + venus_helper_get_framesz_raw(params->hfi_color_fmt, + width, height); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) { + bufreq->size = dec_ops->scratch(width, height, is_interlaced); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) { + bufreq->size = dec_ops->scratch1(width, height, out_min_count, + is_secondary_output, + num_vpp_pipes); + } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST_1) { + bufreq->size = dec_ops->persist1(); + } else { + return -EINVAL; + } + + return 0; +} + +static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, + struct hfi_buffer_requirements *bufreq) +{ + enum hfi_version version = params->version; + struct enc_bufsize_ops *enc_ops; + u32 width = params->width; + u32 height = params->height; + bool is_tenbit = params->enc.is_tenbit; + u32 num_bframes = params->enc.num_b_frames; + u32 codec = params->codec; + u32 work_mode = params->enc.work_mode; + u32 rc_type = params->enc.rc_type; + u32 num_vpp_pipes = params->num_vpp_pipes; + u32 num_ref; + + switch (codec) { + case V4L2_PIX_FMT_H264: + enc_ops = &enc_h264_ops; + break; + case V4L2_PIX_FMT_HEVC: + enc_ops = &enc_h265_ops; + break; + case V4L2_PIX_FMT_VP8: + enc_ops = &enc_vp8_ops; + break; + default: + return -EINVAL; + } + + num_ref = num_bframes > 0 ? num_bframes + 1 : 1; + + bufreq->type = buftype; + bufreq->region_size = 0; + bufreq->count_min = 1; + bufreq->count_actual = 1; + bufreq->hold_count = 1; + bufreq->contiguous = 1; + bufreq->alignment = 256; + + if (buftype == HFI_BUFFER_INPUT) { + bufreq->count_min = MIN_INPUT_BUFFERS; + bufreq->size = + venus_helper_get_framesz_raw(params->hfi_color_fmt, + width, height); + } else if (buftype == HFI_BUFFER_OUTPUT || + buftype == HFI_BUFFER_OUTPUT2) { + bufreq->count_min = + output_buffer_count(VIDC_SESSION_TYPE_ENC, codec); + bufreq->size = calculate_enc_output_frame_size(width, height, + rc_type); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) { + bufreq->size = enc_ops->scratch(width, height, work_mode, + num_vpp_pipes, rc_type); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) { + bufreq->size = enc_ops->scratch1(width, height, num_ref, + is_tenbit, num_vpp_pipes); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_2(version)) { + bufreq->size = enc_ops->scratch2(width, height, num_ref, + is_tenbit); + } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST) { + bufreq->size = enc_ops->persist(); + } else { + return -EINVAL; + } + + return 0; +} + +int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq) +{ + if (session_type == VIDC_SESSION_TYPE_DEC) + return bufreq_dec(params, buftype, bufreq); + else + return bufreq_enc(params, buftype, bufreq); +} diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c new file mode 100644 index 000000000000..8f47804e973f --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +const struct hfi_platform *hfi_platform_get(enum hfi_version version) +{ + switch (version) { + case HFI_VERSION_4XX: + return &hfi_plat_v4; + case HFI_VERSION_6XX: + return &hfi_plat_v6; + default: + break; + } + + return NULL; +} + +unsigned long +hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, u32 session_type) +{ + const struct hfi_platform *plat; + unsigned long freq = 0; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->codec_vpp_freq) + freq = plat->codec_vpp_freq(session_type, codec); + + return freq; +} + +unsigned long +hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session_type) +{ + const struct hfi_platform *plat; + unsigned long freq = 0; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->codec_vpp_freq) + freq = plat->codec_vsp_freq(session_type, codec); + + return freq; +} + +u8 hfi_platform_num_vpp_pipes(enum hfi_version version) +{ + const struct hfi_platform *plat; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->num_vpp_pipes) + return plat->num_vpp_pipes(); + + return 0; +} diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h new file mode 100644 index 000000000000..3819bb2b36bd --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __HFI_PLATFORM_H__ +#define __HFI_PLATFORM_H__ + +#include <linux/types.h> +#include <linux/videodev2.h> + +#include "hfi.h" +#include "hfi_plat_bufs.h" +#include "hfi_helper.h" + +#define MAX_PLANES 4 +#define MAX_FMT_ENTRIES 32 +#define MAX_CAP_ENTRIES 32 +#define MAX_ALLOC_MODE_ENTRIES 16 +#define MAX_CODEC_NUM 32 +#define MAX_SESSIONS 16 + +struct raw_formats { + u32 buftype; + u32 fmt; +}; + +struct hfi_plat_caps { + u32 codec; + u32 domain; + bool cap_bufs_mode_dynamic; + unsigned int num_caps; + struct hfi_capability caps[MAX_CAP_ENTRIES]; + unsigned int num_pl; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + unsigned int num_fmts; + struct raw_formats fmts[MAX_FMT_ENTRIES]; + bool valid; /* used only for Venus v1xx */ +}; + +struct hfi_platform_codec_freq_data { + u32 pixfmt; + u32 session_type; + unsigned long vpp_freq; + unsigned long vsp_freq; +}; + +struct hfi_platform { + unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec); + unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec); + void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count); + const struct hfi_plat_caps *(*capabilities)(unsigned int *entries); + u8 (*num_vpp_pipes)(void); + int (*bufreq)(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq); +}; + +extern const struct hfi_platform hfi_plat_v4; +extern const struct hfi_platform hfi_plat_v6; + +const struct hfi_platform *hfi_platform_get(enum hfi_version version); +unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, + u32 session_type); +unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, + u32 session_type); +u8 hfi_platform_num_vpp_pipes(enum hfi_version version); +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c new file mode 100644 index 000000000000..3848bb6d7408 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +static const struct hfi_plat_caps caps[] = { +{ + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP9, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VP9_PROFILE_P0, 200}, + .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_MPEG2, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1}, + .num_caps = 10, + .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14}, + .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .num_caps = 21, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .num_caps = 24, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, + .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .num_caps = 23, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +} }; + +static const struct hfi_plat_caps *get_capabilities(unsigned int *entries) +{ + *entries = ARRAY_SIZE(caps); + return caps; +} + +static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count) +{ + *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8; + *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 | + HFI_VIDEO_CODEC_MPEG2; + *count = 8; +} + +static const struct hfi_platform_codec_freq_data codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, +}; + +static const struct hfi_platform_codec_freq_data * +get_codec_freq_data(u32 session_type, u32 pixfmt) +{ + const struct hfi_platform_codec_freq_data *data = codec_freq_data; + unsigned int i, data_size = ARRAY_SIZE(codec_freq_data); + const struct hfi_platform_codec_freq_data *found = NULL; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) { + found = &data[i]; + break; + } + } + + return found; +} + +static unsigned long codec_vpp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vpp_freq; + + return 0; +} + +static unsigned long codec_vsp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vsp_freq; + + return 0; +} + +const struct hfi_platform hfi_plat_v4 = { + .codec_vpp_freq = codec_vpp_freq, + .codec_vsp_freq = codec_vsp_freq, + .codecs = get_codecs, + .capabilities = get_capabilities, +}; diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c new file mode 100644 index 000000000000..2278be13cb90 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +static const struct hfi_plat_caps caps[] = { +{ + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 5760, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 5760, 1}, + /* ((5760 * 2880) / 256) */ + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 36, 64800, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 200000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 36, 1958400, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, + .num_caps = 9, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP9, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VP9_PROFILE_P0, 200}, + .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_MPEG2, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1}, + .num_caps = 10, + .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14}, + .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .num_caps = 21, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .num_caps = 24, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, + .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .num_caps = 23, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +} }; + +static const struct hfi_plat_caps *get_capabilities(unsigned int *entries) +{ + *entries = ARRAY_SIZE(caps); + return caps; +} + +static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count) +{ + *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8; + *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 | + HFI_VIDEO_CODEC_MPEG2; + *count = 8; +} + +static const struct hfi_platform_codec_freq_data codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60 }, +}; + +static const struct hfi_platform_codec_freq_data * +get_codec_freq_data(u32 session_type, u32 pixfmt) +{ + const struct hfi_platform_codec_freq_data *data = codec_freq_data; + unsigned int i, data_size = ARRAY_SIZE(codec_freq_data); + const struct hfi_platform_codec_freq_data *found = NULL; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) { + found = &data[i]; + break; + } + } + + return found; +} + +static unsigned long codec_vpp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vpp_freq; + + return 0; +} + +static unsigned long codec_vsp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vsp_freq; + + return 0; +} + +static u8 num_vpp_pipes(void) +{ + return 4; +} + +const struct hfi_platform hfi_plat_v6 = { + .codec_vpp_freq = codec_vpp_freq, + .codec_vsp_freq = codec_vsp_freq, + .codecs = get_codecs, + .capabilities = get_capabilities, + .num_vpp_pipes = num_vpp_pipes, + .bufreq = hfi_plat_bufreq_v6, +}; diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 4be4a75ddcb6..50e03f8fc278 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -372,7 +372,7 @@ static void venus_soft_int(struct venus_hfi_device *hdev) } static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, - void *pkt) + void *pkt, bool sync) { struct device *dev = hdev->core->dev; struct hfi_pkt_hdr *cmd_packet; @@ -394,18 +394,29 @@ static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, return ret; } + if (sync) { + /* + * Inform video hardware to raise interrupt for synchronous + * commands + */ + queue = &hdev->queues[IFACEQ_MSG_IDX]; + queue->qhdr->rx_req = 1; + /* ensure rx_req is updated in memory */ + wmb(); + } + if (rx_req) venus_soft_int(hdev); return 0; } -static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt) +static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt, bool sync) { int ret; mutex_lock(&hdev->lock); - ret = venus_iface_cmdq_write_nolock(hdev, pkt); + ret = venus_iface_cmdq_write_nolock(hdev, pkt, sync); mutex_unlock(&hdev->lock); return ret; @@ -428,7 +439,7 @@ static int venus_hfi_core_set_resource(struct venus_core *core, u32 id, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -778,7 +789,7 @@ static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug) pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -795,7 +806,7 @@ static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode) pkt_sys_coverage_config(pkt, mode); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -816,7 +827,7 @@ static int venus_sys_set_idle_message(struct venus_hfi_device *hdev, pkt_sys_idle_indicator(pkt, enable); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -834,7 +845,7 @@ static int venus_sys_set_power_control(struct venus_hfi_device *hdev, pkt_sys_power_control(pkt, enable); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -885,14 +896,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) return ret; } -static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type) +static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type, bool sync) { struct venus_hfi_device *hdev = to_hfi_priv(inst->core); struct hfi_session_pkt pkt; pkt_session_cmd(&pkt, pkt_type, inst); - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, sync); } static void venus_flush_debug_queue(struct venus_hfi_device *hdev) @@ -922,7 +933,7 @@ static int venus_prepare_power_collapse(struct venus_hfi_device *hdev, pkt_sys_pc_prep(&pkt); - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); if (ret) return ret; @@ -1064,13 +1075,13 @@ static int venus_core_init(struct venus_core *core) venus_set_state(hdev, VENUS_STATE_INIT); - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); if (ret) return ret; pkt_sys_image_version(&version_pkt); - ret = venus_iface_cmdq_write(hdev, &version_pkt); + ret = venus_iface_cmdq_write(hdev, &version_pkt, false); if (ret) dev_warn(dev, "failed to send image version pkt to fw\n"); @@ -1099,7 +1110,7 @@ static int venus_core_ping(struct venus_core *core, u32 cookie) pkt_sys_ping(&pkt, cookie); - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) @@ -1112,7 +1123,7 @@ static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_session_init(struct venus_inst *inst, u32 session_type, @@ -1130,7 +1141,7 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, if (ret) goto err; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, true); if (ret) goto err; @@ -1151,7 +1162,7 @@ static int venus_session_end(struct venus_inst *inst) dev_warn(dev, "fw coverage msg ON failed\n"); } - return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END); + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END, true); } static int venus_session_abort(struct venus_inst *inst) @@ -1160,7 +1171,7 @@ static int venus_session_abort(struct venus_inst *inst) venus_flush_debug_queue(hdev); - return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT); + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT, true); } static int venus_session_flush(struct venus_inst *inst, u32 flush_mode) @@ -1173,22 +1184,22 @@ static int venus_session_flush(struct venus_inst *inst, u32 flush_mode) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, true); } static int venus_session_start(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_START); + return venus_session_cmd(inst, HFI_CMD_SESSION_START, true); } static int venus_session_stop(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_STOP); + return venus_session_cmd(inst, HFI_CMD_SESSION_STOP, true); } static int venus_session_continue(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE); + return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE, false); } static int venus_session_etb(struct venus_inst *inst, @@ -1205,7 +1216,7 @@ static int venus_session_etb(struct venus_inst *inst, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); } else if (session_type == VIDC_SESSION_TYPE_ENC) { struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt; @@ -1213,7 +1224,7 @@ static int venus_session_etb(struct venus_inst *inst, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); } else { ret = -EINVAL; } @@ -1232,7 +1243,7 @@ static int venus_session_ftb(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_session_set_buffers(struct venus_inst *inst, @@ -1252,7 +1263,7 @@ static int venus_session_set_buffers(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_unset_buffers(struct venus_inst *inst, @@ -1272,17 +1283,17 @@ static int venus_session_unset_buffers(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, true); } static int venus_session_load_res(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES); + return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES, true); } static int venus_session_release_res(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES); + return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES, true); } static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr, @@ -1299,7 +1310,7 @@ static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -1320,7 +1331,7 @@ static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_set_property(struct venus_inst *inst, u32 ptype, @@ -1339,7 +1350,7 @@ static int venus_session_set_property(struct venus_inst *inst, u32 ptype, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_get_property(struct venus_inst *inst, u32 ptype) @@ -1352,7 +1363,7 @@ static int venus_session_get_property(struct venus_inst *inst, u32 ptype) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, true); } static int venus_resume(struct venus_core *core) @@ -1591,9 +1602,6 @@ int venus_hfi_create(struct venus_core *core) hdev->suspended = true; core->priv = hdev; core->ops = &venus_hfi_ops; - core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY | - ENC_DEINTERLACE_CAPABILITY | - DEC_MULTI_STREAM_CAPABILITY; ret = venus_interface_queues_init(hdev); if (ret) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index a3850261d697..43c4e3d9e281 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -18,6 +18,7 @@ #include "hfi_parser.h" #include "hfi_venus_io.h" #include "pm_helpers.h" +#include "hfi_platform.h" static bool legacy_binding; @@ -510,7 +511,7 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) if (inst_pos->state != INST_START) continue; - vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; + vpp_freq = inst_pos->clk_data.vpp_freq; coreid = inst_pos->clk_data.core_id; mbs_per_sec = load_per_instance(inst_pos); @@ -559,7 +560,7 @@ static int decide_core(struct venus_inst *inst) return 0; inst_load = load_per_instance(inst); - inst_load *= inst->clk_data.codec_freq_data->vpp_freq; + inst_load *= inst->clk_data.vpp_freq; max_freq = core->res->freq_tbl[0].freq; min_loaded_core(inst, &min_coreid, &min_load); @@ -773,13 +774,6 @@ static int vcodec_domains_get(struct device *dev) core->pmdomains[i] = pd; } - core->pd_dl_venus = device_link_add(dev, core->pmdomains[0], - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS | - DL_FLAG_RPM_ACTIVE); - if (!core->pd_dl_venus) - return -ENODEV; - skip_pmdomains: if (!core->has_opp_table) return 0; @@ -806,14 +800,12 @@ skip_pmdomains: opp_dl_add_err: dev_pm_opp_detach_genpd(core->opp_table); opp_attach_err: - if (core->pd_dl_venus) { - device_link_del(core->pd_dl_venus); - for (i = 0; i < res->vcodec_pmdomains_num; i++) { - if (IS_ERR_OR_NULL(core->pmdomains[i])) - continue; - dev_pm_domain_detach(core->pmdomains[i], true); - } + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); } + return ret; } @@ -826,9 +818,6 @@ static void vcodec_domains_put(struct device *dev) if (!res->vcodec_pmdomains_num) goto skip_pmdomains; - if (core->pd_dl_venus) - device_link_del(core->pd_dl_venus); - for (i = 0; i < res->vcodec_pmdomains_num; i++) { if (IS_ERR_OR_NULL(core->pmdomains[i])) continue; @@ -915,16 +904,30 @@ static void core_put_v4(struct device *dev) static int core_power_v4(struct device *dev, int on) { struct venus_core *core = dev_get_drvdata(dev); + struct device *pmctrl = core->pmdomains[0]; int ret = 0; if (on == POWER_ON) { + if (pmctrl) { + ret = pm_runtime_get_sync(pmctrl); + if (ret < 0) { + pm_runtime_put_noidle(pmctrl); + return ret; + } + } + ret = core_clks_enable(core); + if (ret < 0 && pmctrl) + pm_runtime_put_sync(pmctrl); } else { /* Drop the performance state vote */ if (core->opp_pmdomain) dev_pm_opp_set_rate(dev, 0); core_clks_disable(core); + + if (pmctrl) + pm_runtime_put_sync(pmctrl); } return ret; @@ -939,10 +942,13 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, mbs_per_sec = load_per_instance(inst); - vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + if (inst->state != INST_START) + return 0; + + vpp_freq = mbs_per_sec * inst->clk_data.vpp_freq; /* 21 / 20 is overhead factor */ vpp_freq += vpp_freq / 20; - vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + vsp_freq = mbs_per_sec * inst->clk_data.vsp_freq; /* 10 / 7 is overhead factor */ if (inst->session_type == VIDC_SESSION_TYPE_ENC) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8488411204c3..e4dc97f00fc3 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -13,7 +13,7 @@ #include <media/v4l2-event.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-mem2mem.h> -#include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dma-contig.h> #include "hfi_venus_io.h" #include "hfi_parser.h" @@ -519,8 +519,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) ret = hfi_session_process_buf(inst, &fdata); - if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) + if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) { inst->codec_state = VENUS_DEC_STATE_DRAIN; + inst->drain_active = true; + } } unlock: @@ -637,6 +639,7 @@ static int vdec_output_conf(struct venus_inst *inst) { struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + struct hfi_buffer_requirements bufreq; u32 width = inst->out_width; u32 height = inst->out_height; u32 out_fmt, out2_fmt; @@ -712,6 +715,23 @@ static int vdec_output_conf(struct venus_inst *inst) } if (IS_V3(core) || IS_V4(core)) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output_buf_size) + return -EINVAL; + + if (inst->dpb_fmt) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT2, + &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output2_buf_size) + return -EINVAL; + } + if (inst->output2_buf_size) { ret = venus_helper_set_bufsize(inst, inst->output2_buf_size, @@ -740,8 +760,8 @@ static int vdec_session_init(struct venus_inst *inst) { int ret; - ret = hfi_session_init(inst, inst->fmt_out->pixfmt); - if (ret == -EINVAL) + ret = venus_helper_session_init(inst); + if (ret == -EALREADY) return 0; else if (ret) return ret; @@ -751,10 +771,6 @@ static int vdec_session_init(struct venus_inst *inst) if (ret) goto deinit; - ret = venus_helper_init_codec_freq_data(inst); - if (ret) - goto deinit; - return 0; deinit: hfi_session_deinit(inst); @@ -917,10 +933,6 @@ static int vdec_start_capture(struct venus_inst *inst) return 0; reconfigure: - ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); - if (ret) - return ret; - ret = vdec_output_conf(inst); if (ret) return ret; @@ -948,15 +960,21 @@ reconfigure: venus_pm_load_scale(inst); + inst->next_buf_last = false; + ret = hfi_session_continue(inst); if (ret) goto free_dpb_bufs; inst->codec_state = VENUS_DEC_STATE_DECODING; + if (inst->drain_active) + inst->codec_state = VENUS_DEC_STATE_DRAIN; + inst->streamon_cap = 1; inst->sequence_cap = 0; inst->reconfig = false; + inst->drain_active = false; return 0; @@ -972,7 +990,10 @@ static int vdec_start_output(struct venus_inst *inst) if (inst->codec_state == VENUS_DEC_STATE_SEEK) { ret = venus_helper_process_initial_out_bufs(inst); - inst->codec_state = VENUS_DEC_STATE_DECODING; + if (inst->next_buf_last) + inst->codec_state = VENUS_DEC_STATE_DRC; + else + inst->codec_state = VENUS_DEC_STATE_DECODING; goto done; } @@ -988,6 +1009,7 @@ static int vdec_start_output(struct venus_inst *inst) venus_helper_init_instance(inst); inst->sequence_out = 0; inst->reconfig = false; + inst->next_buf_last = false; ret = vdec_set_properties(inst); if (ret) @@ -1077,13 +1099,14 @@ static int vdec_stop_capture(struct venus_inst *inst) ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); fallthrough; case VENUS_DEC_STATE_DRAIN: - vdec_cancel_dst_buffers(inst); inst->codec_state = VENUS_DEC_STATE_STOPPED; + inst->drain_active = false; + fallthrough; + case VENUS_DEC_STATE_SEEK: + vdec_cancel_dst_buffers(inst); break; case VENUS_DEC_STATE_DRC: - WARN_ON(1); - fallthrough; - case VENUS_DEC_STATE_DRC_FLUSH_DONE: + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; venus_helper_free_dpb_bufs(inst); break; @@ -1102,6 +1125,7 @@ static int vdec_stop_output(struct venus_inst *inst) case VENUS_DEC_STATE_DECODING: case VENUS_DEC_STATE_DRAIN: case VENUS_DEC_STATE_STOPPED: + case VENUS_DEC_STATE_DRC: ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); inst->codec_state = VENUS_DEC_STATE_SEEK; break; @@ -1207,10 +1231,28 @@ static void vdec_buf_cleanup(struct vb2_buffer *vb) static void vdec_vb2_buf_queue(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS }; vdec_pm_get_put(inst); + mutex_lock(&inst->lock); + + if (inst->next_buf_last && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + inst->codec_state == VENUS_DEC_STATE_DRC) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vbuf->sequence = inst->sequence_cap++; + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, 0); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + v4l2_event_queue_fh(&inst->fh, &eos); + inst->next_buf_last = false; + mutex_unlock(&inst->lock); + return; + } + venus_helper_vb2_buf_queue(vb); + mutex_unlock(&inst->lock); } static const struct vb2_ops vdec_vb2_ops = { @@ -1253,20 +1295,15 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; - if (inst->last_buf == vb) { - inst->last_buf = NULL; - vbuf->flags |= V4L2_BUF_FLAG_LAST; - vb2_set_plane_payload(vb, 0, 0); - vb->timestamp = 0; - } - if (vbuf->flags & V4L2_BUF_FLAG_LAST) { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; v4l2_event_queue_fh(&inst->fh, &ev); - if (inst->codec_state == VENUS_DEC_STATE_DRAIN) + if (inst->codec_state == VENUS_DEC_STATE_DRAIN) { + inst->drain_active = false; inst->codec_state = VENUS_DEC_STATE_STOPPED; + } } if (!bytesused) @@ -1334,22 +1371,22 @@ static void vdec_event_change(struct venus_inst *inst, if (inst->bit_depth != ev_data->bit_depth) inst->bit_depth = ev_data->bit_depth; + if (inst->pic_struct != ev_data->pic_struct) + inst->pic_struct = ev_data->pic_struct; + dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); - if (sufficient) { - hfi_session_continue(inst); - } else { - switch (inst->codec_state) { - case VENUS_DEC_STATE_INIT: - inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; - break; - case VENUS_DEC_STATE_DECODING: - inst->codec_state = VENUS_DEC_STATE_DRC; - break; - default: - break; - } + switch (inst->codec_state) { + case VENUS_DEC_STATE_INIT: + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + break; + case VENUS_DEC_STATE_DECODING: + case VENUS_DEC_STATE_DRAIN: + inst->codec_state = VENUS_DEC_STATE_DRC; + break; + default: + break; } /* @@ -1358,19 +1395,17 @@ static void vdec_event_change(struct venus_inst *inst, * itself doesn't mark the last decoder output buffer with HFI EOS flag. */ - if (!sufficient && inst->codec_state == VENUS_DEC_STATE_DRC) { - struct vb2_v4l2_buffer *last; + if (inst->codec_state == VENUS_DEC_STATE_DRC) { int ret; - last = v4l2_m2m_last_dst_buf(inst->m2m_ctx); - if (last) - inst->last_buf = &last->vb2_buf; + inst->next_buf_last = true; ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false); if (ret) dev_dbg(dev, VDBGH "flush output error %d\n", ret); } + inst->next_buf_last = true; inst->reconfig = true; v4l2_event_queue_fh(&inst->fh, &ev); wake_up(&inst->reconf_wait); @@ -1413,8 +1448,7 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event, static void vdec_flush_done(struct venus_inst *inst) { - if (inst->codec_state == VENUS_DEC_STATE_DRC) - inst->codec_state = VENUS_DEC_STATE_DRC_FLUSH_DONE; + dev_dbg(inst->core->dev_dec, VDBGH "flush done\n"); } static const struct hfi_inst_ops vdec_hfi_ops = { @@ -1461,7 +1495,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &vdec_vb2_ops; - src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; @@ -1475,7 +1509,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &vdec_vb2_ops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; @@ -1508,6 +1542,7 @@ static int vdec_open(struct file *file) inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; inst->core_acquired = false; inst->bit_depth = VIDC_BITDEPTH_8; + inst->pic_struct = HFI_INTERLACE_FRAME_PROGRESSIVE; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 1c61602c5de1..6976ed553647 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -10,7 +10,7 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <media/v4l2-mem2mem.h> -#include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dma-contig.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> #include <media/v4l2-ctrls.h> @@ -190,8 +190,10 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->height = clamp(pixmp->height, frame_height_min(inst), frame_height_max(inst)); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = ALIGN(pixmp->width, 128); pixmp->height = ALIGN(pixmp->height, 32); + } pixmp->width = ALIGN(pixmp->width, 2); pixmp->height = ALIGN(pixmp->height, 2); @@ -335,13 +337,13 @@ venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) switch (s->target) { case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - s->r.width = inst->width; - s->r.height = inst->height; - break; - case V4L2_SEL_TGT_CROP: s->r.width = inst->out_width; s->r.height = inst->out_height; break; + case V4L2_SEL_TGT_CROP: + s->r.width = inst->width; + s->r.height = inst->height; + break; default: return -EINVAL; } @@ -360,12 +362,19 @@ venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; + if (s->r.width > inst->out_width || + s->r.height > inst->out_height) + return -EINVAL; + + s->r.width = ALIGN(s->r.width, 2); + s->r.height = ALIGN(s->r.height, 2); + switch (s->target) { case V4L2_SEL_TGT_CROP: - if (s->r.width != inst->out_width || - s->r.height != inst->out_height || - s->r.top != 0 || s->r.left != 0) - return -EINVAL; + s->r.top = 0; + s->r.left = 0; + inst->width = s->r.width; + inst->height = s->r.height; break; default: return -EINVAL; @@ -536,6 +545,7 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_idr_period idrp; struct hfi_quantization quant; struct hfi_quantization_range quant_range; + struct hfi_enable en; u32 ptype, rate_control, bitrate; u32 profile, level; int ret; @@ -588,16 +598,19 @@ static int venc_set_properties(struct venus_inst *inst) return ret; } - /* IDR periodicity, n: - * n = 0 - only the first I-frame is IDR frame - * n = 1 - all I-frames will be IDR frames - * n > 1 - every n-th I-frame will be IDR frame - */ - ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; - idrp.idr_period = 0; - ret = hfi_session_set_property(inst, ptype, &idrp); - if (ret) - return ret; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + /* IDR periodicity, n: + * n = 0 - only the first I-frame is IDR frame + * n = 1 - all I-frames will be IDR frames + * n > 1 - every n-th I-frame will be IDR frame + */ + ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + idrp.idr_period = 0; + ret = hfi_session_set_property(inst, ptype, &idrp); + if (ret) + return ret; + } if (ctr->num_b_frames) { u32 max_num_b_frames = NUM_B_FRAMES_MAX; @@ -655,6 +668,19 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + if (ctr->header_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) + en.enable = 0; + else + en.enable = 1; + + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + if (!ctr->bitrate_peak) bitrate *= 2; else @@ -669,17 +695,28 @@ static int venc_set_properties(struct venus_inst *inst) return ret; ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP; - quant.qp_i = ctr->h264_i_qp; - quant.qp_p = ctr->h264_p_qp; - quant.qp_b = ctr->h264_b_qp; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant.qp_i = ctr->hevc_i_qp; + quant.qp_p = ctr->hevc_p_qp; + quant.qp_b = ctr->hevc_b_qp; + } else { + quant.qp_i = ctr->h264_i_qp; + quant.qp_p = ctr->h264_p_qp; + quant.qp_b = ctr->h264_b_qp; + } quant.layer_id = 0; ret = hfi_session_set_property(inst, ptype, &quant); if (ret) return ret; ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; - quant_range.min_qp = ctr->h264_min_qp; - quant_range.max_qp = ctr->h264_max_qp; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant_range.min_qp = ctr->hevc_min_qp; + quant_range.max_qp = ctr->hevc_max_qp; + } else { + quant_range.min_qp = ctr->h264_min_qp; + quant_range.max_qp = ctr->h264_max_qp; + } quant_range.layer_id = 0; ret = hfi_session_set_property(inst, ptype, &quant_range); if (ret) @@ -724,10 +761,17 @@ static int venc_init_session(struct venus_inst *inst) { int ret; - ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); - if (ret) + ret = venus_helper_session_init(inst); + if (ret == -EALREADY) + return 0; + else if (ret) return ret; + ret = venus_helper_set_stride(inst, inst->out_width, + inst->out_height); + if (ret) + goto deinit; + ret = venus_helper_set_input_resolution(inst, inst->width, inst->height); if (ret) @@ -743,10 +787,6 @@ static int venc_init_session(struct venus_inst *inst) if (ret) goto deinit; - ret = venus_helper_init_codec_freq_data(inst); - if (ret) - goto deinit; - ret = venc_set_properties(inst); if (ret) goto deinit; @@ -762,17 +802,13 @@ static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num) struct hfi_buffer_requirements bufreq; int ret; - ret = venc_init_session(inst); + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); - *num = bufreq.count_actual; - hfi_session_deinit(inst); - - return ret; + return 0; } static int venc_queue_setup(struct vb2_queue *q, @@ -781,7 +817,7 @@ static int venc_queue_setup(struct vb2_queue *q, { struct venus_inst *inst = vb2_get_drv_priv(q); unsigned int num, min = 4; - int ret = 0; + int ret; if (*num_planes) { if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && @@ -803,6 +839,13 @@ static int venc_queue_setup(struct vb2_queue *q, return 0; } + mutex_lock(&inst->lock); + ret = venc_init_session(inst); + mutex_unlock(&inst->lock); + + if (ret) + return ret; + switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; @@ -816,8 +859,8 @@ static int venc_queue_setup(struct vb2_queue *q, inst->num_input_bufs = *num_buffers; sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, - inst->width, - inst->height); + inst->out_width, + inst->out_height); inst->input_buf_size = sizes[0]; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -838,6 +881,49 @@ static int venc_queue_setup(struct vb2_queue *q, return ret; } +static int venc_buf_init(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + inst->buf_count++; + + return venus_helper_vb2_buf_init(vb); +} + +static void venc_release_session(struct venus_inst *inst) +{ + int ret; + + mutex_lock(&inst->lock); + + ret = hfi_session_deinit(inst); + if (ret || inst->session_error) + hfi_session_abort(inst); + + mutex_unlock(&inst->lock); + + venus_pm_load_scale(inst); + INIT_LIST_HEAD(&inst->registeredbufs); + venus_pm_release_core(inst); +} + +static void venc_buf_cleanup(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_buffer *buf = to_venus_buffer(vbuf); + + mutex_lock(&inst->lock); + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (!list_empty(&inst->registeredbufs)) + list_del_init(&buf->reg_list); + mutex_unlock(&inst->lock); + + inst->buf_count--; + if (!inst->buf_count) + venc_release_session(inst); +} + static int venc_verify_conf(struct venus_inst *inst) { enum hfi_version ver = inst->core->res->hfi_version; @@ -888,38 +974,32 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) inst->sequence_cap = 0; inst->sequence_out = 0; - ret = venc_init_session(inst); - if (ret) - goto bufs_done; - ret = venus_pm_acquire_core(inst); if (ret) - goto deinit_sess; + goto error; ret = venc_set_properties(inst); if (ret) - goto deinit_sess; + goto error; ret = venc_verify_conf(inst); if (ret) - goto deinit_sess; + goto error; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, inst->num_output_bufs, 0); if (ret) - goto deinit_sess; + goto error; ret = venus_helper_vb2_start_streaming(inst); if (ret) - goto deinit_sess; + goto error; mutex_unlock(&inst->lock); return 0; -deinit_sess: - hfi_session_deinit(inst); -bufs_done: +error: venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->streamon_out = 0; @@ -929,13 +1009,23 @@ bufs_done: return ret; } +static void venc_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + mutex_lock(&inst->lock); + venus_helper_vb2_buf_queue(vb); + mutex_unlock(&inst->lock); +} + static const struct vb2_ops venc_vb2_ops = { .queue_setup = venc_queue_setup, - .buf_init = venus_helper_vb2_buf_init, + .buf_init = venc_buf_init, + .buf_cleanup = venc_buf_cleanup, .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = venc_start_streaming, .stop_streaming = venus_helper_vb2_stop_streaming, - .buf_queue = venus_helper_vb2_buf_queue, + .buf_queue = venc_vb2_buf_queue, }; static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type, @@ -1001,7 +1091,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &venc_vb2_ops; - src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; @@ -1017,7 +1107,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &venc_vb2_ops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index cf860e6446c0..a52b80055173 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -135,9 +135,60 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: ctr->h264_min_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP: + ctr->h264_i_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP: + ctr->h264_p_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP: + ctr->h264_b_min_qp = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: ctr->h264_max_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP: + ctr->h264_i_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: + ctr->h264_p_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP: + ctr->h264_b_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + ctr->hevc_i_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: + ctr->hevc_p_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: + ctr->hevc_b_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + ctr->hevc_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP: + ctr->hevc_i_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP: + ctr->hevc_p_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP: + ctr->hevc_b_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + ctr->hevc_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP: + ctr->hevc_i_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP: + ctr->hevc_p_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP: + ctr->hevc_b_max_qp = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: ctr->multi_slice_mode = ctrl->val; break; @@ -158,6 +209,20 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: ctr->header_mode = ctrl->val; + mutex_lock(&inst->lock); + if (inst->streamon_out && inst->streamon_cap) { + if (ctrl->val == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) + en.enable = 0; + else + en.enable = 1; + ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) { + mutex_unlock(&inst->lock); + return ret; + } + } + mutex_unlock(&inst->lock); break; case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: break; @@ -208,6 +273,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: ctr->frame_skip_mode = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID: + ctr->base_priority_id = ctrl->val; + break; default: return -EINVAL; } @@ -223,7 +291,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 33); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 51); if (ret) return ret; @@ -289,7 +357,8 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_HEADER_MODE, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + ~((1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)), V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, @@ -311,19 +380,70 @@ int venc_ctrl_init(struct venus_inst *inst) BITRATE_STEP, BITRATE_DEFAULT_PEAK); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, 1, 63, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, 1, 63, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, 1, 63, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, 1, 63, 1, 63); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP, 1, 63, 1, 63); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP, 1, 63, 1, 63); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP, 1, 63, 1, 63); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN, @@ -374,6 +494,10 @@ int venc_ctrl_init(struct venus_inst *inst) (1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT)), V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID, 0, + 6, 1, 0); + ret = inst->ctrl_handler.error; if (ret) goto err; diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index e48d666f2c63..cb3025992817 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -642,7 +642,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin) } asd = v4l2_async_notifier_add_fwnode_subdev(&vin->notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; @@ -842,7 +842,8 @@ static int rvin_mc_parse_of(struct rvin_dev *vin, unsigned int id) } asd = v4l2_async_notifier_add_fwnode_subdev(&vin->group->notifier, - fwnode, sizeof(*asd)); + fwnode, + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 945d2eb87233..e06cd512aba2 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -910,7 +910,7 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) priv->notifier.ops = &rcar_csi2_notify_ops; asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 48280ddb15b9..f30dafbdf61c 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1301,6 +1301,11 @@ void rvin_stop_streaming(struct rvin_dev *vin) spin_lock_irqsave(&vin->qlock, flags); + if (vin->state == STOPPED) { + spin_unlock_irqrestore(&vin->qlock, flags); + return; + } + vin->state = STOPPING; /* Wait until only scratch buffer is used, max 3 interrupts. */ diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index e6ea2b7991b8..457a65bf6b66 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -966,12 +966,9 @@ void rvin_v4l2_unregister(struct rvin_dev *vin) video_unregister_device(&vin->vdev); } -static void rvin_notify(struct v4l2_subdev *sd, - unsigned int notification, void *arg) +static void rvin_notify_video_device(struct rvin_dev *vin, + unsigned int notification, void *arg) { - struct rvin_dev *vin = - container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); - switch (notification) { case V4L2_DEVICE_NOTIFY_EVENT: v4l2_event_queue(&vin->vdev, arg); @@ -981,6 +978,41 @@ static void rvin_notify(struct v4l2_subdev *sd, } } +static void rvin_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct v4l2_subdev *remote; + struct rvin_group *group; + struct media_pad *pad; + struct rvin_dev *vin = + container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); + unsigned int i; + + /* If no media controller, no need to route the event. */ + if (!vin->info->use_mc) { + rvin_notify_video_device(vin, notification, arg); + return; + } + + group = vin->group; + + for (i = 0; i < RCAR_VIN_NUM; i++) { + vin = group->vin[i]; + if (!vin) + continue; + + pad = media_entity_remote_pad(&vin->pad); + if (!pad) + continue; + + remote = media_entity_to_v4l2_subdev(pad->entity); + if (remote != sd) + continue; + + rvin_notify_video_device(vin, notification, arg); + } +} + int rvin_v4l2_register(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index f318cd4b8086..83bd9a412a56 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -1231,7 +1231,7 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) } asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index c9448de885b6..01c1fbb97bf6 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1439,8 +1439,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix, pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline * pix->height / vsub; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } if (fmt->num_planes == 3) { @@ -1448,8 +1446,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix, pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; - memset(pix->plane_fmt[2].reserved, 0, - sizeof(pix->plane_fmt[2].reserved)); } } diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 9b99ff368698..a7c198c17deb 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -648,6 +648,7 @@ static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width, if (get_word_be(&jpeg_buffer, &word)) return 0; skip(&jpeg_buffer, (long)word - 2); + break; case 0: break; default: @@ -793,7 +794,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, pix->colorspace = fmt->colorspace; pix->field = V4L2_FIELD_NONE; pix->num_planes = fmt->num_planes; - memset(pix->reserved, 0, sizeof(pix->reserved)); jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX, fmt->h_align, &pix->height, JPU_HEIGHT_MIN, @@ -808,8 +808,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE + (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h); pix->plane_fmt[0].bytesperline = 0; - memset(pix->plane_fmt[0].reserved, 0, - sizeof(pix->plane_fmt[0].reserved)); } else { unsigned int i, bpl = 0; @@ -822,8 +820,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, for (i = 0; i < pix->num_planes; ++i) { pix->plane_fmt[i].bytesperline = bpl; pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } } diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 4a633ad0e8fa..1678175c49bd 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -152,8 +152,8 @@ static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf) * ceu_subdev - Wraps v4l2 sub-device and provides async subdevice. */ struct ceu_subdev { - struct v4l2_subdev *v4l2_sd; struct v4l2_async_subdev asd; + struct v4l2_subdev *v4l2_sd; /* per-subdevice mbus configuration options */ unsigned int mbus_flags; @@ -174,7 +174,7 @@ struct ceu_device { struct v4l2_device v4l2_dev; /* subdevices descriptors */ - struct ceu_subdev *subdevs; + struct ceu_subdev **subdevs; /* the subdevice currently in use */ struct ceu_subdev *sd; unsigned int sd_index; @@ -1195,7 +1195,7 @@ static int ceu_enum_input(struct file *file, void *priv, if (inp->index >= ceudev->num_sd) return -EINVAL; - ceusd = &ceudev->subdevs[inp->index]; + ceusd = ceudev->subdevs[inp->index]; inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = 0; @@ -1230,7 +1230,7 @@ static int ceu_s_input(struct file *file, void *priv, unsigned int i) return 0; ceu_sd_old = ceudev->sd; - ceudev->sd = &ceudev->subdevs[i]; + ceudev->sd = ceudev->subdevs[i]; /* * Make sure we can generate output image formats and apply @@ -1423,7 +1423,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) * ceu formats. */ if (!ceudev->sd) { - ceudev->sd = &ceudev->subdevs[0]; + ceudev->sd = ceudev->subdevs[0]; ceudev->sd_index = 0; } @@ -1467,8 +1467,8 @@ static const struct v4l2_async_notifier_operations ceu_notify_ops = { /* * ceu_init_async_subdevs() - Initialize CEU subdevices and async_subdevs in - * ceu device. Both DT and platform data parsing use - * this routine. + * ceu device. Both DT and platform data parsing use + * this routine. * * Returns 0 for success, -ENOMEM for failure. */ @@ -1510,21 +1510,16 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, /* Setup the ceu subdevice and the async subdevice. */ async_sd = &pdata->subdevs[i]; - ceu_sd = &ceudev->subdevs[i]; - - INIT_LIST_HEAD(&ceu_sd->asd.list); - - ceu_sd->mbus_flags = async_sd->flags; - ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_I2C; - ceu_sd->asd.match.i2c.adapter_id = async_sd->i2c_adapter_id; - ceu_sd->asd.match.i2c.address = async_sd->i2c_address; - - ret = v4l2_async_notifier_add_subdev(&ceudev->notifier, - &ceu_sd->asd); - if (ret) { + ceu_sd = v4l2_async_notifier_add_i2c_subdev(&ceudev->notifier, + async_sd->i2c_adapter_id, + async_sd->i2c_address, + struct ceu_subdev); + if (IS_ERR(ceu_sd)) { v4l2_async_notifier_cleanup(&ceudev->notifier); - return ret; + return PTR_ERR(ceu_sd); } + ceu_sd->mbus_flags = async_sd->flags; + ceudev->subdevs[i] = ceu_sd; } return pdata->num_subdevs; @@ -1536,7 +1531,7 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, static int ceu_parse_dt(struct ceu_device *ceudev) { struct device_node *of = ceudev->dev->of_node; - struct device_node *ep, *remote; + struct device_node *ep; struct ceu_subdev *ceu_sd; unsigned int i; int num_ep; @@ -1578,20 +1573,15 @@ static int ceu_parse_dt(struct ceu_device *ceudev) } /* Setup the ceu subdevice and the async subdevice. */ - ceu_sd = &ceudev->subdevs[i]; - INIT_LIST_HEAD(&ceu_sd->asd.list); - - remote = of_graph_get_remote_port_parent(ep); - ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; - ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - ceu_sd->asd.match.fwnode = of_fwnode_handle(remote); - - ret = v4l2_async_notifier_add_subdev(&ceudev->notifier, - &ceu_sd->asd); - if (ret) { - of_node_put(remote); + ceu_sd = v4l2_async_notifier_add_fwnode_remote_subdev( + &ceudev->notifier, of_fwnode_handle(ep), + struct ceu_subdev); + if (IS_ERR(ceu_sd)) { + ret = PTR_ERR(ceu_sd); goto error_cleanup; } + ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; + ceudev->subdevs[i] = ceu_sd; of_node_put(ep); } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index f7e9fd305548..7474150b94ed 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -252,7 +252,7 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct rkisp1_sensor_async *rk_asd = NULL; + struct rkisp1_sensor_async *rk_asd; struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev), @@ -265,9 +265,10 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) if (ret) goto err_parse; - rk_asd = kzalloc(sizeof(*rk_asd), GFP_KERNEL); - if (!rk_asd) { - ret = -ENOMEM; + rk_asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, + struct rkisp1_sensor_async); + if (IS_ERR(rk_asd)) { + ret = PTR_ERR(rk_asd); goto err_parse; } @@ -275,11 +276,6 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; - ret = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, - &rk_asd->asd); - if (ret) - goto err_parse; - dev_dbg(rkisp1->dev, "registered ep id %d with %d lanes\n", vep.base.id, rk_asd->lanes); @@ -290,7 +286,6 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) continue; err_parse: fwnode_handle_put(ep); - kfree(rk_asd); v4l2_async_notifier_cleanup(ntf); return ret; } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 889982d8ca41..2e5b57e3aedc 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -600,6 +600,39 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } +static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct rkisp1_isp_mbus_info *mbus_info; + + if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS || + fse->pad == RKISP1_ISP_PAD_SOURCE_STATS) + return -ENOTTY; + + if (fse->index > 0) + return -EINVAL; + + mbus_info = rkisp1_isp_mbus_info_get(fse->code); + if (!mbus_info) + return -EINVAL; + + if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) && + fse->pad == RKISP1_ISP_PAD_SINK_VIDEO) + return -EINVAL; + + if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) && + fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) + return -EINVAL; + + fse->min_width = RKISP1_ISP_MIN_WIDTH; + fse->max_width = RKISP1_ISP_MAX_WIDTH; + fse->min_height = RKISP1_ISP_MIN_HEIGHT; + fse->max_height = RKISP1_ISP_MAX_HEIGHT; + + return 0; +} + static int rkisp1_isp_init_config(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { @@ -880,6 +913,7 @@ static int rkisp1_subdev_link_validate(struct media_link *link) static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_mbus_code = rkisp1_isp_enum_mbus_code, + .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, .init_cfg = rkisp1_isp_init_config, diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index b22dc1d72527..4ac48441f22c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1355,7 +1355,7 @@ static int sh_vou_remove(struct platform_device *pdev) return 0; } -static struct platform_driver __refdata sh_vou = { +static struct platform_driver sh_vou = { .remove = sh_vou_remove, .driver = { .name = "sh-vou", diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 5ceb366648b3..a7a6ea666740 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -826,6 +826,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) dev_err(dev, "reset gpio for tsin%d not valid (gpio=%d)\n", tsin->tsin_id, tsin->rst_gpio); + ret = -EINVAL; goto err_node_put; } diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c index 43f279e2a6a3..f59811e27f51 100644 --- a/drivers/media/platform/sti/hva/hva-hw.c +++ b/drivers/media/platform/sti/hva/hva-hw.c @@ -447,6 +447,7 @@ int hva_hw_runtime_resume(struct device *dev) if (clk_set_rate(hva->clk, CLK_RATE)) { dev_err(dev, "%s failed to set clock frequency\n", HVA_PREFIX); + clk_disable_unprepare(hva->clk); return -EINVAL; } diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b745f1342c2e..bbcc2254fa2e 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -99,13 +99,6 @@ enum state { #define OVERRUN_ERROR_THRESHOLD 3 -struct dcmi_graph_entity { - struct v4l2_async_subdev asd; - - struct device_node *remote_node; - struct v4l2_subdev *source; -}; - struct dcmi_format { u32 fourcc; u32 mbus_code; @@ -139,7 +132,7 @@ struct stm32_dcmi { struct v4l2_device v4l2_dev; struct video_device *vdev; struct v4l2_async_notifier notifier; - struct dcmi_graph_entity entity; + struct v4l2_subdev *source; struct v4l2_format fmt; struct v4l2_rect crop; bool do_crop; @@ -610,7 +603,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, struct v4l2_subdev_pad_config *pad_cfg, struct v4l2_subdev_format *format) { - struct media_entity *entity = &dcmi->entity.source->entity; + struct media_entity *entity = &dcmi->source->entity; struct v4l2_subdev *subdev; struct media_pad *sink_pad = NULL; struct media_pad *src_pad = NULL; @@ -1018,7 +1011,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1152,7 +1145,7 @@ static int dcmi_get_sensor_format(struct stm32_dcmi *dcmi, }; int ret; - ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt); + ret = v4l2_subdev_call(dcmi->source, pad, get_fmt, NULL, &fmt); if (ret) return ret; @@ -1181,7 +1174,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1204,7 +1197,7 @@ static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi, /* * Get sensor bounds first */ - ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection, + ret = v4l2_subdev_call(dcmi->source, pad, get_selection, NULL, &bounds); if (!ret) *r = bounds.r; @@ -1385,7 +1378,7 @@ static int dcmi_enum_framesizes(struct file *file, void *fh, fse.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size, + ret = v4l2_subdev_call(dcmi->source, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -1402,7 +1395,7 @@ static int dcmi_g_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p); + return v4l2_g_parm_cap(video_devdata(file), dcmi->source, p); } static int dcmi_s_parm(struct file *file, void *priv, @@ -1410,7 +1403,7 @@ static int dcmi_s_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p); + return v4l2_s_parm_cap(video_devdata(file), dcmi->source, p); } static int dcmi_enum_frameintervals(struct file *file, void *fh, @@ -1432,7 +1425,7 @@ static int dcmi_enum_frameintervals(struct file *file, void *fh, fie.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.source, pad, + ret = v4l2_subdev_call(dcmi->source, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -1452,7 +1445,7 @@ MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match); static int dcmi_open(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.source; + struct v4l2_subdev *sd = dcmi->source; int ret; if (mutex_lock_interruptible(&dcmi->lock)) @@ -1483,7 +1476,7 @@ unlock: static int dcmi_release(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.source; + struct v4l2_subdev *sd = dcmi->source; bool fh_singular; int ret; @@ -1616,7 +1609,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) { const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)]; unsigned int num_fmts = 0, i, j; - struct v4l2_subdev *subdev = dcmi->entity.source; + struct v4l2_subdev *subdev = dcmi->source; struct v4l2_subdev_mbus_code_enum mbus_code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; @@ -1675,7 +1668,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) static int dcmi_framesizes_init(struct stm32_dcmi *dcmi) { unsigned int num_fsize = 0; - struct v4l2_subdev *subdev = dcmi->entity.source; + struct v4l2_subdev *subdev = dcmi->source; struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .code = dcmi->sd_format->mbus_code, @@ -1727,14 +1720,13 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) * we search for the source subdevice * in order to expose it through V4L2 interface */ - dcmi->entity.source = - media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); - if (!dcmi->entity.source) { + dcmi->source = media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); + if (!dcmi->source) { dev_err(dcmi->dev, "Source subdevice not found\n"); return -ENODEV; } - dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler; + dcmi->vdev->ctrl_handler = dcmi->source->ctrl_handler; ret = dcmi_formats_init(dcmi); if (ret) { @@ -1813,46 +1805,29 @@ static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { .complete = dcmi_graph_notify_complete, }; -static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) -{ - struct device_node *ep = NULL; - struct device_node *remote; - - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; - - remote = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!remote) - return -EINVAL; - - /* Remote node to connect */ - dcmi->entity.remote_node = remote; - dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; -} - static int dcmi_graph_init(struct stm32_dcmi *dcmi) { + struct v4l2_async_subdev *asd; + struct device_node *ep; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node); - if (ret < 0) { - dev_err(dcmi->dev, "Failed to parse graph\n"); - return ret; + ep = of_graph_get_next_endpoint(dcmi->dev->of_node, NULL); + if (!ep) { + dev_err(dcmi->dev, "Failed to get next endpoint\n"); + return -EINVAL; } v4l2_async_notifier_init(&dcmi->notifier); - ret = v4l2_async_notifier_add_subdev(&dcmi->notifier, - &dcmi->entity.asd); - if (ret) { + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &dcmi->notifier, of_fwnode_handle(ep), + struct v4l2_async_subdev); + + of_node_put(ep); + + if (IS_ERR(asd)) { dev_err(dcmi->dev, "Failed to add subdev notifier\n"); - of_node_put(dcmi->entity.remote_node); - return ret; + return PTR_ERR(asd); } dcmi->notifier.ops = &dcmi_graph_notify_ops; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index ec46cff80fdb..8d40a7acba9c 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -118,6 +118,7 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; int ret; @@ -134,10 +135,12 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) csi->bus = vep.bus.parallel; - ret = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, - ep, &csi->asd); - if (ret) + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, ep, + struct v4l2_async_subdev); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto out; + } csi->notifier.ops = &sun4i_csi_notify_ops; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 0f67ff652c2e..a5f61ee0ec4d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -139,7 +139,6 @@ struct sun4i_csi { struct v4l2_mbus_framefmt subdev_fmt; /* V4L2 Async variables */ - struct v4l2_async_subdev asd; struct v4l2_async_notifier notifier; struct v4l2_subdev *src_subdev; int src_pad; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 1a2f65d83a6c..4785faddf630 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -113,8 +113,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi, pix->num_planes = _fmt->num_planes; pix->pixelformat = _fmt->fourcc; - memset(pix->reserved, 0, sizeof(pix->reserved)); - /* Align the width and height on the subsampling */ width = ALIGN(pix->width, _fmt->hsub); height = ALIGN(pix->height, _fmt->vsub); @@ -131,8 +129,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi, bpl = pix->width / hsub * _fmt->bpp[i] / 8; pix->plane_fmt[i].bytesperline = bpl; pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } } diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 806cbf175d39..dd48017859cd 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -116,8 +116,7 @@ void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, - const struct cal_fmt *fmt) +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; @@ -132,9 +131,9 @@ static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, * CSI-2 is DDR and we only count used lanes. * * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * fmt->bpp; + * / (2 * num_lanes) * phy->fmtinfo->bpp; */ - csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp, 2 * num_lanes * 1000); phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); @@ -234,7 +233,42 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error IRQs. */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); +} + +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); +} + +static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static int cal_camerarx_start(struct cal_camerarx *phy) { s64 external_rate; u32 sscounter; @@ -251,6 +285,8 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) return ret; } + cal_camerarx_enable_irqs(phy); + /* * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / @@ -289,7 +325,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate, fmt); + cal_camerarx_config(phy, external_rate); /* * b. Assert the FORCERXMODE signal. @@ -340,6 +376,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); if (ret) { v4l2_subdev_call(phy->sensor, core, s_power, 0); + cal_camerarx_disable_irqs(phy); phy_err(phy, "stream on failed in subdev\n"); return ret; } @@ -359,14 +396,21 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) * implemented. */ + /* Finally, enable the PHY Protocol Interface (PPI). */ + cal_camerarx_ppi_enable(phy); + return 0; } -void cal_camerarx_stop(struct cal_camerarx *phy) +static void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; int ret; + cal_camerarx_ppi_disable(phy); + + cal_camerarx_disable_irqs(phy); + cal_camerarx_power(phy, false); /* Assert Complex IO Reset */ @@ -428,74 +472,6 @@ void cal_camerarx_i913_errata(struct cal_camerarx *phy) camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); } -/* - * Enable the expected IRQ sources - */ -void cal_camerarx_enable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); - - /* Always enable OCPO error */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} - -void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - /* Disable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); -} - -void cal_camerarx_ppi_enable(struct cal_camerarx *phy) -{ - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) -{ - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *phy) { @@ -533,8 +509,8 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, static int cal_camerarx_parse_dt(struct cal_camerarx *phy) { struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct device_node *ep_node; char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + struct device_node *ep_node; unsigned int i; int ret; @@ -582,9 +558,11 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) endpoint->bus.mipi_csi2.flags); /* Retrieve the connected device and store it for later use. */ - phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node); + phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node); if (!phy->sensor_node) { phy_dbg(3, phy, "Can't get remote parent\n"); + of_node_put(phy->sensor_ep_node); ret = -EINVAL; goto done; } @@ -596,11 +574,227 @@ done: return ret; } +/* ------------------------------------------------------------------ + * V4L2 Subdev Operations + * ------------------------------------------------------------------ + */ + +static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cal_camerarx, subdev); +} + +static struct v4l2_mbus_framefmt * +cal_camerarx_get_pad_format(struct cal_camerarx *phy, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&phy->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &phy->formats[pad]; + default: + return NULL; + } +} + +static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + + if (enable) + return cal_camerarx_start(phy); + + cal_camerarx_stop(phy); + return 0; +} + +static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + + /* No transcoding, source and sink codes must match. */ + if (code->pad == CAL_CAMERARX_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = cal_camerarx_get_pad_format(phy, cfg, + CAL_CAMERARX_PAD_SINK, + code->which); + code->code = fmt->code; + return 0; + } + + if (code->index >= cal_num_formats) + return -EINVAL; + + code->code = cal_formats[code->index].code; + + return 0; +} + +static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_format_info *fmtinfo; + + if (fse->index > 0) + return -EINVAL; + + /* No transcoding, source and sink formats must match. */ + if (fse->pad == CAL_CAMERARX_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + fmt = cal_camerarx_get_pad_format(phy, cfg, + CAL_CAMERARX_PAD_SINK, + fse->which); + if (fse->code != fmt->code) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + + return 0; + } + + fmtinfo = cal_format_by_code(fse->code); + if (!fmtinfo) + return -EINVAL; + + fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_height = CAL_MIN_HEIGHT_LINES; + fse->max_height = CAL_MAX_HEIGHT_LINES; + + return 0; +} + +static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_mbus_framefmt *fmt; + + fmt = cal_camerarx_get_pad_format(phy, cfg, format->pad, format->which); + format->format = *fmt; + + return 0; +} + +static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_format_info *fmtinfo; + struct v4l2_mbus_framefmt *fmt; + unsigned int bpp; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == CAL_CAMERARX_PAD_SOURCE) + return cal_camerarx_sd_get_fmt(sd, cfg, format); + + /* + * Default to the first format is the requested media bus code isn't + * supported. + */ + fmtinfo = cal_format_by_code(format->format.code); + if (!fmtinfo) + fmtinfo = &cal_formats[0]; + + /* + * Clamp the size, update the code. The field and colorspace are + * accepted as-is. + */ + bpp = ALIGN(fmtinfo->bpp, 8); + + format->format.width = clamp_t(unsigned int, format->format.width, + CAL_MIN_WIDTH_BYTES * 8 / bpp, + CAL_MAX_WIDTH_BYTES * 8 / bpp); + format->format.height = clamp_t(unsigned int, format->format.height, + CAL_MIN_HEIGHT_LINES, + CAL_MAX_HEIGHT_LINES); + format->format.code = fmtinfo->code; + + /* Store the format and propagate it to the source pad. */ + fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SINK, + format->which); + *fmt = format->format; + + fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SOURCE, + format->which); + *fmt = format->format; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + phy->fmtinfo = fmtinfo; + + return 0; +} + +static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format format = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CAL_CAMERARX_PAD_SINK, + .format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }, + }; + + return cal_camerarx_sd_set_fmt(sd, cfg, &format); +} + +static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { + .s_stream = cal_camerarx_sd_s_stream, +}; + +static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { + .init_cfg = cal_camerarx_sd_init_cfg, + .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, + .enum_frame_size = cal_camerarx_sd_enum_frame_size, + .get_fmt = cal_camerarx_sd_get_fmt, + .set_fmt = cal_camerarx_sd_set_fmt, +}; + +static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { + .video = &cal_camerarx_video_ops, + .pad = &cal_camerarx_pad_ops, +}; + +static struct media_entity_operations cal_camerarx_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ------------------------------------------------------------------ + * Create and Destroy + * ------------------------------------------------------------------ + */ + struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; + struct v4l2_subdev *sd; int ret; phy = kzalloc(sizeof(*phy), GFP_KERNEL); @@ -632,9 +826,31 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, if (ret) goto error; + /* Initialize the V4L2 subdev and media entity. */ + sd = &phy->subdev; + v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); + sd->dev = cal->dev; + + phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.ops = &cal_camerarx_media_ops; + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), + phy->pads); + if (ret) + goto error; + + cal_camerarx_sd_init_cfg(sd, NULL); + + ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); + if (ret) + goto error; + return phy; error: + media_entity_cleanup(&phy->subdev.entity); kfree(phy); return ERR_PTR(ret); } @@ -644,6 +860,9 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) if (!phy) return; + v4l2_device_unregister_subdev(&phy->subdev); + media_entity_cleanup(&phy->subdev.entity); + of_node_put(phy->sensor_ep_node); of_node_put(phy->sensor_node); kfree(phy); } diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index df472a175e83..779f1e1bc529 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -9,7 +9,6 @@ * Laurent Pinchart <laurent.pinchart@ideasonboard.com> */ -#include <linux/delay.h> #include <linux/ioctl.h> #include <linux/pm_runtime.h> #include <linux/videodev2.h> @@ -26,107 +25,6 @@ #include "cal.h" -/* ------------------------------------------------------------------ - * Format Handling - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - /* Print Four-character-code (FOURCC) */ static char *fourcc_to_str(u32 fmt) { @@ -146,31 +44,31 @@ static char *fourcc_to_str(u32 fmt) * ------------------------------------------------------------------ */ -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) +static const struct cal_format_info *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) { - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int k; for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->fourcc == pixelformat) + return fmtinfo; } return NULL; } -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) +static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx, + u32 code) { - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int k; for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->code == code) + return fmtinfo; } return NULL; @@ -193,14 +91,14 @@ static int cal_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; if (f->index >= ctx->num_active_fmt) return -EINVAL; - fmt = ctx->active_fmt[f->index]; + fmtinfo = ctx->active_fmt[f->index]; - f->pixelformat = fmt->fourcc; + f->pixelformat = fmtinfo->fourcc; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } @@ -248,27 +146,23 @@ static int __subdev_set_format(struct cal_ctx *ctx, return 0; } -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) +static void cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_format_info *fmtinfo, + struct v4l2_format *f) { u32 bpl, max_width; - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - /* * Maximum width is bound by the DMA max width in bytes. * We need to recalculate the actual maxi width depending on the * number of bytes per pixels required. */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmtinfo->bpp, 8) >> 3); v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); + &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES, + 0, 0); - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; + bpl = (f->fmt.pix.width * ALIGN(fmtinfo->bpp, 8)) >> 3; f->fmt.pix.bytesperline = ALIGN(bpl, 16); f->fmt.pix.sizeimage = f->fmt.pix.height * @@ -278,8 +172,6 @@ static int cal_calc_format_size(struct cal_ctx *ctx, __func__, fourcc_to_str(f->fmt.pix.pixelformat), f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; } static int cal_g_fmt_vid_cap(struct file *file, void *priv, @@ -296,18 +188,18 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; int ret, found; - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmtinfo) { ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", f->fmt.pix.pixelformat); /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; + fmtinfo = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmtinfo->fourcc; } f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; @@ -316,7 +208,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, ret = 0; found = false; fse.pad = 0; - fse.code = fmt->code; + fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, @@ -348,7 +240,8 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, * updated properly during s_fmt */ f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); + cal_calc_format_size(ctx, fmtinfo, f); + return 0; } static int cal_s_fmt_vid_cap(struct file *file, void *priv, @@ -356,8 +249,11 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, { struct cal_ctx *ctx = video_drvdata(file); struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CAL_CAMERARX_PAD_SINK, + }; + const struct cal_format_info *fmtinfo; int ret; if (vb2_is_busy(q)) { @@ -369,28 +265,31 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmtinfo->code); - ret = __subdev_set_format(ctx, &mbus_fmt); + ret = __subdev_set_format(ctx, &sd_fmt.format); if (ret) return ret; /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { + if (sd_fmt.format.code != fmtinfo->code) { ctx_dbg(3, ctx, "%s subdev changed format on us, this should not happen\n", __func__); return -EINVAL; } - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; + ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + + v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); + + ctx->fmtinfo = fmtinfo; *f = ctx->v_fmt; return 0; @@ -400,13 +299,13 @@ static int cal_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; int ret; /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { + fmtinfo = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmtinfo) { ctx_dbg(3, ctx, "Invalid pixel code: %x\n", fsize->pixel_format); return -EINVAL; @@ -414,7 +313,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, fse.index = fsize->index; fse.pad = 0; - fse.code = fmt->code; + fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, @@ -460,7 +359,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_interval_enum fie = { .index = fival->index, .width = fival->width, @@ -469,11 +368,11 @@ static int cal_enum_frameintervals(struct file *file, void *priv, }; int ret; - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) + fmtinfo = find_format_by_pix(ctx, fival->pixel_format); + if (!fmtinfo) return -EINVAL; - fie.code = fmt->code; + fie.code = fmtinfo->code; ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, NULL, &fie); if (ret) @@ -488,7 +387,6 @@ static const struct v4l2_file_operations cal_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .release = vb2_fop_release, - .read = vb2_fop_read, .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = vb2_fop_mmap, @@ -555,9 +453,6 @@ static int cal_buffer_prepare(struct vb2_buffer *vb) vb.vb2_buf); unsigned long size; - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - size = ctx->v_fmt.fmt.pix.sizeimage; if (vb2_plane_size(vb, 0) < size) { ctx_err(ctx, @@ -575,121 +470,88 @@ static void cal_buffer_queue(struct vb2_buffer *vb) struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; unsigned long flags; /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_lock_irqsave(&ctx->dma.lock, flags); + list_add_tail(&buf->list, &ctx->dma.queue); + spin_unlock_irqrestore(&ctx->dma.lock, flags); } -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +static void cal_release_buffers(struct cal_ctx *ctx, + enum vb2_buffer_state state) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; - unsigned long addr; - unsigned long flags; - int ret; - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; + /* Release all queued buffers. */ + spin_lock_irq(&ctx->dma.lock); + + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (ctx->dma.pending) { + vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state); + ctx->dma.pending = NULL; + } + + if (ctx->dma.active) { + vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state); + ctx->dma.active = NULL; } - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; + spin_unlock_irq(&ctx->dma.lock); +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_buffer *buf; + dma_addr_t addr; + int ret; + + spin_lock_irq(&ctx->dma.lock); + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); + ctx->dma.pending = buf; list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irq(&ctx->dma.lock); - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); pm_runtime_get_sync(ctx->cal->dev); - cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); + cal_ctx_set_dma_addr(ctx, addr); + cal_ctx_start(ctx); - cal_camerarx_enable_irqs(ctx->phy); - - ret = cal_camerarx_start(ctx->phy, ctx->fmt); + ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); if (ret) goto err; - cal_ctx_wr_dma_addr(ctx, addr); - cal_camerarx_ppi_enable(ctx->phy); - if (cal_debug >= 4) cal_quickdump_regs(ctx->cal); return 0; err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); + cal_ctx_stop(ctx); + pm_runtime_put_sync(ctx->cal->dev); + + cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; } static void cal_stop_streaming(struct vb2_queue *vq) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - bool dma_act; - - cal_camerarx_ppi_disable(ctx->phy); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_stop(ctx->phy); - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } + cal_ctx_stop(ctx); - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); + v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); pm_runtime_put_sync(ctx->cal->dev); + + cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); } static const struct vb2_ops cal_video_qops = { @@ -713,20 +575,19 @@ static const struct video_device cal_videodev = { .ioctl_ops = &cal_ioctl_ops, .minor = -1, .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, }; static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { struct v4l2_subdev_mbus_code_enum mbus_code; struct v4l2_mbus_framefmt mbus_fmt; - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int i, j, k; int ret = 0; /* Enumerate sub device formats and enable all matching local formats */ - ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats, sizeof(*ctx->active_fmt), GFP_KERNEL); ctx->num_active_fmt = 0; @@ -744,15 +605,15 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) "subdev %s: code: %04x idx: %u\n", ctx->phy->sensor->name, mbus_code.code, j); - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; + for (k = 0; k < cal_num_formats; k++) { + fmtinfo = &cal_formats[k]; - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; + if (mbus_code.code == fmtinfo->code) { + ctx->active_fmt[i] = fmtinfo; ctx_dbg(2, ctx, "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); + fourcc_to_str(fmtinfo->fourcc), + fmtinfo->code, i); ctx->num_active_fmt = ++i; } } @@ -768,20 +629,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (ret) return ret; - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { + fmtinfo = find_format_by_code(ctx, mbus_fmt.code); + if (!fmtinfo) { ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", mbus_fmt.code); return -EINVAL; } - /* Save current subdev format */ + /* Save current format */ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; + ctx->v_fmt.fmt.pix.field = mbus_fmt.field; + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + ctx->fmtinfo = fmtinfo; return 0; } @@ -809,6 +670,18 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx) return ret; } + ret = media_create_pad_link(&ctx->phy->subdev.entity, + CAL_CAMERARX_PAD_SOURCE, + &vfd->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + ctx_err(ctx, "Failed to create media link for context %u\n", + ctx->index); + video_unregister_device(vfd); + return ret; + } + ctx_info(ctx, "V4L2 device registered as %s\n", video_device_node_name(vfd)); @@ -830,13 +703,14 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) struct vb2_queue *q = &ctx->vb_vidq; int ret; - INIT_LIST_HEAD(&ctx->vidq.active); - spin_lock_init(&ctx->slock); + INIT_LIST_HEAD(&ctx->dma.queue); + spin_lock_init(&ctx->dma.lock); mutex_init(&ctx->mutex); + init_waitqueue_head(&ctx->dma.wait); /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = ctx; q->buf_struct_size = sizeof(struct cal_buffer); q->ops = &cal_video_qops; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 59a0266b1f39..fa0931788040 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -44,6 +44,133 @@ module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); /* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +const struct cal_format_info cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +const unsigned int cal_num_formats = ARRAY_SIZE(cal_formats); + +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].fourcc == fourcc) + return &cal_formats[i]; + } + + return NULL; +} + +const struct cal_format_info *cal_format_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].code == code) + return &cal_formats[i]; + } + + return NULL; +} + +/* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ */ @@ -136,12 +263,9 @@ void cal_quickdump_regs(struct cal_dev *cal) (__force const void *)cal->base, resource_size(cal->res), false); - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { struct cal_camerarx *phy = cal->phy[i]; - if (!phy) - continue; - cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, @@ -156,7 +280,7 @@ void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -void cal_ctx_csi2_config(struct cal_ctx *ctx) +static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -181,11 +305,11 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -void cal_ctx_pix_proc_config(struct cal_ctx *ctx) +static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; - switch (ctx->fmt->bpp) { + switch (ctx->fmtinfo->bpp) { case 8: extract = CAL_PIX_PROC_EXTRACT_B8; pack = CAL_PIX_PROC_PACK_B8; @@ -214,7 +338,7 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) */ dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", - __FILE__, __LINE__, __func__, ctx->fmt->bpp); + __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; pack = CAL_PIX_PROC_PACK_B8; break; @@ -232,14 +356,15 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height) +static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) { + unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; u32 val; val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); - cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, + CAL_WR_DMA_CTRL_YSIZE_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, CAL_WR_DMA_CTRL_DTAG_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, @@ -251,14 +376,8 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); - /* - * width/16 not sure but giving it a whirl. - * zero does not work right - */ - cal_write_field(ctx->cal, - CAL_WR_DMA_OFST(ctx->index), - (width / 16), - CAL_WR_DMA_OFST_MASK); + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index), + stride / 16, CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); @@ -266,11 +385,11 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, /* 64 bit word means no skipping */ cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* - * (width*8)/64 this should be size of an entire line - * in 64bit word but 0 means all data until the end - * is detected automagically + * The XSIZE field is expressed in 64-bit units and prevents overflows + * in case of synchronization issues by limiting the number of bytes + * written per line. */ - cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK); cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); @@ -287,9 +406,74 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) +{ + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr); +} + +static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) +{ + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); +} + +static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) { - cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); + bool stopped; + + spin_lock_irq(&ctx->dma.lock); + stopped = ctx->dma.state == CAL_DMA_STOPPED; + spin_unlock_irq(&ctx->dma.lock); + + return stopped; +} + +void cal_ctx_start(struct cal_ctx *ctx) +{ + ctx->sequence = 0; + ctx->dma.state = CAL_DMA_RUNNING; + + /* Configure the CSI-2, pixel processing and write DMA contexts. */ + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx); + + /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), + CAL_HL_IRQ_MASK(ctx->index)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), + CAL_HL_IRQ_MASK(ctx->index)); +} + +void cal_ctx_stop(struct cal_ctx *ctx) +{ + long timeout; + + /* + * Request DMA stop and wait until it completes. If completion times + * out, forcefully disable the DMA. + */ + spin_lock_irq(&ctx->dma.lock); + ctx->dma.state = CAL_DMA_STOP_REQUESTED; + spin_unlock_irq(&ctx->dma.lock); + + timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), + msecs_to_jiffies(500)); + if (!timeout) { + ctx_err(ctx, "failed to disable dma cleanly\n"); + cal_ctx_wr_dma_disable(ctx); + } + + /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), + CAL_HL_IRQ_MASK(ctx->index)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), + CAL_HL_IRQ_MASK(ctx->index)); + + ctx->dma.state = CAL_DMA_STOPPED; } /* ------------------------------------------------------------------ @@ -297,35 +481,70 @@ void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) * ------------------------------------------------------------------ */ -static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) +static inline void cal_irq_wdma_start(struct cal_ctx *ctx) { - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf; - unsigned long addr; + spin_lock(&ctx->dma.lock); + + if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) { + /* + * If a stop is requested, disable the write DMA context + * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed, + * the current frame will complete and the DMA will then stop. + */ + cal_ctx_wr_dma_disable(ctx); + ctx->dma.state = CAL_DMA_STOP_PENDING; + } else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) { + /* + * Otherwise, if a new buffer is available, queue it to the + * hardware. + */ + struct cal_buffer *buf; + dma_addr_t addr; + + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, + list); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + cal_ctx_set_dma_addr(ctx, addr); - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->next_frm = buf; - list_del(&buf->list); + ctx->dma.pending = buf; + list_del(&buf->list); + } - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_ctx_wr_dma_addr(ctx, addr); + spin_unlock(&ctx->dma.lock); } -static inline void cal_process_buffer_complete(struct cal_ctx *ctx) +static inline void cal_irq_wdma_end(struct cal_ctx *ctx) { - ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); - ctx->cur_frm->vb.field = ctx->m_fmt.field; - ctx->cur_frm->vb.sequence = ctx->sequence++; + struct cal_buffer *buf = NULL; + + spin_lock(&ctx->dma.lock); + + /* If the DMA context was stopping, it is now stopped. */ + if (ctx->dma.state == CAL_DMA_STOP_PENDING) { + ctx->dma.state = CAL_DMA_STOPPED; + wake_up(&ctx->dma.wait); + } - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); - ctx->cur_frm = ctx->next_frm; + /* If a new buffer was queued, complete the current buffer. */ + if (ctx->dma.pending) { + buf = ctx->dma.active; + ctx->dma.active = ctx->dma.pending; + ctx->dma.pending = NULL; + } + + spin_unlock(&ctx->dma.lock); + + if (buf) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.field = ctx->v_fmt.fmt.pix.field; + buf->vb.sequence = ctx->sequence++; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } } static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; - struct cal_ctx *ctx; - struct cal_dmaqueue *dma_q; u32 status; status = cal_read(cal, CAL_HL_IRQSTATUS(0)); @@ -337,7 +556,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); - for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -360,17 +579,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cal_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - - spin_lock(&ctx->slock); - ctx->dma_act = false; - - if (ctx->cur_frm != ctx->next_frm) - cal_process_buffer_complete(ctx); - - spin_unlock(&ctx->slock); - } + if (status & CAL_HL_IRQ_MASK(i)) + cal_irq_wdma_end(cal->ctx[i]); } } @@ -383,17 +593,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cal_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - dma_q = &ctx->vidq; - - spin_lock(&ctx->slock); - ctx->dma_act = true; - if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) - cal_schedule_next_buffer(ctx); - spin_unlock(&ctx->slock); - } + if (status & CAL_HL_IRQ_MASK(i)) + cal_irq_wdma_start(cal->ctx[i]); } } @@ -406,7 +607,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) */ struct cal_v4l2_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_subdev asd; /* Must be first */ struct cal_camerarx *phy; }; @@ -421,6 +622,8 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct cal_camerarx *phy = to_cal_asd(asd)->phy; + int pad; + int ret; if (phy->sensor) { phy_info(phy, "Rejecting subdev %s (Already set!!)", @@ -431,6 +634,25 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, phy->sensor = subdev; phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); + pad = media_entity_get_fwnode_pad(&subdev->entity, + of_fwnode_handle(phy->sensor_ep_node), + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + phy_err(phy, "Sensor %s has no connected source pad\n", + subdev->name); + return pad; + } + + ret = media_create_pad_link(&subdev->entity, pad, + &phy->subdev.entity, CAL_CAMERARX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + phy_err(phy, "Failed to create media link for sensor %s\n", + subdev->name); + return ret; + } + return 0; } @@ -460,26 +682,24 @@ static int cal_async_notifier_register(struct cal_dev *cal) v4l2_async_notifier_init(&cal->notifier); cal->notifier.ops = &cal_async_notifier_ops; - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { struct cal_camerarx *phy = cal->phy[i]; struct cal_v4l2_async_subdev *casd; - struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; - if (!phy || !phy->sensor_node) + if (!phy->sensor_node) continue; fwnode = of_fwnode_handle(phy->sensor_node); - asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, - fwnode, - sizeof(*asd)); - if (IS_ERR(asd)) { + casd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + struct cal_v4l2_async_subdev); + if (IS_ERR(casd)) { phy_err(phy, "Failed to add subdev to notifier\n"); - ret = PTR_ERR(asd); + ret = PTR_ERR(casd); goto error; } - casd = to_cal_asd(asd); casd->phy = phy; } @@ -797,6 +1017,11 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) + goto error_pm_runtime; + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); @@ -816,11 +1041,6 @@ static int cal_probe(struct platform_device *pdev) goto error_camerarx; } - /* Initialize the media device. */ - ret = cal_media_init(cal); - if (ret < 0) - goto error_camerarx; - /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { if (!cal->phy[i]->sensor_node) @@ -848,12 +1068,12 @@ error_context: cal_ctx_v4l2_cleanup(ctx); } - cal_media_cleanup(cal); - error_camerarx: - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + for (i = 0; i < cal->data->num_csi2_phy; i++) cal_camerarx_destroy(cal->phy[i]); + cal_media_cleanup(cal); + error_pm_runtime: pm_runtime_disable(&pdev->dev); @@ -878,7 +1098,7 @@ static int cal_remove(struct platform_device *pdev) cal_media_cleanup(cal); - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + for (i = 0; i < cal->data->num_csi2_phy; i++) cal_camerarx_destroy(cal->phy[i]); pm_runtime_put_sync(&pdev->dev); @@ -890,16 +1110,23 @@ static int cal_remove(struct platform_device *pdev) static int cal_runtime_resume(struct device *dev) { struct cal_dev *cal = dev_get_drvdata(dev); + unsigned int i; if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - cal_camerarx_i913_errata(cal->phy[0]); - cal_camerarx_i913_errata(cal->phy[1]); + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_i913_errata(cal->phy[i]); } + /* + * Enable global interrupts that are not related to a particular + * CAMERARAX or context. + */ + cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + return 0; } diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 4123405ee0cf..d471b7f82519 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -17,6 +17,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/videodev2.h> +#include <linux/wait.h> #include <media/media-device.h> #include <media/v4l2-async.h> @@ -24,21 +25,32 @@ #include <media/v4l2-dev.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> #include <media/videobuf2-v4l2.h> #define CAL_MODULE_NAME "cal" #define CAL_NUM_CONTEXT 2 #define CAL_NUM_CSI2_PORTS 2 -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 +/* + * The width is limited by the size of the CAL_WR_DMA_XSIZE_j.XSIZE field, + * expressed in multiples of 64 bits. The height is limited by the size of the + * CAL_CSI2_CTXi_j.CTXi_LINES and CAL_WR_DMA_CTRL_j.YSIZE fields, expressed in + * lines. + */ +#define CAL_MIN_WIDTH_BYTES 16 +#define CAL_MAX_WIDTH_BYTES (8192 * 8) +#define CAL_MIN_HEIGHT_LINES 1 +#define CAL_MAX_HEIGHT_LINES 16383 + +#define CAL_CAMERARX_PAD_SINK 0 +#define CAL_CAMERARX_PAD_SOURCE 1 struct device; struct device_node; struct resource; struct regmap; struct regmap_fied; -struct v4l2_subdev; /* CTRL_CORE_CAMERRX_CONTROL register field id */ enum cal_camerarx_field { @@ -49,7 +61,14 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cal_fmt { +enum cal_dma_state { + CAL_DMA_RUNNING, + CAL_DMA_STOP_REQUESTED, + CAL_DMA_STOP_PENDING, + CAL_DMA_STOPPED, +}; + +struct cal_format_info { u32 fourcc; u32 code; /* Bits per pixel */ @@ -63,8 +82,38 @@ struct cal_buffer { struct list_head list; }; +/** + * struct cal_dmaqueue - Queue of DMA buffers + * @active: Buffer being DMA'ed to for the current frame + */ struct cal_dmaqueue { - struct list_head active; + /** + * Protects all fields in the cal_dmaqueue. + */ + spinlock_t lock; + + /** + * Buffers queued to the driver and waiting for DMA processing. + * Buffers are added to the list by the vb2 .buffer_queue() operation, + * and move to @pending when they are scheduled for the next frame. + */ + struct list_head queue; + /** + * Buffer provided to the hardware to DMA the next frame. Will move to + * @active at the end of the current frame. + */ + struct cal_buffer *pending; + /** + * Buffer being DMA'ed to for the current frame. Will be retired and + * given back to vb2 at the end of the current frame if a @pending + * buffer has been scheduled to replace it. + */ + struct cal_buffer *active; + + /** State of the DMA engine. */ + enum cal_dma_state state; + /** Wait queue to signal a @state transition to CAL_DMA_STOPPED. */ + struct wait_queue_head wait; }; struct cal_camerarx_data { @@ -108,8 +157,14 @@ struct cal_camerarx { unsigned int instance; struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_ep_node; struct device_node *sensor_node; struct v4l2_subdev *sensor; + + struct v4l2_subdev subdev; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + const struct cal_format_info *fmtinfo; }; struct cal_dev { @@ -149,33 +204,22 @@ struct cal_ctx { /* v4l2_ioctl mutex */ struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - struct cal_dmaqueue vidq; + struct cal_dmaqueue dma; /* video capture */ - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; + struct v4l2_format v_fmt; /* Current subdev enumerated format */ - const struct cal_fmt **active_fmt; + const struct cal_format_info **active_fmt; unsigned int num_active_fmt; unsigned int sequence; struct vb2_queue vb_vidq; unsigned int index; unsigned int cport; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; }; extern unsigned int cal_debug; @@ -215,7 +259,7 @@ static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) iowrite32(val, cal->base + offset); } -static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +static __always_inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) { return FIELD_GET(mask, cal_read(cal, offset)); } @@ -239,25 +283,22 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask) *valp = val; } +extern const struct cal_format_info cal_formats[]; +extern const unsigned int cal_num_formats; +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc); +const struct cal_format_info *cal_format_by_code(u32 code); + void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); -void cal_camerarx_stop(struct cal_camerarx *phy); -void cal_camerarx_enable_irqs(struct cal_camerarx *phy); -void cal_camerarx_disable_irqs(struct cal_camerarx *phy); -void cal_camerarx_ppi_enable(struct cal_camerarx *phy); -void cal_camerarx_ppi_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance); void cal_camerarx_destroy(struct cal_camerarx *phy); -void cal_ctx_csi2_config(struct cal_ctx *ctx); -void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height); -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); +void cal_ctx_start(struct cal_ctx *ctx); +void cal_ctx_stop(struct cal_ctx *ctx); int cal_ctx_v4l2_register(struct cal_ctx *ctx); void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 779dd74b82d0..10251b787674 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1683,7 +1683,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, } } - memset(pix->reserved, 0, sizeof(pix->reserved)); for (i = 0; i < pix->num_planes; i++) { plane_fmt = &pix->plane_fmt[i]; depth = fmt->vpdma_fmt[i]->depth; @@ -1713,7 +1712,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, plane_fmt->bytesperline * depth) >> 3; } - memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); } return 0; diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 53570250a25d..133122e38515 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -370,19 +370,13 @@ static int video_mux_async_register(struct video_mux *vmux, if (!ep) continue; - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - fwnode_handle_put(ep); - return -ENOMEM; - } - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &vmux->notifier, ep, asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &vmux->notifier, ep, struct v4l2_async_subdev); fwnode_handle_put(ep); - if (ret) { - kfree(asd); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); /* OK if asd already exists */ if (ret != -EEXIST) return ret; diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 56c62122a81a..37cf33c7e6ca 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -44,16 +44,16 @@ struct vsp1_uif; #define VSP1_MAX_UIF 2 #define VSP1_MAX_WPF 4 -#define VSP1_HAS_LUT (1 << 1) -#define VSP1_HAS_SRU (1 << 2) -#define VSP1_HAS_BRU (1 << 3) -#define VSP1_HAS_CLU (1 << 4) -#define VSP1_HAS_WPF_VFLIP (1 << 5) -#define VSP1_HAS_WPF_HFLIP (1 << 6) -#define VSP1_HAS_HGO (1 << 7) -#define VSP1_HAS_HGT (1 << 8) -#define VSP1_HAS_BRS (1 << 9) -#define VSP1_HAS_EXT_DL (1 << 10) +#define VSP1_HAS_LUT BIT(1) +#define VSP1_HAS_SRU BIT(2) +#define VSP1_HAS_BRU BIT(3) +#define VSP1_HAS_CLU BIT(4) +#define VSP1_HAS_WPF_VFLIP BIT(5) +#define VSP1_HAS_WPF_HFLIP BIT(6) +#define VSP1_HAS_HGO BIT(7) +#define VSP1_HAS_HGT BIT(8) +#define VSP1_HAS_BRS BIT(9) +#define VSP1_HAS_EXT_DL BIT(10) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index dc62533cf32c..aa66e4f5f3f3 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -882,8 +882,10 @@ static int vsp1_probe(struct platform_device *pdev) } done: - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + rcar_fcp_put(vsp1->fcp); + } return ret; } diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index cc2856efea59..bf4015d852e3 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -359,7 +359,7 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, dev_dbg(xdev->dev, "parsing node %p\n", fwnode); while (1) { - struct v4l2_async_subdev *asd; + struct xvip_graph_entity *xge; ep = fwnode_graph_get_next_endpoint(fwnode, ep); if (ep == NULL) @@ -382,12 +382,12 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, continue; } - asd = v4l2_async_notifier_add_fwnode_subdev( + xge = v4l2_async_notifier_add_fwnode_subdev( &xdev->notifier, remote, - sizeof(struct xvip_graph_entity)); + struct xvip_graph_entity); fwnode_handle_put(remote); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + if (IS_ERR(xge)) { + ret = PTR_ERR(xge); goto err_notifier_cleanup; } } |