1
0
mirror of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2026-01-18 04:20:44 +00:00
Neil Armstrong a5925a2ce0 media: iris: add VPU33 specific encoding buffer calculation
The VPU33 found in the SM8650 Platform requires some slighly different
buffer calculation for encoding to allow working with the latest
firwware uploaded on linux-firmware at [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/commit/?id=ece445af91bbee49bf0d8b23c2b99b596ae6eac7

Suggested-by: Vikash Garodia <quic_vgarodia@quicinc.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Vikash Garodia <quic_vgarodia@quicinc.com>
Signed-off-by: Bryan O'Donoghue <bod@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
2025-09-10 09:02:48 +02:00

785 lines
23 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include "iris_buffer.h"
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_vpu_buffer.h"
#define PIXELS_4K 4096
#define MAX_WIDTH 4096
#define MAX_HEIGHT 2304
#define Y_STRIDE_ALIGN 128
#define UV_STRIDE_ALIGN 128
#define Y_SCANLINE_ALIGN 32
#define UV_SCANLINE_ALIGN 16
#define UV_SCANLINE_ALIGN_QC08C 32
#define META_STRIDE_ALIGNED 64
#define META_SCANLINE_ALIGNED 16
#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))
/*
* NV12:
* YUV 4:2:0 image with a plane of 8 bit Y samples followed
* by an interleaved U/V plane containing 8 bit 2x2 subsampled
* colour difference samples.
*
* <-Y/UV_Stride (aligned to 128)->
* <------- Width ------->
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height |
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32)
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . V |
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . V
* U V U V U V U V U V U V . . . . ^
* U V U V U V U V U V U V . . . . |
* U V U V U V U V U V U V . . . . |
* U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16)
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . V
* . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K
*
* y_stride : Width aligned to 128
* uv_stride : Width aligned to 128
* y_scanlines: Height aligned to 32
* uv_scanlines: Height/2 aligned to 16
* Total size = align((y_stride * y_scanlines
* + uv_stride * uv_scanlines , 4096)
*
* Note: All the alignments are hardware requirements.
*/
static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
{
u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines;
struct v4l2_format *f;
if (inst->domain == DECODER)
f = inst->fmt_dst;
else
f = inst->fmt_src;
y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN);
uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN);
y_plane = y_stride * y_scanlines;
uv_plane = uv_stride * uv_scanlines;
return ALIGN(y_plane + uv_plane, PIXELS_4K);
}
/*
* QC08C:
* Compressed Macro-tile format for NV12.
* Contains 4 planes in the following order -
* (A) Y_Meta_Plane
* (B) Y_UBWC_Plane
* (C) UV_Meta_Plane
* (D) UV_UBWC_Plane
*
* Y_Meta_Plane consists of meta information to decode compressed
* tile data in Y_UBWC_Plane.
* Y_UBWC_Plane consists of Y data in compressed macro-tile format.
* UBWC decoder block will use the Y_Meta_Plane data together with
* Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
*
* UV_Meta_Plane consists of meta information to decode compressed
* tile data in UV_UBWC_Plane.
* UV_UBWC_Plane consists of UV data in compressed macro-tile format.
* UBWC decoder block will use UV_Meta_Plane data together with
* UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
* subsampled color difference samples.
*
* Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
* and randomly accessible. There is no dependency between tiles.
*
* <----- y_meta_stride ----> (aligned to 64)
* <-------- Width ------>
* M M M M M M M M M M M M . . ^ ^
* M M M M M M M M M M M M . . | |
* M M M M M M M M M M M M . . Height |
* M M M M M M M M M M M M . . | y_meta_scanlines (aligned to 16)
* M M M M M M M M M M M M . . | |
* M M M M M M M M M M M M . . | |
* M M M M M M M M M M M M . . | |
* M M M M M M M M M M M M . . V |
* . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
* . . . . . . . . . . . . . . V
* <--Compressed tile y_stride---> (aligned to 128)
* <------- Width ------->
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height |
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile y_scanlines (aligned to 32)
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . V |
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
* . . . . . . . . . . . . . . . . V
* <----- uv_meta_stride ----> (aligned to 64)
* M M M M M M M M M M M M . . ^
* M M M M M M M M M M M M . . |
* M M M M M M M M M M M M . . |
* M M M M M M M M M M M M . . uv_meta_scanlines (aligned to 16)
* . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . V
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
* <--Compressed tile uv_stride---> (aligned to 128)
* U* V* U* V* U* V* U* V* . . . . ^
* U* V* U* V* U* V* U* V* . . . . |
* U* V* U* V* U* V* U* V* . . . . |
* U* V* U* V* U* V* U* V* . . . . uv_scanlines (aligned to 32)
* . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . V
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
*
* y_stride: width aligned to 128
* uv_stride: width aligned to 128
* y_scanlines: height aligned to 32
* uv_scanlines: height aligned to 32
* y_plane: buffer size aligned to 4096
* uv_plane: buffer size aligned to 4096
* y_meta_stride: width aligned to 64
* y_meta_scanlines: height aligned to 16
* y_meta_plane: buffer size aligned to 4096
* uv_meta_stride: width aligned to 64
* uv_meta_scanlines: height aligned to 16
* uv_meta_plane: buffer size aligned to 4096
*
* Total size = align( y_plane + uv_plane +
* y_meta_plane + uv_meta_plane, 4096)
*
* Note: All the alignments are hardware requirements.
*/
static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
{
u32 y_plane, uv_plane, y_stride, uv_stride;
struct v4l2_format *f = inst->fmt_dst;
u32 uv_meta_stride, uv_meta_plane;
u32 y_meta_stride, y_meta_plane;
y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1),
META_STRIDE_ALIGNED);
y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height,
META_SCANLINE_ALIGNED >> 1),
META_SCANLINE_ALIGNED);
y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);
y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN), PIXELS_4K);
uv_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width / 2, META_STRIDE_ALIGNED >> 2),
META_STRIDE_ALIGNED);
uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height / 2,
META_SCANLINE_ALIGNED >> 1),
META_SCANLINE_ALIGNED);
uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);
uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
uv_plane = ALIGN(uv_stride * ALIGN(f->fmt.pix_mp.height / 2, UV_SCANLINE_ALIGN_QC08C),
PIXELS_4K);
return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
}
static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
u32 base_res_mbs = NUM_MBS_4K;
u32 frame_size, num_mbs;
u32 div_factor = 2;
num_mbs = iris_get_mbpf(inst);
if (num_mbs > NUM_MBS_4K) {
div_factor = 4;
base_res_mbs = caps->max_mbpf;
} else {
if (inst->codec == V4L2_PIX_FMT_VP9)
div_factor = 1;
}
/*
* frame_size = YUVsize / div_factor
* where YUVsize = resolution_in_MBs * MBs_in_pixel * 3 / 2
*/
frame_size = base_res_mbs * (16 * 16) * 3 / 2 / div_factor;
return ALIGN(frame_size, PIXELS_4K);
}
static u32 iris_enc_bitstream_buffer_size(struct iris_inst *inst)
{
u32 aligned_width, aligned_height, bitstream_size, yuv_size;
int bitrate_mode, frame_rc;
struct v4l2_format *f;
f = inst->fmt_dst;
bitrate_mode = inst->fw_caps[BITRATE_MODE].value;
frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value;
aligned_width = ALIGN(f->fmt.pix_mp.width, 32);
aligned_height = ALIGN(f->fmt.pix_mp.height, 32);
bitstream_size = aligned_width * aligned_height * 3;
yuv_size = (aligned_width * aligned_height * 3) >> 1;
if (aligned_width * aligned_height > (4096 * 2176))
/* bitstream_size = 0.25 * yuv_size; */
bitstream_size = (bitstream_size >> 3);
else if (aligned_width * aligned_height > (1280 * 720))
/* bitstream_size = 0.5 * yuv_size; */
bitstream_size = (bitstream_size >> 2);
if ((!frame_rc || bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) &&
bitstream_size < yuv_size)
bitstream_size = (bitstream_size << 1);
return ALIGN(bitstream_size, 4096);
}
int iris_get_buffer_size(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
if (inst->domain == DECODER) {
switch (buffer_type) {
case BUF_INPUT:
return iris_dec_bitstream_buffer_size(inst);
case BUF_OUTPUT:
return iris_yuv_buffer_size_nv12(inst);
case BUF_DPB:
return iris_yuv_buffer_size_qc08c(inst);
default:
return 0;
}
} else {
switch (buffer_type) {
case BUF_INPUT:
return iris_yuv_buffer_size_nv12(inst);
case BUF_OUTPUT:
return iris_enc_bitstream_buffer_size(inst);
default:
return 0;
}
}
}
static void iris_fill_internal_buf_info(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
struct iris_buffers *buffers = &inst->buffers[buffer_type];
buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type);
buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
}
void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->dec_ip_int_buf_tbl;
internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
} else {
internal_buf_type = platform_data->dec_op_int_buf_tbl;
internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->enc_ip_int_buf_tbl;
internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
} else {
internal_buf_type = platform_data->enc_op_int_buf_tbl;
internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
}
}
}
static int iris_create_internal_buffer(struct iris_inst *inst,
enum iris_buffer_type buffer_type, u32 index)
{
struct iris_buffers *buffers = &inst->buffers[buffer_type];
struct iris_core *core = inst->core;
struct iris_buffer *buffer;
if (!buffers->size)
return 0;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer)
return -ENOMEM;
INIT_LIST_HEAD(&buffer->list);
buffer->type = buffer_type;
buffer->index = index;
buffer->buffer_size = buffers->size;
buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
list_add_tail(&buffer->list, &buffers->list);
buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
&buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
if (!buffer->kvaddr)
return -ENOMEM;
return 0;
}
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
u32 internal_buffer_count, i, j;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
int ret;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->dec_ip_int_buf_tbl;
internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->dec_op_int_buf_tbl;
internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->enc_ip_int_buf_tbl;
internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->enc_op_int_buf_tbl;
internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
}
}
for (i = 0; i < internal_buffer_count; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
for (j = 0; j < buffers->min_count; j++) {
ret = iris_create_internal_buffer(inst, internal_buf_type[i], j);
if (ret)
return ret;
}
}
return 0;
}
int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
{
const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
int ret;
ret = hfi_ops->session_queue_buf(inst, buf);
if (ret)
return ret;
buf->attr &= ~BUF_ATTR_DEFERRED;
buf->attr |= BUF_ATTR_QUEUED;
return 0;
}
int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type)
{
struct iris_buffer *buffer, *next;
struct iris_buffers *buffers;
int ret = 0;
buffers = &inst->buffers[buffer_type];
list_for_each_entry_safe(buffer, next, &buffers->list, list) {
if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
continue;
if (buffer->attr & BUF_ATTR_QUEUED)
continue;
if (buffer->attr & BUF_ATTR_DEFERRED) {
ret = iris_queue_buffer(inst, buffer);
if (ret)
return ret;
}
}
return ret;
}
int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
struct iris_buffer *buffer, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->dec_ip_int_buf_tbl;
internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->dec_op_int_buf_tbl;
internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->enc_ip_int_buf_tbl;
internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->enc_op_int_buf_tbl;
internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
}
}
for (i = 0; i < internal_buffer_count; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
list_for_each_entry_safe(buffer, next, &buffers->list, list) {
if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
continue;
if (buffer->attr & BUF_ATTR_QUEUED)
continue;
if (buffer->type == BUF_DPB && inst->state != IRIS_INST_STREAMING) {
buffer->attr |= BUF_ATTR_DEFERRED;
continue;
}
ret = iris_queue_buffer(inst, buffer);
if (ret)
return ret;
}
}
return 0;
}
int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
{
struct iris_core *core = inst->core;
list_del(&buffer->list);
dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr,
buffer->device_addr, buffer->dma_attrs);
kfree(buffer);
return 0;
}
static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
struct iris_buffer *buf, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
u32 i, len;
int ret;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->dec_ip_int_buf_tbl;
len = platform_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->dec_op_int_buf_tbl;
len = platform_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = platform_data->enc_ip_int_buf_tbl;
len = platform_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->enc_op_int_buf_tbl;
len = platform_data->enc_op_int_buf_tbl_size;
}
}
for (i = 0; i < len; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
list_for_each_entry_safe(buf, next, &buffers->list, list) {
/*
* during stream on, skip destroying internal(DPB) buffer
* if firmware did not return it.
* during close, destroy all buffers irrespectively.
*/
if (!force && buf->attr & BUF_ATTR_QUEUED)
continue;
ret = iris_destroy_internal_buffer(inst, buf);
if (ret)
return ret;
}
}
if (force) {
if (inst->domain == DECODER)
buffers = &inst->buffers[BUF_PERSIST];
else
buffers = &inst->buffers[BUF_ARP];
list_for_each_entry_safe(buf, next, &buffers->list, list) {
ret = iris_destroy_internal_buffer(inst, buf);
if (ret)
return ret;
}
}
return 0;
}
int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane)
{
return iris_destroy_internal_buffers(inst, plane, true);
}
int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane)
{
return iris_destroy_internal_buffers(inst, plane, false);
}
static int iris_release_internal_buffers(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
struct iris_buffers *buffers = &inst->buffers[buffer_type];
struct iris_buffer *buffer, *next;
int ret;
list_for_each_entry_safe(buffer, next, &buffers->list, list) {
if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
continue;
if (!(buffer->attr & BUF_ATTR_QUEUED))
continue;
ret = hfi_ops->session_release_buf(inst, buffer);
if (ret)
return ret;
buffer->attr |= BUF_ATTR_PENDING_RELEASE;
}
return 0;
}
static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
if (inst->domain == DECODER) {
internal_buf_type = platform_data->dec_ip_int_buf_tbl;
internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = platform_data->enc_ip_int_buf_tbl;
internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
}
for (i = 0; i < internal_buffer_count; i++) {
ret = iris_release_internal_buffers(inst, internal_buf_type[i]);
if (ret)
return ret;
}
return 0;
}
int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst, enum iris_buffer_type buffer_type)
{
struct iris_buffers *buffers = &inst->buffers[buffer_type];
struct iris_buffer *buffer, *next;
int ret;
u32 i;
if (!list_empty(&buffers->list))
return 0;
iris_fill_internal_buf_info(inst, buffer_type);
for (i = 0; i < buffers->min_count; i++) {
ret = iris_create_internal_buffer(inst, buffer_type, i);
if (ret)
return ret;
}
list_for_each_entry_safe(buffer, next, &buffers->list, list) {
if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
continue;
if (buffer->attr & BUF_ATTR_QUEUED)
continue;
ret = iris_queue_buffer(inst, buffer);
if (ret)
return ret;
}
return 0;
}
int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst)
{
int ret;
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
ret = iris_release_input_internal_buffers(inst);
if (ret)
return ret;
ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
return iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
}
int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_m2m_buffer *buffer, *n;
struct iris_buffer *buf;
int ret;
iris_scale_power(inst);
if (buf_type == BUF_INPUT) {
v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
buf = to_iris_buffer(&buffer->vb);
if (!(buf->attr & BUF_ATTR_DEFERRED))
continue;
ret = iris_queue_buffer(inst, buf);
if (ret)
return ret;
}
} else {
v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
buf = to_iris_buffer(&buffer->vb);
if (!(buf->attr & BUF_ATTR_DEFERRED))
continue;
ret = iris_queue_buffer(inst, buf);
if (ret)
return ret;
}
}
return 0;
}
void iris_vb2_queue_error(struct iris_inst *inst)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct vb2_queue *q;
q = v4l2_m2m_get_src_vq(m2m_ctx);
vb2_queue_error(q);
q = v4l2_m2m_get_dst_vq(m2m_ctx);
vb2_queue_error(q);
}
static struct vb2_v4l2_buffer *
iris_helper_find_buf(struct iris_inst *inst, u32 type, u32 idx)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
if (V4L2_TYPE_IS_OUTPUT(type))
return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
else
return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
}
static void iris_get_ts_metadata(struct iris_inst *inst, u64 timestamp_ns,
struct vb2_v4l2_buffer *vbuf)
{
u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
u32 i;
for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) {
if (inst->tss[i].ts_ns != timestamp_ns)
continue;
vbuf->flags &= ~mask;
vbuf->flags |= inst->tss[i].flags;
vbuf->timecode = inst->tss[i].tc;
return;
}
vbuf->flags &= ~mask;
vbuf->flags |= inst->tss[inst->metadata_idx].flags;
vbuf->timecode = inst->tss[inst->metadata_idx].tc;
}
int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct vb2_v4l2_buffer *vbuf;
struct vb2_buffer *vb2;
u32 type, state;
switch (buf->type) {
case BUF_INPUT:
type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
break;
case BUF_OUTPUT:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
break;
default:
return 0; /* Internal DPB Buffers */
}
vbuf = iris_helper_find_buf(inst, type, buf->index);
if (!vbuf)
return -EINVAL;
vb2 = &vbuf->vb2_buf;
vbuf->flags |= buf->flags;
if (buf->flags & V4L2_BUF_FLAG_ERROR) {
state = VB2_BUF_STATE_ERROR;
vb2_set_plane_payload(vb2, 0, 0);
vb2->timestamp = 0;
v4l2_m2m_buf_done(vbuf, state);
return 0;
}
if (V4L2_TYPE_IS_CAPTURE(type)) {
vb2_set_plane_payload(vb2, 0, buf->data_size);
vbuf->sequence = inst->sequence_cap++;
iris_get_ts_metadata(inst, buf->timestamp, vbuf);
} else {
vbuf->sequence = inst->sequence_out++;
}
if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
if (!v4l2_m2m_has_stopped(m2m_ctx)) {
const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
v4l2_event_queue_fh(&inst->fh, &ev);
v4l2_m2m_mark_stopped(m2m_ctx);
}
inst->last_buffer_dequeued = true;
}
state = VB2_BUF_STATE_DONE;
vb2->timestamp = buf->timestamp;
v4l2_m2m_buf_done(vbuf, state);
return 0;
}