Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,14 @@
config VIDEO_QCOM_IRIS
tristate "Qualcomm iris V4L2 decoder driver"
depends on VIDEO_DEV
depends on ARCH_QCOM || COMPILE_TEST
select V4L2_MEM2MEM_DEV
select QCOM_MDT_LOADER
select QCOM_SCM
select QCOM_UBWC_CONFIG
select VIDEOBUF2_DMA_CONTIG
help
This is a V4L2 driver for Qualcomm iris video accelerator
hardware. It accelerates decoding operations on various
Qualcomm SoCs.
To compile this driver as a module choose m here.
@@ -0,0 +1,32 @@
qcom-iris-objs += iris_buffer.o \
iris_common.o \
iris_core.o \
iris_ctrls.o \
iris_firmware.o \
iris_hfi_common.o \
iris_hfi_gen1.o \
iris_hfi_gen1_command.o \
iris_hfi_gen1_response.o \
iris_hfi_gen2.o \
iris_hfi_gen2_command.o \
iris_hfi_gen2_packet.o \
iris_hfi_gen2_response.o \
iris_hfi_queue.o \
iris_platform_vpu2.o \
iris_platform_vpu3x.o \
iris_power.o \
iris_probe.o \
iris_resources.o \
iris_state.o \
iris_utils.o \
iris_vidc.o \
iris_vb2.o \
iris_vdec.o \
iris_venc.o \
iris_vpu2.o \
iris_vpu3x.o \
iris_vpu4x.o \
iris_vpu_buffer.o \
iris_vpu_common.o \
obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
@@ -0,0 +1,993 @@
// 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 Y_STRIDE_ALIGN_P010 256
#define UV_STRIDE_ALIGN 128
#define UV_STRIDE_ALIGN_P010 256
#define Y_SCANLINE_ALIGN 32
#define Y_SCANLINE_ALIGN_QC10C 16
#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);
}
/*
* P010:
* YUV 4:2:0 image with a plane of 10 bit Y samples followed
* by an interleaved U/V plane containing 10 bit 2x2 subsampled
* colour difference samples.
*
* <-Y/UV_Stride (aligned to 256)->
* <----- Width*2 ------->
* 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*2 aligned to 256
* uv_stride : Width*2 aligned to 256
* 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_p010(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 * 2, Y_STRIDE_ALIGN_P010);
uv_stride = ALIGN(f->fmt.pix_mp.width * 2, UV_STRIDE_ALIGN_P010);
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;
u32 uv_meta_stride, uv_meta_plane;
u32 y_meta_stride, y_meta_plane;
struct v4l2_format *f = NULL;
if (inst->domain == DECODER)
f = inst->fmt_dst;
else
f = inst->fmt_src;
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);
}
/*
* QC10C:
* UBWC-compressed format for P010.
* 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 10 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 10 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 ----------> (aligned to 48)
* 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 . . | Meta_Y_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 256)
* <------- Width * 4/3 ---------> (aligned to 48)
* 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 16)
* 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)
* <----- Width / 2 --------> (aligned to 24)
* 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/2 |
* M M M M M M M M M M M M . . V M_UV_Scanlines (aligned to 16)
* . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . V
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
* <--Compressed tile UV stride--> (aligned to 256)
* <------- Width * 4/3 ---------> (aligned to 48)
* 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 256
* uv_stride: width aligned to 256
* y_scanlines: height aligned to 16
* uv_scanlines: height aligned to 16
* 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_qc10c(struct iris_inst *inst)
{
u32 y_plane, uv_plane, y_stride, uv_stride;
u32 uv_meta_stride, uv_meta_plane;
u32 y_meta_stride, y_meta_plane;
struct v4l2_format *f;
if (inst->domain == DECODER)
f = inst->fmt_dst;
else
f = inst->fmt_src;
y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, 48),
META_STRIDE_ALIGNED);
y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height, 4),
META_SCANLINE_ALIGNED);
y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);
y_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, Y_STRIDE_ALIGN_P010);
y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN_QC10C),
PIXELS_4K);
uv_meta_stride = ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.width + 1) >> 1, 24),
META_STRIDE_ALIGNED);
uv_meta_plane = uv_meta_stride *
ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.height + 1) >> 1, 4),
META_SCANLINE_ALIGNED);
uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);
uv_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, UV_STRIDE_ALIGN_P010);
uv_plane = ALIGN(uv_stride * ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN),
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:
if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
return iris_yuv_buffer_size_qc08c(inst);
else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C)
return iris_yuv_buffer_size_qc10c(inst);
else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010)
return iris_yuv_buffer_size_p010(inst);
else
return iris_yuv_buffer_size_nv12(inst);
case BUF_DPB:
if (iris_fmt_is_10bit(inst->fmt_dst->fmt.pix_mp.pixelformat))
return iris_yuv_buffer_size_qc10c(inst);
else
return iris_yuv_buffer_size_qc08c(inst);
default:
return 0;
}
} else {
switch (buffer_type) {
case BUF_INPUT:
if (inst->fmt_src->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
return iris_yuv_buffer_size_qc08c(inst);
else
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_firmware_desc->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_firmware_data *firmware_data = inst->core->iris_firmware_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
internal_buffer_count = firmware_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 = firmware_data->dec_op_int_buf_tbl;
internal_buffer_count = firmware_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 = firmware_data->enc_ip_int_buf_tbl;
internal_buffer_count = firmware_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 = firmware_data->enc_op_int_buf_tbl;
internal_buffer_count = firmware_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_obj(*buffer);
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;
buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
&buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
if (!buffer->kvaddr) {
kfree(buffer);
return -ENOMEM;
}
list_add_tail(&buffer->list, &buffers->list);
return 0;
}
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
{
const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_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 = firmware_data->dec_ip_int_buf_tbl;
internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->dec_op_int_buf_tbl;
internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->enc_op_int_buf_tbl;
internal_buffer_count = firmware_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_session_ops *hfi_ops = inst->hfi_session_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_firmware_data *firmware_data = inst->core->iris_firmware_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 = firmware_data->dec_ip_int_buf_tbl;
internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->dec_op_int_buf_tbl;
internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->enc_op_int_buf_tbl;
internal_buffer_count = firmware_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_firmware_data *firmware_data = inst->core->iris_firmware_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 = firmware_data->dec_ip_int_buf_tbl;
len = firmware_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->dec_op_int_buf_tbl;
len = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
len = firmware_data->enc_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->enc_op_int_buf_tbl;
len = firmware_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_session_ops *hfi_ops = inst->hfi_session_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;
buffer->attr |= BUF_ATTR_PENDING_RELEASE;
ret = hfi_ops->session_release_buf(inst, buffer);
if (ret) {
buffer->attr &= ~BUF_ATTR_PENDING_RELEASE;
return ret;
}
}
return 0;
}
static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
if (inst->domain == DECODER) {
internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
internal_buffer_count = firmware_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;
}
@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_BUFFER_H__
#define __IRIS_BUFFER_H__
#include <media/videobuf2-v4l2.h>
struct iris_inst;
#define to_iris_buffer(ptr) container_of(ptr, struct iris_buffer, vb2)
/**
* enum iris_buffer_type
*
* @BUF_INPUT: input buffer to the iris hardware
* @BUF_OUTPUT: output buffer from the iris hardware
* @BUF_BIN: buffer to store intermediate bin data
* @BUF_ARP: buffer for auto register programming
* @BUF_COMV: buffer to store colocated motion vectors
* @BUF_NON_COMV: buffer to hold config data for HW
* @BUF_LINE: buffer to store decoding/encoding context data for HW
* @BUF_DPB: buffer to store display picture buffers for reference
* @BUF_PERSIST: buffer to store session context data
* @BUF_SCRATCH_1: buffer to store decoding/encoding context data for HW
* @BUF_SCRATCH_2: buffer to store encoding context data for HW
* @BUF_VPSS: buffer to store VPSS context data for HW
* @BUF_PARTIAL: buffer for AV1 IBC data
* @BUF_TYPE_MAX: max buffer types
*/
enum iris_buffer_type {
BUF_INPUT = 1,
BUF_OUTPUT,
BUF_BIN,
BUF_ARP,
BUF_COMV,
BUF_NON_COMV,
BUF_LINE,
BUF_DPB,
BUF_PERSIST,
BUF_SCRATCH_1,
BUF_SCRATCH_2,
BUF_VPSS,
BUF_PARTIAL,
BUF_TYPE_MAX,
};
/*
* enum iris_buffer_attributes
*
* BUF_ATTR_DEFERRED: buffer queued by client but not submitted to firmware.
* BUF_ATTR_PENDING_RELEASE: buffers requested to be released from firmware.
* BUF_ATTR_QUEUED: buffers submitted to firmware.
* BUF_ATTR_DEQUEUED: buffers received from firmware.
* BUF_ATTR_BUFFER_DONE: buffers sent back to vb2.
*/
enum iris_buffer_attributes {
BUF_ATTR_DEFERRED = BIT(0),
BUF_ATTR_PENDING_RELEASE = BIT(1),
BUF_ATTR_QUEUED = BIT(2),
BUF_ATTR_DEQUEUED = BIT(3),
BUF_ATTR_BUFFER_DONE = BIT(4),
};
/**
* struct iris_buffer
*
* @vb2: v4l2 vb2 buffer
* @list: list head for the iris_buffers structure
* @inst: iris instance structure
* @type: enum for type of iris buffer
* @index: identifier for the iris buffer
* @fd: file descriptor of the buffer
* @buffer_size: accessible buffer size in bytes starting from addr_offset
* @data_offset: accessible buffer offset from base address
* @data_size: data size in bytes
* @device_addr: device address of the buffer
* @kvaddr: kernel virtual address of the buffer
* @dma_attrs: dma attributes
* @flags: buffer flags. It is represented as bit masks.
* @timestamp: timestamp of the buffer in nano seconds (ns)
* @attr: enum for iris buffer attributes
*/
struct iris_buffer {
struct vb2_v4l2_buffer vb2;
struct list_head list;
struct iris_inst *inst;
enum iris_buffer_type type;
u32 index;
int fd;
size_t buffer_size;
u32 data_offset;
size_t data_size;
dma_addr_t device_addr;
void *kvaddr;
unsigned long dma_attrs;
u32 flags; /* V4L2_BUF_FLAG_* */
u64 timestamp;
enum iris_buffer_attributes attr;
};
struct iris_buffers {
struct list_head list;
u32 min_count;
u32 size;
};
int iris_get_buffer_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
void iris_get_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type);
int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst, enum iris_buffer_type buf_type);
int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst);
int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf);
int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type);
int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf);
void iris_vb2_queue_error(struct iris_inst *inst);
#endif
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <media/v4l2-mem2mem.h>
#include "iris_common.h"
#include "iris_ctrls.h"
#include "iris_instance.h"
#include "iris_power.h"
int iris_vb2_buffer_to_driver(struct vb2_buffer *vb2, struct iris_buffer *buf)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
buf->type = iris_v4l2_type_to_driver(vb2->type);
buf->index = vb2->index;
buf->fd = vb2->planes[0].m.fd;
buf->buffer_size = vb2->planes[0].length;
buf->data_offset = vb2->planes[0].data_offset;
buf->data_size = vb2->planes[0].bytesused - vb2->planes[0].data_offset;
buf->flags = vbuf->flags;
buf->timestamp = vb2->timestamp;
buf->attr = 0;
return 0;
}
void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
{
u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
struct vb2_buffer *vb = &vbuf->vb2_buf;
u64 ts_us = vb->timestamp;
if (inst->metadata_idx >= ARRAY_SIZE(inst->tss))
inst->metadata_idx = 0;
do_div(ts_us, NSEC_PER_USEC);
inst->tss[inst->metadata_idx].flags = vbuf->flags & mask;
inst->tss[inst->metadata_idx].tc = vbuf->timecode;
inst->tss[inst->metadata_idx].ts_us = ts_us;
inst->tss[inst->metadata_idx].ts_ns = vb->timestamp;
inst->metadata_idx++;
}
int iris_process_streamon_input(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state set_sub_state = 0;
int ret;
iris_scale_power(inst);
ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
ret = iris_inst_change_sub_state(inst, IRIS_INST_SUB_INPUT_PAUSE, 0);
if (ret)
return ret;
}
if (inst->domain == DECODER &&
(inst->sub_state & IRIS_INST_SUB_DRC ||
inst->sub_state & IRIS_INST_SUB_DRAIN ||
inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) {
if (!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) {
if (hfi_ops->session_pause) {
ret = hfi_ops->session_pause(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
}
set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
}
}
ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
inst->last_buffer_dequeued = false;
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
int iris_process_streamon_output(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
bool drain_active, drc_active, first_ipsc;
int ret = 0;
iris_scale_power(inst);
first_ipsc = inst->sub_state & IRIS_INST_SUB_FIRST_IPSC;
drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
drc_active = inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST;
if (drc_active)
clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
else if (drain_active)
clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
/* Input internal buffer reconfiguration required in case of resolution change */
if (first_ipsc || drc_active) {
ret = iris_alloc_and_queue_input_int_bufs(inst);
if (ret)
return ret;
ret = iris_set_stage(inst, STAGE);
if (ret)
return ret;
ret = iris_set_pipe(inst, PIPE);
if (ret)
return ret;
}
if (inst->state == IRIS_INST_INPUT_STREAMING &&
inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
if (!drain_active)
ret = hfi_ops->session_resume_drc(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
else if (hfi_ops->session_resume_drain)
ret = hfi_ops->session_resume_drain(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
}
if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
clear_sub_state |= IRIS_INST_SUB_FIRST_IPSC;
ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE)
clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
inst->last_buffer_dequeued = false;
return iris_inst_change_sub_state(inst, clear_sub_state, 0);
}
static void iris_flush_deferred_buffers(struct iris_inst *inst,
enum iris_buffer_type type)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_m2m_buffer *buffer, *n;
struct iris_buffer *buf;
if (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) {
if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
buf->attr |= BUF_ATTR_BUFFER_DONE;
buf->data_size = 0;
iris_vb2_buffer_done(inst, buf);
}
}
}
} else {
v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
buf = to_iris_buffer(&buffer->vb);
if (buf->attr & BUF_ATTR_DEFERRED) {
if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
buf->attr |= BUF_ATTR_BUFFER_DONE;
buf->data_size = 0;
iris_vb2_buffer_done(inst, buf);
}
}
}
}
}
static void iris_kill_session(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
if (!inst->session_id)
return;
hfi_ops->session_close(inst);
iris_inst_change_state(inst, IRIS_INST_ERROR);
}
int iris_session_streamoff(struct iris_inst *inst, u32 plane)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_buffer_type buffer_type;
int ret;
switch (plane) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
buffer_type = BUF_INPUT;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
buffer_type = BUF_OUTPUT;
break;
default:
return -EINVAL;
}
ret = hfi_ops->session_stop(inst, plane);
if (ret)
goto error;
ret = iris_inst_state_change_streamoff(inst, plane);
if (ret)
goto error;
iris_flush_deferred_buffers(inst, buffer_type);
return 0;
error:
iris_kill_session(inst);
iris_flush_deferred_buffers(inst, buffer_type);
return ret;
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_COMMON_H__
#define __IRIS_COMMON_H__
struct iris_inst;
struct iris_buffer;
int iris_vb2_buffer_to_driver(struct vb2_buffer *vb2, struct iris_buffer *buf);
void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
int iris_process_streamon_input(struct iris_inst *inst);
int iris_process_streamon_output(struct iris_inst *inst);
int iris_session_streamoff(struct iris_inst *inst, u32 plane);
#endif
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include "iris_core.h"
#include "iris_firmware.h"
#include "iris_state.h"
#include "iris_vpu_common.h"
void iris_core_deinit(struct iris_core *core)
{
pm_runtime_resume_and_get(core->dev);
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_DEINIT) {
iris_fw_unload(core);
iris_vpu_power_off(core);
iris_hfi_queues_deinit(core);
core->state = IRIS_CORE_DEINIT;
}
mutex_unlock(&core->lock);
pm_runtime_put_sync(core->dev);
}
static int iris_wait_for_system_response(struct iris_core *core)
{
int ret;
if (core->state == IRIS_CORE_ERROR)
return -EIO;
ret = wait_for_completion_timeout(&core->core_init_done,
msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE));
if (!ret) {
core->state = IRIS_CORE_ERROR;
return -ETIMEDOUT;
}
return 0;
}
int iris_core_init(struct iris_core *core)
{
int ret;
mutex_lock(&core->lock);
if (core->state == IRIS_CORE_INIT) {
ret = 0;
goto exit;
} else if (core->state == IRIS_CORE_ERROR) {
ret = -EINVAL;
goto error;
}
core->state = IRIS_CORE_INIT;
ret = iris_hfi_queues_init(core);
if (ret)
goto error;
ret = iris_vpu_power_on(core);
if (ret)
goto error_queue_deinit;
ret = iris_fw_load(core);
if (ret)
goto error_power_off;
ret = iris_vpu_boot_firmware(core);
if (ret)
goto error_unload_fw;
ret = iris_vpu_switch_to_hwmode(core);
if (ret)
goto error_unload_fw;
core->iris_firmware_data->init_hfi_ops(core);
ret = iris_hfi_core_init(core);
if (ret)
goto error_unload_fw;
mutex_unlock(&core->lock);
return iris_wait_for_system_response(core);
error_unload_fw:
iris_fw_unload(core);
error_power_off:
iris_vpu_power_off(core);
error_queue_deinit:
iris_hfi_queues_deinit(core);
error:
core->state = IRIS_CORE_DEINIT;
exit:
mutex_unlock(&core->lock);
return ret;
}
@@ -0,0 +1,131 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_CORE_H__
#define __IRIS_CORE_H__
#include <linux/types.h>
#include <linux/pm_domain.h>
#include <media/v4l2-device.h>
#include "iris_hfi_common.h"
#include "iris_hfi_queue.h"
#include "iris_platform_common.h"
#include "iris_resources.h"
#include "iris_state.h"
struct icc_info {
const char *name;
u32 bw_min_kbps;
u32 bw_max_kbps;
};
#define IRIS_FW_VERSION_LENGTH 128
#define IFACEQ_CORE_PKT_SIZE (1024 * 4)
enum domain_type {
ENCODER = BIT(0),
DECODER = BIT(1),
};
struct qcom_ubwc_cfg_data;
/**
* struct iris_core - holds core parameters valid for all instances
*
* @dev: reference to device structure
* @reg_base: IO memory base address
* @irq: iris irq
* @v4l2_dev: a holder for v4l2 device structure
* @vdev_dec: iris video device structure for decoder
* @vdev_enc: iris video device structure for encoder
* @iris_v4l2_file_ops: iris v4l2 file ops
* @iris_v4l2_ioctl_ops_dec: iris v4l2 ioctl ops for decoder
* @iris_v4l2_ioctl_ops_enc: iris v4l2 ioctl ops for encoder
* @iris_vb2_ops: iris vb2 ops
* @icc_tbl: table of iris interconnects
* @icc_count: count of iris interconnects
* @pmdomain_tbl: table of iris power domains
* @opp_pmdomain_tbl: table of opp power domains
* @clock_tbl: table of iris clocks
* @clk_count: count of iris clocks
* @resets: table of iris reset clocks
* @controller_resets: table of controller reset clocks
* @iris_platform_data: a structure for platform data
* @iris_firmware_data: a pointer to the firmware (or HFI) specific data
* @iris_firmware_desc: a pointer to the firmware-specific descriptive data
* @ubwc_cfg: UBWC configuration for the platform
* @state: current state of core
* @iface_q_table_daddr: device address for interface queue table memory
* @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
* @iface_q_table_vaddr: virtual address for interface queue table memory
* @sfr_vaddr: virtual address for SFR (Sub System Failure Reason) register memory
* @command_queue: shared interface queue to send commands to firmware
* @message_queue: shared interface queue to receive responses from firmware
* @debug_queue: shared interface queue to receive debug info from firmware
* @lock: a lock for this strucure
* @response_packet: a pointer to response packet from fw to driver
* @header_id: id of packet header
* @packet_id: id of packet
* @power: a structure for clock and bw information
* @hfi_sys_ops: iris HFI system ops
* @core_init_done: structure of signal completion for system response
* @intr_status: interrupt status
* @sys_error_handler: a delayed work for handling system fatal error
* @instances: a list_head of all instances
* @inst_fw_caps_dec: an array of supported instance capabilities by decoder
* @inst_fw_caps_enc: an array of supported instance capabilities by encoder
*/
struct iris_core {
struct device *dev;
void __iomem *reg_base;
int irq;
struct v4l2_device v4l2_dev;
struct video_device *vdev_dec;
struct video_device *vdev_enc;
const struct v4l2_file_operations *iris_v4l2_file_ops;
const struct v4l2_ioctl_ops *iris_v4l2_ioctl_ops_dec;
const struct v4l2_ioctl_ops *iris_v4l2_ioctl_ops_enc;
const struct vb2_ops *iris_vb2_ops;
struct icc_bulk_data *icc_tbl;
u32 icc_count;
struct dev_pm_domain_list *pmdomain_tbl;
struct dev_pm_domain_list *opp_pmdomain_tbl;
struct clk_bulk_data *clock_tbl;
u32 clk_count;
struct reset_control_bulk_data *resets;
struct reset_control_bulk_data *controller_resets;
const struct iris_platform_data *iris_platform_data;
const struct iris_firmware_data *iris_firmware_data;
const struct iris_firmware_desc *iris_firmware_desc;
const struct qcom_ubwc_cfg_data *ubwc_cfg;
enum iris_core_state state;
dma_addr_t iface_q_table_daddr;
dma_addr_t sfr_daddr;
void *iface_q_table_vaddr;
void *sfr_vaddr;
struct iris_iface_q_info command_queue;
struct iris_iface_q_info message_queue;
struct iris_iface_q_info debug_queue;
struct mutex lock; /* lock for core related operations */
u8 *response_packet;
u32 header_id;
u32 packet_id;
struct iris_core_power power;
const struct iris_hfi_sys_ops *hfi_sys_ops;
struct completion core_init_done;
u32 intr_status;
struct delayed_work sys_error_handler;
struct list_head instances;
/* encoder and decoder have overlapping caps, so two different arrays are required */
struct platform_inst_fw_cap inst_fw_caps_dec[INST_FW_CAP_MAX];
struct platform_inst_fw_cap inst_fw_caps_enc[INST_FW_CAP_MAX];
};
int iris_core_init(struct iris_core *core);
void iris_core_deinit(struct iris_core *core);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_CTRLS_H__
#define __IRIS_CTRLS_H__
#include "iris_platform_common.h"
struct iris_core;
struct iris_inst;
int iris_ctrls_init(struct iris_inst *inst);
void iris_session_init_caps(struct iris_core *core);
int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_properties(struct iris_inst *inst, u32 plane);
#endif
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/firmware.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "iris_core.h"
#include "iris_firmware.h"
#define IRIS_PAS_ID 9
#define MAX_FIRMWARE_NAME_SIZE 128
static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
{
const struct firmware *firmware = NULL;
struct device *dev = core->dev;
struct resource res;
phys_addr_t mem_phys;
size_t res_size;
ssize_t fw_size;
void *mem_virt;
int ret;
if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
return -EINVAL;
ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
if (ret)
return ret;
mem_phys = res.start;
res_size = resource_size(&res);
ret = request_firmware(&firmware, fw_name, dev);
if (ret)
return ret;
fw_size = qcom_mdt_get_size(firmware);
if (fw_size < 0 || res_size < (size_t)fw_size) {
ret = -EINVAL;
goto err_release_fw;
}
mem_virt = memremap(mem_phys, res_size, MEMREMAP_WC);
if (!mem_virt) {
ret = -ENOMEM;
goto err_release_fw;
}
ret = qcom_mdt_load(dev, firmware, fw_name,
IRIS_PAS_ID, mem_virt, mem_phys, res_size, NULL);
memunmap(mem_virt);
err_release_fw:
release_firmware(firmware);
return ret;
}
int iris_fw_load(struct iris_core *core)
{
const struct tz_cp_config *cp_config;
const char *fwpath = NULL;
int i, ret;
ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
&fwpath);
if (ret)
fwpath = core->iris_firmware_desc->fwname;
ret = iris_load_fw_to_memory(core, fwpath);
if (ret) {
dev_err(core->dev, "firmware download failed\n");
return -ENOMEM;
}
ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID);
if (ret) {
dev_err(core->dev, "auth and reset failed: %d\n", ret);
return ret;
}
for (i = 0; i < core->iris_platform_data->tz_cp_config_data_size; i++) {
cp_config = &core->iris_platform_data->tz_cp_config_data[i];
ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
cp_config->cp_size,
cp_config->cp_nonpixel_start,
cp_config->cp_nonpixel_size);
if (ret) {
dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
qcom_scm_pas_shutdown(IRIS_PAS_ID);
return ret;
}
}
return ret;
}
int iris_fw_unload(struct iris_core *core)
{
return qcom_scm_pas_shutdown(IRIS_PAS_ID);
}
int iris_set_hw_state(struct iris_core *core, bool resume)
{
return qcom_scm_set_remote_state(resume, 0);
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_FIRMWARE_H__
#define __IRIS_FIRMWARE_H__
struct iris_core;
int iris_fw_load(struct iris_core *core);
int iris_fw_unload(struct iris_core *core);
int iris_set_hw_state(struct iris_core *core, bool resume);
#endif
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include "iris_firmware.h"
#include "iris_core.h"
#include "iris_hfi_common.h"
#include "iris_vpu_common.h"
u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries)
{
switch (hfi_primaries) {
case HFI_PRIMARIES_RESERVED:
return V4L2_COLORSPACE_DEFAULT;
case HFI_PRIMARIES_BT709:
return V4L2_COLORSPACE_REC709;
case HFI_PRIMARIES_BT470_SYSTEM_M:
return V4L2_COLORSPACE_470_SYSTEM_M;
case HFI_PRIMARIES_BT470_SYSTEM_BG:
return V4L2_COLORSPACE_470_SYSTEM_BG;
case HFI_PRIMARIES_BT601_525:
return V4L2_COLORSPACE_SMPTE170M;
case HFI_PRIMARIES_SMPTE_ST240M:
return V4L2_COLORSPACE_SMPTE240M;
case HFI_PRIMARIES_BT2020:
return V4L2_COLORSPACE_BT2020;
case V4L2_COLORSPACE_DCI_P3:
return HFI_PRIMARIES_SMPTE_RP431_2;
default:
return V4L2_COLORSPACE_DEFAULT;
}
}
u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics)
{
switch (hfi_characterstics) {
case HFI_TRANSFER_RESERVED:
return V4L2_XFER_FUNC_DEFAULT;
case HFI_TRANSFER_BT709:
return V4L2_XFER_FUNC_709;
case HFI_TRANSFER_SMPTE_ST240M:
return V4L2_XFER_FUNC_SMPTE240M;
case HFI_TRANSFER_SRGB_SYCC:
return V4L2_XFER_FUNC_SRGB;
case HFI_TRANSFER_SMPTE_ST2084_PQ:
return V4L2_XFER_FUNC_SMPTE2084;
default:
return V4L2_XFER_FUNC_DEFAULT;
}
}
u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients)
{
switch (hfi_coefficients) {
case HFI_MATRIX_COEFF_RESERVED:
return V4L2_YCBCR_ENC_DEFAULT;
case HFI_MATRIX_COEFF_BT709:
return V4L2_YCBCR_ENC_709;
case HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625:
return V4L2_YCBCR_ENC_XV601;
case HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625:
return V4L2_YCBCR_ENC_601;
case HFI_MATRIX_COEFF_SMPTE_ST240:
return V4L2_YCBCR_ENC_SMPTE240M;
case HFI_MATRIX_COEFF_BT2020_NON_CONSTANT:
return V4L2_YCBCR_ENC_BT2020;
case HFI_MATRIX_COEFF_BT2020_CONSTANT:
return V4L2_YCBCR_ENC_BT2020_CONST_LUM;
default:
return V4L2_YCBCR_ENC_DEFAULT;
}
}
int iris_hfi_core_init(struct iris_core *core)
{
const struct iris_hfi_sys_ops *hfi_ops = core->hfi_sys_ops;
int ret;
ret = hfi_ops->sys_init(core);
if (ret)
return ret;
ret = hfi_ops->sys_image_version(core);
if (ret)
return ret;
return hfi_ops->sys_interframe_powercollapse(core);
}
irqreturn_t iris_hfi_isr(int irq, void *data)
{
disable_irq_nosync(irq);
return IRQ_WAKE_THREAD;
}
irqreturn_t iris_hfi_isr_handler(int irq, void *data)
{
struct iris_core *core = data;
if (!core)
return IRQ_NONE;
mutex_lock(&core->lock);
pm_runtime_mark_last_busy(core->dev);
iris_vpu_clear_interrupt(core);
mutex_unlock(&core->lock);
core->hfi_sys_ops->sys_hfi_response_handler(core);
if (!iris_vpu_watchdog(core, core->intr_status))
enable_irq(irq);
return IRQ_HANDLED;
}
int iris_hfi_pm_suspend(struct iris_core *core)
{
int ret;
ret = iris_vpu_prepare_pc(core);
if (ret) {
pm_runtime_mark_last_busy(core->dev);
ret = -EAGAIN;
goto error;
}
ret = iris_set_hw_state(core, false);
if (ret)
goto error;
iris_vpu_power_off(core);
return 0;
error:
dev_err(core->dev, "failed to suspend\n");
return ret;
}
int iris_hfi_pm_resume(struct iris_core *core)
{
const struct iris_hfi_sys_ops *ops = core->hfi_sys_ops;
int ret;
ret = iris_vpu_power_on(core);
if (ret)
goto error;
ret = iris_set_hw_state(core, true);
if (ret)
goto err_power_off;
ret = iris_vpu_boot_firmware(core);
if (ret)
goto err_suspend_hw;
ret = iris_vpu_switch_to_hwmode(core);
if (ret)
goto err_suspend_hw;
ret = ops->sys_interframe_powercollapse(core);
if (ret)
goto err_suspend_hw;
return 0;
err_suspend_hw:
iris_set_hw_state(core, false);
err_power_off:
iris_vpu_power_off(core);
error:
dev_err(core->dev, "failed to resume\n");
return -EBUSY;
}
@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_COMMON_H__
#define __IRIS_HFI_COMMON_H__
#include <linux/types.h>
#include <media/v4l2-device.h>
#include "iris_buffer.h"
struct iris_inst;
struct iris_core;
enum hfi_packet_port_type {
HFI_PORT_NONE = 0x00000000,
HFI_PORT_BITSTREAM = 0x00000001,
HFI_PORT_RAW = 0x00000002,
};
enum hfi_packet_payload_info {
HFI_PAYLOAD_NONE = 0x00000000,
HFI_PAYLOAD_U32 = 0x00000001,
HFI_PAYLOAD_S32 = 0x00000002,
HFI_PAYLOAD_U64 = 0x00000003,
HFI_PAYLOAD_S64 = 0x00000004,
HFI_PAYLOAD_STRUCTURE = 0x00000005,
HFI_PAYLOAD_BLOB = 0x00000006,
HFI_PAYLOAD_STRING = 0x00000007,
HFI_PAYLOAD_Q16 = 0x00000008,
HFI_PAYLOAD_U32_ENUM = 0x00000009,
HFI_PAYLOAD_32_PACKED = 0x0000000a,
HFI_PAYLOAD_U32_ARRAY = 0x0000000b,
HFI_PAYLOAD_S32_ARRAY = 0x0000000c,
HFI_PAYLOAD_64_PACKED = 0x0000000d,
};
enum hfi_packet_host_flags {
HFI_HOST_FLAGS_NONE = 0x00000000,
HFI_HOST_FLAGS_INTR_REQUIRED = 0x00000001,
HFI_HOST_FLAGS_RESPONSE_REQUIRED = 0x00000002,
HFI_HOST_FLAGS_NON_DISCARDABLE = 0x00000004,
HFI_HOST_FLAGS_GET_PROPERTY = 0x00000008,
};
enum hfi_color_primaries {
HFI_PRIMARIES_RESERVED = 0,
HFI_PRIMARIES_BT709 = 1,
HFI_PRIMARIES_UNSPECIFIED = 2,
HFI_PRIMARIES_BT470_SYSTEM_M = 4,
HFI_PRIMARIES_BT470_SYSTEM_BG = 5,
HFI_PRIMARIES_BT601_525 = 6,
HFI_PRIMARIES_SMPTE_ST240M = 7,
HFI_PRIMARIES_GENERIC_FILM = 8,
HFI_PRIMARIES_BT2020 = 9,
HFI_PRIMARIES_SMPTE_ST428_1 = 10,
HFI_PRIMARIES_SMPTE_RP431_2 = 11,
HFI_PRIMARIES_SMPTE_EG431_1 = 12,
HFI_PRIMARIES_SMPTE_EBU_TECH = 22,
};
enum hfi_transfer_characteristics {
HFI_TRANSFER_RESERVED = 0,
HFI_TRANSFER_BT709 = 1,
HFI_TRANSFER_UNSPECIFIED = 2,
HFI_TRANSFER_BT470_SYSTEM_M = 4,
HFI_TRANSFER_BT470_SYSTEM_BG = 5,
HFI_TRANSFER_BT601_525_OR_625 = 6,
HFI_TRANSFER_SMPTE_ST240M = 7,
HFI_TRANSFER_LINEAR = 8,
HFI_TRANSFER_LOG_100_1 = 9,
HFI_TRANSFER_LOG_SQRT = 10,
HFI_TRANSFER_XVYCC = 11,
HFI_TRANSFER_BT1361_0 = 12,
HFI_TRANSFER_SRGB_SYCC = 13,
HFI_TRANSFER_BT2020_14 = 14,
HFI_TRANSFER_BT2020_15 = 15,
HFI_TRANSFER_SMPTE_ST2084_PQ = 16,
HFI_TRANSFER_SMPTE_ST428_1 = 17,
HFI_TRANSFER_BT2100_2_HLG = 18,
};
enum hfi_matrix_coefficients {
HFI_MATRIX_COEFF_SRGB_SMPTE_ST428_1 = 0,
HFI_MATRIX_COEFF_BT709 = 1,
HFI_MATRIX_COEFF_UNSPECIFIED = 2,
HFI_MATRIX_COEFF_RESERVED = 3,
HFI_MATRIX_COEFF_FCC_TITLE_47 = 4,
HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625 = 5,
HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625 = 6,
HFI_MATRIX_COEFF_SMPTE_ST240 = 7,
HFI_MATRIX_COEFF_YCGCO = 8,
HFI_MATRIX_COEFF_BT2020_NON_CONSTANT = 9,
HFI_MATRIX_COEFF_BT2020_CONSTANT = 10,
HFI_MATRIX_COEFF_SMPTE_ST2085 = 11,
HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_NON_CONSTANT = 12,
HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_CONSTANT = 13,
HFI_MATRIX_COEFF_BT2100 = 14,
};
struct iris_hfi_prop_type_handle {
u32 type;
int (*handle)(struct iris_inst *inst, u32 plane);
};
struct iris_hfi_sys_ops {
int (*sys_init)(struct iris_core *core);
int (*sys_image_version)(struct iris_core *core);
int (*sys_interframe_powercollapse)(struct iris_core *core);
int (*sys_pc_prep)(struct iris_core *core);
void (*sys_hfi_response_handler)(struct iris_core *core);
struct iris_inst *(*sys_get_instance)(void);
};
struct iris_hfi_session_ops {
int (*session_set_config_params)(struct iris_inst *inst, u32 plane);
int (*session_set_property)(struct iris_inst *inst,
u32 packet_type, u32 flag, u32 plane, u32 payload_type,
void *payload, u32 payload_size);
int (*session_open)(struct iris_inst *inst);
int (*session_start)(struct iris_inst *inst, u32 plane);
int (*session_queue_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
int (*session_release_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
int (*session_pause)(struct iris_inst *inst, u32 plane);
int (*session_resume_drc)(struct iris_inst *inst, u32 plane);
int (*session_stop)(struct iris_inst *inst, u32 plane);
int (*session_drain)(struct iris_inst *inst, u32 plane);
int (*session_resume_drain)(struct iris_inst *inst, u32 plane);
int (*session_close)(struct iris_inst *inst);
};
struct hfi_subscription_params {
u32 bitstream_resolution;
u32 crop_offsets[2];
u32 bit_depth;
u32 coded_frames;
u32 fw_min_count;
u32 pic_order_cnt;
u32 color_info;
u32 profile;
u32 level;
u32 tier;
u32 drap;
u32 film_grain;
u32 super_block;
};
u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics);
u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients);
int iris_hfi_core_init(struct iris_core *core);
int iris_hfi_pm_suspend(struct iris_core *core);
int iris_hfi_pm_resume(struct iris_core *core);
irqreturn_t iris_hfi_isr(int irq, void *data);
irqreturn_t iris_hfi_isr_handler(int irq, void *data);
#endif
@@ -0,0 +1,445 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "iris_ctrls.h"
#include "iris_platform_common.h"
#include "iris_hfi_gen1.h"
#include "iris_hfi_gen1_defines.h"
#include "iris_vpu_buffer.h"
#define BITRATE_MIN 32000
#define BITRATE_MAX 160000000
#define BITRATE_STEP 100
static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
{
.cap_id = PIPE,
/* .max, .min and .value are set via platform data */
.step_or_mask = 1,
.hfi_id = HFI_PROPERTY_PARAM_WORK_ROUTE,
.set = iris_set_pipe,
},
{
.cap_id = STAGE,
.min = STAGE_1,
.max = STAGE_2,
.step_or_mask = 1,
.value = STAGE_2,
.hfi_id = HFI_PROPERTY_PARAM_WORK_MODE,
.set = iris_set_stage,
},
};
static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
{
.cap_id = STAGE,
.min = STAGE_1,
.max = STAGE_2,
.step_or_mask = 1,
.value = STAGE_2,
.hfi_id = HFI_PROPERTY_PARAM_WORK_MODE,
.set = iris_set_stage,
},
{
.cap_id = PROFILE_H264,
.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
.max = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) |
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH),
.value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
.hfi_id = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_profile_level_gen1,
},
{
.cap_id = PROFILE_HEVC,
.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10),
.value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.hfi_id = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_profile_level_gen1,
},
{
.cap_id = LEVEL_H264,
.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
.value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
.hfi_id = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_profile_level_gen1,
},
{
.cap_id = LEVEL_HEVC,
.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
.value = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
.hfi_id = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_profile_level_gen1,
},
{
.cap_id = HEADER_MODE,
.min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
.max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
BIT(V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
.value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_header_mode_gen1,
},
{
.cap_id = BITRATE,
.min = BITRATE_MIN,
.max = BITRATE_MAX,
.step_or_mask = BITRATE_STEP,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = BITRATE_MODE,
.min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
.max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR),
.value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
.hfi_id = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_bitrate_mode_gen1,
},
{
.cap_id = FRAME_SKIP_MODE,
.min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
.max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) |
BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT),
.value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
},
{
.cap_id = FRAME_RC_ENABLE,
.min = 0,
.max = 1,
.step_or_mask = 1,
.value = 1,
},
{
.cap_id = GOP_SIZE,
.min = 0,
.max = (1 << 16) - 1,
.step_or_mask = 1,
.value = 30,
.set = iris_set_u32
},
{
.cap_id = ENTROPY_MODE,
.min = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
.max = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC),
.value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
.hfi_id = HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
.set = iris_set_entropy_mode_gen1,
},
{
.cap_id = MIN_FRAME_QP_H264,
.min = MIN_QP_8BIT,
.max = MAX_QP,
.step_or_mask = 1,
.value = MIN_QP_8BIT,
.hfi_id = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_qp_range,
},
{
.cap_id = MIN_FRAME_QP_HEVC,
.min = MIN_QP_8BIT,
.max = MAX_QP_HEVC,
.step_or_mask = 1,
.value = MIN_QP_8BIT,
.hfi_id = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_qp_range,
},
{
.cap_id = MAX_FRAME_QP_H264,
.min = MIN_QP_8BIT,
.max = MAX_QP,
.step_or_mask = 1,
.value = MAX_QP,
.hfi_id = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_qp_range,
},
{
.cap_id = MAX_FRAME_QP_HEVC,
.min = MIN_QP_8BIT,
.max = MAX_QP_HEVC,
.step_or_mask = 1,
.value = MAX_QP_HEVC,
.hfi_id = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_qp_range,
},
{
.cap_id = IR_TYPE,
.min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
.max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC,
.step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) |
BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC),
.value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
},
{
.cap_id = IR_PERIOD,
.min = 0,
.max = ((4096 * 2304) >> 8),
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_ir_period_gen1,
},
{
.cap_id = LTR_COUNT,
.min = 0,
.max = MAX_LTR_FRAME_COUNT_GEN1,
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_PARAM_VENC_LTRMODE,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_ltr_count_gen1,
},
{
.cap_id = USE_LTR,
.min = 0,
.max = ((1 << MAX_LTR_FRAME_COUNT_GEN1) - 1),
.step_or_mask = 0,
.value = 0,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME,
.flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_use_ltr,
},
{
.cap_id = MARK_LTR,
.min = 0,
.max = (MAX_LTR_FRAME_COUNT_GEN1 - 1),
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME,
.flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_mark_ltr,
},
{
.cap_id = B_FRAME,
.min = 0,
.max = 3,
.step_or_mask = 1,
.value = 0,
.flags = CAP_FLAG_OUTPUT_PORT,
},
{
.cap_id = INTRA_PERIOD,
.min = 0,
.max = 1,
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_intra_period,
},
{
.cap_id = LAYER_ENABLE,
.min = 0,
.max = 1,
.step_or_mask = 1,
.value = 0,
.flags = CAP_FLAG_OUTPUT_PORT,
},
{
.cap_id = LAYER_TYPE_H264,
.min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
.max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P),
.value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
},
{
.cap_id = LAYER_COUNT_H264,
.min = 0,
.max = MAX_HIER_CODING_LAYER_GEN1,
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER,
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_layer_count_gen1,
},
{
.cap_id = LAYER0_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = LAYER1_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = LAYER2_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = LAYER3_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = LAYER4_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
{
.cap_id = LAYER5_BITRATE_H264,
.min = 1,
.max = BITRATE_MAX,
.step_or_mask = 1,
.value = BITRATE_DEFAULT,
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
};
static const u32 sm8250_vdec_input_config_param_default[] = {
HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE,
HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
HFI_PROPERTY_PARAM_FRAME_SIZE,
HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE,
};
static const u32 sm8250_venc_input_config_param[] = {
HFI_PROPERTY_CONFIG_FRAME_RATE,
HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO,
HFI_PROPERTY_PARAM_FRAME_SIZE,
HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
};
static const u32 sm8250_dec_ip_int_buf_tbl[] = {
BUF_BIN,
BUF_SCRATCH_1,
};
static const u32 sm8250_dec_op_int_buf_tbl[] = {
BUF_DPB,
};
static const u32 sm8250_enc_ip_int_buf_tbl[] = {
BUF_BIN,
BUF_SCRATCH_1,
BUF_SCRATCH_2,
};
const struct iris_firmware_data iris_hfi_gen1_data = {
.init_hfi_ops = &iris_hfi_gen1_sys_ops_init,
.inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
.dec_input_config_params_default =
sm8250_vdec_input_config_param_default,
.dec_input_config_params_default_size =
ARRAY_SIZE(sm8250_vdec_input_config_param_default),
.enc_input_config_params = sm8250_venc_input_config_param,
.enc_input_config_params_size =
ARRAY_SIZE(sm8250_venc_input_config_param),
.dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
.dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl),
.dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl,
.dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl),
.enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl,
.enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl),
};
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_GEN1_H__
#define __IRIS_HFI_GEN1_H__
struct iris_core;
void iris_hfi_gen1_sys_ops_init(struct iris_core *core);
void iris_hfi_gen1_response_handler(struct iris_core *core);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,600 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_GEN1_DEFINES_H__
#define __IRIS_HFI_GEN1_DEFINES_H__
#include <linux/types.h>
#define HFI_VIDEO_ARCH_OX 0x1
#define HFI_SESSION_TYPE_ENC 1
#define HFI_SESSION_TYPE_DEC 2
#define HFI_VIDEO_CODEC_H264 0x00000002
#define HFI_VIDEO_CODEC_HEVC 0x00002000
#define HFI_VIDEO_CODEC_VP9 0x00004000
#define HFI_ERR_NONE 0x0
#define HFI_CMD_SYS_INIT 0x10001
#define HFI_CMD_SYS_PC_PREP 0x10002
#define HFI_CMD_SYS_SET_PROPERTY 0x10005
#define HFI_CMD_SYS_GET_PROPERTY 0x10006
#define HFI_CMD_SYS_SESSION_INIT 0x10007
#define HFI_CMD_SYS_SESSION_END 0x10008
#define HFI_CMD_SESSION_SET_PROPERTY 0x11001
#define HFI_CMD_SESSION_SET_BUFFERS 0x11002
#define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001
#define HFI_CMD_SESSION_START 0x211002
#define HFI_CMD_SESSION_STOP 0x211003
#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004
#define HFI_CMD_SESSION_FILL_BUFFER 0x211005
#define HFI_CMD_SESSION_FLUSH 0x211008
#define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b
#define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c
#define HFI_CMD_SESSION_CONTINUE 0x21100d
#define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008
#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d
#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010
#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012
#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013
#define HFI_EVENT_SYS_ERROR 0x1
#define HFI_EVENT_SESSION_ERROR 0x2
#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES 0x1000001
#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002
#define HFI_EVENT_SESSION_SEQUENCE_CHANGED 0x1000003
#define HFI_BUFFERFLAG_EOS 0x00000001
#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
#define HFI_FLUSH_OUTPUT 0x1000002
#define HFI_FLUSH_OUTPUT2 0x1000003
#define HFI_FLUSH_ALL 0x1000004
#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e
#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL 0x201001
#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO 0x201002
#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE 0x201008
#define HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL 0x20100c
#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001
#define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS 0x120300e
#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004
#define HFI_BUFFER_INPUT 0x1
#define HFI_BUFFER_OUTPUT 0x2
#define HFI_BUFFER_OUTPUT2 0x3
#define HFI_BUFFER_INTERNAL_PERSIST 0x4
#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
#define HFI_BUFFER_INTERNAL_SCRATCH 0x6
#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x7
#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x8
#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5
#define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6
#define HFI_PROPERTY_PARAM_FRAME_SIZE 0x1001
#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO 0x1002
#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT 0x1003
#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT 0x1005
#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015
#define HFI_PROPERTY_PARAM_WORK_ROUTE 0x1017
#define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001
#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002
#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001
#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007
#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009
#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a
#define HFI_CORE_ID_1 1
#define HFI_COLOR_FORMAT_NV12 0x02
#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002
#define HFI_MSG_SYS_INIT 0x20001
#define HFI_MSG_SYS_SESSION_INIT 0x20006
#define HFI_MSG_SYS_SESSION_END 0x20007
#define HFI_MSG_SYS_COV 0x20009
#define HFI_MSG_SYS_PROPERTY_INFO 0x2000a
#define HFI_MSG_EVENT_NOTIFY 0x21001
#define HFI_MSG_SESSION_LOAD_RESOURCES 0x221001
#define HFI_MSG_SESSION_START 0x221002
#define HFI_MSG_SESSION_STOP 0x221003
#define HFI_MSG_SESSION_FLUSH 0x221006
#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007
#define HFI_MSG_SESSION_FILL_BUFFER 0x221008
#define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a
#define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c
#define HFI_GEN1_PICTURE_I 0x00000001
#define HFI_GEN1_PICTURE_P 0x00000002
#define HFI_GEN1_PICTURE_B 0x00000004
#define HFI_GEN1_PICTURE_IDR 0x00000008
#define HFI_FRAME_NOTCODED 0x7f002000
#define HFI_FRAME_YUV 0x7f004000
#define HFI_UNUSED_PICT 0x10000000
#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000
#define HFI_RATE_CONTROL_OFF 0x1000001
#define HFI_RATE_CONTROL_VBR_VFR 0x1000002
#define HFI_RATE_CONTROL_VBR_CFR 0x1000003
#define HFI_RATE_CONTROL_CBR_VFR 0x1000004
#define HFI_RATE_CONTROL_CBR_CFR 0x1000005
#define HFI_RATE_CONTROL_CQ 0x1000008
#define HFI_H264_ENTROPY_CAVLC 0x1
#define HFI_H264_ENTROPY_CABAC 0x2
#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL 0x2005002
#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003
#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004
#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009
#define HFI_INTRA_REFRESH_NONE 0x1
#define HFI_INTRA_REFRESH_CYCLIC 0x2
#define HFI_INTRA_REFRESH_ADAPTIVE 0x3
#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4
#define HFI_INTRA_REFRESH_RANDOM 0x5
#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d
#define HFI_LTR_MODE_DISABLE 0x0
#define HFI_LTR_MODE_MANUAL 0x1
#define HFI_LTR_MODE_PERIODIC 0x2
#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020
#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001
#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003
#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009
#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a
#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008
#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b
struct hfi_pkt_hdr {
u32 size;
u32 pkt_type;
};
struct hfi_session_hdr_pkt {
struct hfi_pkt_hdr hdr;
u32 session_id;
};
struct hfi_session_open_pkt {
struct hfi_session_hdr_pkt shdr;
u32 session_domain;
u32 session_codec;
};
struct hfi_session_pkt {
struct hfi_session_hdr_pkt shdr;
};
struct hfi_sys_init_pkt {
struct hfi_pkt_hdr hdr;
u32 arch_type;
};
struct hfi_sys_set_property_pkt {
struct hfi_pkt_hdr hdr;
u32 num_properties;
u32 data[];
};
struct hfi_sys_get_property_pkt {
struct hfi_pkt_hdr hdr;
u32 num_properties;
u32 data;
};
struct hfi_session_set_property_pkt {
struct hfi_session_hdr_pkt shdr;
u32 num_properties;
u32 data[];
};
struct hfi_sys_pc_prep_pkt {
struct hfi_pkt_hdr hdr;
};
struct hfi_session_set_buffers_pkt {
struct hfi_session_hdr_pkt shdr;
u32 buffer_type;
u32 buffer_size;
u32 extradata_size;
u32 min_buffer_size;
u32 num_buffers;
u32 buffer_info[];
};
struct hfi_session_empty_buffer_compressed_pkt {
struct hfi_session_hdr_pkt shdr;
u32 time_stamp_hi;
u32 time_stamp_lo;
u32 flags;
u32 mark_target;
u32 mark_data;
u32 offset;
u32 alloc_len;
u32 filled_len;
u32 input_tag;
u32 packet_buffer;
u32 extradata_buffer;
u32 data;
};
struct hfi_session_empty_buffer_uncompressed_pkt {
struct hfi_session_hdr_pkt shdr;
u32 view_id;
u32 time_stamp_hi;
u32 time_stamp_lo;
u32 flags;
u32 mark_target;
u32 mark_data;
u32 alloc_len;
u32 filled_len;
u32 offset;
u32 input_tag;
u32 packet_buffer;
u32 extradata_buffer;
u32 data;
};
struct hfi_session_fill_buffer_pkt {
struct hfi_session_hdr_pkt shdr;
u32 stream_id;
u32 offset;
u32 alloc_len;
u32 filled_len;
u32 output_tag;
u32 packet_buffer;
u32 extradata_buffer;
u32 data;
};
struct hfi_session_flush_pkt {
struct hfi_session_hdr_pkt shdr;
u32 flush_type;
};
struct hfi_session_release_buffer_pkt {
struct hfi_session_hdr_pkt shdr;
u32 buffer_type;
u32 buffer_size;
u32 extradata_size;
u32 response_req;
u32 num_buffers;
u32 buffer_info[];
};
struct hfi_buffer_info {
u32 buffer_addr;
u32 extradata_addr;
};
struct hfi_msg_event_notify_pkt {
struct hfi_session_hdr_pkt shdr;
u32 event_id;
u32 event_data1;
u32 event_data2;
u32 ext_event_data[];
};
struct hfi_msg_sys_init_done_pkt {
struct hfi_pkt_hdr hdr;
u32 error_type;
u32 num_properties;
u32 data[];
};
struct hfi_msg_session_hdr_pkt {
struct hfi_session_hdr_pkt shdr;
u32 error_type;
};
struct hfi_msg_session_init_done_pkt {
struct hfi_msg_session_hdr_pkt shdr;
u32 num_properties;
u32 data[];
};
struct hfi_msg_sys_property_info_pkt {
struct hfi_pkt_hdr hdr;
u32 num_properties;
u32 property;
u8 data[];
};
struct hfi_msg_session_flush_done_pkt {
struct hfi_msg_session_hdr_pkt shdr;
u32 flush_type;
};
struct hfi_enable {
u32 enable;
};
struct hfi_profile_level {
u32 profile;
u32 level;
};
struct hfi_framesize {
u32 buffer_type;
u32 width;
u32 height;
};
struct hfi_videocores_usage_type {
u32 video_core_enable_mask;
};
struct hfi_video_work_mode {
u32 video_work_mode;
};
struct hfi_video_work_route {
u32 video_work_route;
};
struct hfi_bit_depth {
u32 buffer_type;
u32 bit_depth;
};
struct hfi_pic_struct {
u32 progressive_only;
};
struct hfi_colour_space {
u32 colour_space;
};
struct hfi_extradata_input_crop {
u32 size;
u32 version;
u32 port_index;
u32 left;
u32 top;
u32 width;
u32 height;
};
struct hfi_dpb_counts {
u32 max_dpb_count;
u32 max_ref_frames;
u32 max_dec_buffering;
u32 max_reorder_frames;
u32 fw_min_count;
};
struct hfi_uncompressed_format_select {
u32 buffer_type;
u32 format;
};
struct hfi_uncompressed_plane_constraints {
u32 stride_multiples;
u32 max_stride;
u32 min_plane_buffer_height_multiple;
u32 buffer_alignment;
};
struct hfi_uncompressed_plane_actual_constraints_info {
u32 buffer_type;
u32 num_planes;
struct hfi_uncompressed_plane_constraints plane_format[2];
};
struct hfi_uncompressed_plane_actual {
int actual_stride;
u32 actual_plane_buffer_height;
};
struct hfi_uncompressed_plane_actual_info {
u32 buffer_type;
u32 num_planes;
struct hfi_uncompressed_plane_actual plane_format[2];
};
struct hfi_buffer_count_actual {
u32 type;
u32 count_actual;
u32 count_min_host;
};
struct hfi_buffer_size_actual {
u32 type;
u32 size;
};
struct hfi_multi_stream {
u32 buffer_type;
u32 enable;
};
struct hfi_buffer_requirements {
u32 type;
u32 size;
u32 region_size;
u32 hold_count;
u32 count_min;
u32 count_actual;
u32 contiguous;
u32 alignment;
};
struct hfi_bitrate {
u32 bitrate;
u32 layer_id;
};
#define HFI_H264_CABAC_MODEL_0 0x1
struct hfi_h264_entropy_control {
u32 entropy_mode;
u32 cabac_model;
};
struct hfi_quantization_v2 {
u32 qp_packed;
u32 layer_id;
u32 enable;
u32 reserved[3];
};
struct hfi_quantization_range_v2 {
struct hfi_quantization_v2 min_qp;
struct hfi_quantization_v2 max_qp;
u32 reserved[4];
};
struct hfi_framerate {
u32 buffer_type;
u32 framerate;
};
struct hfi_intra_refresh {
u32 mode;
u32 mbs;
};
struct hfi_ltr_mode {
u32 mode;
u32 count;
u32 trust_mode;
};
struct hfi_ltr_use {
u32 ref_ltr;
u32 use_constrnt;
u32 frames;
};
struct hfi_ltr_mark {
u32 mark_frame;
};
struct hfi_max_num_b_frames {
u32 max_num_b_frames;
};
struct hfi_intra_period {
u32 pframes;
u32 bframes;
};
struct hfi_event_data {
u32 error;
u32 height;
u32 width;
u32 event_type;
u32 packet_buffer;
u32 extradata_buffer;
u32 tag;
u32 profile;
u32 level;
u32 bit_depth;
u32 pic_struct;
u32 colour_space;
u32 entropy_mode;
u32 buf_count;
struct {
u32 left, top;
u32 width, height;
} input_crop;
};
struct hfi_msg_session_empty_buffer_done_pkt {
struct hfi_msg_session_hdr_pkt shdr;
u32 offset;
u32 filled_len;
u32 input_tag;
u32 packet_buffer;
u32 extradata_buffer;
u32 data[];
};
struct hfi_msg_session_fbd_compressed_pkt {
struct hfi_session_hdr_pkt shdr;
u32 time_stamp_hi;
u32 time_stamp_lo;
u32 error_type;
u32 flags;
u32 mark_target;
u32 mark_data;
u32 stats;
u32 offset;
u32 alloc_len;
u32 filled_len;
u32 input_tag;
u32 output_tag;
u32 picture_type;
u32 packet_buffer;
u32 extradata_buffer;
u32 data[];
};
struct hfi_msg_session_fbd_uncompressed_plane0_pkt {
struct hfi_session_hdr_pkt shdr;
u32 stream_id;
u32 view_id;
u32 error_type;
u32 time_stamp_hi;
u32 time_stamp_lo;
u32 flags;
u32 mark_target;
u32 mark_data;
u32 stats;
u32 alloc_len;
u32 filled_len;
u32 offset;
u32 frame_width;
u32 frame_height;
u32 start_x_coord;
u32 start_y_coord;
u32 input_tag;
u32 input_tag2;
u32 output_tag;
u32 picture_type;
u32 packet_buffer;
u32 extradata_buffer;
u32 data[];
};
struct hfi_msg_session_release_buffers_done_pkt {
struct hfi_msg_session_hdr_pkt shdr;
u32 num_buffers;
u32 buffer_info[];
};
struct hfi_msg_sys_debug_pkt {
struct hfi_pkt_hdr hdr;
u32 msg_type;
u32 msg_size;
u32 time_stamp_hi;
u32 time_stamp_lo;
u8 msg_data[];
};
struct hfi_msg_sys_coverage_pkt {
struct hfi_pkt_hdr hdr;
u32 msg_size;
u32 time_stamp_hi;
u32 time_stamp_lo;
u8 msg_data[];
};
#endif
@@ -0,0 +1,700 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bitfield.h>
#include <media/v4l2-mem2mem.h>
#include "iris_hfi_gen1.h"
#include "iris_hfi_gen1_defines.h"
#include "iris_instance.h"
#include "iris_vdec.h"
#include "iris_vpu_buffer.h"
static void iris_hfi_gen1_read_changed_params(struct iris_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
u32 num_properties_changed = pkt->event_data2;
u8 *data_ptr = (u8 *)&pkt->ext_event_data[0];
u32 primaries, matrix_coeff, transfer_char;
struct hfi_dpb_counts *iris_vpu_dpb_count;
struct hfi_profile_level *profile_level;
struct hfi_buffer_requirements *bufreq;
struct hfi_extradata_input_crop *crop;
struct hfi_colour_space *colour_info;
struct iris_core *core = inst->core;
u32 colour_description_present_flag;
u32 video_signal_type_present_flag;
struct hfi_event_data event = {0};
struct hfi_bit_depth *pixel_depth;
struct hfi_pic_struct *pic_struct;
struct hfi_framesize *frame_sz;
struct vb2_queue *dst_q;
struct v4l2_ctrl *ctrl;
u32 full_range, ptype;
do {
ptype = *((u32 *)data_ptr);
switch (ptype) {
case HFI_PROPERTY_PARAM_FRAME_SIZE:
data_ptr += sizeof(u32);
frame_sz = (struct hfi_framesize *)data_ptr;
event.width = frame_sz->width;
event.height = frame_sz->height;
data_ptr += sizeof(*frame_sz);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
data_ptr += sizeof(u32);
profile_level = (struct hfi_profile_level *)data_ptr;
event.profile = profile_level->profile;
event.level = profile_level->level;
data_ptr += sizeof(*profile_level);
break;
case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
data_ptr += sizeof(u32);
pixel_depth = (struct hfi_bit_depth *)data_ptr;
event.bit_depth = pixel_depth->bit_depth;
data_ptr += sizeof(*pixel_depth);
break;
case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
data_ptr += sizeof(u32);
pic_struct = (struct hfi_pic_struct *)data_ptr;
event.pic_struct = pic_struct->progressive_only;
data_ptr += sizeof(*pic_struct);
break;
case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
data_ptr += sizeof(u32);
colour_info = (struct hfi_colour_space *)data_ptr;
event.colour_space = colour_info->colour_space;
data_ptr += sizeof(*colour_info);
break;
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
data_ptr += sizeof(u32);
event.entropy_mode = *(u32 *)data_ptr;
data_ptr += sizeof(u32);
break;
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
data_ptr += sizeof(u32);
bufreq = (struct hfi_buffer_requirements *)data_ptr;
event.buf_count = bufreq->count_min;
data_ptr += sizeof(*bufreq);
break;
case HFI_INDEX_EXTRADATA_INPUT_CROP:
data_ptr += sizeof(u32);
crop = (struct hfi_extradata_input_crop *)data_ptr;
event.input_crop.left = crop->left;
event.input_crop.top = crop->top;
event.input_crop.width = crop->width;
event.input_crop.height = crop->height;
data_ptr += sizeof(*crop);
break;
case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
data_ptr += sizeof(u32);
iris_vpu_dpb_count = (struct hfi_dpb_counts *)data_ptr;
event.buf_count = iris_vpu_dpb_count->fw_min_count;
data_ptr += sizeof(*iris_vpu_dpb_count);
break;
default:
break;
}
num_properties_changed--;
} while (num_properties_changed > 0);
pixmp_ip->width = event.width;
pixmp_ip->height = event.height;
pixmp_op->width = ALIGN(event.width, 128);
pixmp_op->height = ALIGN(event.height, 32);
pixmp_op->plane_fmt[0].bytesperline = ALIGN(event.width, 128);
pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
matrix_coeff = FIELD_GET(GENMASK(7, 0), event.colour_space);
transfer_char = FIELD_GET(GENMASK(15, 8), event.colour_space);
primaries = FIELD_GET(GENMASK(23, 16), event.colour_space);
colour_description_present_flag = FIELD_GET(GENMASK(24, 24), event.colour_space);
full_range = FIELD_GET(GENMASK(25, 25), event.colour_space);
video_signal_type_present_flag = FIELD_GET(GENMASK(29, 29), event.colour_space);
pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT;
pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT;
pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT;
if (video_signal_type_present_flag) {
pixmp_op->quantization =
full_range ?
V4L2_QUANTIZATION_FULL_RANGE :
V4L2_QUANTIZATION_LIM_RANGE;
if (colour_description_present_flag) {
pixmp_op->colorspace =
iris_hfi_get_v4l2_color_primaries(primaries);
pixmp_op->xfer_func =
iris_hfi_get_v4l2_transfer_char(transfer_char);
pixmp_op->ycbcr_enc =
iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff);
}
}
pixmp_ip->colorspace = pixmp_op->colorspace;
pixmp_ip->xfer_func = pixmp_op->xfer_func;
pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc;
pixmp_ip->quantization = pixmp_op->quantization;
if (event.input_crop.width > 0 && event.input_crop.height > 0) {
inst->crop.left = event.input_crop.left;
inst->crop.top = event.input_crop.top;
inst->crop.width = event.input_crop.width;
inst->crop.height = event.input_crop.height;
} else {
inst->crop.left = 0;
inst->crop.top = 0;
inst->crop.width = event.width;
inst->crop.height = event.height;
}
inst->fw_min_count = event.buf_count;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
if (ctrl)
v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count);
dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
if (event.bit_depth || !event.pic_struct) {
dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
event.bit_depth, event.pic_struct);
iris_inst_change_state(inst, IRIS_INST_ERROR);
}
}
static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
struct hfi_session_flush_pkt flush_pkt;
u32 num_properties_changed;
int ret;
ret = iris_inst_sub_state_change_drc(inst);
if (ret)
return;
switch (pkt->event_data1) {
case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
break;
default:
iris_inst_change_state(inst, IRIS_INST_ERROR);
return;
}
num_properties_changed = pkt->event_data2;
if (!num_properties_changed) {
iris_inst_change_state(inst, IRIS_INST_ERROR);
return;
}
iris_hfi_gen1_read_changed_params(inst, pkt);
if (inst->state != IRIS_INST_ERROR && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) {
flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
flush_pkt.shdr.session_id = inst->session_id;
flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
if (!iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size))
inst->flush_responses_pending++;
}
iris_vdec_src_change(inst);
iris_inst_sub_state_change_drc_last(inst);
}
static void
iris_hfi_gen1_sys_event_notify(struct iris_core *core, void *packet)
{
struct hfi_msg_event_notify_pkt *pkt = packet;
struct iris_inst *instance;
if (pkt->event_id == HFI_EVENT_SYS_ERROR)
dev_err(core->dev, "sys error (type: %x, session id:%x, data1:%x, data2:%x)\n",
pkt->event_id, pkt->shdr.session_id, pkt->event_data1,
pkt->event_data2);
core->state = IRIS_CORE_ERROR;
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list)
iris_inst_change_state(instance, IRIS_INST_ERROR);
mutex_unlock(&core->lock);
schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
}
static void
iris_hfi_gen1_event_session_error(struct iris_inst *inst, struct hfi_msg_event_notify_pkt *pkt)
{
switch (pkt->event_data1) {
/* non fatal session errors */
case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
dev_dbg(inst->core->dev, "session error: event id:%x, session id:%x\n",
pkt->event_data1, pkt->shdr.session_id);
break;
/* fatal session errors */
default:
/*
* firmware fills event_data2 as an additional information about the
* hfi command for which session error has ouccured.
*/
dev_err(inst->core->dev,
"session error for command: %x, event id:%x, session id:%x\n",
pkt->event_data2, pkt->event_data1,
pkt->shdr.session_id);
iris_vb2_queue_error(inst);
iris_inst_change_state(inst, IRIS_INST_ERROR);
break;
}
}
static void iris_hfi_gen1_session_event_notify(struct iris_inst *inst, void *packet)
{
struct hfi_msg_event_notify_pkt *pkt = packet;
switch (pkt->event_id) {
case HFI_EVENT_SESSION_ERROR:
iris_hfi_gen1_event_session_error(inst, pkt);
break;
case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
iris_hfi_gen1_event_seq_changed(inst, pkt);
break;
default:
break;
}
}
static void iris_hfi_gen1_sys_init_done(struct iris_core *core, void *packet)
{
struct hfi_msg_sys_init_done_pkt *pkt = packet;
if (pkt->error_type != HFI_ERR_NONE) {
core->state = IRIS_CORE_ERROR;
return;
}
complete(&core->core_init_done);
}
static void
iris_hfi_gen1_sys_get_prop_image_version(struct iris_core *core,
struct hfi_msg_sys_property_info_pkt *pkt)
{
int req_bytes = pkt->hdr.size - sizeof(*pkt);
char fw_version[IRIS_FW_VERSION_LENGTH];
u8 *str_image_version;
u32 i;
if (req_bytes < IRIS_FW_VERSION_LENGTH - 1 || !pkt->data[0] || pkt->num_properties > 1) {
dev_err(core->dev, "bad packet\n");
return;
}
str_image_version = pkt->data;
if (!str_image_version) {
dev_err(core->dev, "firmware version not available\n");
return;
}
for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) {
if (str_image_version[i] != '\0')
fw_version[i] = str_image_version[i];
else
fw_version[i] = ' ';
}
fw_version[i] = '\0';
dev_dbg(core->dev, "firmware version: %s\n", fw_version);
}
static void iris_hfi_gen1_sys_property_info(struct iris_core *core, void *packet)
{
struct hfi_msg_sys_property_info_pkt *pkt = packet;
if (!pkt->num_properties) {
dev_dbg(core->dev, "no properties\n");
return;
}
switch (pkt->property) {
case HFI_PROPERTY_SYS_IMAGE_VERSION:
iris_hfi_gen1_sys_get_prop_image_version(core, pkt);
break;
default:
dev_dbg(core->dev, "unknown property data\n");
break;
}
}
static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
{
struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_m2m_buffer *m2m_buffer, *n;
struct iris_buffer *buf = NULL;
bool found = false;
/* EOS buffer sent via drain won't be in v4l2 buffer list */
if (pkt->packet_buffer == 0xdeadb000)
return;
v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
buf = to_iris_buffer(&m2m_buffer->vb);
if (buf->index == pkt->input_tag) {
found = true;
break;
}
}
if (!found)
goto error;
if (pkt->shdr.error_type == HFI_ERR_SESSION_UNSUPPORTED_STREAM) {
buf->flags = V4L2_BUF_FLAG_ERROR;
iris_vb2_queue_error(inst);
iris_inst_change_state(inst, IRIS_INST_ERROR);
}
if (!(buf->attr & BUF_ATTR_QUEUED))
return;
buf->attr &= ~BUF_ATTR_QUEUED;
if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
buf->attr |= BUF_ATTR_BUFFER_DONE;
iris_vb2_buffer_done(inst, buf);
}
return;
error:
iris_inst_change_state(inst, IRIS_INST_ERROR);
dev_err(inst->core->dev, "error in etb done\n");
}
static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
{
struct hfi_msg_session_fbd_uncompressed_plane0_pkt *uncom_pkt = packet;
struct hfi_msg_session_fbd_compressed_pkt *com_pkt = packet;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_m2m_buffer *m2m_buffer, *n;
struct hfi_session_flush_pkt flush_pkt;
u32 timestamp_hi;
u32 timestamp_lo;
struct iris_core *core = inst->core;
u32 filled_len;
u32 pic_type;
u32 output_tag;
struct iris_buffer *buf, *iter;
struct iris_buffers *buffers;
u32 hfi_flags;
u32 offset;
u64 timestamp_us = 0;
bool found = false;
u32 flags = 0;
if (inst->domain == DECODER) {
timestamp_hi = uncom_pkt->time_stamp_hi;
timestamp_lo = uncom_pkt->time_stamp_lo;
filled_len = uncom_pkt->filled_len;
pic_type = uncom_pkt->picture_type;
output_tag = uncom_pkt->output_tag;
hfi_flags = uncom_pkt->flags;
offset = uncom_pkt->offset;
} else {
timestamp_hi = com_pkt->time_stamp_hi;
timestamp_lo = com_pkt->time_stamp_lo;
filled_len = com_pkt->filled_len;
pic_type = com_pkt->picture_type;
output_tag = com_pkt->output_tag;
hfi_flags = com_pkt->flags;
offset = com_pkt->offset;
}
if ((hfi_flags & HFI_BUFFERFLAG_EOS) && !filled_len) {
reinit_completion(&inst->flush_completion);
flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
flush_pkt.shdr.session_id = inst->session_id;
flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
if (!iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size))
inst->flush_responses_pending++;
iris_inst_sub_state_change_drain_last(inst);
}
if (iris_split_mode_enabled(inst) && inst->domain == DECODER &&
uncom_pkt->stream_id == 0) {
buffers = &inst->buffers[BUF_DPB];
if (!buffers)
goto error;
found = false;
list_for_each_entry(iter, &buffers->list, list) {
if (!(iter->attr & BUF_ATTR_QUEUED))
continue;
found = (iter->index == output_tag &&
iter->data_offset == offset);
if (found) {
buf = iter;
break;
}
}
} else {
v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
buf = to_iris_buffer(&m2m_buffer->vb);
if (!(buf->attr & BUF_ATTR_QUEUED))
continue;
found = (buf->index == output_tag &&
buf->data_offset == offset);
if (found)
break;
}
}
if (!found)
goto error;
buf->data_offset = offset;
buf->data_size = filled_len;
if (filled_len) {
timestamp_us = timestamp_hi;
timestamp_us = (timestamp_us << 32) | timestamp_lo;
} else {
if (inst->domain == DECODER && uncom_pkt->stream_id == 1 &&
!inst->last_buffer_dequeued) {
if (iris_drc_pending(inst) || iris_drain_pending(inst)) {
flags |= V4L2_BUF_FLAG_LAST;
inst->last_buffer_dequeued = true;
}
} else if (inst->domain == ENCODER) {
if (!inst->last_buffer_dequeued && iris_drain_pending(inst)) {
flags |= V4L2_BUF_FLAG_LAST;
inst->last_buffer_dequeued = true;
}
}
}
buf->timestamp = timestamp_us;
switch (pic_type) {
case HFI_GEN1_PICTURE_IDR:
case HFI_GEN1_PICTURE_I:
flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
case HFI_GEN1_PICTURE_P:
flags |= V4L2_BUF_FLAG_PFRAME;
break;
case HFI_GEN1_PICTURE_B:
flags |= V4L2_BUF_FLAG_BFRAME;
break;
case HFI_FRAME_NOTCODED:
case HFI_UNUSED_PICT:
case HFI_FRAME_YUV:
default:
break;
}
buf->attr &= ~BUF_ATTR_QUEUED;
buf->attr |= BUF_ATTR_DEQUEUED;
buf->attr |= BUF_ATTR_BUFFER_DONE;
if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT)
flags |= V4L2_BUF_FLAG_ERROR;
if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME)
flags |= V4L2_BUF_FLAG_ERROR;
buf->flags |= flags;
iris_vb2_buffer_done(inst, buf);
return;
error:
iris_inst_change_state(inst, IRIS_INST_ERROR);
dev_err(core->dev, "error in ftb done\n");
}
struct iris_hfi_gen1_response_pkt_info {
u32 pkt;
u32 pkt_sz;
};
static const struct iris_hfi_gen1_response_pkt_info pkt_infos[] = {
{
.pkt = HFI_MSG_EVENT_NOTIFY,
.pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
},
{
.pkt = HFI_MSG_SYS_INIT,
.pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
},
{
.pkt = HFI_MSG_SYS_PROPERTY_INFO,
.pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
},
{
.pkt = HFI_MSG_SYS_SESSION_INIT,
.pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
},
{
.pkt = HFI_MSG_SYS_SESSION_END,
.pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
},
{
.pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
.pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
},
{
.pkt = HFI_MSG_SESSION_START,
.pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
},
{
.pkt = HFI_MSG_SESSION_STOP,
.pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
},
{
.pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
.pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
},
{
.pkt = HFI_MSG_SESSION_FILL_BUFFER,
.pkt_sz = sizeof(struct hfi_msg_session_fbd_compressed_pkt),
},
{
.pkt = HFI_MSG_SESSION_FLUSH,
.pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
},
{
.pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
.pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
},
{
.pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
.pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
},
};
static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response)
{
struct hfi_pkt_hdr *hdr = (struct hfi_pkt_hdr *)response;
const struct iris_hfi_gen1_response_pkt_info *pkt_info;
struct device *dev = core->dev;
struct hfi_session_pkt *pkt;
struct iris_inst *inst;
bool found = false;
u32 i;
for (i = 0; i < ARRAY_SIZE(pkt_infos); i++) {
pkt_info = &pkt_infos[i];
if (pkt_info->pkt != hdr->pkt_type)
continue;
found = true;
break;
}
if (!found || hdr->size < pkt_info->pkt_sz) {
dev_err(dev, "bad packet size (%d should be %d, pkt type:%x, found %d)\n",
hdr->size, pkt_info->pkt_sz, hdr->pkt_type, found);
return;
}
switch (hdr->pkt_type) {
case HFI_MSG_SYS_INIT:
iris_hfi_gen1_sys_init_done(core, hdr);
break;
case HFI_MSG_SYS_PROPERTY_INFO:
iris_hfi_gen1_sys_property_info(core, hdr);
break;
case HFI_MSG_EVENT_NOTIFY:
pkt = (struct hfi_session_pkt *)hdr;
inst = iris_get_instance(core, pkt->shdr.session_id);
if (inst) {
mutex_lock(&inst->lock);
iris_hfi_gen1_session_event_notify(inst, hdr);
mutex_unlock(&inst->lock);
} else {
iris_hfi_gen1_sys_event_notify(core, hdr);
}
break;
default:
pkt = (struct hfi_session_pkt *)hdr;
inst = iris_get_instance(core, pkt->shdr.session_id);
if (!inst) {
dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
pkt->shdr.session_id,
pkt_info ? pkt_info->pkt : 0);
return;
}
mutex_lock(&inst->lock);
if (hdr->pkt_type == HFI_MSG_SESSION_EMPTY_BUFFER) {
iris_hfi_gen1_session_etb_done(inst, hdr);
} else if (hdr->pkt_type == HFI_MSG_SESSION_FILL_BUFFER) {
iris_hfi_gen1_session_ftb_done(inst, hdr);
} else {
struct hfi_msg_session_hdr_pkt *shdr;
shdr = (struct hfi_msg_session_hdr_pkt *)hdr;
if (shdr->error_type != HFI_ERR_NONE)
iris_inst_change_state(inst, IRIS_INST_ERROR);
if (pkt_info->pkt == HFI_MSG_SESSION_FLUSH) {
if (!(--inst->flush_responses_pending))
complete(&inst->flush_completion);
} else {
complete(&inst->completion);
}
}
mutex_unlock(&inst->lock);
break;
}
}
static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet)
{
struct hfi_msg_sys_coverage_pkt *pkt;
while (!iris_hfi_queue_dbg_read(core, packet)) {
pkt = (struct hfi_msg_sys_coverage_pkt *)packet;
if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
struct hfi_msg_sys_debug_pkt *pkt =
(struct hfi_msg_sys_debug_pkt *)packet;
dev_dbg(core->dev, "%s", pkt->msg_data);
}
}
}
void iris_hfi_gen1_response_handler(struct iris_core *core)
{
memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
iris_hfi_gen1_handle_response(core, core->response_packet);
memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
}
iris_hfi_gen1_flush_debug_queue(core, core->response_packet);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_GEN2_H__
#define __IRIS_HFI_GEN2_H__
#include "iris_instance.h"
struct iris_core;
#define to_iris_inst_hfi_gen2(ptr) \
container_of(ptr, struct iris_inst_hfi_gen2, inst)
/**
* struct iris_inst_hfi_gen2 - holds per video instance parameters for hfi_gen2
*
* @inst: pointer to iris_instance structure
* @packet: HFI packet
* @ipsc_properties_set: boolean to set ipsc properties to fw
* @opsc_properties_set: boolean to set opsc properties to fw
* @hfi_frame_info: structure of frame info
* @src_subcr_params: subscription params to fw on input port
* @dst_subcr_params: subscription params to fw on output port
*/
struct iris_inst_hfi_gen2 {
struct iris_inst inst;
struct iris_hfi_header *packet;
bool ipsc_properties_set;
bool opsc_properties_set;
struct iris_hfi_frame_info hfi_frame_info;
struct hfi_subscription_params src_subcr_params;
struct hfi_subscription_params dst_subcr_params;
};
void iris_hfi_gen2_sys_ops_init(struct iris_core *core);
void iris_hfi_gen2_response_handler(struct iris_core *core);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_GEN2_DEFINES_H__
#define __IRIS_HFI_GEN2_DEFINES_H__
#include <linux/types.h>
#define HFI_VIDEO_ARCH_LX 0x1
#define HFI_CMD_BEGIN 0x01000000
#define HFI_CMD_INIT 0x01000001
#define HFI_CMD_POWER_COLLAPSE 0x01000002
#define HFI_CMD_OPEN 0x01000003
#define HFI_CMD_CLOSE 0x01000004
#define HFI_CMD_START 0x01000005
#define HFI_CMD_STOP 0x01000006
#define HFI_CMD_DRAIN 0x01000007
#define HFI_CMD_RESUME 0x01000008
#define HFI_CMD_BUFFER 0x01000009
#define HFI_CMD_SUBSCRIBE_MODE 0x0100000B
#define HFI_CMD_SETTINGS_CHANGE 0x0100000C
#define HFI_CMD_PAUSE 0x01000011
#define HFI_CMD_END 0x01FFFFFF
#define HFI_BITMASK_BITSTREAM_WIDTH 0xffff0000
#define HFI_BITMASK_BITSTREAM_HEIGHT 0x0000ffff
#define HFI_BITMASK_FRAME_MBS_ONLY_FLAG 0x00000001
#define HFI_PROP_BEGIN 0x03000000
#define HFI_PROP_IMAGE_VERSION 0x03000001
#define HFI_PROP_INTRA_FRAME_POWER_COLLAPSE 0x03000002
#define HFI_PROP_UBWC_MAX_CHANNELS 0x03000003
#define HFI_PROP_UBWC_MAL_LENGTH 0x03000004
#define HFI_PROP_UBWC_HBB 0x03000005
#define HFI_PROP_UBWC_BANK_SWZL_LEVEL1 0x03000006
#define HFI_PROP_UBWC_BANK_SWZL_LEVEL2 0x03000007
#define HFI_PROP_UBWC_BANK_SWZL_LEVEL3 0x03000008
#define HFI_PROP_UBWC_BANK_SPREADING 0x03000009
#define HFI_PROP_CODEC 0x03000100
#define HFI_PROP_COLOR_FORMAT 0x03000101
#define HFI_PROP_BITSTREAM_RESOLUTION 0x03000103
#define HFI_PROP_LINEAR_STRIDE_SCANLINE 0x03000104
#define HFI_PROP_CROP_OFFSETS 0x03000105
#define HFI_PROP_PROFILE 0x03000107
#define HFI_PROP_LEVEL 0x03000108
#define HFI_PROP_TIER 0x03000109
#define HFI_PROP_STAGE 0x0300010a
#define HFI_PROP_PIPE 0x0300010b
#define HFI_PROP_FRAME_RATE 0x0300010c
#define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f
#define HFI_PROP_CODED_FRAMES 0x03000120
#define HFI_PROP_CABAC_SESSION 0x03000121
#define HFI_PROP_BUFFER_HOST_MAX_COUNT 0x03000123
#define HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT 0x03000124
#define HFI_PROP_PIC_ORDER_CNT_TYPE 0x03000128
enum hfi_rate_control {
HFI_RC_VBR_CFR = 0x00000000,
HFI_RC_CBR_CFR = 0x00000001,
HFI_RC_CQ = 0x00000002,
HFI_RC_OFF = 0x00000003,
HFI_RC_CBR_VFR = 0x00000004,
HFI_RC_LOSSLESS = 0x00000005,
};
#define HFI_PROP_RATE_CONTROL 0x0300012a
#define HFI_PROP_QP_PACKED 0x0300012e
#define HFI_PROP_MIN_QP_PACKED 0x0300012f
#define HFI_PROP_MAX_QP_PACKED 0x03000130
#define HFI_PROP_IR_RANDOM_PERIOD 0x03000131
#define HFI_PROP_LTR_COUNT 0x03000134
#define HFI_PROP_LTR_MARK 0x03000135
#define HFI_PROP_LTR_USE 0x03000136
enum hfi_layer_encoding_type {
HFI_HIER_P_SLIDING_WINDOW = 0x1,
HFI_HIER_P_HYBRID_LTR = 0x2,
HFI_HIER_B = 0x3,
};
#define HFI_PROP_LAYER_ENCODING_TYPE 0x03000138
#define HFI_PROP_LAYER_COUNT 0x03000139
#define HFI_PROP_TOTAL_BITRATE 0x0300013b
#define HFI_PROP_BITRATE_LAYER1 0x0300013c
#define HFI_PROP_BITRATE_LAYER2 0x0300013d
#define HFI_PROP_BITRATE_LAYER3 0x0300013e
#define HFI_PROP_BITRATE_LAYER4 0x0300013f
#define HFI_PROP_BITRATE_LAYER5 0x03000140
#define HFI_PROP_BITRATE_LAYER6 0x03000141
#define HFI_PROP_MAX_GOP_FRAMES 0x03000146
#define HFI_PROP_MAX_B_FRAMES 0x03000147
#define HFI_PROP_QUALITY_MODE 0x03000148
enum hfi_seq_header_mode {
HFI_SEQ_HEADER_SEPERATE_FRAME = 0x00000001,
HFI_SEQ_HEADER_JOINED_WITH_1ST_FRAME = 0x00000002,
HFI_SEQ_HEADER_PREFIX_WITH_SYNC_FRAME = 0x00000004,
HFI_SEQ_HEADER_METADATA = 0x00000008,
};
#define HFI_PROP_SEQ_HEADER_MODE 0x03000149
enum hfi_rotation {
HFI_ROTATION_NONE = 0x00000000,
HFI_ROTATION_90 = 0x00000001,
HFI_ROTATION_180 = 0x00000002,
HFI_ROTATION_270 = 0x00000003,
};
#define HFI_PROP_ROTATION 0x0300014b
enum hfi_flip {
HFI_DISABLE_FLIP = 0x00000000,
HFI_HORIZONTAL_FLIP = 0x00000001,
HFI_VERTICAL_FLIP = 0x00000002,
};
#define HFI_PROP_FLIP 0x0300014c
#define HFI_PROP_SIGNAL_COLOR_INFO 0x03000155
#define HFI_PROP_PICTURE_TYPE 0x03000162
#define HFI_PROP_DEC_DEFAULT_HEADER 0x03000168
#define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169
#define HFI_PROP_NO_OUTPUT 0x0300016a
#define HFI_PROP_BUFFER_MARK 0x0300016c
#define HFI_PROP_WORST_COMPRESSION_RATIO 0x03000174
#define HFI_PROP_WORST_COMPLEXITY_FACTOR 0x03000175
#define HFI_PROP_RAW_RESOLUTION 0x03000178
#define HFI_PROP_TOTAL_PEAK_BITRATE 0x0300017C
#define HFI_PROP_AV1_FILM_GRAIN_PRESENT 0x03000180
#define HFI_PROP_AV1_SUPER_BLOCK_ENABLED 0x03000181
#define HFI_PROP_AV1_OP_POINT 0x03000182
#define HFI_PROP_IR_CYCLIC_PERIOD 0x0300017E
#define HFI_PROP_OPB_ENABLE 0x03000184
#define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187
#define HFI_PROP_AV1_DRAP_CONFIG 0x03000189
#define HFI_PROP_UBWC_STRIDE_SCANLINE 0x03000190
#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
#define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197
#define HFI_PROP_END 0x03FFFFFF
#define HFI_SESSION_ERROR_BEGIN 0x04000000
#define HFI_ERROR_UNKNOWN_SESSION 0x04000001
#define HFI_ERROR_MAX_SESSIONS 0x04000002
#define HFI_ERROR_FATAL 0x04000003
#define HFI_ERROR_INVALID_STATE 0x04000004
#define HFI_ERROR_INSUFFICIENT_RESOURCES 0x04000005
#define HFI_ERROR_BUFFER_NOT_SET 0x04000006
#define HFI_ERROR_STREAM_UNSUPPORTED 0x04000008
#define HFI_SESSION_ERROR_END 0x04FFFFFF
#define HFI_SYSTEM_ERROR_BEGIN 0x05000000
#define HFI_SYS_ERROR_WD_TIMEOUT 0x05000001
#define HFI_SYSTEM_ERROR_END 0x05FFFFFF
#define HFI_INFORMATION_BEGIN 0x06000000
#define HFI_INFO_UNSUPPORTED 0x06000001
#define HFI_INFO_DATA_CORRUPT 0x06000002
#define HFI_INFO_BUFFER_OVERFLOW 0x06000004
#define HFI_INFO_HFI_FLAG_DRAIN_LAST 0x06000006
#define HFI_INFO_HFI_FLAG_PSC_LAST 0x06000007
#define HFI_INFORMATION_END 0x06FFFFFF
enum hfi_property_mode_type {
HFI_MODE_PORT_SETTINGS_CHANGE = 0x00000001,
HFI_MODE_PROPERTY = 0x00000002,
};
enum hfi_color_format {
HFI_COLOR_FMT_OPAQUE = 0,
HFI_COLOR_FMT_NV12 = 1,
HFI_COLOR_FMT_NV12_UBWC = 2,
HFI_COLOR_FMT_P010 = 3,
HFI_COLOR_FMT_TP10_UBWC = 4,
HFI_COLOR_FMT_RGBA8888 = 5,
HFI_COLOR_FMT_RGBA8888_UBWC = 6,
HFI_COLOR_FMT_NV21 = 7,
};
enum hfi_codec_type {
HFI_CODEC_DECODE_AVC = 1,
HFI_CODEC_ENCODE_AVC = 2,
HFI_CODEC_DECODE_HEVC = 3,
HFI_CODEC_ENCODE_HEVC = 4,
HFI_CODEC_DECODE_VP9 = 5,
HFI_CODEC_DECODE_AV1 = 7,
};
enum hfi_picture_type {
HFI_GEN2_PICTURE_IDR = 0x00000001,
HFI_GEN2_PICTURE_P = 0x00000002,
HFI_GEN2_PICTURE_B = 0x00000004,
HFI_GEN2_PICTURE_I = 0x00000008,
HFI_GEN2_PICTURE_CRA = 0x00000010,
HFI_GEN2_PICTURE_BLA = 0x00000020,
HFI_GEN2_PICTURE_NOSHOW = 0x00000040,
};
enum hfi_buffer_type {
HFI_BUFFER_BITSTREAM = 0x00000001,
HFI_BUFFER_RAW = 0x00000002,
HFI_BUFFER_METADATA = 0x00000003,
HFI_BUFFER_SUBCACHE = 0x00000004,
HFI_BUFFER_PARTIAL_DATA = 0x00000005,
HFI_BUFFER_DPB = 0x00000006,
HFI_BUFFER_BIN = 0x00000007,
HFI_BUFFER_LINE = 0x00000008,
HFI_BUFFER_ARP = 0x00000009,
HFI_BUFFER_COMV = 0x0000000A,
HFI_BUFFER_NON_COMV = 0x0000000B,
HFI_BUFFER_PERSIST = 0x0000000C,
HFI_BUFFER_VPSS = 0x0000000D,
};
enum hfi_buffer_host_flags {
HFI_BUF_HOST_FLAG_RELEASE = 0x00000001,
HFI_BUF_HOST_FLAG_READONLY = 0x00000010,
HFI_BUF_HOST_FLAG_CODEC_CONFIG = 0x00000100,
HFI_BUF_HOST_FLAGS_CB_NON_SECURE = 0x00000200,
};
enum hfi_buffer_firmware_flags {
HFI_BUF_FW_FLAG_RELEASE_DONE = 0x00000001,
HFI_BUF_FW_FLAG_READONLY = 0x00000010,
HFI_BUF_FW_FLAG_LAST = 0x10000000,
HFI_BUF_FW_FLAG_PSC_LAST = 0x20000000,
};
enum hfi_packet_firmware_flags {
HFI_FW_FLAGS_SUCCESS = 0x00000001,
HFI_FW_FLAGS_INFORMATION = 0x00000002,
HFI_FW_FLAGS_SESSION_ERROR = 0x00000004,
HFI_FW_FLAGS_SYSTEM_ERROR = 0x00000008,
};
struct hfi_debug_header {
u32 size;
u32 debug_level;
u32 reserved[2];
};
#endif
@@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/printk.h>
#include <linux/soc/qcom/ubwc.h>
#include "iris_hfi_common.h"
#include "iris_hfi_gen2.h"
#include "iris_hfi_gen2_packet.h"
u32 iris_hfi_gen2_get_color_primaries(u32 primaries)
{
switch (primaries) {
case V4L2_COLORSPACE_DEFAULT:
return HFI_PRIMARIES_RESERVED;
case V4L2_COLORSPACE_REC709:
return HFI_PRIMARIES_BT709;
case V4L2_COLORSPACE_470_SYSTEM_M:
return HFI_PRIMARIES_BT470_SYSTEM_M;
case V4L2_COLORSPACE_470_SYSTEM_BG:
return HFI_PRIMARIES_BT470_SYSTEM_BG;
case V4L2_COLORSPACE_SMPTE170M:
return HFI_PRIMARIES_BT601_525;
case V4L2_COLORSPACE_SMPTE240M:
return HFI_PRIMARIES_SMPTE_ST240M;
case V4L2_COLORSPACE_BT2020:
return HFI_PRIMARIES_BT2020;
case V4L2_COLORSPACE_DCI_P3:
return HFI_PRIMARIES_SMPTE_RP431_2;
default:
return HFI_PRIMARIES_RESERVED;
}
}
u32 iris_hfi_gen2_get_transfer_char(u32 characterstics)
{
switch (characterstics) {
case V4L2_XFER_FUNC_DEFAULT:
return HFI_TRANSFER_RESERVED;
case V4L2_XFER_FUNC_709:
return HFI_TRANSFER_BT709;
case V4L2_XFER_FUNC_SMPTE240M:
return HFI_TRANSFER_SMPTE_ST240M;
case V4L2_XFER_FUNC_SRGB:
return HFI_TRANSFER_SRGB_SYCC;
case V4L2_XFER_FUNC_SMPTE2084:
return HFI_TRANSFER_SMPTE_ST2084_PQ;
default:
return HFI_TRANSFER_RESERVED;
}
}
u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients)
{
switch (coefficients) {
case V4L2_YCBCR_ENC_DEFAULT:
return HFI_MATRIX_COEFF_RESERVED;
case V4L2_YCBCR_ENC_709:
return HFI_MATRIX_COEFF_BT709;
case V4L2_YCBCR_ENC_XV709:
return HFI_MATRIX_COEFF_BT709;
case V4L2_YCBCR_ENC_XV601:
return HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625;
case V4L2_YCBCR_ENC_601:
return HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625;
case V4L2_YCBCR_ENC_SMPTE240M:
return HFI_MATRIX_COEFF_SMPTE_ST240;
case V4L2_YCBCR_ENC_BT2020:
return HFI_MATRIX_COEFF_BT2020_NON_CONSTANT;
case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
return HFI_MATRIX_COEFF_BT2020_CONSTANT;
default:
return HFI_MATRIX_COEFF_RESERVED;
}
}
u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
u32 colour_description_present_flag, u32 full_range,
u32 video_format, u32 video_signal_type_present_flag)
{
return (matrix_coeff & 0xFF) |
((transfer_char << 8) & 0xFF00) |
((primaries << 16) & 0xFF0000) |
((colour_description_present_flag << 24) & 0x1000000) |
((full_range << 25) & 0x2000000) |
((video_format << 26) & 0x1C000000) |
((video_signal_type_present_flag << 29) & 0x20000000);
}
static void iris_hfi_gen2_create_header(struct iris_hfi_header *hdr,
u32 session_id, u32 header_id)
{
memset(hdr, 0, sizeof(*hdr));
hdr->size = sizeof(*hdr);
hdr->session_id = session_id;
hdr->header_id = header_id;
hdr->num_packets = 0;
}
static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_type,
u32 pkt_flags, u32 payload_type, u32 port,
u32 packet_id, void *payload, u32 payload_size)
{
struct iris_hfi_packet *pkt = (struct iris_hfi_packet *)((u8 *)hdr + hdr->size);
u32 pkt_size = sizeof(*pkt) + payload_size;
memset(pkt, 0, pkt_size);
pkt->size = pkt_size;
pkt->type = pkt_type;
pkt->flags = pkt_flags;
pkt->payload_info = payload_type;
pkt->port = port;
pkt->packet_id = packet_id;
if (payload_size)
memcpy(&pkt->payload[0], payload, payload_size);
hdr->num_packets++;
hdr->size += pkt->size;
}
void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr)
{
const struct qcom_ubwc_cfg_data *ubwc = core->ubwc_cfg;
u32 payload = 0;
iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
payload = HFI_VIDEO_ARCH_LX;
iris_hfi_gen2_create_packet(hdr,
HFI_CMD_INIT,
(HFI_HOST_FLAGS_RESPONSE_REQUIRED |
HFI_HOST_FLAGS_INTR_REQUIRED |
HFI_HOST_FLAGS_NON_DISCARDABLE),
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = qcom_ubwc_macrotile_mode(ubwc) ? 8 : 4;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_MAX_CHANNELS,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = qcom_ubwc_min_acc_length_64b(ubwc) ? 64 : 32;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_MAL_LENGTH,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = ubwc->highest_bank_bit;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_HBB,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL1);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL1,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL2);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL2,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL3);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL3,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
payload = qcom_ubwc_bank_spread(ubwc);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SPREADING,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
}
void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr)
{
iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_IMAGE_VERSION,
(HFI_HOST_FLAGS_RESPONSE_REQUIRED |
HFI_HOST_FLAGS_INTR_REQUIRED |
HFI_HOST_FLAGS_GET_PROPERTY),
HFI_PAYLOAD_NONE,
HFI_PORT_NONE,
core->packet_id++,
NULL, 0);
}
void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
u32 flags, u32 port, u32 session_id,
u32 payload_type, void *payload,
u32 payload_size)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
struct iris_core *core = inst->core;
iris_hfi_gen2_create_header(inst_hfi_gen2->packet, session_id, core->header_id++);
iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
pkt_type,
flags,
payload_type,
port,
core->packet_id++,
payload,
payload_size);
}
void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
u32 pkt_type, u32 flags, u32 port,
u32 payload_type, void *payload, u32 payload_size)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
struct iris_core *core = inst->core;
iris_hfi_gen2_create_header(inst_hfi_gen2->packet, inst->session_id, core->header_id++);
iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
pkt_type,
flags,
payload_type,
port,
core->packet_id++,
payload,
payload_size);
}
void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
struct iris_hfi_header *hdr)
{
u32 payload = 1; /* HFI_TRUE */
iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_INTRA_FRAME_POWER_COLLAPSE,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_U32,
HFI_PORT_NONE,
core->packet_id++,
&payload,
sizeof(u32));
}
void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr)
{
iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
iris_hfi_gen2_create_packet(hdr,
HFI_CMD_POWER_COLLAPSE,
HFI_HOST_FLAGS_NONE,
HFI_PAYLOAD_NONE,
HFI_PORT_NONE,
core->packet_id++,
NULL, 0);
}
@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_GEN2_PACKET_H__
#define __IRIS_HFI_GEN2_PACKET_H__
#include "iris_hfi_gen2_defines.h"
struct iris_core;
/**
* struct iris_hfi_header
*
* @size: size of the total packet in bytes including hfi_header
* @session_id: For session level hfi_header session_id is non-zero.
* For system level hfi_header session_id is zero.
* @header_id: unique header id for each hfi_header
* @reserved: reserved for future use
* @num_packets: number of hfi_packet that are included with the hfi_header
*/
struct iris_hfi_header {
u32 size;
u32 session_id;
u32 header_id;
u32 reserved[4];
u32 num_packets;
};
/**
* struct iris_hfi_packet
*
* @size: size of the hfi_packet in bytes including payload
* @type: one of the below hfi_packet types:
* HFI_CMD_*,
* HFI_PROP_*,
* HFI_ERROR_*,
* HFI_INFO_*,
* HFI_SYS_ERROR_*
* @flags: hfi_packet flags. It is represented as bit masks.
* host packet flags are "enum hfi_packet_host_flags"
* firmware packet flags are "enum hfi_packet_firmware_flags"
* @payload_info: payload information indicated by "enum hfi_packet_payload_info"
* @port: hfi_packet port type indicated by "enum hfi_packet_port_type"
* This is bitmask and may be applicable to multiple ports.
* @packet_id: host hfi_packet contains unique packet id.
* firmware returns host packet id in response packet
* wherever applicable. If not applicable firmware sets it to zero.
* @reserved: reserved for future use.
* @payload: flexible array of payload having additional packet information.
*/
struct iris_hfi_packet {
u32 size;
u32 type;
u32 flags;
u32 payload_info;
u32 port;
u32 packet_id;
u32 reserved[2];
u32 payload[];
};
/**
* struct iris_hfi_buffer
*
* @type: buffer type indicated by "enum hfi_buffer_type"
* FW needs to return proper type for any buffer command.
* @index: index of the buffer
* @base_address: base address of the buffer.
* This buffer address is always 4KBytes aligned.
* @addr_offset: accessible buffer offset from base address
* Decoder bitstream buffer: 256 Bytes aligned
* Firmware can uniquely identify a buffer based on
* base_address & addr_offset.
* HW can read memory only from base_address+addr_offset.
* @buffer_size: accessible buffer size in bytes starting from addr_offset
* @data_offset: data starts from "base_address + addr_offset + data_offset"
* RAW buffer: data_offset is 0. Restriction: 4KBytes aligned
* decoder bitstream buffer: no restriction (can be any value)
* @data_size: data size in bytes
* @flags: buffer flags. It is represented as bit masks.
* host buffer flags are "enum hfi_buffer_host_flags"
* firmware buffer flags are "enum hfi_buffer_firmware_flags"
* @timestamp: timestamp of the buffer in nano seconds (ns)
* It is Presentation timestamp (PTS) for encoder & decoder.
* Decoder: it is pass through from bitstream to raw buffer.
* firmware does not need to return as part of input buffer done.
* For any internal buffers: there is no timestamp. Host sets as 0.
* @reserved: reserved for future use
*/
struct iris_hfi_buffer {
u32 type;
u32 index;
u64 base_address;
u32 addr_offset;
u32 buffer_size;
u32 data_offset;
u32 data_size;
u64 timestamp;
u32 flags;
u32 reserved[5];
};
u32 iris_hfi_gen2_get_color_primaries(u32 primaries);
u32 iris_hfi_gen2_get_transfer_char(u32 characterstics);
u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients);
u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
u32 colour_description_present_flag, u32 full_range,
u32 video_format, u32 video_signal_type_present_flag);
void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr);
void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr);
void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
u32 flags, u32 port, u32 session_id,
u32 payload_type, void *payload,
u32 payload_size);
void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
u32 pkt_type, u32 flags, u32 port,
u32 payload_type, void *payload, u32 payload_size);
void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
struct iris_hfi_header *hdr);
void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,317 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include "iris_core.h"
#include "iris_hfi_queue.h"
#include "iris_vpu_common.h"
static int iris_hfi_queue_write(struct iris_iface_q_info *qinfo, void *packet, u32 packet_size)
{
struct iris_hfi_queue_header *queue = qinfo->qhdr;
u32 write_idx = queue->write_idx * sizeof(u32);
u32 read_idx = queue->read_idx * sizeof(u32);
u32 empty_space, new_write_idx, residue;
u32 *write_ptr;
if (write_idx < read_idx)
empty_space = read_idx - write_idx;
else
empty_space = IFACEQ_QUEUE_SIZE - (write_idx - read_idx);
if (empty_space < packet_size)
return -ENOSPC;
queue->tx_req = 0;
new_write_idx = write_idx + packet_size;
write_ptr = (u32 *)((u8 *)qinfo->kernel_vaddr + write_idx);
if (write_ptr < (u32 *)qinfo->kernel_vaddr ||
write_ptr > (u32 *)(qinfo->kernel_vaddr +
IFACEQ_QUEUE_SIZE))
return -EINVAL;
if (new_write_idx < IFACEQ_QUEUE_SIZE) {
memcpy(write_ptr, packet, packet_size);
} else {
residue = new_write_idx - IFACEQ_QUEUE_SIZE;
memcpy(write_ptr, packet, (packet_size - residue));
memcpy(qinfo->kernel_vaddr,
packet + (packet_size - residue), residue);
new_write_idx = residue;
}
/* Make sure packet is written before updating the write index */
mb();
queue->write_idx = new_write_idx / sizeof(u32);
/* Make sure write index is updated before an interrupt is raised */
mb();
return 0;
}
static int iris_hfi_queue_read(struct iris_iface_q_info *qinfo, void *packet)
{
struct iris_hfi_queue_header *queue = qinfo->qhdr;
u32 write_idx = queue->write_idx * sizeof(u32);
u32 read_idx = queue->read_idx * sizeof(u32);
u32 packet_size, receive_request = 0;
u32 new_read_idx, residue;
u32 *read_ptr;
int ret = 0;
if (queue->queue_type == IFACEQ_MSGQ_ID)
receive_request = 1;
if (read_idx == write_idx) {
queue->rx_req = receive_request;
/* Ensure qhdr is updated in main memory */
mb();
return -ENODATA;
}
read_ptr = qinfo->kernel_vaddr + read_idx;
if (read_ptr < (u32 *)qinfo->kernel_vaddr ||
read_ptr > (u32 *)(qinfo->kernel_vaddr +
IFACEQ_QUEUE_SIZE - sizeof(*read_ptr)))
return -ENODATA;
packet_size = *read_ptr;
if (!packet_size)
return -EINVAL;
new_read_idx = read_idx + packet_size;
if (packet_size <= IFACEQ_CORE_PKT_SIZE) {
if (new_read_idx < IFACEQ_QUEUE_SIZE) {
memcpy(packet, read_ptr, packet_size);
} else {
residue = new_read_idx - IFACEQ_QUEUE_SIZE;
memcpy(packet, read_ptr, (packet_size - residue));
memcpy((packet + (packet_size - residue)),
qinfo->kernel_vaddr, residue);
new_read_idx = residue;
}
} else {
new_read_idx = write_idx;
ret = -EBADMSG;
}
queue->rx_req = receive_request;
queue->read_idx = new_read_idx / sizeof(u32);
/* Ensure qhdr is updated in main memory */
mb();
return ret;
}
int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size)
{
struct iris_iface_q_info *q_info = &core->command_queue;
if (core->state == IRIS_CORE_ERROR || core->state == IRIS_CORE_DEINIT)
return -EINVAL;
if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) {
iris_vpu_raise_interrupt(core);
} else {
dev_err(core->dev, "queue full\n");
return -ENODATA;
}
return 0;
}
int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size)
{
int ret;
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
goto exit;
mutex_lock(&core->lock);
ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size);
if (ret) {
mutex_unlock(&core->lock);
goto exit;
}
mutex_unlock(&core->lock);
pm_runtime_put_autosuspend(core->dev);
return 0;
exit:
pm_runtime_put_sync(core->dev);
return ret;
}
int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt)
{
struct iris_iface_q_info *q_info = &core->message_queue;
int ret = 0;
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_INIT) {
ret = -EINVAL;
goto unlock;
}
if (iris_hfi_queue_read(q_info, pkt)) {
ret = -ENODATA;
goto unlock;
}
unlock:
mutex_unlock(&core->lock);
return ret;
}
int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt)
{
struct iris_iface_q_info *q_info = &core->debug_queue;
int ret = 0;
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_INIT) {
ret = -EINVAL;
goto unlock;
}
if (iris_hfi_queue_read(q_info, pkt)) {
ret = -ENODATA;
goto unlock;
}
unlock:
mutex_unlock(&core->lock);
return ret;
}
static void iris_hfi_queue_set_header(struct iris_core *core, u32 queue_id,
struct iris_iface_q_info *iface_q)
{
iface_q->qhdr->status = 0x1;
iface_q->qhdr->start_addr = iface_q->device_addr;
iface_q->qhdr->header_type = IFACEQ_DFLT_QHDR;
iface_q->qhdr->queue_type = queue_id;
iface_q->qhdr->q_size = IFACEQ_QUEUE_SIZE / sizeof(u32);
iface_q->qhdr->pkt_size = 0; /* variable packet size */
iface_q->qhdr->rx_wm = 0x1;
iface_q->qhdr->tx_wm = 0x1;
iface_q->qhdr->rx_req = 0x1;
iface_q->qhdr->tx_req = 0x0;
iface_q->qhdr->rx_irq_status = 0x0;
iface_q->qhdr->tx_irq_status = 0x0;
iface_q->qhdr->read_idx = 0x0;
iface_q->qhdr->write_idx = 0x0;
/*
* Set receive request to zero on debug queue as there is no
* need of interrupt from video hardware for debug messages
*/
if (queue_id == IFACEQ_DBGQ_ID)
iface_q->qhdr->rx_req = 0;
}
static void
iris_hfi_queue_init(struct iris_core *core, u32 queue_id, struct iris_iface_q_info *iface_q)
{
struct iris_hfi_queue_table_header *q_tbl_hdr = core->iface_q_table_vaddr;
u32 offset = sizeof(*q_tbl_hdr) + (queue_id * IFACEQ_QUEUE_SIZE);
iface_q->device_addr = core->iface_q_table_daddr + offset;
iface_q->kernel_vaddr =
(void *)((char *)core->iface_q_table_vaddr + offset);
iface_q->qhdr = &q_tbl_hdr->q_hdr[queue_id];
iris_hfi_queue_set_header(core, queue_id, iface_q);
}
static void iris_hfi_queue_deinit(struct iris_iface_q_info *iface_q)
{
iface_q->qhdr = NULL;
iface_q->kernel_vaddr = NULL;
iface_q->device_addr = 0;
}
int iris_hfi_queues_init(struct iris_core *core)
{
struct iris_hfi_queue_table_header *q_tbl_hdr;
u32 queue_size;
/* Iris hardware requires 4K queue alignment */
queue_size = ALIGN((sizeof(*q_tbl_hdr) + (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ)), SZ_4K);
core->iface_q_table_vaddr = dma_alloc_attrs(core->dev, queue_size,
&core->iface_q_table_daddr,
GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!core->iface_q_table_vaddr) {
dev_err(core->dev, "queues alloc and map failed\n");
return -ENOMEM;
}
core->sfr_vaddr = dma_alloc_attrs(core->dev, SFR_SIZE,
&core->sfr_daddr,
GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!core->sfr_vaddr) {
dev_err(core->dev, "sfr alloc and map failed\n");
dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr,
core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
return -ENOMEM;
}
iris_hfi_queue_init(core, IFACEQ_CMDQ_ID, &core->command_queue);
iris_hfi_queue_init(core, IFACEQ_MSGQ_ID, &core->message_queue);
iris_hfi_queue_init(core, IFACEQ_DBGQ_ID, &core->debug_queue);
q_tbl_hdr = (struct iris_hfi_queue_table_header *)core->iface_q_table_vaddr;
q_tbl_hdr->version = 0;
q_tbl_hdr->device_addr = (void *)core;
strscpy(q_tbl_hdr->name, "iris-hfi-queues", sizeof(q_tbl_hdr->name));
q_tbl_hdr->size = sizeof(*q_tbl_hdr);
q_tbl_hdr->qhdr0_offset = sizeof(*q_tbl_hdr) -
(IFACEQ_NUMQ * sizeof(struct iris_hfi_queue_header));
q_tbl_hdr->qhdr_size = sizeof(q_tbl_hdr->q_hdr[0]);
q_tbl_hdr->num_q = IFACEQ_NUMQ;
q_tbl_hdr->num_active_q = IFACEQ_NUMQ;
/* Write sfr size in first word to be used by firmware */
*((u32 *)core->sfr_vaddr) = SFR_SIZE;
return 0;
}
void iris_hfi_queues_deinit(struct iris_core *core)
{
u32 queue_size;
if (!core->iface_q_table_vaddr)
return;
iris_hfi_queue_deinit(&core->debug_queue);
iris_hfi_queue_deinit(&core->message_queue);
iris_hfi_queue_deinit(&core->command_queue);
dma_free_attrs(core->dev, SFR_SIZE, core->sfr_vaddr,
core->sfr_daddr, DMA_ATTR_WRITE_COMBINE);
core->sfr_vaddr = NULL;
core->sfr_daddr = 0;
queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
(IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr,
core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
core->iface_q_table_vaddr = NULL;
core->iface_q_table_daddr = 0;
}
@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_QUEUE_H__
#define __IRIS_HFI_QUEUE_H__
struct iris_core;
/*
* Max 64 Buffers ( 32 input buffers and 32 output buffers)
* can be queued by v4l2 framework at any given time.
*/
#define IFACEQ_MAX_BUF_COUNT 64
/*
* Max session supported are 16.
* this value is used to calcualte the size of
* individual shared queue.
*/
#define IFACE_MAX_PARALLEL_SESSIONS 16
#define IFACEQ_DFLT_QHDR 0x0101
#define IFACEQ_MAX_PKT_SIZE 1024 /* Maximum size of a packet in the queue */
/*
* SFR: Subsystem Failure Reason
* when hardware goes into bad state/failure, firmware fills this memory
* and driver will get to know the actual failure reason from this SFR buffer.
*/
#define SFR_SIZE SZ_4K /* Iris hardware requires 4K queue alignment */
#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
IFACEQ_MAX_BUF_COUNT * IFACE_MAX_PARALLEL_SESSIONS)
/*
* Memory layout of the shared queues:
*
* ||=================|| ^ ^ ^
* || || | | |
* || Queue Table || 288 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V | |
* ||-----------------|| ^ | |
* || || | | |
* || Command Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V 456 Bytes |
* ||-----------------|| ^ | |
* || || | | |
* || Message Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V | Buffer size aligned to 4k
* ||-----------------|| ^ | Overall Queue Size = 2,404 KB
* || || | | |
* || Debug Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||=================|| V V |
* ||=================|| ^ |
* || || | |
* || Command || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* ||=================|| ^ |
* || || | |
* || Message || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* ||=================|| ^ |
* || || | |
* || Debug || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* || || |
* ||=================|| V
*/
/*
* Shared queues are used for communication between driver and firmware.
* There are 3 types of queues:
* Command queue - driver to write any command to firmware.
* Message queue - firmware to send any response to driver.
* Debug queue - firmware to write debug message.
*/
/* Host-firmware shared queue ids */
enum iris_iface_queue {
IFACEQ_CMDQ_ID,
IFACEQ_MSGQ_ID,
IFACEQ_DBGQ_ID,
IFACEQ_NUMQ, /* not an index */
};
/**
* struct iris_hfi_queue_header
*
* @status: Queue status, bits (7:0), 0x1 - active, 0x0 - inactive
* @start_addr: Queue start address in non cached memory
* @queue_type: Queue ID
* @header_type: Default queue header
* @q_size: Queue size
* Number of queue packets if pkt_size is non-zero
* Queue size in bytes if pkt_size is zero
* @pkt_size: Size of queue packet entries
* 0x0: variable queue packet size
* non zero: size of queue packet entry, fixed
* @pkt_drop_cnt: Number of packets dropped by sender
* @rx_wm: Receiver watermark, applicable in event driven mode
* @tx_wm: Sender watermark, applicable in event driven mode
* @rx_req: Receiver sets this bit if queue is empty
* @tx_req: Sender sets this bit if queue is full
* @rx_irq_status: Receiver sets this bit and triggers an interrupt to
* the sender after packets are dequeued. Sender clears this bit
* @tx_irq_status: Sender sets this bit and triggers an interrupt to
* the receiver after packets are queued. Receiver clears this bit
* @read_idx: Index till where receiver has consumed the packets from the queue.
* @write_idx: Index till where sender has written the packets into the queue.
*/
struct iris_hfi_queue_header {
u32 status;
u32 start_addr;
u16 queue_type;
u16 header_type;
u32 q_size;
u32 pkt_size;
u32 pkt_drop_cnt;
u32 rx_wm;
u32 tx_wm;
u32 rx_req;
u32 tx_req;
u32 rx_irq_status;
u32 tx_irq_status;
u32 read_idx;
u32 write_idx;
};
/**
* struct iris_hfi_queue_table_header
*
* @version: Queue table version number
* @size: Queue table size from version to last parametr in qhdr entry
* @qhdr0_offset: Offset to the start of first qhdr
* @qhdr_size: Queue header size in bytes
* @num_q: Total number of queues in Queue table
* @num_active_q: Total number of active queues
* @device_addr: Device address of the queue
* @name: Queue name in characters
* @q_hdr: Array of queue headers
*/
struct iris_hfi_queue_table_header {
u32 version;
u32 size;
u32 qhdr0_offset;
u32 qhdr_size;
u32 num_q;
u32 num_active_q;
void *device_addr;
char name[256]; /* NUL-terminated array of characters */
struct iris_hfi_queue_header q_hdr[IFACEQ_NUMQ];
};
struct iris_iface_q_info {
struct iris_hfi_queue_header *qhdr;
dma_addr_t device_addr;
void *kernel_vaddr;
};
int iris_hfi_queues_init(struct iris_core *core);
void iris_hfi_queues_deinit(struct iris_core *core);
int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size);
int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size);
int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt);
int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt);
#endif
@@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_INSTANCE_H__
#define __IRIS_INSTANCE_H__
#include <media/v4l2-ctrls.h>
#include "iris_buffer.h"
#include "iris_core.h"
#include "iris_utils.h"
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
struct iris_hfi_session_ops;
enum iris_fmt_type_out {
IRIS_FMT_H264,
IRIS_FMT_HEVC,
IRIS_FMT_VP9,
IRIS_FMT_AV1,
};
enum iris_fmt_type_cap {
IRIS_FMT_NV12,
IRIS_FMT_QC08C,
IRIS_FMT_TP10,
IRIS_FMT_QC10C,
};
/**
* struct iris_inst - holds per video instance parameters
*
* @list: used for attach an instance to the core
* @core: pointer to core structure
* @session_id: id of current video session
* @hfi_session_ops: iris HFI session ops
* @ctx_q_lock: lock to serialize queues related ioctls
* @lock: lock to seralise forward and reverse threads
* @fh: reference of v4l2 file handler
* @fmt_src: structure of v4l2_format for source
* @fmt_dst: structure of v4l2_format for destination
* @ctrl_handler: reference of v4l2 ctrl handler
* @domain: domain type: encoder or decoder
* @crop: structure of crop info
* @compose: structure of compose info
* @completion: structure of signal completions
* @flush_completion: structure of signal completions for flush cmd
* @flush_responses_pending: counter to track number of pending flush responses
* @fw_caps: array of supported instance firmware capabilities
* @buffers: array of different iris buffers
* @fw_min_count: minimnum count of buffers needed by fw
* @state: instance state
* @sub_state: instance sub state
* @once_per_session_set: boolean to set once per session property
* @max_input_data_size: max size of input data
* @power: structure of power info
* @icc_data: structure of interconnect data
* @m2m_dev: a reference to m2m device structure
* @m2m_ctx: a reference to m2m context structure
* @sequence_cap: a sequence counter for capture queue
* @sequence_out: a sequence counter for output queue
* @tss: timestamp metadata
* @metadata_idx: index for metadata buffer
* @codec: codec type
* @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver
* @last_buf_ns: start time of received input buffer for current one second FPS window
* @frame_counter: input buffer counter for current one second FPS window
* @frame_rate: frame rate of current instance
* @operating_rate: operating rate of current instance
* @hfi_rc_type: rate control type
* @enc_raw_width: source image width for encoder instance
* @enc_raw_height: source image height for encoder instance
* @enc_scale_width: scale width for encoder instance
* @enc_scale_height: scale height for encoder instance
* @hfi_layer_type: hierarchical coding layer type
* @hfi_layer_count: hierarchical coding layer count
*/
struct iris_inst {
struct list_head list;
struct iris_core *core;
u32 session_id;
const struct iris_hfi_session_ops *hfi_session_ops;
struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */
struct mutex lock; /* lock to serialize forward and reverse threads */
struct v4l2_fh fh;
struct v4l2_format *fmt_src;
struct v4l2_format *fmt_dst;
struct v4l2_ctrl_handler ctrl_handler;
enum domain_type domain;
struct iris_hfi_rect_desc crop;
struct iris_hfi_rect_desc compose;
struct completion completion;
struct completion flush_completion;
u32 flush_responses_pending;
struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX];
struct iris_buffers buffers[BUF_TYPE_MAX];
u32 fw_min_count;
enum iris_inst_state state;
enum iris_inst_sub_state sub_state;
bool once_per_session_set;
size_t max_input_data_size;
struct iris_inst_power power;
struct icc_vote_data icc_data;
struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx;
u32 sequence_cap;
u32 sequence_out;
struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
u32 metadata_idx;
u32 codec;
bool last_buffer_dequeued;
u64 last_buf_ns;
u32 frame_counter;
u32 frame_rate;
u32 operating_rate;
u32 hfi_rc_type;
u32 enc_raw_width;
u32 enc_raw_height;
u32 enc_scale_width;
u32 enc_scale_height;
u32 hfi_layer_type;
u32 hfi_layer_count;
};
#endif
@@ -0,0 +1,329 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_PLATFORM_COMMON_H__
#define __IRIS_PLATFORM_COMMON_H__
#include <linux/bits.h>
#include "iris_buffer.h"
struct iris_core;
struct iris_inst;
#define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */
#define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */
#define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma))
#define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8)
#define BIT_DEPTH_10 REGISTER_BIT_DEPTH(10, 10)
#define CODED_FRAMES_PROGRESSIVE 0x0
#define DEFAULT_MAX_HOST_BUF_COUNT 64
#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
#define DEFAULT_FPS 30
#define MAXIMUM_FPS 480
#define NUM_MBS_8K ((8192 * 4352) / 256)
#define MIN_QP_8BIT 1
#define MAX_QP 51
#define MAX_QP_HEVC 63
#define DEFAULT_QP 20
#define BITRATE_DEFAULT 20000000
#define INVALID_DEFAULT_MARK_OR_USE_LTR -1
#define MAX_LTR_FRAME_COUNT_GEN1 4
#define MAX_LTR_FRAME_COUNT_GEN2 2
#define MAX_LAYER_HB 3
#define MAX_AVC_LAYER_HP_HYBRID_LTR 5
#define MAX_AVC_LAYER_HP_SLIDING_WINDOW 3
#define MAX_HEVC_LAYER_HP_SLIDING_WINDOW 3
#define MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW 5
#define MAX_HIER_CODING_LAYER_GEN1 6
enum stage_type {
STAGE_1 = 1,
STAGE_2 = 2,
};
enum pipe_type {
PIPE_1 = 1,
PIPE_2 = 2,
PIPE_4 = 4,
};
extern const struct iris_firmware_data iris_hfi_gen1_data;
extern const struct iris_firmware_data iris_hfi_gen2_data;
extern const struct iris_platform_data qcs8300_data;
extern const struct iris_platform_data sc7280_data;
extern const struct iris_platform_data sm8250_data;
extern const struct iris_platform_data sm8550_data;
extern const struct iris_platform_data sm8650_data;
extern const struct iris_platform_data sm8750_data;
extern const struct iris_platform_data x1p42100_data;
enum platform_clk_type {
IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */
IRIS_CTRL_CLK,
IRIS_AHB_CLK,
IRIS_HW_CLK,
IRIS_HW_AHB_CLK,
IRIS_AXI1_CLK,
IRIS_CTRL_FREERUN_CLK,
IRIS_HW_FREERUN_CLK,
IRIS_BSE_HW_CLK,
IRIS_VPP0_HW_CLK,
IRIS_VPP1_HW_CLK,
IRIS_APV_HW_CLK,
};
struct platform_clk_data {
enum platform_clk_type clk_type;
const char *clk_name;
};
struct tz_cp_config {
u32 cp_start;
u32 cp_size;
u32 cp_nonpixel_start;
u32 cp_nonpixel_size;
};
struct platform_inst_caps {
u32 min_frame_width;
u32 max_frame_width;
u32 min_frame_height;
u32 max_frame_height;
u32 max_mbpf;
u32 mb_cycles_vsp;
u32 mb_cycles_vpp;
u32 mb_cycles_fw;
u32 mb_cycles_fw_vpp;
u32 max_frame_rate;
u32 max_operating_rate;
};
enum platform_inst_fw_cap_type {
PROFILE_H264 = 1,
PROFILE_HEVC,
PROFILE_VP9,
LEVEL_H264,
LEVEL_HEVC,
LEVEL_VP9,
PROFILE_AV1,
LEVEL_AV1,
TIER_AV1,
DRAP,
FILM_GRAIN,
SUPER_BLOCK,
ENH_LAYER_COUNT,
INPUT_BUF_HOST_MAX_COUNT,
OUTPUT_BUF_HOST_MAX_COUNT,
STAGE,
PIPE,
POC,
CODED_FRAMES,
BIT_DEPTH,
RAP_FRAME,
TIER,
HEADER_MODE,
PREPEND_SPSPPS_TO_IDR,
BITRATE,
BITRATE_PEAK,
BITRATE_MODE,
FRAME_SKIP_MODE,
FRAME_RC_ENABLE,
GOP_SIZE,
ENTROPY_MODE,
MIN_FRAME_QP_H264,
MIN_FRAME_QP_HEVC,
MAX_FRAME_QP_H264,
MAX_FRAME_QP_HEVC,
I_FRAME_MIN_QP_H264,
I_FRAME_MIN_QP_HEVC,
P_FRAME_MIN_QP_H264,
P_FRAME_MIN_QP_HEVC,
B_FRAME_MIN_QP_H264,
B_FRAME_MIN_QP_HEVC,
I_FRAME_MAX_QP_H264,
I_FRAME_MAX_QP_HEVC,
P_FRAME_MAX_QP_H264,
P_FRAME_MAX_QP_HEVC,
B_FRAME_MAX_QP_H264,
B_FRAME_MAX_QP_HEVC,
I_FRAME_QP_H264,
I_FRAME_QP_HEVC,
P_FRAME_QP_H264,
P_FRAME_QP_HEVC,
B_FRAME_QP_H264,
B_FRAME_QP_HEVC,
ROTATION,
HFLIP,
VFLIP,
IR_TYPE,
IR_PERIOD,
LTR_COUNT,
USE_LTR,
MARK_LTR,
B_FRAME,
INTRA_PERIOD,
LAYER_ENABLE,
LAYER_TYPE_H264,
LAYER_TYPE_HEVC,
LAYER_COUNT_H264,
LAYER_COUNT_HEVC,
LAYER0_BITRATE_H264,
LAYER1_BITRATE_H264,
LAYER2_BITRATE_H264,
LAYER3_BITRATE_H264,
LAYER4_BITRATE_H264,
LAYER5_BITRATE_H264,
LAYER0_BITRATE_HEVC,
LAYER1_BITRATE_HEVC,
LAYER2_BITRATE_HEVC,
LAYER3_BITRATE_HEVC,
LAYER4_BITRATE_HEVC,
LAYER5_BITRATE_HEVC,
INST_FW_CAP_MAX,
};
enum platform_inst_fw_cap_flags {
CAP_FLAG_DYNAMIC_ALLOWED = BIT(0),
CAP_FLAG_MENU = BIT(1),
CAP_FLAG_INPUT_PORT = BIT(2),
CAP_FLAG_OUTPUT_PORT = BIT(3),
CAP_FLAG_CLIENT_SET = BIT(4),
CAP_FLAG_BITMASK = BIT(5),
CAP_FLAG_VOLATILE = BIT(6),
};
struct platform_inst_fw_cap {
enum platform_inst_fw_cap_type cap_id;
s64 min;
s64 max;
s64 step_or_mask;
s64 value;
u32 hfi_id;
enum platform_inst_fw_cap_flags flags;
int (*set)(struct iris_inst *inst,
enum platform_inst_fw_cap_type cap_id);
};
struct bw_info {
u32 mbs_per_sec;
u32 bw_ddr;
};
struct iris_core_power {
u64 clk_freq;
u64 icc_bw;
};
struct iris_inst_power {
u64 min_freq;
u32 icc_bw;
};
struct icc_vote_data {
u32 height, width;
u32 fps;
};
enum platform_pm_domain_type {
IRIS_CTRL_POWER_DOMAIN,
IRIS_HW_POWER_DOMAIN,
IRIS_VPP0_HW_POWER_DOMAIN,
IRIS_VPP1_HW_POWER_DOMAIN,
IRIS_APV_HW_POWER_DOMAIN,
};
struct iris_firmware_data {
void (*init_hfi_ops)(struct iris_core *core);
u32 core_arch;
const struct platform_inst_fw_cap *inst_fw_caps_dec;
u32 inst_fw_caps_dec_size;
const struct platform_inst_fw_cap *inst_fw_caps_enc;
u32 inst_fw_caps_enc_size;
const u32 *dec_input_config_params_default;
unsigned int dec_input_config_params_default_size;
const u32 *dec_input_config_params_hevc;
unsigned int dec_input_config_params_hevc_size;
const u32 *dec_input_config_params_vp9;
unsigned int dec_input_config_params_vp9_size;
const u32 *dec_input_config_params_av1;
unsigned int dec_input_config_params_av1_size;
const u32 *dec_output_config_params;
unsigned int dec_output_config_params_size;
const u32 *enc_input_config_params;
unsigned int enc_input_config_params_size;
const u32 *enc_output_config_params;
unsigned int enc_output_config_params_size;
const u32 *dec_input_prop;
unsigned int dec_input_prop_size;
const u32 *dec_output_prop_avc;
unsigned int dec_output_prop_avc_size;
const u32 *dec_output_prop_hevc;
unsigned int dec_output_prop_hevc_size;
const u32 *dec_output_prop_vp9;
unsigned int dec_output_prop_vp9_size;
const u32 *dec_output_prop_av1;
unsigned int dec_output_prop_av1_size;
const u32 *dec_ip_int_buf_tbl;
unsigned int dec_ip_int_buf_tbl_size;
const u32 *dec_op_int_buf_tbl;
unsigned int dec_op_int_buf_tbl_size;
const u32 *enc_ip_int_buf_tbl;
unsigned int enc_ip_int_buf_tbl_size;
const u32 *enc_op_int_buf_tbl;
unsigned int enc_op_int_buf_tbl_size;
};
struct iris_firmware_desc {
const struct iris_firmware_data *firmware_data;
u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type);
const char *fwname;
};
struct iris_platform_data {
/*
* XXX: replace with gen1 / gen2 pointers once we have platforms
* supporting both firmware kinds.
*/
const struct iris_firmware_desc *firmware_desc;
const struct vpu_ops *vpu_ops;
const struct icc_info *icc_tbl;
unsigned int icc_tbl_size;
const struct bw_info *bw_tbl_dec;
unsigned int bw_tbl_dec_size;
const char * const *pmdomain_tbl;
unsigned int pmdomain_tbl_size;
const char * const *opp_pd_tbl;
unsigned int opp_pd_tbl_size;
const struct platform_clk_data *clk_tbl;
const char * const *opp_clk_tbl;
unsigned int clk_tbl_size;
const char * const *clk_rst_tbl;
unsigned int clk_rst_tbl_size;
const char * const *controller_rst_tbl;
unsigned int controller_rst_tbl_size;
u64 dma_mask;
const u32 *inst_iris_fmts;
u32 inst_iris_fmts_size;
struct platform_inst_caps *inst_caps;
const struct tz_cp_config *tz_cp_config_data;
u32 tz_cp_config_data_size;
u32 num_vpp_pipe;
bool no_aon;
u32 max_session_count;
/* max number of macroblocks per frame supported */
u32 max_core_mbpf;
/* max number of macroblocks per second supported */
u32 max_core_mbps;
};
#endif
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_PLATFORM_QCS8300_H__
#define __IRIS_PLATFORM_QCS8300_H__
static struct platform_inst_caps platform_inst_cap_qcs8300 = {
.min_frame_width = 96,
.max_frame_width = 4096,
.min_frame_height = 96,
.max_frame_height = 4096,
.max_mbpf = (4096 * 2176) / 256,
.mb_cycles_vpp = 200,
.mb_cycles_fw = 326389,
.mb_cycles_fw_vpp = 44156,
.max_frame_rate = MAXIMUM_FPS,
.max_operating_rate = MAXIMUM_FPS,
};
#endif
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef __IRIS_PLATFORM_SC7280_H__
#define __IRIS_PLATFORM_SC7280_H__
static const struct bw_info sc7280_bw_table_dec[] = {
{ ((3840 * 2160) / 256) * 60, 1896000, },
{ ((3840 * 2160) / 256) * 30, 968000, },
{ ((1920 * 1080) / 256) * 60, 618000, },
{ ((1920 * 1080) / 256) * 30, 318000, },
};
static const char * const sc7280_opp_pd_table[] = { "cx" };
static const struct platform_clk_data sc7280_clk_table[] = {
{IRIS_CTRL_CLK, "core" },
{IRIS_AXI_CLK, "iface" },
{IRIS_AHB_CLK, "bus" },
{IRIS_HW_CLK, "vcodec_core" },
{IRIS_HW_AHB_CLK, "vcodec_bus" },
};
static const char * const sc7280_opp_clk_table[] = {
"vcodec_core",
NULL,
};
#endif
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef __IRIS_PLATFORM_SM8250_H__
#define __IRIS_PLATFORM_SM8250_H__
static const struct bw_info sm8250_bw_table_dec[] = {
{ ((4096 * 2160) / 256) * 60, 2403000 },
{ ((4096 * 2160) / 256) * 30, 1224000 },
{ ((1920 * 1080) / 256) * 60, 812000 },
{ ((1920 * 1080) / 256) * 30, 416000 },
};
static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" };
static const struct platform_clk_data sm8250_clk_table[] = {
{IRIS_AXI_CLK, "iface" },
{IRIS_CTRL_CLK, "core" },
{IRIS_HW_CLK, "vcodec0_core" },
};
static const char * const sm8250_opp_clk_table[] = {
"vcodec0_core",
NULL,
};
#endif
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_PLATFORM_SM8550_H__
#define __IRIS_PLATFORM_SM8550_H__
static const char * const sm8550_clk_reset_table[] = { "bus" };
static const struct platform_clk_data sm8550_clk_table[] = {
{IRIS_AXI_CLK, "iface" },
{IRIS_CTRL_CLK, "core" },
{IRIS_HW_CLK, "vcodec0_core" },
};
static struct platform_inst_caps platform_inst_cap_sm8550 = {
.min_frame_width = 96,
.max_frame_width = 8192,
.min_frame_height = 96,
.max_frame_height = 8192,
.max_mbpf = (8192 * 4352) / 256,
.mb_cycles_vpp = 200,
.mb_cycles_fw = 489583,
.mb_cycles_fw_vpp = 66234,
.max_frame_rate = MAXIMUM_FPS,
.max_operating_rate = MAXIMUM_FPS,
};
#endif
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_PLATFORM_SM8650_H__
#define __IRIS_PLATFORM_SM8650_H__
static const char * const sm8650_clk_reset_table[] = { "bus", "core" };
static const char * const sm8650_controller_reset_table[] = { "xo" };
#endif
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2025 Linaro Ltd
*/
#ifndef __MEDIA_IRIS_PLATFORM_SM8750_H__
#define __MEDIA_IRIS_PLATFORM_SM8750_H__
static const char * const sm8750_clk_reset_table[] = {
"bus0", "bus1", "core", "vcodec0_core"
};
static const struct platform_clk_data sm8750_clk_table[] = {
{IRIS_AXI_CLK, "iface" },
{IRIS_CTRL_CLK, "core" },
{IRIS_HW_CLK, "vcodec0_core" },
{IRIS_AXI1_CLK, "iface1" },
{IRIS_CTRL_FREERUN_CLK, "core_freerun" },
{IRIS_HW_FREERUN_CLK, "vcodec0_core_freerun" },
};
#endif
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "iris_core.h"
#include "iris_ctrls.h"
#include "iris_platform_common.h"
#include "iris_resources.h"
#include "iris_hfi_gen1.h"
#include "iris_hfi_gen1_defines.h"
#include "iris_vpu_buffer.h"
#include "iris_vpu_common.h"
#include "iris_instance.h"
#include "iris_platform_sc7280.h"
#include "iris_platform_sm8250.h"
static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = {
.firmware_data = &iris_hfi_gen1_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu20_p1.mbn",
};
static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = {
.firmware_data = &iris_hfi_gen1_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu20_p4.mbn",
};
static const u32 iris_fmts_vpu2_dec[] = {
[IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
[IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
[IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9,
};
static struct platform_inst_caps platform_inst_cap_vpu2 = {
.min_frame_width = 128,
.max_frame_width = 8192,
.min_frame_height = 128,
.max_frame_height = 8192,
.max_mbpf = 138240,
.mb_cycles_vsp = 25,
.mb_cycles_vpp = 200,
.max_frame_rate = MAXIMUM_FPS,
.max_operating_rate = MAXIMUM_FPS,
};
static const struct icc_info iris_icc_info_vpu2[] = {
{ "cpu-cfg", 1000, 1000 },
{ "video-mem", 1000, 15000000 },
};
static const char * const iris_clk_reset_table_vpu2[] = { "bus", "core" };
static const char * const iris_pmdomain_table_vpu2[] = { "venus", "vcodec0" };
static const struct tz_cp_config tz_cp_config_vpu2[] = {
{
.cp_start = 0,
.cp_size = 0x25800000,
.cp_nonpixel_start = 0x01000000,
.cp_nonpixel_size = 0x24800000,
},
};
const struct iris_platform_data sc7280_data = {
.firmware_desc = &iris_vpu20_p1_gen1_desc,
.vpu_ops = &iris_vpu2_ops,
.icc_tbl = iris_icc_info_vpu2,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
.bw_tbl_dec = sc7280_bw_table_dec,
.bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec),
.pmdomain_tbl = iris_pmdomain_table_vpu2,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2),
.opp_pd_tbl = sc7280_opp_pd_table,
.opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table),
.clk_tbl = sc7280_clk_table,
.clk_tbl_size = ARRAY_SIZE(sc7280_clk_table),
.opp_clk_tbl = sc7280_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu2_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec),
.inst_caps = &platform_inst_cap_vpu2,
.tz_cp_config_data = tz_cp_config_vpu2,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2),
.num_vpp_pipe = 1,
.no_aon = true,
.max_session_count = 16,
.max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256,
/* max spec for SC7280 is 4096x2176@60fps */
.max_core_mbps = 4096 * 2176 / 256 * 60,
};
const struct iris_platform_data sm8250_data = {
.firmware_desc = &iris_vpu20_p4_gen1_desc,
.vpu_ops = &iris_vpu2_ops,
.icc_tbl = iris_icc_info_vpu2,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
.clk_rst_tbl = iris_clk_reset_table_vpu2,
.clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2),
.bw_tbl_dec = sm8250_bw_table_dec,
.bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
.pmdomain_tbl = iris_pmdomain_table_vpu2,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2),
.opp_pd_tbl = sm8250_opp_pd_table,
.opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
.clk_tbl = sm8250_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
.opp_clk_tbl = sm8250_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu2_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec),
.inst_caps = &platform_inst_cap_vpu2,
.tz_cp_config_data = tz_cp_config_vpu2,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2),
.num_vpp_pipe = 4,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K,
.max_core_mbps = ((7680 * 4320) / 256) * 60,
};
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2025 Linaro Ltd
*/
#include "iris_core.h"
#include "iris_ctrls.h"
#include "iris_hfi_gen2.h"
#include "iris_hfi_gen2_defines.h"
#include "iris_platform_common.h"
#include "iris_vpu_buffer.h"
#include "iris_vpu_common.h"
#include "iris_platform_qcs8300.h"
#include "iris_platform_sm8550.h"
#include "iris_platform_sm8650.h"
#include "iris_platform_sm8750.h"
#include "iris_platform_x1p42100.h"
static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = {
.firmware_data = &iris_hfi_gen2_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu30_p4_s6.mbn",
};
static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = {
.firmware_data = &iris_hfi_gen2_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu30_p4.mbn",
};
static const struct iris_firmware_desc iris_vpu30_p1_gen2_desc = {
.firmware_data = &iris_hfi_gen2_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu30_p1_s7.mbn",
};
static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = {
.firmware_data = &iris_hfi_gen2_data,
.get_vpu_buffer_size = iris_vpu33_buf_size,
.fwname = "qcom/vpu/vpu33_p4.mbn",
};
static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = {
.firmware_data = &iris_hfi_gen2_data,
.get_vpu_buffer_size = iris_vpu33_buf_size,
.fwname = "qcom/vpu/vpu35_p4.mbn",
};
static const u32 iris_fmts_vpu3x_dec[] = {
[IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
[IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
[IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9,
[IRIS_FMT_AV1] = V4L2_PIX_FMT_AV1,
};
static const struct icc_info iris_icc_info_vpu3x[] = {
{ "cpu-cfg", 1000, 1000 },
{ "video-mem", 1000, 15000000 },
};
static const struct bw_info iris_bw_table_dec_vpu3x[] = {
{ ((4096 * 2160) / 256) * 60, 1608000 },
{ ((4096 * 2160) / 256) * 30, 826000 },
{ ((1920 * 1080) / 256) * 60, 567000 },
{ ((1920 * 1080) / 256) * 30, 294000 },
};
static const char * const iris_pmdomain_table_vpu3x[] = { "venus", "vcodec0" };
static const char * const iris_opp_pd_table_vpu3x[] = { "mxc", "mmcx" };
static const char * const iris_opp_clk_table_vpu3x[] = {
"vcodec0_core",
NULL,
};
static const struct tz_cp_config tz_cp_config_vpu3[] = {
{
.cp_start = 0,
.cp_size = 0x25800000,
.cp_nonpixel_start = 0x01000000,
.cp_nonpixel_size = 0x24800000,
},
};
/*
* Shares most of SM8550 data except:
* - inst_caps to platform_inst_cap_qcs8300
*/
const struct iris_platform_data qcs8300_data = {
.firmware_desc = &iris_vpu30_p4_s6_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
.clk_rst_tbl = sm8550_clk_reset_table,
.clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
.bw_tbl_dec = iris_bw_table_dec_vpu3x,
.bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
.pmdomain_tbl = iris_pmdomain_table_vpu3x,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
.opp_pd_tbl = iris_opp_pd_table_vpu3x,
.opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
.opp_clk_tbl = iris_opp_clk_table_vpu3x,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu3x_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
.inst_caps = &platform_inst_cap_qcs8300,
.tz_cp_config_data = tz_cp_config_vpu3,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
.num_vpp_pipe = 2,
.max_session_count = 16,
.max_core_mbpf = ((4096 * 2176) / 256) * 4,
.max_core_mbps = (((3840 * 2176) / 256) * 120),
};
const struct iris_platform_data sm8550_data = {
.firmware_desc = &iris_vpu30_p4_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
.clk_rst_tbl = sm8550_clk_reset_table,
.clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
.bw_tbl_dec = iris_bw_table_dec_vpu3x,
.bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
.pmdomain_tbl = iris_pmdomain_table_vpu3x,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
.opp_pd_tbl = iris_opp_pd_table_vpu3x,
.opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
.opp_clk_tbl = iris_opp_clk_table_vpu3x,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu3x_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
.inst_caps = &platform_inst_cap_sm8550,
.tz_cp_config_data = tz_cp_config_vpu3,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
.num_vpp_pipe = 4,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K * 2,
.max_core_mbps = ((7680 * 4320) / 256) * 60,
};
/*
* Shares most of SM8550 data except:
* - vpu_ops to iris_vpu33_ops
* - clk_rst_tbl to sm8650_clk_reset_table
* - controller_rst_tbl to sm8650_controller_reset_table
*/
const struct iris_platform_data sm8650_data = {
.firmware_desc = &iris_vpu33_p4_gen2_desc,
.vpu_ops = &iris_vpu33_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
.clk_rst_tbl = sm8650_clk_reset_table,
.clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table),
.controller_rst_tbl = sm8650_controller_reset_table,
.controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table),
.bw_tbl_dec = iris_bw_table_dec_vpu3x,
.bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
.pmdomain_tbl = iris_pmdomain_table_vpu3x,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
.opp_pd_tbl = iris_opp_pd_table_vpu3x,
.opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
.opp_clk_tbl = iris_opp_clk_table_vpu3x,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu3x_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
.inst_caps = &platform_inst_cap_sm8550,
.tz_cp_config_data = tz_cp_config_vpu3,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
.num_vpp_pipe = 4,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K * 2,
.max_core_mbps = ((7680 * 4320) / 256) * 60,
};
const struct iris_platform_data sm8750_data = {
.firmware_desc = &iris_vpu35_p4_gen2_desc,
.vpu_ops = &iris_vpu35_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
.clk_rst_tbl = sm8750_clk_reset_table,
.clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table),
.bw_tbl_dec = iris_bw_table_dec_vpu3x,
.bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
.pmdomain_tbl = iris_pmdomain_table_vpu3x,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
.opp_pd_tbl = iris_opp_pd_table_vpu3x,
.opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
.clk_tbl = sm8750_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8750_clk_table),
.opp_clk_tbl = iris_opp_clk_table_vpu3x,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu3x_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
.inst_caps = &platform_inst_cap_sm8550,
.tz_cp_config_data = tz_cp_config_vpu3,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
.num_vpp_pipe = 4,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K * 2,
.max_core_mbps = ((7680 * 4320) / 256) * 60,
};
/*
* Shares most of SM8550 data except:
* - clk_tbl and opp_clk_tbl for x1p42100
* - different firmware
* - different num_vpp_pipe
*/
const struct iris_platform_data x1p42100_data = {
.firmware_desc = &iris_vpu30_p1_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
.clk_rst_tbl = sm8550_clk_reset_table,
.clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
.bw_tbl_dec = iris_bw_table_dec_vpu3x,
.bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
.pmdomain_tbl = iris_pmdomain_table_vpu3x,
.pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
.opp_pd_tbl = iris_opp_pd_table_vpu3x,
.opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
.clk_tbl = x1p42100_clk_table,
.clk_tbl_size = ARRAY_SIZE(x1p42100_clk_table),
.opp_clk_tbl = x1p42100_opp_clk_table,
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
.inst_iris_fmts = iris_fmts_vpu3x_dec,
.inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
.inst_caps = &platform_inst_cap_sm8550,
.tz_cp_config_data = tz_cp_config_vpu3,
.tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
.num_vpp_pipe = 1,
.max_session_count = 16,
.max_core_mbpf = NUM_MBS_8K * 2,
.max_core_mbps = ((7680 * 4320) / 256) * 60,
};
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef __IRIS_PLATFORM_X1P42100_H__
#define __IRIS_PLATFORM_X1P42100_H__
static const struct platform_clk_data x1p42100_clk_table[] = {
{IRIS_AXI_CLK, "iface" },
{IRIS_CTRL_CLK, "core" },
{IRIS_HW_CLK, "vcodec0_core" },
{IRIS_BSE_HW_CLK, "vcodec0_bse" },
};
static const char *const x1p42100_opp_clk_table[] = {
"vcodec0_core",
"vcodec0_bse",
NULL,
};
#endif
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-mem2mem.h>
#include "iris_buffer.h"
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_resources.h"
#include "iris_vpu_common.h"
static u32 iris_calc_bw(struct iris_inst *inst, struct icc_vote_data *data)
{
const struct bw_info *bw_tbl = NULL;
struct iris_core *core = inst->core;
u32 num_rows, i, mbs, mbps;
u32 icc_bw = 0;
mbs = DIV_ROUND_UP(data->height, 16) * DIV_ROUND_UP(data->width, 16);
mbps = mbs * data->fps;
if (mbps == 0)
goto exit;
bw_tbl = core->iris_platform_data->bw_tbl_dec;
num_rows = core->iris_platform_data->bw_tbl_dec_size;
for (i = 0; i < num_rows; i++) {
if (i != 0 && mbps > bw_tbl[i].mbs_per_sec)
break;
icc_bw = bw_tbl[i].bw_ddr;
}
exit:
return icc_bw;
}
static int iris_set_interconnects(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *instance;
u64 total_bw_ddr = 0;
int ret;
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list) {
if (!instance->max_input_data_size)
continue;
total_bw_ddr += instance->power.icc_bw;
}
ret = iris_set_icc_bw(core, total_bw_ddr);
mutex_unlock(&core->lock);
return ret;
}
static int iris_vote_interconnects(struct iris_inst *inst)
{
struct icc_vote_data *vote_data = &inst->icc_data;
struct v4l2_format *inp_f = inst->fmt_src;
vote_data->width = inp_f->fmt.pix_mp.width;
vote_data->height = inp_f->fmt.pix_mp.height;
vote_data->fps = DEFAULT_FPS;
inst->power.icc_bw = iris_calc_bw(inst, vote_data);
return iris_set_interconnects(inst);
}
static int iris_set_clocks(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *instance;
u64 freq = 0;
int ret;
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list) {
if (!instance->max_input_data_size)
continue;
freq += instance->power.min_freq;
}
core->power.clk_freq = freq;
ret = iris_opp_set_rate(core->dev, freq);
mutex_unlock(&core->lock);
return ret;
}
static int iris_scale_clocks(struct iris_inst *inst)
{
const struct vpu_ops *vpu_ops = inst->core->iris_platform_data->vpu_ops;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_m2m_buffer *buffer, *n;
struct iris_buffer *buf;
size_t data_size = 0;
v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
buf = to_iris_buffer(&buffer->vb);
data_size = max(data_size, buf->data_size);
}
inst->max_input_data_size = data_size;
if (!inst->max_input_data_size)
return 0;
inst->power.min_freq = vpu_ops->calc_freq(inst, inst->max_input_data_size);
return iris_set_clocks(inst);
}
int iris_scale_power(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
int ret;
if (pm_runtime_suspended(core->dev)) {
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
return ret;
pm_runtime_put_autosuspend(core->dev);
}
ret = iris_scale_clocks(inst);
if (ret)
return ret;
return iris_vote_interconnects(inst);
}
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_POWER_H__
#define __IRIS_POWER_H__
struct iris_inst;
int iris_scale_power(struct iris_inst *inst);
#endif
@@ -0,0 +1,407 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/soc/qcom/ubwc.h>
#include "iris_core.h"
#include "iris_ctrls.h"
#include "iris_vidc.h"
static int iris_init_icc(struct iris_core *core)
{
const struct icc_info *icc_tbl;
u32 i = 0;
icc_tbl = core->iris_platform_data->icc_tbl;
core->icc_count = core->iris_platform_data->icc_tbl_size;
core->icc_tbl = devm_kzalloc(core->dev,
sizeof(struct icc_bulk_data) * core->icc_count,
GFP_KERNEL);
if (!core->icc_tbl)
return -ENOMEM;
for (i = 0; i < core->icc_count; i++) {
core->icc_tbl[i].name = icc_tbl[i].name;
core->icc_tbl[i].avg_bw = icc_tbl[i].bw_min_kbps;
core->icc_tbl[i].peak_bw = 0;
}
return devm_of_icc_bulk_get(core->dev, core->icc_count, core->icc_tbl);
}
static int iris_init_power_domains(struct iris_core *core)
{
int ret;
struct dev_pm_domain_attach_data iris_pd_data = {
.pd_names = core->iris_platform_data->pmdomain_tbl,
.num_pd_names = core->iris_platform_data->pmdomain_tbl_size,
.pd_flags = PD_FLAG_NO_DEV_LINK,
};
struct dev_pm_domain_attach_data iris_opp_pd_data = {
.pd_names = core->iris_platform_data->opp_pd_tbl,
.num_pd_names = core->iris_platform_data->opp_pd_tbl_size,
.pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
};
struct dev_pm_opp_config iris_opp_clk_data = {
.clk_names = core->iris_platform_data->opp_clk_tbl,
.config_clks = dev_pm_opp_config_clks_simple,
};
ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
if (ret < 0)
return ret;
ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl);
/* backwards compatibility for incomplete ABI SM8250 */
if (ret == -ENODEV &&
of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) {
iris_opp_pd_data.num_pd_names--;
ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data,
&core->opp_pmdomain_tbl);
}
if (ret < 0)
return ret;
ret = devm_pm_opp_set_config(core->dev, &iris_opp_clk_data);
if (ret)
return ret;
return devm_pm_opp_of_add_table(core->dev);
}
static int iris_init_clocks(struct iris_core *core)
{
int ret;
ret = devm_clk_bulk_get_all(core->dev, &core->clock_tbl);
if (ret < 0)
return ret;
core->clk_count = ret;
return 0;
}
static int iris_init_reset_table(struct iris_core *core,
struct reset_control_bulk_data **resets,
const char * const *rst_tbl, u32 rst_tbl_size)
{
u32 i = 0;
*resets = devm_kzalloc(core->dev,
sizeof(struct reset_control_bulk_data) * rst_tbl_size,
GFP_KERNEL);
if (!*resets)
return -ENOMEM;
for (i = 0; i < rst_tbl_size; i++)
(*resets)[i].id = rst_tbl[i];
return devm_reset_control_bulk_get_exclusive(core->dev, rst_tbl_size, *resets);
}
static int iris_init_resets(struct iris_core *core)
{
int ret;
ret = iris_init_reset_table(core, &core->resets,
core->iris_platform_data->clk_rst_tbl,
core->iris_platform_data->clk_rst_tbl_size);
if (ret)
return ret;
if (!core->iris_platform_data->controller_rst_tbl_size)
return 0;
return iris_init_reset_table(core, &core->controller_resets,
core->iris_platform_data->controller_rst_tbl,
core->iris_platform_data->controller_rst_tbl_size);
}
static int iris_init_resources(struct iris_core *core)
{
int ret;
ret = iris_init_icc(core);
if (ret)
return ret;
ret = iris_init_power_domains(core);
if (ret)
return ret;
ret = iris_init_clocks(core);
if (ret)
return ret;
return iris_init_resets(core);
}
static int iris_register_video_device(struct iris_core *core, enum domain_type type)
{
struct video_device *vdev;
int ret;
vdev = video_device_alloc();
if (!vdev)
return -ENOMEM;
vdev->release = video_device_release;
vdev->fops = core->iris_v4l2_file_ops;
vdev->vfl_dir = VFL_DIR_M2M;
vdev->v4l2_dev = &core->v4l2_dev;
vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
if (type == DECODER) {
strscpy(vdev->name, "qcom-iris-decoder", sizeof(vdev->name));
vdev->ioctl_ops = core->iris_v4l2_ioctl_ops_dec;
core->vdev_dec = vdev;
} else if (type == ENCODER) {
strscpy(vdev->name, "qcom-iris-encoder", sizeof(vdev->name));
vdev->ioctl_ops = core->iris_v4l2_ioctl_ops_enc;
core->vdev_enc = vdev;
} else {
ret = -EINVAL;
goto err_vdev_release;
}
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
goto err_vdev_release;
video_set_drvdata(vdev, core);
return 0;
err_vdev_release:
video_device_release(vdev);
return ret;
}
static void iris_remove(struct platform_device *pdev)
{
struct iris_core *core;
core = platform_get_drvdata(pdev);
if (!core)
return;
iris_core_deinit(core);
video_unregister_device(core->vdev_dec);
video_unregister_device(core->vdev_enc);
v4l2_device_unregister(&core->v4l2_dev);
mutex_destroy(&core->lock);
}
static void iris_sys_error_handler(struct work_struct *work)
{
struct iris_core *core =
container_of(work, struct iris_core, sys_error_handler.work);
iris_core_deinit(core);
iris_core_init(core);
}
static int iris_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iris_core *core;
u64 dma_mask;
int ret;
core = devm_kzalloc(&pdev->dev, sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
core->dev = dev;
core->state = IRIS_CORE_DEINIT;
mutex_init(&core->lock);
init_completion(&core->core_init_done);
core->response_packet = devm_kzalloc(core->dev, IFACEQ_CORE_PKT_SIZE, GFP_KERNEL);
if (!core->response_packet)
return -ENOMEM;
INIT_LIST_HEAD(&core->instances);
INIT_DELAYED_WORK(&core->sys_error_handler, iris_sys_error_handler);
core->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(core->reg_base))
return PTR_ERR(core->reg_base);
core->irq = platform_get_irq(pdev, 0);
if (core->irq < 0)
return core->irq;
core->iris_platform_data = of_device_get_match_data(core->dev);
core->iris_firmware_desc = core->iris_platform_data->firmware_desc;
core->iris_firmware_data = core->iris_firmware_desc->firmware_data;
core->ubwc_cfg = qcom_ubwc_config_get_data();
if (IS_ERR(core->ubwc_cfg))
return PTR_ERR(core->ubwc_cfg);
ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
iris_hfi_isr_handler,
IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
"iris", core);
if (ret)
return ret;
iris_init_ops(core);
ret = iris_init_resources(core);
if (ret)
return ret;
iris_session_init_caps(core);
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
return ret;
ret = iris_register_video_device(core, DECODER);
if (ret)
goto err_v4l2_unreg;
ret = iris_register_video_device(core, ENCODER);
if (ret)
goto err_vdev_unreg_dec;
platform_set_drvdata(pdev, core);
dma_mask = core->iris_platform_data->dma_mask;
ret = dma_set_mask_and_coherent(dev, dma_mask);
if (ret)
goto err_vdev_unreg_enc;
dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32));
pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE);
pm_runtime_use_autosuspend(core->dev);
ret = devm_pm_runtime_enable(core->dev);
if (ret)
goto err_vdev_unreg_enc;
return 0;
err_vdev_unreg_enc:
video_unregister_device(core->vdev_enc);
err_vdev_unreg_dec:
video_unregister_device(core->vdev_dec);
err_v4l2_unreg:
v4l2_device_unregister(&core->v4l2_dev);
return ret;
}
static int __maybe_unused iris_pm_suspend(struct device *dev)
{
struct iris_core *core;
int ret = 0;
core = dev_get_drvdata(dev);
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_INIT)
goto exit;
ret = iris_hfi_pm_suspend(core);
exit:
mutex_unlock(&core->lock);
return ret;
}
static int __maybe_unused iris_pm_resume(struct device *dev)
{
struct iris_core *core;
int ret = 0;
core = dev_get_drvdata(dev);
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_INIT)
goto exit;
ret = iris_hfi_pm_resume(core);
pm_runtime_mark_last_busy(core->dev);
exit:
mutex_unlock(&core->lock);
return ret;
}
static const struct dev_pm_ops iris_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(iris_pm_suspend, iris_pm_resume, NULL)
};
static const struct of_device_id iris_dt_match[] = {
{
.compatible = "qcom,qcs8300-iris",
.data = &qcs8300_data,
},
{
.compatible = "qcom,sc7280-venus",
.data = &sc7280_data,
},
{
.compatible = "qcom,sm8250-venus",
.data = &sm8250_data,
},
{
.compatible = "qcom,sm8550-iris",
.data = &sm8550_data,
},
{
.compatible = "qcom,sm8650-iris",
.data = &sm8650_data,
},
{
.compatible = "qcom,sm8750-iris",
.data = &sm8750_data,
},
{
.compatible = "qcom,x1p42100-iris",
.data = &x1p42100_data,
},
{ },
};
MODULE_DEVICE_TABLE(of, iris_dt_match);
static struct platform_driver qcom_iris_driver = {
.probe = iris_probe,
.remove = iris_remove,
.driver = {
.name = "qcom-iris",
.of_match_table = iris_dt_match,
.pm = &iris_pm_ops,
},
};
module_platform_driver(qcom_iris_driver);
MODULE_DESCRIPTION("Qualcomm iris video driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/interconnect.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include "iris_core.h"
#include "iris_resources.h"
#define BW_THRESHOLD 50000
int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw)
{
unsigned long bw_kbps = 0, bw_prev = 0;
const struct icc_info *icc_tbl;
int ret = 0, i;
icc_tbl = core->iris_platform_data->icc_tbl;
for (i = 0; i < core->icc_count; i++) {
if (!strcmp(core->icc_tbl[i].name, "video-mem")) {
bw_kbps = icc_bw;
bw_prev = core->power.icc_bw;
bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
icc_tbl[i].bw_min_kbps, icc_tbl[i].bw_max_kbps);
if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev)
return ret;
core->icc_tbl[i].avg_bw = bw_kbps;
core->power.icc_bw = bw_kbps;
break;
}
}
return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
}
int iris_unset_icc_bw(struct iris_core *core)
{
u32 i;
core->power.icc_bw = 0;
for (i = 0; i < core->icc_count; i++) {
core->icc_tbl[i].avg_bw = 0;
core->icc_tbl[i].peak_bw = 0;
}
return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
}
int iris_opp_set_rate(struct device *dev, unsigned long freq)
{
struct dev_pm_opp *opp __free(put_opp);
opp = devfreq_recommended_opp(dev, &freq, 0);
if (IS_ERR(opp))
return PTR_ERR(opp);
return dev_pm_opp_set_opp(dev, opp);
}
int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
{
int ret;
ret = iris_opp_set_rate(core->dev, ULONG_MAX);
if (ret)
return ret;
ret = pm_runtime_get_sync(pd_dev);
if (ret < 0)
return ret;
return ret;
}
int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
{
int ret;
ret = iris_opp_set_rate(core->dev, 0);
if (ret)
return ret;
pm_runtime_put_sync(pd_dev);
return 0;
}
static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type)
{
const struct platform_clk_data *clk_tbl;
u32 clk_cnt, i, j;
clk_tbl = core->iris_platform_data->clk_tbl;
clk_cnt = core->iris_platform_data->clk_tbl_size;
for (i = 0; i < clk_cnt; i++) {
if (clk_tbl[i].clk_type == clk_type) {
for (j = 0; core->clock_tbl && j < core->clk_count; j++) {
if (!strcmp(core->clock_tbl[j].id, clk_tbl[i].clk_name))
return core->clock_tbl[j].clk;
}
}
}
return NULL;
}
int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type)
{
struct clk *clock;
clock = iris_get_clk_by_type(core, clk_type);
if (!clock)
return -ENOENT;
return clk_prepare_enable(clock);
}
int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type)
{
struct clk *clock;
clock = iris_get_clk_by_type(core, clk_type);
if (!clock)
return -EINVAL;
clk_disable_unprepare(clock);
return 0;
}
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_RESOURCES_H__
#define __IRIS_RESOURCES_H__
struct iris_core;
int iris_opp_set_rate(struct device *dev, unsigned long freq);
int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev);
int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev);
int iris_unset_icc_bw(struct iris_core *core);
int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw);
int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type);
int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type);
#endif
@@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <media/v4l2-mem2mem.h>
#include "iris_instance.h"
static bool iris_allow_inst_state_change(struct iris_inst *inst,
enum iris_inst_state req_state)
{
switch (inst->state) {
case IRIS_INST_INIT:
if (req_state == IRIS_INST_INPUT_STREAMING ||
req_state == IRIS_INST_OUTPUT_STREAMING ||
req_state == IRIS_INST_DEINIT)
return true;
return false;
case IRIS_INST_INPUT_STREAMING:
if (req_state == IRIS_INST_INIT ||
req_state == IRIS_INST_STREAMING ||
req_state == IRIS_INST_DEINIT)
return true;
return false;
case IRIS_INST_OUTPUT_STREAMING:
if (req_state == IRIS_INST_INIT ||
req_state == IRIS_INST_STREAMING ||
req_state == IRIS_INST_DEINIT)
return true;
return false;
case IRIS_INST_STREAMING:
if (req_state == IRIS_INST_INPUT_STREAMING ||
req_state == IRIS_INST_OUTPUT_STREAMING ||
req_state == IRIS_INST_DEINIT)
return true;
return false;
case IRIS_INST_DEINIT:
if (req_state == IRIS_INST_INIT)
return true;
return false;
default:
return false;
}
}
int iris_inst_change_state(struct iris_inst *inst,
enum iris_inst_state request_state)
{
if (inst->state == IRIS_INST_ERROR)
return 0;
if (inst->state == request_state)
return 0;
if (request_state == IRIS_INST_ERROR)
goto change_state;
if (!iris_allow_inst_state_change(inst, request_state))
return -EINVAL;
change_state:
inst->state = request_state;
dev_dbg(inst->core->dev, "state changed from %x to %x\n",
inst->state, request_state);
return 0;
}
int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane)
{
enum iris_inst_state new_state = IRIS_INST_ERROR;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
if (inst->state == IRIS_INST_INIT)
new_state = IRIS_INST_INPUT_STREAMING;
else if (inst->state == IRIS_INST_OUTPUT_STREAMING)
new_state = IRIS_INST_STREAMING;
} else if (V4L2_TYPE_IS_CAPTURE(plane)) {
if (inst->state == IRIS_INST_INIT)
new_state = IRIS_INST_OUTPUT_STREAMING;
else if (inst->state == IRIS_INST_INPUT_STREAMING)
new_state = IRIS_INST_STREAMING;
}
return iris_inst_change_state(inst, new_state);
}
int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane)
{
enum iris_inst_state new_state = IRIS_INST_ERROR;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
if (inst->state == IRIS_INST_INPUT_STREAMING)
new_state = IRIS_INST_INIT;
else if (inst->state == IRIS_INST_STREAMING)
new_state = IRIS_INST_OUTPUT_STREAMING;
} else if (V4L2_TYPE_IS_CAPTURE(plane)) {
if (inst->state == IRIS_INST_OUTPUT_STREAMING)
new_state = IRIS_INST_INIT;
else if (inst->state == IRIS_INST_STREAMING)
new_state = IRIS_INST_INPUT_STREAMING;
}
return iris_inst_change_state(inst, new_state);
}
static bool iris_inst_allow_sub_state(struct iris_inst *inst, enum iris_inst_sub_state sub_state)
{
if (!sub_state)
return true;
switch (inst->state) {
case IRIS_INST_INIT:
if (sub_state & IRIS_INST_SUB_LOAD_RESOURCES)
return true;
return false;
case IRIS_INST_INPUT_STREAMING:
if (sub_state & (IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_DRC |
IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_INPUT_PAUSE))
return true;
return false;
case IRIS_INST_OUTPUT_STREAMING:
if (sub_state & (IRIS_INST_SUB_DRC_LAST |
IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE |
IRIS_INST_SUB_LOAD_RESOURCES))
return true;
return false;
case IRIS_INST_STREAMING:
if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
return true;
return false;
case IRIS_INST_DEINIT:
if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
return true;
return false;
default:
return false;
}
}
int iris_inst_change_sub_state(struct iris_inst *inst,
enum iris_inst_sub_state clear_sub_state,
enum iris_inst_sub_state set_sub_state)
{
enum iris_inst_sub_state prev_sub_state;
if (inst->state == IRIS_INST_ERROR)
return 0;
if (!clear_sub_state && !set_sub_state)
return 0;
if ((clear_sub_state & set_sub_state) ||
set_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE ||
clear_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE)
return -EINVAL;
prev_sub_state = inst->sub_state;
if (!iris_inst_allow_sub_state(inst, set_sub_state))
return -EINVAL;
inst->sub_state |= set_sub_state;
inst->sub_state &= ~clear_sub_state;
if (inst->sub_state != prev_sub_state)
dev_dbg(inst->core->dev, "sub_state changed from %x to %x\n",
prev_sub_state, inst->sub_state);
return 0;
}
int iris_inst_sub_state_change_drc(struct iris_inst *inst)
{
enum iris_inst_sub_state set_sub_state = 0;
if (inst->sub_state & IRIS_INST_SUB_DRC)
return -EINVAL;
if (inst->state == IRIS_INST_INPUT_STREAMING ||
inst->state == IRIS_INST_INIT)
set_sub_state = IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_INPUT_PAUSE;
else
set_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_INPUT_PAUSE;
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
int iris_inst_sub_state_change_drain_last(struct iris_inst *inst)
{
enum iris_inst_sub_state set_sub_state;
if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)
return -EINVAL;
if (!(inst->sub_state & IRIS_INST_SUB_DRAIN))
return -EINVAL;
set_sub_state = IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
int iris_inst_sub_state_change_drc_last(struct iris_inst *inst)
{
enum iris_inst_sub_state set_sub_state;
if (inst->sub_state & IRIS_INST_SUB_DRC_LAST)
return -EINVAL;
if (!(inst->sub_state & IRIS_INST_SUB_DRC) ||
!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE))
return -EINVAL;
if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
return 0;
set_sub_state = IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
{
enum iris_inst_sub_state set_sub_state;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
if (inst->sub_state & IRIS_INST_SUB_DRC &&
!(inst->sub_state & IRIS_INST_SUB_DRC_LAST))
return -EINVAL;
if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
!(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST))
return -EINVAL;
set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
} else {
set_sub_state = IRIS_INST_SUB_OUTPUT_PAUSE;
}
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
bool iris_drc_pending(struct iris_inst *inst)
{
return inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST;
}
bool iris_drain_pending(struct iris_inst *inst)
{
return inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
}
bool iris_allow_cmd(struct iris_inst *inst, u32 cmd)
{
struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
if (cmd == V4L2_DEC_CMD_START || cmd == V4L2_ENC_CMD_START) {
if (vb2_is_streaming(src_q) || vb2_is_streaming(dst_q))
if (iris_drc_pending(inst) || iris_drain_pending(inst))
return true;
} else if (cmd == V4L2_DEC_CMD_STOP || cmd == V4L2_ENC_CMD_STOP) {
if (vb2_is_streaming(src_q))
if (inst->sub_state != IRIS_INST_SUB_DRAIN)
return true;
}
return false;
}
@@ -0,0 +1,146 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_STATE_H__
#define __IRIS_STATE_H__
struct iris_inst;
/**
* enum iris_core_state
*
* @IRIS_CORE_DEINIT: default state.
* @IRIS_CORE_INIT: core state with core initialized. FW loaded and
* HW brought out of reset, shared queues established
* between host driver and firmware.
* @IRIS_CORE_ERROR: error state.
*
* -----------
* |
* V
* -----------
* +--->| DEINIT |<---+
* | ----------- |
* | | |
* | v |
* | ----------- |
* | / \ |
* | / \ |
* | / \ |
* | v v v
* ----------- -----------
* | INIT |--->| ERROR |
* ----------- -----------
*/
enum iris_core_state {
IRIS_CORE_DEINIT,
IRIS_CORE_INIT,
IRIS_CORE_ERROR,
};
/**
* enum iris_inst_state
*
* @IRIS_INST_INIT: video instance is opened.
* @IRIS_INST_INPUT_STREAMING: stream on is completed on output plane.
* @IRIS_INST_OUTPUT_STREAMING: stream on is completed on capture plane.
* @IRIS_INST_STREAMING: stream on is completed on both output and capture planes.
* @IRIS_INST_DEINIT: video instance is closed.
* @IRIS_INST_ERROR: error state.
* |
* V
* -------------
* +--------| INIT |----------+
* | ------------- |
* | ^ ^ |
* | / \ |
* | / \ |
* | v v |
* | ----------- ----------- |
* | | INPUT OUTPUT | |
* |---| STREAMING STREAMING |---|
* | ----------- ----------- |
* | ^ ^ |
* | \ / |
* | \ / |
* | v v |
* | ------------- |
* |--------| STREAMING |-----------|
* | ------------- |
* | | |
* | | |
* | v |
* | ----------- |
* +-------->| DEINIT |<----------+
* | ----------- |
* | | |
* | | |
* | v |
* | ---------- |
* +-------->| ERROR |<------------+
* ----------
*/
enum iris_inst_state {
IRIS_INST_DEINIT,
IRIS_INST_INIT,
IRIS_INST_INPUT_STREAMING,
IRIS_INST_OUTPUT_STREAMING,
IRIS_INST_STREAMING,
IRIS_INST_ERROR,
};
#define IRIS_INST_SUB_STATES 8
#define IRIS_INST_MAX_SUB_STATE_VALUE ((1 << IRIS_INST_SUB_STATES) - 1)
/**
* enum iris_inst_sub_state
*
* @IRIS_INST_SUB_FIRST_IPSC: indicates source change is received from firmware
* when output port is not yet streaming.
* @IRIS_INST_SUB_DRC: indicates source change is received from firmware
* when output port is streaming and source change event is
* sent to client.
* @IRIS_INST_SUB_DRC_LAST: indicates last buffer is received from firmware
* as part of source change.
* @IRIS_INST_SUB_DRAIN: indicates drain is in progress.
* @IRIS_INST_SUB_DRAIN_LAST: indicates last buffer is received from firmware
* as part of drain sequence.
* @IRIS_INST_SUB_INPUT_PAUSE: source change is received form firmware. This
* indicates that firmware is paused to process
* any further input frames.
* @IRIS_INST_SUB_OUTPUT_PAUSE: last buffer is received form firmware as part
* of drc sequence. This indicates that
* firmware is paused to process any further output frames.
* @IRIS_INST_SUB_LOAD_RESOURCES: indicates all the resources have been loaded by the
* firmware and it is ready for processing.
*/
enum iris_inst_sub_state {
IRIS_INST_SUB_FIRST_IPSC = BIT(0),
IRIS_INST_SUB_DRC = BIT(1),
IRIS_INST_SUB_DRC_LAST = BIT(2),
IRIS_INST_SUB_DRAIN = BIT(3),
IRIS_INST_SUB_DRAIN_LAST = BIT(4),
IRIS_INST_SUB_INPUT_PAUSE = BIT(5),
IRIS_INST_SUB_OUTPUT_PAUSE = BIT(6),
IRIS_INST_SUB_LOAD_RESOURCES = BIT(7),
};
int iris_inst_change_state(struct iris_inst *inst,
enum iris_inst_state request_state);
int iris_inst_change_sub_state(struct iris_inst *inst,
enum iris_inst_sub_state clear_sub_state,
enum iris_inst_sub_state set_sub_state);
int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane);
int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane);
int iris_inst_sub_state_change_drc(struct iris_inst *inst);
int iris_inst_sub_state_change_drain_last(struct iris_inst *inst);
int iris_inst_sub_state_change_drc_last(struct iris_inst *inst);
int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane);
bool iris_allow_cmd(struct iris_inst *inst, u32 cmd);
bool iris_drc_pending(struct iris_inst *inst);
bool iris_drain_pending(struct iris_inst *inst);
#endif
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include <media/v4l2-mem2mem.h>
#include "iris_instance.h"
#include "iris_utils.h"
bool iris_res_is_less_than(u32 width, u32 height,
u32 ref_width, u32 ref_height)
{
u32 num_mbs = NUM_MBS_PER_FRAME(height, width);
u32 max_side = max(ref_width, ref_height);
if (num_mbs < NUM_MBS_PER_FRAME(ref_height, ref_width) &&
width < max_side &&
height < max_side)
return true;
return false;
}
int iris_get_mbpf(struct iris_inst *inst)
{
struct v4l2_format *inp_f = inst->fmt_src;
u32 height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
u32 width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
return NUM_MBS_PER_FRAME(height, width);
}
bool iris_split_mode_enabled(struct iris_inst *inst)
{
return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 ||
inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C ||
inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010 ||
inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C;
}
bool iris_fmt_is_8bit(u32 pixelformat)
{
return pixelformat == V4L2_PIX_FMT_NV12 ||
pixelformat == V4L2_PIX_FMT_QC08C;
}
bool iris_fmt_is_10bit(u32 pixelformat)
{
return pixelformat == V4L2_PIX_FMT_P010 ||
pixelformat == V4L2_PIX_FMT_QC10C;
}
void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
enum vb2_buffer_state state)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct vb2_v4l2_buffer *buf;
if (V4L2_TYPE_IS_OUTPUT(type)) {
while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx)))
v4l2_m2m_buf_done(buf, state);
} else if (V4L2_TYPE_IS_CAPTURE(type)) {
while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
v4l2_m2m_buf_done(buf, state);
}
}
int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
{
struct completion *done;
int ret;
done = is_flush ? &inst->flush_completion : &inst->completion;
mutex_unlock(&inst->lock);
ret = wait_for_completion_timeout(done, msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE));
mutex_lock(&inst->lock);
if (!ret) {
iris_inst_change_state(inst, IRIS_INST_ERROR);
return -ETIMEDOUT;
}
return 0;
}
struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id)
{
struct iris_inst *inst;
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
if (inst->session_id == session_id) {
mutex_unlock(&core->lock);
return inst;
}
}
mutex_unlock(&core->lock);
return NULL;
}
int iris_check_core_mbpf(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *instance;
u32 total_mbpf = 0;
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list)
total_mbpf += iris_get_mbpf(instance);
mutex_unlock(&core->lock);
if (total_mbpf > core->iris_platform_data->max_core_mbpf)
return -ENOMEM;
return 0;
}
int iris_check_core_mbps(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *instance;
u32 total_mbps = 0, fps = 0;
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list) {
fps = max(instance->frame_rate, instance->operating_rate);
total_mbps += iris_get_mbpf(instance) * fps;
}
mutex_unlock(&core->lock);
if (total_mbps > core->iris_platform_data->max_core_mbps)
return -ENOMEM;
return 0;
}
bool is_rotation_90_or_270(struct iris_inst *inst)
{
return inst->fw_caps[ROTATION].value == 90 ||
inst->fw_caps[ROTATION].value == 270;
}
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_UTILS_H__
#define __IRIS_UTILS_H__
struct iris_core;
#include "iris_buffer.h"
struct iris_hfi_rect_desc {
u32 left;
u32 top;
u32 width;
u32 height;
};
struct iris_hfi_frame_info {
u32 picture_type;
u32 no_output;
u32 data_corrupt;
u32 overflow;
};
struct iris_ts_metadata {
u64 ts_ns;
u64 ts_us;
u32 flags;
struct v4l2_timecode tc;
};
#define NUM_MBS_PER_FRAME(height, width) \
(DIV_ROUND_UP(height, 16) * DIV_ROUND_UP(width, 16))
static inline enum iris_buffer_type iris_v4l2_type_to_driver(u32 type)
{
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return BUF_INPUT;
else
return BUF_OUTPUT;
}
bool iris_res_is_less_than(u32 width, u32 height,
u32 ref_width, u32 ref_height);
int iris_get_mbpf(struct iris_inst *inst);
bool iris_split_mode_enabled(struct iris_inst *inst);
bool iris_fmt_is_8bit(u32 pixelformat);
bool iris_fmt_is_10bit(u32 pixelformat);
struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
enum vb2_buffer_state state);
int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
int iris_check_core_mbpf(struct iris_inst *inst);
int iris_check_core_mbps(struct iris_inst *inst);
bool is_rotation_90_or_270(struct iris_inst *inst);
#endif
@@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include "iris_common.h"
#include "iris_instance.h"
#include "iris_vb2.h"
#include "iris_vdec.h"
#include "iris_venc.h"
#include "iris_power.h"
static int iris_check_inst_mbpf(struct iris_inst *inst)
{
struct platform_inst_caps *caps;
u32 mbpf, max_mbpf;
caps = inst->core->iris_platform_data->inst_caps;
max_mbpf = caps->max_mbpf;
mbpf = iris_get_mbpf(inst);
if (mbpf > max_mbpf)
return -ENOMEM;
return 0;
}
static int iris_check_resolution_supported(struct iris_inst *inst)
{
u32 width, height, min_width, min_height, max_width, max_height;
struct platform_inst_caps *caps;
caps = inst->core->iris_platform_data->inst_caps;
width = inst->fmt_src->fmt.pix_mp.width;
height = inst->fmt_src->fmt.pix_mp.height;
min_width = caps->min_frame_width;
max_width = caps->max_frame_width;
min_height = caps->min_frame_height;
max_height = caps->max_frame_height;
if (!(min_width <= width && width <= max_width) ||
!(min_height <= height && height <= max_height))
return -EINVAL;
return 0;
}
static int iris_check_session_supported(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *instance = NULL;
bool found = false;
int ret;
list_for_each_entry(instance, &core->instances, list) {
if (instance == inst)
found = true;
}
if (!found) {
ret = -EINVAL;
goto exit;
}
ret = iris_check_core_mbpf(inst);
if (ret)
goto exit;
ret = iris_check_inst_mbpf(inst);
if (ret)
goto exit;
ret = iris_check_resolution_supported(inst);
if (ret)
goto exit;
return 0;
exit:
dev_err(inst->core->dev, "current session not supported(%d)\n", ret);
return ret;
}
int iris_vb2_buf_init(struct vb2_buffer *vb2)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
struct iris_buffer *buf = to_iris_buffer(vbuf);
buf->device_addr = vb2_dma_contig_plane_dma_addr(vb2, 0);
return 0;
}
int iris_vb2_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct iris_inst *inst;
struct iris_core *core;
struct v4l2_format *f;
int ret = 0;
inst = vb2_get_drv_priv(q);
mutex_lock(&inst->lock);
if (inst->state == IRIS_INST_ERROR) {
ret = -EBUSY;
goto unlock;
}
core = inst->core;
f = V4L2_TYPE_IS_OUTPUT(q->type) ? inst->fmt_src : inst->fmt_dst;
if (*num_planes) {
if (*num_planes != f->fmt.pix_mp.num_planes ||
sizes[0] < f->fmt.pix_mp.plane_fmt[0].sizeimage)
ret = -EINVAL;
goto unlock;
}
ret = iris_check_session_supported(inst);
if (ret)
goto unlock;
if (!inst->once_per_session_set) {
inst->once_per_session_set = true;
ret = inst->hfi_session_ops->session_open(inst);
if (ret) {
ret = -EINVAL;
dev_err(core->dev, "session open failed\n");
goto unlock;
}
ret = iris_inst_change_state(inst, IRIS_INST_INIT);
if (ret)
goto unlock;
}
*num_planes = 1;
sizes[0] = f->fmt.pix_mp.plane_fmt[0].sizeimage;
unlock:
mutex_unlock(&inst->lock);
return ret;
}
int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
{
enum iris_buffer_type buf_type;
struct iris_inst *inst;
int ret = 0;
inst = vb2_get_drv_priv(q);
mutex_lock(&inst->lock);
if (inst->state == IRIS_INST_ERROR) {
ret = -EBUSY;
goto error;
}
if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
!V4L2_TYPE_IS_CAPTURE(q->type)) {
ret = -EINVAL;
goto error;
}
iris_scale_power(inst);
ret = iris_check_session_supported(inst);
if (ret)
goto error;
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
if (inst->domain == DECODER)
ret = iris_vdec_streamon_input(inst);
else
ret = iris_venc_streamon_input(inst);
} else if (V4L2_TYPE_IS_CAPTURE(q->type)) {
if (inst->domain == DECODER)
ret = iris_vdec_streamon_output(inst);
else
ret = iris_venc_streamon_output(inst);
}
if (ret)
goto error;
buf_type = iris_v4l2_type_to_driver(q->type);
if (inst->domain == DECODER) {
if (buf_type == BUF_INPUT)
ret = iris_queue_deferred_buffers(inst, BUF_INPUT);
if (!ret && inst->state == IRIS_INST_STREAMING) {
ret = iris_queue_internal_deferred_buffers(inst, BUF_DPB);
if (!ret)
ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT);
}
} else {
if (inst->state == IRIS_INST_STREAMING) {
ret = iris_queue_deferred_buffers(inst, BUF_INPUT);
if (!ret)
ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT);
}
}
if (ret)
goto error;
mutex_unlock(&inst->lock);
return ret;
error:
iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED);
iris_inst_change_state(inst, IRIS_INST_ERROR);
mutex_unlock(&inst->lock);
return ret;
}
void iris_vb2_stop_streaming(struct vb2_queue *q)
{
struct iris_inst *inst;
int ret = 0;
inst = vb2_get_drv_priv(q);
if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
return;
mutex_lock(&inst->lock);
if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
!V4L2_TYPE_IS_CAPTURE(q->type))
goto exit;
ret = iris_session_streamoff(inst, q->type);
if (ret)
goto exit;
exit:
iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
if (ret)
iris_inst_change_state(inst, IRIS_INST_ERROR);
mutex_unlock(&inst->lock);
}
int iris_vb2_buf_prepare(struct vb2_buffer *vb)
{
struct iris_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
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)
return -EINVAL;
}
if (!(inst->sub_state & IRIS_INST_SUB_DRC)) {
if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
return -EINVAL;
if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
return -EINVAL;
}
return 0;
}
int iris_vb2_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
v4l2_buf->field = V4L2_FIELD_NONE;
return 0;
}
void iris_vb2_buf_queue(struct vb2_buffer *vb2)
{
static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS };
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
struct v4l2_m2m_ctx *m2m_ctx;
struct iris_inst *inst;
int ret = 0;
inst = vb2_get_drv_priv(vb2->vb2_queue);
mutex_lock(&inst->lock);
if (inst->state == IRIS_INST_ERROR) {
ret = -EBUSY;
goto exit;
}
if (vbuf->field == V4L2_FIELD_ANY)
vbuf->field = V4L2_FIELD_NONE;
m2m_ctx = inst->m2m_ctx;
if (!vb2->planes[0].bytesused && V4L2_TYPE_IS_OUTPUT(vb2->type)) {
ret = -EINVAL;
goto exit;
}
if (!inst->last_buffer_dequeued && V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
if ((inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST) ||
(inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)) {
vbuf->flags |= V4L2_BUF_FLAG_LAST;
vbuf->sequence = inst->sequence_cap++;
vbuf->field = V4L2_FIELD_NONE;
vb2_set_plane_payload(vb2, 0, 0);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
if (!v4l2_m2m_has_stopped(m2m_ctx)) {
v4l2_event_queue_fh(&inst->fh, &eos);
v4l2_m2m_mark_stopped(m2m_ctx);
}
inst->last_buffer_dequeued = true;
goto exit;
}
}
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
if (inst->domain == DECODER)
ret = iris_vdec_qbuf(inst, vbuf);
else
ret = iris_venc_qbuf(inst, vbuf);
exit:
if (ret) {
iris_inst_change_state(inst, IRIS_INST_ERROR);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
}
mutex_unlock(&inst->lock);
}
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VB2_H__
#define __IRIS_VB2_H__
int iris_vb2_buf_init(struct vb2_buffer *vb2);
int iris_vb2_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], struct device *alloc_devs[]);
int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
void iris_vb2_stop_streaming(struct vb2_queue *q);
int iris_vb2_buf_prepare(struct vb2_buffer *vb);
int iris_vb2_buf_out_validate(struct vb2_buffer *vb);
void iris_vb2_buf_queue(struct vb2_buffer *vb2);
#endif
@@ -0,0 +1,542 @@
// 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_common.h"
#include "iris_ctrls.h"
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_vdec.h"
#include "iris_vpu_buffer.h"
#define DEFAULT_CODEC_ALIGNMENT 16
int iris_vdec_inst_init(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct v4l2_format *f;
inst->fmt_src = kzalloc_obj(*inst->fmt_src);
inst->fmt_dst = kzalloc_obj(*inst->fmt_dst);
inst->fw_min_count = 0;
f = inst->fmt_src;
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
f->fmt.pix_mp.width = DEFAULT_WIDTH;
f->fmt.pix_mp.height = DEFAULT_HEIGHT;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
inst->codec = f->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.num_planes = 1;
f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
f = inst->fmt_dst;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128);
f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32);
f->fmt.pix_mp.num_planes = 1;
f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
inst->frame_rate = MAXIMUM_FPS;
memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0],
INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
return iris_ctrls_init(inst);
}
static const u32 iris_vdec_formats_cap[] = {
[IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12,
[IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C,
[IRIS_FMT_TP10] = V4L2_PIX_FMT_P010,
[IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C,
};
static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
unsigned int size, i;
const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = inst->core->iris_platform_data->inst_iris_fmts;
size = inst->core->iris_platform_data->inst_iris_fmts_size;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_vdec_formats_cap;
size = ARRAY_SIZE(iris_vdec_formats_cap);
break;
default:
return false;
}
for (i = 0; i < size; i++) {
if (fmt[i] == pixfmt)
break;
}
if (i == size)
return false;
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (iris_fmt_is_8bit(pixfmt) &&
inst->fw_caps[BIT_DEPTH].value == BIT_DEPTH_10)
return false;
if (iris_fmt_is_10bit(pixfmt) &&
inst->fw_caps[BIT_DEPTH].value != BIT_DEPTH_10)
return false;
}
return true;
}
static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
unsigned int size;
const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = inst->core->iris_platform_data->inst_iris_fmts;
size = inst->core->iris_platform_data->inst_iris_fmts_size;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_vdec_formats_cap;
size = ARRAY_SIZE(iris_vdec_formats_cap);
break;
default:
return 0;
}
if (index >= size)
return 0;
return fmt[index];
}
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
u32 fmt;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
f->pixelformat = fmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
f->pixelformat = fmt;
break;
default:
return -EINVAL;
}
return 0;
}
int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_format *f_inst;
struct vb2_queue *src_q;
bool supported;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
supported = check_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (!supported) {
f_inst = inst->fmt_src;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (!supported) {
f_inst = inst->fmt_dst;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
}
src_q = v4l2_m2m_get_src_vq(m2m_ctx);
if (vb2_is_streaming(src_q)) {
f_inst = inst->fmt_src;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
}
break;
default:
return -EINVAL;
}
if (pixmp->field == V4L2_FIELD_ANY)
pixmp->field = V4L2_FIELD_NONE;
pixmp->num_planes = 1;
return 0;
}
int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_format *fmt, *output_fmt;
struct vb2_queue *q;
u32 codec_align;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
if (vb2_is_busy(q))
return -EBUSY;
/* Width and height are optional, so fall back to a valid placeholder
* resolution until the real one is decoded from the bitstream.
*/
if (f->fmt.pix_mp.width == 0 && f->fmt.pix_mp.height == 0) {
f->fmt.pix_mp.width = DEFAULT_WIDTH;
f->fmt.pix_mp.height = DEFAULT_HEIGHT;
}
iris_vdec_try_fmt(inst, f);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_src;
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
inst->codec = fmt->fmt.pix_mp.pixelformat;
codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
fmt->fmt.pix_mp.num_planes = 1;
fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
output_fmt = inst->fmt_dst;
output_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
output_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
/* Update capture format based on new ip w/h */
output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT);
inst->crop.left = 0;
inst->crop.top = 0;
inst->crop.width = f->fmt.pix_mp.width;
inst->crop.height = f->fmt.pix_mp.height;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
fmt->fmt.pix_mp.num_planes = 1;
switch (f->fmt.pix_mp.pixelformat) {
case V4L2_PIX_FMT_P010:
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
ALIGN(f->fmt.pix_mp.width * 2, 256);
break;
case V4L2_PIX_FMT_QC10C:
fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16);
fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
ALIGN(f->fmt.pix_mp.width * 4 / 3, 256);
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_QC08C:
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
ALIGN(f->fmt.pix_mp.width, 128);
break;
}
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
inst->crop.top = 0;
inst->crop.left = 0;
inst->crop.width = f->fmt.pix_mp.width;
inst->crop.height = f->fmt.pix_mp.height;
break;
default:
return -EINVAL;
}
memcpy(f, fmt, sizeof(*fmt));
return 0;
}
int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat)
{
bool supported;
supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (!supported)
supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
return supported ? 0 : -EINVAL;
}
int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub)
{
int ret = 0;
switch (sub->type) {
case V4L2_EVENT_EOS:
ret = v4l2_event_subscribe(&inst->fh, sub, 0, NULL);
break;
case V4L2_EVENT_SOURCE_CHANGE:
ret = v4l2_src_change_event_subscribe(&inst->fh, sub);
break;
case V4L2_EVENT_CTRL:
ret = v4l2_ctrl_subscribe_event(&inst->fh, sub);
break;
default:
return -EINVAL;
}
return ret;
}
void iris_vdec_src_change(struct iris_inst *inst)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
struct v4l2_event event = {0};
struct vb2_queue *src_q;
src_q = v4l2_m2m_get_src_vq(m2m_ctx);
if (!vb2_is_streaming(src_q))
return;
event.type = V4L2_EVENT_SOURCE_CHANGE;
event.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION;
v4l2_event_queue_fh(&inst->fh, &event);
}
int iris_vdec_streamon_input(struct iris_inst *inst)
{
int ret;
ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_alloc_and_queue_persist_bufs(inst, BUF_PERSIST);
if (ret)
return ret;
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
inst->frame_counter = 0;
return iris_process_streamon_input(inst);
}
int iris_vdec_streamon_output(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
ret = iris_process_streamon_output(inst);
if (ret)
goto error;
ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
goto error;
return ret;
error:
iris_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
return ret;
}
int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
{
struct iris_buffer *buf = to_iris_buffer(vbuf);
struct vb2_buffer *vb2 = &vbuf->vb2_buf;
u64 cur_buf_ns, delta_ns;
struct vb2_queue *q;
int ret;
ret = iris_vb2_buffer_to_driver(vb2, buf);
if (ret)
return ret;
if (buf->type == BUF_INPUT)
iris_set_ts_metadata(inst, vbuf);
q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type);
if (!vb2_is_streaming(q)) {
buf->attr |= BUF_ATTR_DEFERRED;
return 0;
}
if (buf->type == BUF_INPUT) {
cur_buf_ns = ktime_get_ns();
if (!inst->frame_counter)
inst->last_buf_ns = cur_buf_ns;
inst->frame_counter++;
delta_ns = cur_buf_ns - inst->last_buf_ns;
if (delta_ns >= NSEC_PER_SEC) {
inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS,
MAXIMUM_FPS);
inst->frame_counter = 0;
}
}
iris_scale_power(inst);
return iris_queue_buffer(inst, buf);
}
int iris_vdec_start_cmd(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
struct vb2_queue *dst_vq;
int ret;
dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
if (inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
vb2_clear_last_buffer_dequeued(dst_vq);
clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
ret = hfi_ops->session_resume_drc(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
}
if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
ret = hfi_ops->session_resume_drc(inst,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
}
} else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
vb2_clear_last_buffer_dequeued(dst_vq);
clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
if (hfi_ops->session_resume_drain) {
ret =
hfi_ops->session_resume_drain(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
}
clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
}
if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
if (hfi_ops->session_resume_drain) {
ret =
hfi_ops->session_resume_drain(inst,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
}
clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
}
} else {
dev_err(inst->core->dev, "start called before receiving last_flag\n");
iris_inst_change_state(inst, IRIS_INST_ERROR);
return -EBUSY;
}
return iris_inst_change_sub_state(inst, clear_sub_state, 0);
}
int iris_vdec_stop_cmd(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
}
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VDEC_H__
#define __IRIS_VDEC_H__
struct iris_inst;
int iris_vdec_inst_init(struct iris_inst *inst);
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat);
int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub);
void iris_vdec_src_change(struct iris_inst *inst);
int iris_vdec_streamon_input(struct iris_inst *inst);
int iris_vdec_streamon_output(struct iris_inst *inst);
int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
int iris_vdec_start_cmd(struct iris_inst *inst);
int iris_vdec_stop_cmd(struct iris_inst *inst);
#endif
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include "iris_buffer.h"
#include "iris_common.h"
#include "iris_ctrls.h"
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_venc.h"
#include "iris_vpu_buffer.h"
int iris_venc_inst_init(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct v4l2_format *f;
inst->fmt_src = kzalloc_obj(*inst->fmt_src);
inst->fmt_dst = kzalloc_obj(*inst->fmt_dst);
if (!inst->fmt_src || !inst->fmt_dst) {
kfree(inst->fmt_src);
kfree(inst->fmt_dst);
return -ENOMEM;
}
f = inst->fmt_dst;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
f->fmt.pix_mp.width = DEFAULT_WIDTH;
f->fmt.pix_mp.height = DEFAULT_HEIGHT;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
inst->codec = f->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.num_planes = 1;
f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
f = inst->fmt_src;
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128);
f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32);
f->fmt.pix_mp.num_planes = 1;
f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
inst->crop.left = 0;
inst->crop.top = 0;
inst->crop.width = DEFAULT_WIDTH;
inst->crop.height = DEFAULT_HEIGHT;
inst->operating_rate = DEFAULT_FPS;
inst->frame_rate = DEFAULT_FPS;
inst->enc_raw_width = DEFAULT_WIDTH;
inst->enc_raw_height = DEFAULT_HEIGHT;
inst->enc_scale_width = DEFAULT_WIDTH;
inst->enc_scale_height = DEFAULT_HEIGHT;
memcpy(&inst->fw_caps[0], &core->inst_fw_caps_enc[0],
INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
return iris_ctrls_init(inst);
}
static const u32 iris_venc_formats_cap[] = {
[IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
[IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
};
static const u32 iris_venc_formats_out[] = {
[IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12,
[IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C,
};
static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
unsigned int size, i;
const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = iris_venc_formats_out;
size = ARRAY_SIZE(iris_venc_formats_out);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_venc_formats_cap;
size = ARRAY_SIZE(iris_venc_formats_cap);
break;
default:
return false;
}
for (i = 0; i < size; i++) {
if (fmt[i] == pixfmt)
return true;
}
return false;
}
static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
unsigned int size;
const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = iris_venc_formats_out;
size = ARRAY_SIZE(iris_venc_formats_out);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = iris_venc_formats_cap;
size = ARRAY_SIZE(iris_venc_formats_cap);
break;
default:
return 0;
}
if (index >= size)
return 0;
return fmt[index];
}
int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
u32 fmt;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
f->pixelformat = fmt;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
f->pixelformat = fmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL;
break;
default:
return -EINVAL;
}
return 0;
}
int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_format *f_inst;
bool supported;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
supported = check_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (!supported) {
f_inst = inst->fmt_src;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (!supported) {
f_inst = inst->fmt_dst;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
}
break;
default:
return -EINVAL;
}
if (pixmp->field == V4L2_FIELD_ANY)
pixmp->field = V4L2_FIELD_NONE;
pixmp->num_planes = 1;
return 0;
}
static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_format *fmt;
u32 codec_align;
bool supported;
iris_venc_try_fmt(inst, f);
supported = check_format(inst, f->fmt.pix_mp.pixelformat, f->type);
if (!supported)
return -EINVAL;
codec_align = (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) ? 32 : 16;
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
/*
* If output format size != input format size,
* it is considered a scaling case,
* and the scaled size needs to be saved.
*/
if (f->fmt.pix_mp.width != inst->fmt_src->fmt.pix_mp.width ||
f->fmt.pix_mp.height != inst->fmt_src->fmt.pix_mp.height) {
inst->enc_scale_width = f->fmt.pix_mp.width;
inst->enc_scale_height = f->fmt.pix_mp.height;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
}
fmt->fmt.pix_mp.num_planes = 1;
fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
if (f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_DEFAULT &&
f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_REC709)
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
inst->codec = f->fmt.pix_mp.pixelformat;
memcpy(f, fmt, sizeof(struct v4l2_format));
return 0;
}
static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_format *fmt, *output_fmt;
iris_venc_try_fmt(inst, f);
if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_src;
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
fmt->fmt.pix_mp.num_planes = 1;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128);
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
output_fmt = inst->fmt_dst;
output_fmt->fmt.pix_mp.width = fmt->fmt.pix_mp.width;
output_fmt->fmt.pix_mp.height = fmt->fmt.pix_mp.height;
output_fmt->fmt.pix_mp.colorspace = fmt->fmt.pix_mp.colorspace;
output_fmt->fmt.pix_mp.xfer_func = fmt->fmt.pix_mp.xfer_func;
output_fmt->fmt.pix_mp.ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc;
output_fmt->fmt.pix_mp.quantization = fmt->fmt.pix_mp.quantization;
inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
inst->enc_raw_width = f->fmt.pix_mp.width;
inst->enc_raw_height = f->fmt.pix_mp.height;
inst->enc_scale_width = f->fmt.pix_mp.width;
inst->enc_scale_height = f->fmt.pix_mp.height;
if (f->fmt.pix_mp.width != inst->crop.width ||
f->fmt.pix_mp.height != inst->crop.height) {
inst->crop.top = 0;
inst->crop.left = 0;
inst->crop.width = fmt->fmt.pix_mp.width;
inst->crop.height = fmt->fmt.pix_mp.height;
iris_venc_s_fmt_output(inst, output_fmt);
}
memcpy(f, fmt, sizeof(struct v4l2_format));
return 0;
}
int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct vb2_queue *q;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
if (vb2_is_busy(q))
return -EBUSY;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
return iris_venc_s_fmt_input(inst, f);
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
return iris_venc_s_fmt_output(inst, f);
default:
return -EINVAL;
}
}
int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat)
{
bool supported;
supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (!supported)
supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
return supported ? 0 : -EINVAL;
}
int iris_venc_subscribe_event(struct iris_inst *inst,
const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_EOS:
return v4l2_event_subscribe(&inst->fh, sub, 0, NULL);
case V4L2_EVENT_CTRL:
return v4l2_ctrl_subscribe_event(&inst->fh, sub);
default:
return -EINVAL;
}
}
int iris_venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s)
{
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
switch (s->target) {
case V4L2_SEL_TGT_CROP:
s->r.left = 0;
s->r.top = 0;
if (s->r.width > inst->fmt_src->fmt.pix_mp.width ||
s->r.height > inst->fmt_src->fmt.pix_mp.height)
return -EINVAL;
inst->crop.left = s->r.left;
inst->crop.top = s->r.top;
inst->crop.width = s->r.width;
inst->crop.height = s->r.height;
inst->fmt_dst->fmt.pix_mp.width = inst->crop.width;
inst->fmt_dst->fmt.pix_mp.height = inst->crop.height;
return iris_venc_s_fmt_output(inst, inst->fmt_dst);
default:
return -EINVAL;
}
}
int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
struct v4l2_fract *timeperframe = NULL;
u32 default_rate = DEFAULT_FPS;
bool is_frame_rate = false;
u32 fps, max_rate;
int ret = 0;
if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
timeperframe = &s_parm->parm.output.timeperframe;
max_rate = caps->max_operating_rate;
s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
} else {
timeperframe = &s_parm->parm.capture.timeperframe;
is_frame_rate = true;
max_rate = caps->max_frame_rate;
s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
}
if (!timeperframe->denominator || !timeperframe->numerator) {
if (!timeperframe->numerator)
timeperframe->numerator = 1;
if (!timeperframe->denominator)
timeperframe->denominator = default_rate;
}
fps = timeperframe->denominator / timeperframe->numerator;
if (!fps)
return -EINVAL;
if (fps > max_rate) {
ret = -ENOMEM;
goto reset_rate;
}
if (is_frame_rate)
inst->frame_rate = fps;
else
inst->operating_rate = fps;
if ((s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_is_streaming(src_q)) ||
(s_parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vb2_is_streaming(dst_q))) {
ret = iris_check_core_mbpf(inst);
if (ret)
goto reset_rate;
ret = iris_check_core_mbps(inst);
if (ret)
goto reset_rate;
}
return 0;
reset_rate:
if (ret) {
if (is_frame_rate)
inst->frame_rate = default_rate;
else
inst->operating_rate = default_rate;
}
return ret;
}
int iris_venc_g_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm)
{
struct v4l2_fract *timeperframe = NULL;
if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
timeperframe = &s_parm->parm.output.timeperframe;
timeperframe->numerator = 1;
timeperframe->denominator = inst->operating_rate;
s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
} else {
timeperframe = &s_parm->parm.capture.timeperframe;
timeperframe->numerator = 1;
timeperframe->denominator = inst->frame_rate;
s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
}
return 0;
}
int iris_venc_streamon_input(struct iris_inst *inst)
{
int ret;
ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_alloc_and_queue_persist_bufs(inst, BUF_ARP);
if (ret)
return ret;
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
return iris_process_streamon_input(inst);
}
int iris_venc_streamon_output(struct iris_inst *inst)
{
int ret;
ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
goto error;
ret = iris_alloc_and_queue_persist_bufs(inst, BUF_ARP);
if (ret)
return ret;
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
goto error;
ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
goto error;
ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
goto error;
ret = iris_process_streamon_output(inst);
if (ret)
goto error;
return ret;
error:
iris_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
return ret;
}
int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
{
struct iris_buffer *buf = to_iris_buffer(vbuf);
struct vb2_buffer *vb2 = &vbuf->vb2_buf;
struct vb2_queue *q;
int ret;
ret = iris_vb2_buffer_to_driver(vb2, buf);
if (ret)
return ret;
if (buf->type == BUF_INPUT)
iris_set_ts_metadata(inst, vbuf);
q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type);
if (!vb2_is_streaming(q)) {
buf->attr |= BUF_ATTR_DEFERRED;
return 0;
}
iris_scale_power(inst);
return iris_queue_buffer(inst, buf);
}
int iris_venc_start_cmd(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
struct vb2_queue *dst_vq;
int ret;
dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
vb2_clear_last_buffer_dequeued(dst_vq);
clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
if (hfi_ops->session_resume_drain) {
ret = hfi_ops->session_resume_drain(inst,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
}
clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
}
if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
if (hfi_ops->session_resume_drain) {
ret = hfi_ops->session_resume_drain(inst,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
}
clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
}
} else {
dev_err(inst->core->dev, "start called before receiving last_flag\n");
iris_inst_change_state(inst, IRIS_INST_ERROR);
return -EBUSY;
}
inst->last_buffer_dequeued = false;
return iris_inst_change_sub_state(inst, clear_sub_state, 0);
}
int iris_venc_stop_cmd(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
iris_scale_power(inst);
return ret;
}
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _IRIS_VENC_H_
#define _IRIS_VENC_H_
struct iris_inst;
int iris_venc_inst_init(struct iris_inst *inst);
int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat);
int iris_venc_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub);
int iris_venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s);
int iris_venc_g_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm);
int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm);
int iris_venc_streamon_input(struct iris_inst *inst);
int iris_venc_streamon_output(struct iris_inst *inst);
int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
int iris_venc_start_cmd(struct iris_inst *inst);
int iris_venc_stop_cmd(struct iris_inst *inst);
#endif
@@ -0,0 +1,719 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#include "iris_vidc.h"
#include "iris_instance.h"
#include "iris_vdec.h"
#include "iris_venc.h"
#include "iris_vb2.h"
#include "iris_vpu_buffer.h"
#include "iris_platform_common.h"
#define IRIS_DRV_NAME "iris_driver"
#define IRIS_BUS_NAME "platform:iris_icc"
#define STEP_WIDTH 1
#define STEP_HEIGHT 1
static void iris_v4l2_fh_init(struct iris_inst *inst, struct file *filp)
{
if (inst->domain == ENCODER)
v4l2_fh_init(&inst->fh, inst->core->vdev_enc);
else if (inst->domain == DECODER)
v4l2_fh_init(&inst->fh, inst->core->vdev_dec);
inst->fh.ctrl_handler = &inst->ctrl_handler;
v4l2_fh_add(&inst->fh, filp);
}
static void iris_v4l2_fh_deinit(struct iris_inst *inst, struct file *filp)
{
v4l2_fh_del(&inst->fh, filp);
inst->fh.ctrl_handler = NULL;
v4l2_fh_exit(&inst->fh);
}
static void iris_add_session(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *iter;
u32 count = 0;
mutex_lock(&core->lock);
list_for_each_entry(iter, &core->instances, list)
count++;
if (count < core->iris_platform_data->max_session_count)
list_add_tail(&inst->list, &core->instances);
mutex_unlock(&core->lock);
}
static void iris_remove_session(struct iris_inst *inst)
{
struct iris_core *core = inst->core;
struct iris_inst *iter, *temp;
mutex_lock(&core->lock);
list_for_each_entry_safe(iter, temp, &core->instances, list) {
if (iter->session_id == inst->session_id) {
list_del_init(&iter->list);
break;
}
}
mutex_unlock(&core->lock);
}
static inline struct iris_inst *iris_get_inst(struct file *filp)
{
return container_of(file_to_v4l2_fh(filp), struct iris_inst, fh);
}
static void iris_m2m_device_run(void *priv)
{
}
static void iris_m2m_job_abort(void *priv)
{
struct iris_inst *inst = priv;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
v4l2_m2m_job_finish(inst->m2m_dev, m2m_ctx);
}
static const struct v4l2_m2m_ops iris_m2m_ops = {
.device_run = iris_m2m_device_run,
.job_abort = iris_m2m_job_abort,
};
static int
iris_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
{
struct iris_inst *inst = priv;
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->ops = inst->core->iris_vb2_ops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->drv_priv = inst;
src_vq->buf_struct_size = sizeof(struct iris_buffer);
src_vq->min_reqbufs_allocation = MIN_BUFFERS;
src_vq->dev = inst->core->dev;
src_vq->lock = &inst->ctx_q_lock;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->ops = inst->core->iris_vb2_ops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->drv_priv = inst;
dst_vq->buf_struct_size = sizeof(struct iris_buffer);
dst_vq->min_reqbufs_allocation = MIN_BUFFERS;
dst_vq->dev = inst->core->dev;
dst_vq->lock = &inst->ctx_q_lock;
return vb2_queue_init(dst_vq);
}
int iris_open(struct file *filp)
{
struct iris_core *core = video_drvdata(filp);
struct video_device *vdev;
struct iris_inst *inst;
u32 session_type;
int ret;
vdev = video_devdata(filp);
if (strcmp(vdev->name, "qcom-iris-decoder") == 0)
session_type = DECODER;
else if (strcmp(vdev->name, "qcom-iris-encoder") == 0)
session_type = ENCODER;
else
return -EINVAL;
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
return ret;
ret = iris_core_init(core);
if (ret) {
dev_err(core->dev, "core init failed\n");
pm_runtime_put_sync(core->dev);
return ret;
}
pm_runtime_put_sync(core->dev);
inst = core->hfi_sys_ops->sys_get_instance();
if (!inst)
return -ENOMEM;
inst->core = core;
inst->domain = session_type;
inst->session_id = hash32_ptr(inst);
inst->state = IRIS_INST_DEINIT;
mutex_init(&inst->lock);
mutex_init(&inst->ctx_q_lock);
INIT_LIST_HEAD(&inst->buffers[BUF_BIN].list);
INIT_LIST_HEAD(&inst->buffers[BUF_ARP].list);
INIT_LIST_HEAD(&inst->buffers[BUF_COMV].list);
INIT_LIST_HEAD(&inst->buffers[BUF_NON_COMV].list);
INIT_LIST_HEAD(&inst->buffers[BUF_LINE].list);
INIT_LIST_HEAD(&inst->buffers[BUF_DPB].list);
INIT_LIST_HEAD(&inst->buffers[BUF_PERSIST].list);
INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_1].list);
INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_2].list);
INIT_LIST_HEAD(&inst->buffers[BUF_VPSS].list);
INIT_LIST_HEAD(&inst->buffers[BUF_PARTIAL].list);
init_completion(&inst->completion);
init_completion(&inst->flush_completion);
iris_v4l2_fh_init(inst, filp);
inst->m2m_dev = v4l2_m2m_init(&iris_m2m_ops);
if (IS_ERR_OR_NULL(inst->m2m_dev)) {
ret = -EINVAL;
goto fail_v4l2_fh_deinit;
}
inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, iris_m2m_queue_init);
if (IS_ERR_OR_NULL(inst->m2m_ctx)) {
ret = -EINVAL;
goto fail_m2m_release;
}
if (inst->domain == DECODER)
ret = iris_vdec_inst_init(inst);
else if (inst->domain == ENCODER)
ret = iris_venc_inst_init(inst);
if (ret)
goto fail_m2m_ctx_release;
iris_add_session(inst);
inst->fh.m2m_ctx = inst->m2m_ctx;
return 0;
fail_m2m_ctx_release:
v4l2_m2m_ctx_release(inst->m2m_ctx);
fail_m2m_release:
v4l2_m2m_release(inst->m2m_dev);
fail_v4l2_fh_deinit:
iris_v4l2_fh_deinit(inst, filp);
mutex_destroy(&inst->ctx_q_lock);
mutex_destroy(&inst->lock);
kfree(inst);
return ret;
}
static void iris_session_close(struct iris_inst *inst)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
bool wait_for_response = true;
int ret;
if (inst->state == IRIS_INST_DEINIT)
return;
reinit_completion(&inst->completion);
ret = hfi_ops->session_close(inst);
if (ret)
wait_for_response = false;
if (wait_for_response)
iris_wait_for_session_response(inst, false);
}
static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane)
{
const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buf, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
u32 count = 0;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
internal_buf_type = firmware_data->dec_op_int_buf_tbl;
internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
for (i = 0; i < internal_buffer_count; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
count = 0;
list_for_each_entry_safe(buf, next, &buffers->list, list)
count++;
if (count)
dev_err(inst->core->dev, "%d buffer of type %d not released",
count, internal_buf_type[i]);
}
if (inst->domain == DECODER)
buffers = &inst->buffers[BUF_PERSIST];
else
buffers = &inst->buffers[BUF_ARP];
count = 0;
list_for_each_entry_safe(buf, next, &buffers->list, list)
count++;
if (count)
dev_err(inst->core->dev, "%d buffer of type %d not released",
count, inst->domain == DECODER ? BUF_PERSIST : BUF_ARP);
}
int iris_close(struct file *filp)
{
struct iris_inst *inst = iris_get_inst(filp);
v4l2_ctrl_handler_free(&inst->ctrl_handler);
v4l2_m2m_ctx_release(inst->m2m_ctx);
v4l2_m2m_release(inst->m2m_dev);
mutex_lock(&inst->lock);
iris_session_close(inst);
iris_inst_change_state(inst, IRIS_INST_DEINIT);
iris_v4l2_fh_deinit(inst, filp);
iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
iris_remove_session(inst);
mutex_unlock(&inst->lock);
mutex_destroy(&inst->ctx_q_lock);
mutex_destroy(&inst->lock);
kfree(inst->fmt_src);
kfree(inst->fmt_dst);
kfree(inst);
return 0;
}
static int iris_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
{
struct iris_inst *inst = iris_get_inst(filp);
if (inst->domain == DECODER)
return iris_vdec_enum_fmt(inst, f);
else if (inst->domain == ENCODER)
return iris_venc_enum_fmt(inst, f);
else
return -EINVAL;
}
static int iris_try_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
{
struct iris_inst *inst = iris_get_inst(filp);
int ret = 0;
mutex_lock(&inst->lock);
if (inst->domain == DECODER)
ret = iris_vdec_try_fmt(inst, f);
else if (inst->domain == ENCODER)
ret = iris_venc_try_fmt(inst, f);
mutex_unlock(&inst->lock);
return ret;
}
static int iris_s_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
{
struct iris_inst *inst = iris_get_inst(filp);
int ret = 0;
mutex_lock(&inst->lock);
if (inst->domain == DECODER)
ret = iris_vdec_s_fmt(inst, f);
else if (inst->domain == ENCODER)
ret = iris_venc_s_fmt(inst, f);
mutex_unlock(&inst->lock);
return ret;
}
static int iris_g_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
{
struct iris_inst *inst = iris_get_inst(filp);
int ret = 0;
mutex_lock(&inst->lock);
if (V4L2_TYPE_IS_OUTPUT(f->type))
*f = *inst->fmt_src;
else if (V4L2_TYPE_IS_CAPTURE(f->type))
*f = *inst->fmt_dst;
else
ret = -EINVAL;
mutex_unlock(&inst->lock);
return ret;
}
static int iris_enum_framesizes(struct file *filp, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct iris_inst *inst = iris_get_inst(filp);
struct platform_inst_caps *caps;
int ret = 0;
if (fsize->index)
return -EINVAL;
if (inst->domain == DECODER)
ret = iris_vdec_validate_format(inst, fsize->pixel_format);
else
ret = iris_venc_validate_format(inst, fsize->pixel_format);
if (ret)
return ret;
caps = inst->core->iris_platform_data->inst_caps;
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
fsize->stepwise.min_width = caps->min_frame_width;
fsize->stepwise.max_width = caps->max_frame_width;
fsize->stepwise.step_width = STEP_WIDTH;
fsize->stepwise.min_height = caps->min_frame_height;
fsize->stepwise.max_height = caps->max_frame_height;
fsize->stepwise.step_height = STEP_HEIGHT;
return ret;
}
static int iris_enum_frameintervals(struct file *filp, void *fh,
struct v4l2_frmivalenum *fival)
{
struct iris_inst *inst = iris_get_inst(filp);
struct iris_core *core = inst->core;
struct platform_inst_caps *caps;
u32 fps, mbpf;
int ret = 0;
if (inst->domain == DECODER)
return -ENOTTY;
if (fival->index)
return -EINVAL;
ret = iris_venc_validate_format(inst, fival->pixel_format);
if (ret)
return ret;
if (!fival->width || !fival->height)
return -EINVAL;
caps = inst->core->iris_platform_data->inst_caps;
if (fival->width > caps->max_frame_width ||
fival->width < caps->min_frame_width ||
fival->height > caps->max_frame_height ||
fival->height < caps->min_frame_height)
return -EINVAL;
mbpf = NUM_MBS_PER_FRAME(fival->height, fival->width);
fps = DIV_ROUND_UP(core->iris_platform_data->max_core_mbps, mbpf);
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
fival->stepwise.min.numerator = 1;
fival->stepwise.min.denominator =
min_t(u32, fps, MAXIMUM_FPS);
fival->stepwise.max.numerator = 1;
fival->stepwise.max.denominator = 1;
fival->stepwise.step.numerator = 1;
fival->stepwise.step.denominator = MAXIMUM_FPS;
return 0;
}
static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap)
{
struct iris_inst *inst = iris_get_inst(filp);
strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver));
if (inst->domain == DECODER)
strscpy(cap->card, "Iris Decoder", sizeof(cap->card));
else
strscpy(cap->card, "Iris Encoder", sizeof(cap->card));
return 0;
}
static int iris_g_selection(struct file *filp, void *fh, struct v4l2_selection *s)
{
struct iris_inst *inst = iris_get_inst(filp);
if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
inst->domain == DECODER)
return -EINVAL;
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
inst->domain == ENCODER)
return -EINVAL;
if (inst->domain == DECODER) {
switch (s->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_COMPOSE_PADDED:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE:
s->r.left = inst->crop.left;
s->r.top = inst->crop.top;
s->r.width = inst->crop.width;
s->r.height = inst->crop.height;
break;
default:
return -EINVAL;
}
} else if (inst->domain == ENCODER) {
switch (s->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
s->r.width = inst->fmt_src->fmt.pix_mp.width;
s->r.height = inst->fmt_src->fmt.pix_mp.height;
break;
case V4L2_SEL_TGT_CROP:
s->r.width = inst->crop.width;
s->r.height = inst->crop.height;
break;
default:
return -EINVAL;
}
s->r.left = inst->crop.left;
s->r.top = inst->crop.top;
}
return 0;
}
static int iris_s_selection(struct file *filp, void *fh, struct v4l2_selection *s)
{
struct iris_inst *inst = iris_get_inst(filp);
if (inst->domain == DECODER)
return -EINVAL;
else if (inst->domain == ENCODER)
return iris_venc_s_selection(inst, s);
return -EINVAL;
}
static int iris_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
{
struct iris_inst *inst = container_of(fh, struct iris_inst, fh);
if (inst->domain == DECODER)
return iris_vdec_subscribe_event(inst, sub);
else if (inst->domain == ENCODER)
return iris_venc_subscribe_event(inst, sub);
return -EINVAL;
}
static int iris_s_parm(struct file *filp, void *fh, struct v4l2_streamparm *a)
{
struct iris_inst *inst = iris_get_inst(filp);
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return -EINVAL;
if (inst->domain == ENCODER)
return iris_venc_s_param(inst, a);
else
return -EINVAL;
}
static int iris_g_parm(struct file *filp, void *fh, struct v4l2_streamparm *a)
{
struct iris_inst *inst = iris_get_inst(filp);
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return -EINVAL;
if (inst->domain == ENCODER)
return iris_venc_g_param(inst, a);
else
return -EINVAL;
}
static int iris_dec_cmd(struct file *filp, void *fh,
struct v4l2_decoder_cmd *dec)
{
struct iris_inst *inst = iris_get_inst(filp);
int ret = 0;
mutex_lock(&inst->lock);
if (dec->cmd != V4L2_DEC_CMD_STOP && dec->cmd != V4L2_DEC_CMD_START) {
ret = -EINVAL;
goto unlock;
}
if (inst->state == IRIS_INST_DEINIT)
goto unlock;
if (!iris_allow_cmd(inst, dec->cmd)) {
ret = -EBUSY;
goto unlock;
}
if (dec->cmd == V4L2_DEC_CMD_START)
ret = iris_vdec_start_cmd(inst);
else if (dec->cmd == V4L2_DEC_CMD_STOP)
ret = iris_vdec_stop_cmd(inst);
else
ret = -EINVAL;
unlock:
mutex_unlock(&inst->lock);
return ret;
}
static int iris_enc_cmd(struct file *filp, void *fh,
struct v4l2_encoder_cmd *enc)
{
struct iris_inst *inst = iris_get_inst(filp);
int ret = 0;
mutex_lock(&inst->lock);
if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START) {
ret = -EINVAL;
goto unlock;
}
if (inst->state == IRIS_INST_DEINIT)
goto unlock;
if (!iris_allow_cmd(inst, enc->cmd)) {
ret = -EBUSY;
goto unlock;
}
if (enc->cmd == V4L2_ENC_CMD_START)
ret = iris_venc_start_cmd(inst);
else if (enc->cmd == V4L2_ENC_CMD_STOP)
ret = iris_venc_stop_cmd(inst);
else
ret = -EINVAL;
unlock:
mutex_unlock(&inst->lock);
return ret;
}
static const struct v4l2_file_operations iris_v4l2_file_ops = {
.owner = THIS_MODULE,
.open = iris_open,
.release = iris_close,
.unlocked_ioctl = video_ioctl2,
.poll = v4l2_m2m_fop_poll,
.mmap = v4l2_m2m_fop_mmap,
};
static const struct vb2_ops iris_vb2_ops = {
.buf_init = iris_vb2_buf_init,
.queue_setup = iris_vb2_queue_setup,
.start_streaming = iris_vb2_start_streaming,
.stop_streaming = iris_vb2_stop_streaming,
.buf_prepare = iris_vb2_buf_prepare,
.buf_out_validate = iris_vb2_buf_out_validate,
.buf_queue = iris_vb2_buf_queue,
};
static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops_dec = {
.vidioc_enum_fmt_vid_cap = iris_enum_fmt,
.vidioc_enum_fmt_vid_out = iris_enum_fmt,
.vidioc_try_fmt_vid_cap_mplane = iris_try_fmt_vid_mplane,
.vidioc_try_fmt_vid_out_mplane = iris_try_fmt_vid_mplane,
.vidioc_s_fmt_vid_cap_mplane = iris_s_fmt_vid_mplane,
.vidioc_s_fmt_vid_out_mplane = iris_s_fmt_vid_mplane,
.vidioc_g_fmt_vid_cap_mplane = iris_g_fmt_vid_mplane,
.vidioc_g_fmt_vid_out_mplane = iris_g_fmt_vid_mplane,
.vidioc_enum_framesizes = iris_enum_framesizes,
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_querycap = iris_querycap,
.vidioc_g_selection = iris_g_selection,
.vidioc_subscribe_event = iris_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
.vidioc_decoder_cmd = iris_dec_cmd,
};
static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops_enc = {
.vidioc_enum_fmt_vid_cap = iris_enum_fmt,
.vidioc_enum_fmt_vid_out = iris_enum_fmt,
.vidioc_try_fmt_vid_cap_mplane = iris_try_fmt_vid_mplane,
.vidioc_try_fmt_vid_out_mplane = iris_try_fmt_vid_mplane,
.vidioc_s_fmt_vid_cap_mplane = iris_s_fmt_vid_mplane,
.vidioc_s_fmt_vid_out_mplane = iris_s_fmt_vid_mplane,
.vidioc_g_fmt_vid_cap_mplane = iris_g_fmt_vid_mplane,
.vidioc_g_fmt_vid_out_mplane = iris_g_fmt_vid_mplane,
.vidioc_enum_framesizes = iris_enum_framesizes,
.vidioc_enum_frameintervals = iris_enum_frameintervals,
.vidioc_querycap = iris_querycap,
.vidioc_subscribe_event = iris_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_g_selection = iris_g_selection,
.vidioc_s_selection = iris_s_selection,
.vidioc_s_parm = iris_s_parm,
.vidioc_g_parm = iris_g_parm,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
.vidioc_encoder_cmd = iris_enc_cmd,
};
void iris_init_ops(struct iris_core *core)
{
core->iris_v4l2_file_ops = &iris_v4l2_file_ops;
core->iris_vb2_ops = &iris_vb2_ops;
core->iris_v4l2_ioctl_ops_dec = &iris_v4l2_ioctl_ops_dec;
core->iris_v4l2_ioctl_ops_enc = &iris_v4l2_ioctl_ops_enc;
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VIDC_H__
#define __IRIS_VIDC_H__
struct iris_core;
void iris_init_ops(struct iris_core *core);
int iris_open(struct file *filp);
int iris_close(struct file *filp);
#endif
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bits.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
#include "iris_instance.h"
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
struct v4l2_format *inp_f = inst->fmt_src;
u32 mbs_per_second, mbpf, height, width;
unsigned long vpp_freq, vsp_freq;
u32 fps = inst->frame_rate;
width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
mbpf = NUM_MBS_PER_FRAME(height, width);
mbs_per_second = mbpf * fps;
vpp_freq = mbs_per_second * caps->mb_cycles_vpp;
/* 21 / 20 is overhead factor */
vpp_freq += vpp_freq / 20;
vsp_freq = mbs_per_second * caps->mb_cycles_vsp;
/* 10 / 7 is overhead factor */
vsp_freq += ((fps * data_size * 8) * 10) / 7;
return max(vpp_freq, vsp_freq);
}
const struct vpu_ops iris_vpu2_ops = {
.power_off_hw = iris_vpu_power_off_hw,
.power_on_hw = iris_vpu_power_on_hw,
.power_off_controller = iris_vpu_power_off_controller,
.power_on_controller = iris_vpu_power_on_controller,
.calc_freq = iris_vpu2_calc_freq,
.set_hwmode = iris_vpu_set_hwmode,
};
@@ -0,0 +1,283 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2025 Linaro Ltd
*/
#include <linux/iopoll.h>
#include <linux/reset.h>
#include "iris_instance.h"
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
#define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18)
#define SW_RESET BIT(0)
#define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20)
#define NOC_HALT BIT(0)
#define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28)
static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core)
{
u32 value, pwr_status;
value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
pwr_status = value & BIT(1);
return pwr_status ? false : true;
}
static void iris_vpu3_power_off_hardware(struct iris_core *core)
{
u32 reg_val = 0, value, i;
int ret;
if (iris_vpu3x_hw_power_collapsed(core))
goto disable_power;
dev_err(core->dev, "video hw is power on\n");
value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
if (value)
writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
reg_val, reg_val & 0x400000, 2000, 20000);
if (ret)
goto disable_power;
}
writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
reg_val, reg_val & 0x3, 200, 2000);
if (ret)
goto disable_power;
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
reg_val, !(reg_val & 0x3), 200, 2000);
if (ret)
goto disable_power;
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
disable_power:
iris_vpu_power_off_hw(core);
}
static void iris_vpu33_power_off_hardware(struct iris_core *core)
{
bool handshake_done = false, handshake_busy = false;
u32 reg_val = 0, value, i;
u32 count = 0;
int ret;
if (iris_vpu3x_hw_power_collapsed(core))
goto disable_power;
dev_err(core->dev, "video hw is power on\n");
value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
if (value)
writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
reg_val, reg_val & 0x400000, 2000, 20000);
if (ret)
goto disable_power;
}
/* Retry up to 1000 times as recommended by hardware documentation */
do {
/* set MNoC to low power */
writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
udelay(15);
value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS);
handshake_done = value & NOC_LPI_STATUS_DONE;
handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
if (handshake_done || !handshake_busy)
break;
writel(0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
udelay(15);
} while (++count < 1000);
if (!handshake_done && handshake_busy)
dev_err(core->dev, "LPI handshake timeout\n");
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
reg_val, reg_val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
writel(0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
disable_power:
iris_vpu_power_off_hw(core);
}
static int iris_vpu33_power_off_controller(struct iris_core *core)
{
u32 xo_rst_tbl_size = core->iris_platform_data->controller_rst_tbl_size;
u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
u32 val = 0;
int ret;
writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
val, val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
val, val == 0, 200, 2000);
if (ret)
goto disable_power;
writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
/* Disable MVP NoC clock */
val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
val |= NOC_HALT;
writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
/* enable MVP NoC reset */
val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
val |= SW_RESET;
writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
/* poll AON spare register bit0 to become zero with 50ms timeout */
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_SPARE,
val, (val & BIT(0)) == 0, 1000, 50000);
if (ret)
goto disable_power;
/* enable bit(1) to avoid cvp noc xo reset */
val = readl(core->reg_base + AON_WRAPPER_SPARE);
val |= BIT(1);
writel(val, core->reg_base + AON_WRAPPER_SPARE);
reset_control_bulk_assert(xo_rst_tbl_size, core->controller_resets);
/* De-assert MVP NoC reset */
val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
val &= ~SW_RESET;
writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
usleep_range(80, 100);
reset_control_bulk_deassert(xo_rst_tbl_size, core->controller_resets);
/* reset AON spare register */
writel(0, core->reg_base + AON_WRAPPER_SPARE);
/* Enable MVP NoC clock */
val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
val &= ~NOC_HALT;
writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
return 0;
}
static int iris_vpu35_power_on_hw(struct iris_core *core)
{
int ret;
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
if (ret)
return ret;
ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
if (ret)
goto err_disable_power;
ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK);
if (ret)
goto err_disable_axi_clk;
ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
if (ret)
goto err_disable_hw_free_clk;
return 0;
err_disable_hw_free_clk:
iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
err_disable_axi_clk:
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
return ret;
}
static void iris_vpu35_power_off_hw(struct iris_core *core)
{
iris_vpu33_power_off_hardware(core);
iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
}
const struct vpu_ops iris_vpu3_ops = {
.power_off_hw = iris_vpu3_power_off_hardware,
.power_on_hw = iris_vpu_power_on_hw,
.power_off_controller = iris_vpu_power_off_controller,
.power_on_controller = iris_vpu_power_on_controller,
.calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
.set_hwmode = iris_vpu_set_hwmode,
};
const struct vpu_ops iris_vpu33_ops = {
.power_off_hw = iris_vpu33_power_off_hardware,
.power_on_hw = iris_vpu_power_on_hw,
.power_off_controller = iris_vpu33_power_off_controller,
.power_on_controller = iris_vpu_power_on_controller,
.calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
.set_hwmode = iris_vpu_set_hwmode,
};
const struct vpu_ops iris_vpu35_ops = {
.power_off_hw = iris_vpu35_power_off_hw,
.power_on_hw = iris_vpu35_power_on_hw,
.power_off_controller = iris_vpu35_vpu4x_power_off_controller,
.power_on_controller = iris_vpu35_vpu4x_power_on_controller,
.program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers,
.calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
.set_hwmode = iris_vpu_set_hwmode,
};
@@ -0,0 +1,371 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/iopoll.h>
#include <linux/reset.h>
#include "iris_instance.h"
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
#define AON_WRAPPER_MVP_NOC_RESET_SYNCRST (AON_MVP_NOC_RESET + 0x08)
#define CPU_CS_APV_BRIDGE_SYNC_RESET (CPU_BASE_OFFS + 0x174)
#define MVP_NOC_RESET_REQ_MASK 0x70103
#define VPU_IDLE_BITS 0x7103
#define WRAPPER_EFUSE_MONITOR (WRAPPER_BASE_OFFS + 0x08)
#define APV_CLK_HALT BIT(1)
#define CORE_CLK_HALT BIT(0)
#define CORE_PWR_ON BIT(1)
#define DISABLE_VIDEO_APV_BIT BIT(27)
#define DISABLE_VIDEO_VPP1_BIT BIT(28)
#define DISABLE_VIDEO_VPP0_BIT BIT(29)
static int iris_vpu4x_genpd_set_hwmode(struct iris_core *core, bool hw_mode, u32 efuse_value)
{
int ret;
ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], hw_mode);
if (ret)
return ret;
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
[IRIS_VPP0_HW_POWER_DOMAIN], hw_mode);
if (ret)
goto restore_hw_domain_mode;
}
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
[IRIS_VPP1_HW_POWER_DOMAIN], hw_mode);
if (ret)
goto restore_vpp0_domain_mode;
}
if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) {
ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs
[IRIS_APV_HW_POWER_DOMAIN], hw_mode);
if (ret)
goto restore_vpp1_domain_mode;
}
return 0;
restore_vpp1_domain_mode:
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP1_HW_POWER_DOMAIN],
!hw_mode);
restore_vpp0_domain_mode:
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP0_HW_POWER_DOMAIN],
!hw_mode);
restore_hw_domain_mode:
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], !hw_mode);
return ret;
}
static int iris_vpu4x_power_on_apv(struct iris_core *core)
{
int ret;
ret = iris_enable_power_domains(core,
core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
if (ret)
return ret;
ret = iris_prepare_enable_clock(core, IRIS_APV_HW_CLK);
if (ret)
goto disable_apv_hw_power_domain;
return 0;
disable_apv_hw_power_domain:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
return ret;
}
static void iris_vpu4x_power_off_apv(struct iris_core *core)
{
bool handshake_done, handshake_busy;
u32 value, count = 0;
int ret;
value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
if (value & APV_CLK_HALT)
writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
do {
writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
usleep_range(10, 20);
value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS);
handshake_done = value & NOC_LPI_STATUS_DONE;
handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
if (handshake_done || !handshake_busy)
break;
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
usleep_range(10, 20);
} while (++count < 1000);
if (!handshake_done && handshake_busy)
dev_err(core->dev, "LPI handshake timeout\n");
writel(0x080200, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
value, value & 0x080200, 200, 2000);
if (ret)
goto disable_clocks_and_power;
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST);
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
value, value == 0x0, 200, 2000);
if (ret)
goto disable_clocks_and_power;
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
CPU_CS_APV_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
disable_clocks_and_power:
iris_disable_unprepare_clock(core, IRIS_APV_HW_CLK);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]);
}
static void iris_vpu4x_ahb_sync_reset_apv(struct iris_core *core)
{
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
CPU_CS_APV_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET);
}
static void iris_vpu4x_ahb_sync_reset_hardware(struct iris_core *core)
{
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
}
static int iris_vpu4x_enable_hardware_clocks(struct iris_core *core, u32 efuse_value)
{
int ret;
ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
if (ret)
return ret;
ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK);
if (ret)
goto disable_axi_clock;
ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
if (ret)
goto disable_hw_free_run_clock;
ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK);
if (ret)
goto disable_hw_clock;
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
ret = iris_prepare_enable_clock(core, IRIS_VPP0_HW_CLK);
if (ret)
goto disable_bse_hw_clock;
}
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
ret = iris_prepare_enable_clock(core, IRIS_VPP1_HW_CLK);
if (ret)
goto disable_vpp0_hw_clock;
}
return 0;
disable_vpp0_hw_clock:
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK);
disable_bse_hw_clock:
iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
disable_hw_clock:
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
disable_hw_free_run_clock:
iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
disable_axi_clock:
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
return ret;
}
static void iris_vpu4x_disable_hardware_clocks(struct iris_core *core, u32 efuse_value)
{
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
iris_disable_unprepare_clock(core, IRIS_VPP1_HW_CLK);
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK);
iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
}
static int iris_vpu4x_power_on_hardware(struct iris_core *core)
{
u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
int ret;
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
if (ret)
return ret;
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP0_HW_POWER_DOMAIN]);
if (ret)
goto disable_hw_power_domain;
}
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP1_HW_POWER_DOMAIN]);
if (ret)
goto disable_vpp0_power_domain;
}
ret = iris_vpu4x_enable_hardware_clocks(core, efuse_value);
if (ret)
goto disable_vpp1_power_domain;
if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) {
ret = iris_vpu4x_power_on_apv(core);
if (ret)
goto disable_hw_clocks;
}
return 0;
disable_hw_clocks:
iris_vpu4x_disable_hardware_clocks(core, efuse_value);
disable_vpp1_power_domain:
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP1_HW_POWER_DOMAIN]);
disable_vpp0_power_domain:
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP0_HW_POWER_DOMAIN]);
disable_hw_power_domain:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
return ret;
}
static void iris_vpu4x_power_off_hardware(struct iris_core *core)
{
u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
bool handshake_done, handshake_busy;
u32 value, count = 0;
int ret;
iris_vpu4x_genpd_set_hwmode(core, false, efuse_value);
if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
iris_vpu4x_power_off_apv(core);
value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
if (!(value & CORE_PWR_ON))
goto disable_clocks_and_power;
value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
if (value & CORE_CLK_HALT)
writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN, value,
value & VPU_IDLE_BITS, 2000, 20000);
do {
writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
usleep_range(10, 20);
value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS);
handshake_done = value & NOC_LPI_STATUS_DONE;
handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
if (handshake_done || !handshake_busy)
break;
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
usleep_range(10, 20);
} while (++count < 1000);
if (!handshake_done && handshake_busy)
dev_err(core->dev, "LPI handshake timeout\n");
writel(MVP_NOC_RESET_REQ_MASK, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
value, value & MVP_NOC_RESET_REQ_MASK, 200, 2000);
if (ret)
goto disable_clocks_and_power;
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST);
writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
value, value == 0x0, 200, 2000);
if (ret)
goto disable_clocks_and_power;
writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base +
CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
disable_clocks_and_power:
iris_vpu4x_disable_hardware_clocks(core, efuse_value);
if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP1_HW_POWER_DOMAIN]);
if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
[IRIS_VPP0_HW_POWER_DOMAIN]);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
}
static int iris_vpu4x_set_hwmode(struct iris_core *core)
{
u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
iris_vpu4x_ahb_sync_reset_apv(core);
iris_vpu4x_ahb_sync_reset_hardware(core);
return iris_vpu4x_genpd_set_hwmode(core, true, efuse_value);
}
const struct vpu_ops iris_vpu4x_ops = {
.power_off_hw = iris_vpu4x_power_off_hardware,
.power_on_hw = iris_vpu4x_power_on_hardware,
.power_off_controller = iris_vpu35_vpu4x_power_off_controller,
.power_on_controller = iris_vpu35_vpu4x_power_on_controller,
.program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers,
.calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
.set_hwmode = iris_vpu4x_set_hwmode,
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,293 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VPU_BUFFER_H__
#define __IRIS_VPU_BUFFER_H__
struct iris_inst;
#define MIN_BUFFERS 4
#define DMA_ALIGNMENT 256
#define HFI_ALIGNMENT_4096 4096
#define NUM_HW_PIC_BUF 32
#define LCU_MAX_SIZE_PELS 64
#define LCU_MIN_SIZE_PELS 16
#define HDR10_HIST_EXTRADATA_SIZE (4 * 1024)
#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
#define MAX_TILE_COLUMNS 32
#define BIN_BUFFER_THRESHOLD (1280 * 736)
#define VPP_CMD_MAX_SIZE (BIT(20))
#define H264D_MAX_SLICE 1800
#define SIZE_H264D_BUFTAB_T 256
#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4)
#define SIZE_H264D_VPP_CMD_PER_BUF 512
#define NUM_SLIST_BUF_H264 (256 + 32)
#define SIZE_SLIST_BUF_H264 512
#define H264_DISPLAY_BUF_SIZE 3328
#define H264_NUM_FRM_INFO 66
#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)
#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32))
#define NUM_SLIST_BUF_H265 (80 + 20)
#define SIZE_SLIST_BUF_H265 (BIT(10))
#define H265_DISPLAY_BUF_SIZE (3072)
#define H265_NUM_FRM_INFO (48)
#define SIZE_ONE_SLICE_BUF 256
#define VP9_NUM_FRAME_INFO_BUF 32
#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
#define VP9_PROB_TABLE_SIZE (3840)
#define VP9_FRAME_INFO_BUF_SIZE (6144)
#define VP9_FRAME_INFO_BUF_SIZE_VPU4X (6400)
#define BUFFER_ALIGNMENT_16_BYTES 16
#define BUFFER_ALIGNMENT_32_BYTES 32
#define BUFFER_ALIGNMENT_64_BYTES 64
#define BUFFER_ALIGNMENT_256_BYTES 256
#define BUFFER_ALIGNMENT_512_BYTES 512
#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES)
#define MAX_SUPERFRAME_HEADER_LEN (34)
#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_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8)
#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8)
#define VP9_UDC_HEADER_BUF_SIZE (3 * 128)
#define SIZE_SEI_USERDATA 4096
#define SIZE_DOLBY_RPU_METADATA (41 * 1024)
#define H264_CABAC_HDR_RATIO_HD_TOT 1
#define H264_CABAC_RES_RATIO_HD_TOT 3
#define H265D_MAX_SLICE 3600
#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T
#define H265_CABAC_HDR_RATIO_HD_TOT 2
#define H265_CABAC_RES_RATIO_HD_TOT 2
#define SIZE_H265D_VPP_CMD_PER_BUF (256)
#define SIZE_THREE_DIMENSION_USERDATA 768
#define SIZE_H265D_ARP 9728
#define VPX_DECODER_FRAME_CONCURENCY_LVL (2)
#define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1
#define VPX_DECODER_FRAME_BIN_RES_BUDGET 3
#define VPX_DECODER_FRAME_BIN_DENOMINATOR 2
#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO (3 / 2)
#define SIZE_H264D_HW_PIC_T (BIT(11))
#define FE_LFT_CTRL_LINE_NUMBERS 4
#define FE_LFT_DB_DATA_LINE_NUMBERS 2
#define FE_LFT_LR_DATA_LINE_NUMBERS 4
#define FE_TOP_CTRL_LINE_NUMBERS 3
#define FE_TOP_DATA_LUMA_LINE_NUMBERS 2
#define FE_TOP_DATA_CHROMA_LINE_NUMBERS 3
#define FE_SDC_DATA_PER_BLOCK 16
#define SE_CTRL_DATA_PER_BLOCK 2020
#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96
#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192
#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 16
#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE 384
#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640
#define AV1_CABAC_HDR_RATIO_HD_TOT 2
#define AV1_CABAC_RES_RATIO_HD_TOT 2
#define AV1D_LCU_MAX_SIZE_PELS 128
#define AV1D_LCU_MIN_SIZE_PELS 64
#define AV1D_MAX_TILE_COLS 64
#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192
#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96
#define AV1D_NUM_HW_PIC_BUF 16
#define AV1D_NUM_FRAME_HEADERS 16
#define SIZE_AV1D_SEQUENCE_HEADER 768
#define SIZE_AV1D_METADATA 512
#define SIZE_AV1D_FRAME_HEADER 1280
#define SIZE_AV1D_TILE_OFFSET 65536
#define SIZE_AV1D_QM 3328
#define SIZE_AV1D_PROB_TABLE 22784
#define SIZE_SLICE_CMD_BUFFER (ALIGN(20480, 256))
#define SIZE_SPS_PPS_SLICE_HDR (2048 + 4096)
#define SIZE_BSE_SLICE_CMD_BUF ((((8192 << 2) + 7) & (~7)) * 3)
#define SIZE_LAMBDA_LUT (256 * 11)
#define HFI_COL_FMT_NV12C_Y_TILE_HEIGHT (8)
#define HFI_COL_FMT_NV12C_Y_TILE_WIDTH (32)
#define HFI_COL_FMT_TP10C_Y_TILE_HEIGHT (4)
#define HFI_COL_FMT_TP10C_Y_TILE_WIDTH (48)
#define IRIS_METADATA_STRIDE_MULTIPLE 64
#define IRIS_METADATA_HEIGHT_MULTIPLE 16
#define HFI_BUFFER_ARP_ENC 204800
#define LOG2_16 4
#define LOG2_32 5
#define LLB_UNIT_SIZE 16
#define MAX_WIDTH 4096
#define MAX_HEIGHT 2304
#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))
#define NUM_MBS_720P (((ALIGN(1280, 16)) >> 4) * ((ALIGN(736, 16)) >> 4))
#define BITS_PER_PIX 16
#define NUM_LINES_LUMA 10
#define NUM_LINES_CHROMA 6
#define AV1D_LCU_MAX_SIZE_PELS 128
#define AV1D_LCU_MIN_SIZE_PELS 64
#define AV1D_MAX_TILE_COLS 64
#define BITS_PER_CTRL_PACK 128
#define NUM_CTRL_PACK_LCU 10
static inline u32 size_h264d_lb_fe_top_data(u32 frame_width)
{
return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN(frame_width, 16) * 3;
}
static inline u32 size_h264d_lb_fe_top_ctrl(u32 frame_width)
{
return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
}
static inline u32 size_h264d_lb_fe_left_ctrl(u32 frame_height)
{
return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
}
static inline u32 size_h264d_lb_se_top_ctrl(u32 frame_width)
{
return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
}
static inline u32 size_h264d_lb_se_left_ctrl(u32 frame_height)
{
return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
}
static inline u32 size_h264d_lb_pe_top_data(u32 frame_width)
{
return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
}
static inline u32 size_h264d_lb_vsp_top(u32 frame_width)
{
return (DIV_ROUND_UP(frame_width, 16) << 7);
}
static inline u32 size_h264d_lb_recon_dma_metadata_wr(u32 frame_height)
{
return ALIGN(frame_height, 16) * 32;
}
static inline u32 size_h264d_qp(u32 frame_width, u32 frame_height)
{
return DIV_ROUND_UP(frame_width, 64) * DIV_ROUND_UP(frame_height, 64) * 128;
}
static inline u32 size_av1d_lb_fe_top_data(u32 frame_width, u32 frame_height)
{
return (ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) *
((BITS_PER_PIX * NUM_LINES_LUMA) >> 3) +
ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) / 2 *
((BITS_PER_PIX * NUM_LINES_CHROMA) >> 3) * 2);
}
static inline u32 size_av1d_lb_fe_left_data(u32 frame_width, u32 frame_height)
{
return (32 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 16) +
16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 8) * 2 +
24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 16) +
24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 12) * 2 +
24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 16) +
16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 16) +
16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 +
ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS * 12) * 2);
}
static inline u32 size_av1d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
{
return (NUM_CTRL_PACK_LCU * ((frame_width + AV1D_LCU_MIN_SIZE_PELS - 1) /
AV1D_LCU_MIN_SIZE_PELS) * BITS_PER_CTRL_PACK / 8);
}
static inline u32 size_av1d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
{
return (16 * ((ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 16) +
(ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS)) +
3 * 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) /
AV1D_LCU_MIN_SIZE_PELS));
}
static inline u32 size_av1d_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
{
return (((frame_width + 7) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE);
}
static inline u32 size_av1d_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
{
return (max(((frame_height + 15) / 16) *
MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
max(((frame_height + 31) / 32) *
MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
((frame_height + 63) / 64) *
MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE)));
}
static inline u32 size_av1d_lb_pe_top_data(u32 frame_width, u32 frame_height)
{
return (max(((frame_width + 15) / 16) *
MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE,
max(((frame_width + 31) / 32) *
MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE,
((frame_width + 63) / 64) *
MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE)));
}
static inline u32 size_av1d_lb_vsp_top(u32 frame_width, u32 frame_height)
{
return (max(((frame_width + 63) / 64) * 1280,
((frame_width + 127) / 128) * MAX_HEIGHT));
}
static inline u32 size_av1d_lb_recon_dma_metadata_wr(u32 frame_width,
u32 frame_height)
{
return ((ALIGN(frame_height, 8) / (4 / 2)) * 64);
}
static inline u32 size_av1d_qp(u32 frame_width, u32 frame_height)
{
return size_h264d_qp(frame_width, frame_height);
}
u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
u32 iris_vpu33_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type);
#endif
@@ -0,0 +1,507 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/iopoll.h>
#include <linux/pm_opp.h>
#include <linux/reset.h>
#include "iris_core.h"
#include "iris_instance.h"
#include "iris_vpu_common.h"
#include "iris_vpu_register_defines.h"
#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C)
#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30)
#define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48)
#define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C)
#define CTRL_INIT_IDLE_MSG_BMSK 0x40000000
#define CTRL_ERROR_STATUS__M 0xfe
#define CTRL_STATUS_PC_READY 0x100
#define QTBL_INFO (CPU_CS_BASE_OFFS + 0x50)
#define QTBL_ENABLE BIT(0)
#define QTBL_ADDR (CPU_CS_BASE_OFFS + 0x54)
#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE_OFFS + 0x58)
#define SFR_ADDR (CPU_CS_BASE_OFFS + 0x5C)
#define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64)
#define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68)
static void iris_vpu_interrupt_init(struct iris_core *core)
{
u32 mask_val;
mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK |
WRAPPER_INTR_MASK_A2HCPU_BMSK);
writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
}
static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core)
{
u32 queue_size, value;
const struct vpu_ops *vpu_ops = core->iris_platform_data->vpu_ops;
/* Iris hardware requires 4K queue alignment */
queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
(IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
value = (u32)core->iface_q_table_daddr;
writel(value, core->reg_base + UC_REGION_ADDR);
/* Iris hardware requires 1M queue alignment */
value = ALIGN(SFR_SIZE + queue_size, SZ_1M);
writel(value, core->reg_base + UC_REGION_SIZE);
value = (u32)core->iface_q_table_daddr;
writel(value, core->reg_base + QTBL_ADDR);
writel(QTBL_ENABLE, core->reg_base + QTBL_INFO);
if (core->sfr_daddr) {
value = (u32)core->sfr_daddr + core->iris_firmware_data->core_arch;
writel(value, core->reg_base + SFR_ADDR);
}
if (vpu_ops->program_bootup_registers)
vpu_ops->program_bootup_registers(core);
}
int iris_vpu_boot_firmware(struct iris_core *core)
{
u32 ctrl_init = BIT(0), ctrl_status = 0, count = 0, max_tries = 1000;
iris_vpu_setup_ucregion_memory_map(core);
writel(ctrl_init, core->reg_base + CTRL_INIT);
writel(0x1, core->reg_base + CPU_CS_SCIACMDARG3);
while (!ctrl_status && count < max_tries) {
ctrl_status = readl(core->reg_base + CTRL_STATUS);
if ((ctrl_status & CTRL_ERROR_STATUS__M) == 0x4) {
dev_err(core->dev, "invalid setting for uc_region\n");
break;
}
usleep_range(50, 100);
count++;
}
if (count >= max_tries) {
dev_err(core->dev, "error booting up iris firmware\n");
return -ETIME;
}
writel(HOST2XTENSA_INTR_ENABLE, core->reg_base + CPU_CS_H2XSOFTINTEN);
writel(0x0, core->reg_base + CPU_CS_X2RPMH);
return 0;
}
void iris_vpu_raise_interrupt(struct iris_core *core)
{
writel(1 << CPU_IC_SOFTINT_H2A_SHFT, core->reg_base + CPU_IC_SOFTINT);
}
void iris_vpu_clear_interrupt(struct iris_core *core)
{
u32 intr_status, mask;
intr_status = readl(core->reg_base + WRAPPER_INTR_STATUS);
mask = (WRAPPER_INTR_STATUS_A2H_BMSK |
WRAPPER_INTR_STATUS_A2HWD_BMSK |
CTRL_INIT_IDLE_MSG_BMSK);
if (intr_status & mask)
core->intr_status |= intr_status;
writel(CLEAR_XTENSA2HOST_INTR, core->reg_base + CPU_CS_A2HSOFTINTCLR);
}
int iris_vpu_watchdog(struct iris_core *core, u32 intr_status)
{
if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK) {
dev_err(core->dev, "received watchdog interrupt\n");
return -ETIME;
}
return 0;
}
int iris_vpu_prepare_pc(struct iris_core *core)
{
u32 wfi_status, idle_status, pc_ready;
u32 ctrl_status, val = 0;
int ret;
ctrl_status = readl(core->reg_base + CTRL_STATUS);
pc_ready = ctrl_status & CTRL_STATUS_PC_READY;
idle_status = ctrl_status & BIT(30);
if (pc_ready)
return 0;
wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
wfi_status &= BIT(0);
if (!wfi_status || !idle_status)
goto skip_power_off;
ret = core->hfi_sys_ops->sys_pc_prep(core);
if (ret)
goto skip_power_off;
ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val,
val & CTRL_STATUS_PC_READY, 250, 2500);
if (ret)
goto skip_power_off;
ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS,
val, val & BIT(0), 250, 2500);
if (ret)
goto skip_power_off;
return 0;
skip_power_off:
ctrl_status = readl(core->reg_base + CTRL_STATUS);
wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
wfi_status &= BIT(0);
dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n",
wfi_status, idle_status, pc_ready, ctrl_status);
return -EAGAIN;
}
int iris_vpu_power_off_controller(struct iris_core *core)
{
u32 val = 0;
int ret;
writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
if (!core->iris_platform_data->no_aon) {
writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
val, val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
}
writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
val, val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
val, val == 0, 200, 2000);
if (ret)
goto disable_power;
writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
disable_power:
iris_disable_unprepare_clock(core, IRIS_AHB_CLK);
iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
return 0;
}
void iris_vpu_power_off_hw(struct iris_core *core)
{
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
}
void iris_vpu_power_off(struct iris_core *core)
{
iris_opp_set_rate(core->dev, 0);
core->iris_platform_data->vpu_ops->power_off_hw(core);
core->iris_platform_data->vpu_ops->power_off_controller(core);
iris_unset_icc_bw(core);
if (!iris_vpu_watchdog(core, core->intr_status))
disable_irq_nosync(core->irq);
}
int iris_vpu_power_on_controller(struct iris_core *core)
{
u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
int ret;
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
if (ret)
return ret;
ret = reset_control_bulk_reset(rst_tbl_size, core->resets);
if (ret)
goto err_disable_power;
ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
if (ret)
goto err_disable_power;
ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
if (ret)
goto err_disable_axi_clock;
ret = iris_prepare_enable_clock(core, IRIS_AHB_CLK);
if (ret && ret != -ENOENT)
goto err_disable_ctrl_clock;
return 0;
err_disable_ctrl_clock:
iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
err_disable_axi_clock:
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
return ret;
}
int iris_vpu_power_on_hw(struct iris_core *core)
{
int ret;
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
if (ret)
return ret;
ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
if (ret)
goto err_disable_power;
ret = iris_prepare_enable_clock(core, IRIS_HW_AHB_CLK);
if (ret && ret != -ENOENT)
goto err_disable_hw_clock;
ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK);
if (ret && ret != -ENOENT)
goto err_disable_hw_ahb_clock;
return 0;
err_disable_hw_ahb_clock:
iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
err_disable_hw_clock:
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
return ret;
}
int iris_vpu_set_hwmode(struct iris_core *core)
{
return dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true);
}
int iris_vpu_switch_to_hwmode(struct iris_core *core)
{
return core->iris_platform_data->vpu_ops->set_hwmode(core);
}
int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core)
{
u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
bool handshake_done, handshake_busy;
u32 count = 0, val = 0;
int ret;
writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
val, val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
/* Retry up to 1000 times as recommended by hardware documentation */
do {
/* set MNoC to low power */
writel(REQ_POWER_DOWN_PREP, core->reg_base +
AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
usleep_range(10, 20);
val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS);
handshake_done = val & NOC_LPI_STATUS_DONE;
handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE);
if (handshake_done || !handshake_busy)
break;
writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
usleep_range(10, 20);
} while (++count < 1000);
if (!handshake_done && handshake_busy)
dev_err(core->dev, "LPI handshake timeout\n");
ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS,
val, val & BIT(0), 200, 2000);
if (ret)
goto disable_power;
writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL);
writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
val, val == 0, 200, 2000);
disable_power:
iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
return 0;
}
int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core)
{
int ret;
ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
if (ret)
return ret;
ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK);
if (ret)
goto err_disable_power;
ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK);
if (ret)
goto err_disable_axi1_clk;
ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
if (ret)
goto err_disable_ctrl_free_clk;
return 0;
err_disable_ctrl_free_clk:
iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
err_disable_axi1_clk:
iris_disable_unprepare_clock(core, IRIS_AXI1_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
return ret;
}
void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core)
{
writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0);
}
u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
struct v4l2_format *inp_f = inst->fmt_src;
u32 height, width, mbs_per_second, mbpf;
u64 fw_cycles, fw_vpp_cycles;
u64 vsp_cycles, vpp_cycles;
u32 fps = inst->frame_rate;
width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
mbpf = NUM_MBS_PER_FRAME(height, width);
mbs_per_second = mbpf * fps;
fw_cycles = fps * caps->mb_cycles_fw;
fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp;
vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value);
/* 21 / 20 is minimum overhead factor */
vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles);
/* 1.059 is multi-pipe overhead */
if (inst->fw_caps[PIPE].value > 1)
vpp_cycles += div_u64(vpp_cycles * 59, 1000);
/* 1.05 is VPP FW overhead */
if (inst->fw_caps[STAGE].value == STAGE_2)
vpp_cycles += div_u64(vpp_cycles * 5, 100);
vsp_cycles = fps * data_size * 8;
vsp_cycles = div_u64(vsp_cycles, 2);
/* VSP FW overhead 1.05 */
vsp_cycles = div_u64(vsp_cycles * 21, 20);
if (inst->fw_caps[STAGE].value == STAGE_1)
vsp_cycles = vsp_cycles * 3;
return max3(vpp_cycles, vsp_cycles, fw_cycles);
}
int iris_vpu_power_on(struct iris_core *core)
{
u32 freq;
int ret;
ret = iris_set_icc_bw(core, INT_MAX);
if (ret)
goto err;
ret = core->iris_platform_data->vpu_ops->power_on_controller(core);
if (ret)
goto err_unvote_icc;
ret = core->iris_platform_data->vpu_ops->power_on_hw(core);
if (ret)
goto err_power_off_ctrl;
freq = core->power.clk_freq ? core->power.clk_freq :
(u32)ULONG_MAX;
iris_opp_set_rate(core->dev, freq);
iris_vpu_set_preset_registers(core);
iris_vpu_interrupt_init(core);
core->intr_status = 0;
enable_irq(core->irq);
return 0;
err_power_off_ctrl:
core->iris_platform_data->vpu_ops->power_off_controller(core);
err_unvote_icc:
iris_unset_icc_bw(core);
err:
dev_err(core->dev, "power on failed\n");
return ret;
}
void iris_vpu_set_preset_registers(struct iris_core *core)
{
writel(0x0, core->reg_base + 0xb0088);
}
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VPU_COMMON_H__
#define __IRIS_VPU_COMMON_H__
struct iris_core;
extern const struct vpu_ops iris_vpu2_ops;
extern const struct vpu_ops iris_vpu3_ops;
extern const struct vpu_ops iris_vpu33_ops;
extern const struct vpu_ops iris_vpu35_ops;
extern const struct vpu_ops iris_vpu4x_ops;
struct vpu_ops {
void (*power_off_hw)(struct iris_core *core);
int (*power_on_hw)(struct iris_core *core);
int (*power_off_controller)(struct iris_core *core);
int (*power_on_controller)(struct iris_core *core);
void (*program_bootup_registers)(struct iris_core *core);
u64 (*calc_freq)(struct iris_inst *inst, size_t data_size);
int (*set_hwmode)(struct iris_core *core);
};
int iris_vpu_boot_firmware(struct iris_core *core);
void iris_vpu_raise_interrupt(struct iris_core *core);
void iris_vpu_clear_interrupt(struct iris_core *core);
int iris_vpu_watchdog(struct iris_core *core, u32 intr_status);
int iris_vpu_prepare_pc(struct iris_core *core);
int iris_vpu_power_on_controller(struct iris_core *core);
int iris_vpu_power_on_hw(struct iris_core *core);
int iris_vpu_set_hwmode(struct iris_core *core);
int iris_vpu_switch_to_hwmode(struct iris_core *core);
int iris_vpu_power_on(struct iris_core *core);
int iris_vpu_power_off_controller(struct iris_core *core);
void iris_vpu_power_off_hw(struct iris_core *core);
void iris_vpu_power_off(struct iris_core *core);
int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core);
int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core);
void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core);
u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size);
void iris_vpu_set_preset_registers(struct iris_core *core);
#endif
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_VPU_REGISTER_DEFINES_H__
#define __IRIS_VPU_REGISTER_DEFINES_H__
#define VCODEC_BASE_OFFS 0x00000000
#define AON_MVP_NOC_RESET 0x0001F000
#define CPU_BASE_OFFS 0x000A0000
#define WRAPPER_BASE_OFFS 0x000B0000
#define WRAPPER_TZ_BASE_OFFS 0x000C0000
#define AON_BASE_OFFS 0x000E0000
#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
#define CPU_CS_BASE_OFFS (CPU_BASE_OFFS)
#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS)
#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C)
#define CLEAR_XTENSA2HOST_INTR BIT(0)
#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148)
#define HOST2XTENSA_INTR_ENABLE BIT(0)
#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150)
#define CPU_IC_SOFTINT_H2A_SHFT 0x0
#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
#define CORE_BRIDGE_SW_RESET BIT(0)
#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
#define MSK_CORE_POWER_ON BIT(1)
#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C)
#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3)
#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2)
#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10)
#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3)
#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2)
#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
#define REQ_POWER_DOWN_PREP BIT(0)
#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
#define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */
#define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */
#define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */
#define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78)
#define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80)
#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
#define CORE_CLK_RUN 0x0
#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
#define CTL_AXI_CLK_HALT BIT(0)
#define CTL_CLK_HALT BIT(1)
#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
#define RESET_HIGH BIT(0)
#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
#endif