summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorDaniel Almeida <daniel.almeida@collabora.com>2023-03-06 19:18:50 +0300
committerMauro Carvalho Chehab <mchehab@kernel.org>2023-06-09 18:13:01 +0300
commit9de30f579980b498606a9c2440b73ae3b670771b (patch)
treecb0e031facf39a89b65b0653cb7355a7b43965b7 /drivers/media
parent2104793233c2b803107fa39baa9fa149648adce7 (diff)
downloadlinux-9de30f579980b498606a9c2440b73ae3b670771b.tar.xz
media: Add AV1 uAPI
This patch adds the AOMedia Video 1 (AV1) kernel uAPI. This design is based on currently available AV1 API implementations and aims to support the development of AV1 stateless video codecs on Linux. Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com> Co-developed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c258
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c61
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c1
3 files changed, 320 insertions, 0 deletions
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 29169170880a..9fd37e94db17 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -350,6 +350,19 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
pr_cont("HEVC_DECODE_PARAMS");
break;
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ pr_cont("AV1_SEQUENCE");
+ break;
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ pr_cont("AV1_TILE_GROUP_ENTRY");
+ break;
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ pr_cont("AV1_FRAME");
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ pr_cont("AV1_FILM_GRAIN");
+ break;
+
default:
pr_cont("unknown type %d", ctrl->type);
break;
@@ -547,6 +560,231 @@ validate_vp9_frame(struct v4l2_ctrl_vp9_frame *frame)
return 0;
}
+static int validate_av1_quantization(struct v4l2_av1_quantization *q)
+{
+ if (q->flags > GENMASK(2, 0))
+ return -EINVAL;
+
+ if (q->delta_q_y_dc < -64 || q->delta_q_y_dc > 63 ||
+ q->delta_q_u_dc < -64 || q->delta_q_u_dc > 63 ||
+ q->delta_q_v_dc < -64 || q->delta_q_v_dc > 63 ||
+ q->delta_q_u_ac < -64 || q->delta_q_u_ac > 63 ||
+ q->delta_q_v_ac < -64 || q->delta_q_v_ac > 63 ||
+ q->delta_q_res > GENMASK(1, 0))
+ return -EINVAL;
+
+ if (q->qm_y > GENMASK(3, 0) ||
+ q->qm_u > GENMASK(3, 0) ||
+ q->qm_v > GENMASK(3, 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_segmentation(struct v4l2_av1_segmentation *s)
+{
+ u32 i;
+ u32 j;
+
+ if (s->flags > GENMASK(4, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(s->feature_data); i++) {
+ static const int segmentation_feature_signed[] = { 1, 1, 1, 1, 1, 0, 0, 0 };
+ static const int segmentation_feature_max[] = { 255, 63, 63, 63, 63, 7, 0, 0};
+
+ for (j = 0; j < ARRAY_SIZE(s->feature_data[j]); j++) {
+ s32 limit = segmentation_feature_max[j];
+
+ if (segmentation_feature_signed[j]) {
+ if (s->feature_data[i][j] < -limit ||
+ s->feature_data[i][j] > limit)
+ return -EINVAL;
+ } else {
+ if (s->feature_data[i][j] < 0 || s->feature_data[i][j] > limit)
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int validate_av1_loop_filter(struct v4l2_av1_loop_filter *lf)
+{
+ u32 i;
+
+ if (lf->flags > GENMASK(3, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(lf->level); i++) {
+ if (lf->level[i] > GENMASK(5, 0))
+ return -EINVAL;
+ }
+
+ if (lf->sharpness > GENMASK(2, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) {
+ if (lf->ref_deltas[i] < -64 || lf->ref_deltas[i] > 63)
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) {
+ if (lf->mode_deltas[i] < -64 || lf->mode_deltas[i] > 63)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_av1_cdef(struct v4l2_av1_cdef *cdef)
+{
+ u32 i;
+
+ if (cdef->damping_minus_3 > GENMASK(1, 0) ||
+ cdef->bits > GENMASK(1, 0))
+ return -EINVAL;
+
+ for (i = 0; i < 1 << cdef->bits; i++) {
+ if (cdef->y_pri_strength[i] > GENMASK(3, 0) ||
+ cdef->y_sec_strength[i] > 4 ||
+ cdef->uv_pri_strength[i] > GENMASK(3, 0) ||
+ cdef->uv_sec_strength[i] > 4)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_av1_loop_restauration(struct v4l2_av1_loop_restoration *lr)
+{
+ if (lr->lr_unit_shift > 3 || lr->lr_uv_shift > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_film_grain(struct v4l2_ctrl_av1_film_grain *fg)
+{
+ u32 i;
+
+ if (fg->flags > GENMASK(4, 0))
+ return -EINVAL;
+
+ if (fg->film_grain_params_ref_idx > GENMASK(2, 0) ||
+ fg->num_y_points > 14 ||
+ fg->num_cb_points > 10 ||
+ fg->num_cr_points > GENMASK(3, 0) ||
+ fg->grain_scaling_minus_8 > GENMASK(1, 0) ||
+ fg->ar_coeff_lag > GENMASK(1, 0) ||
+ fg->ar_coeff_shift_minus_6 > GENMASK(1, 0) ||
+ fg->grain_scale_shift > GENMASK(1, 0))
+ return -EINVAL;
+
+ if (!(fg->flags & V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN))
+ return 0;
+
+ for (i = 1; i < fg->num_y_points; i++)
+ if (fg->point_y_value[i] <= fg->point_y_value[i - 1])
+ return -EINVAL;
+
+ for (i = 1; i < fg->num_cb_points; i++)
+ if (fg->point_cb_value[i] <= fg->point_cb_value[i - 1])
+ return -EINVAL;
+
+ for (i = 1; i < fg->num_cr_points; i++)
+ if (fg->point_cr_value[i] <= fg->point_cr_value[i - 1])
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
+{
+ int ret = 0;
+
+ ret = validate_av1_quantization(&f->quantization);
+ if (ret)
+ return ret;
+ ret = validate_av1_segmentation(&f->segmentation);
+ if (ret)
+ return ret;
+ ret = validate_av1_loop_filter(&f->loop_filter);
+ if (ret)
+ return ret;
+ ret = validate_av1_cdef(&f->cdef);
+ if (ret)
+ return ret;
+ ret = validate_av1_loop_restauration(&f->loop_restoration);
+ if (ret)
+ return ret;
+
+ if (f->flags &
+ ~(V4L2_AV1_FRAME_FLAG_SHOW_FRAME |
+ V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME |
+ V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE |
+ V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE |
+ V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS |
+ V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV |
+ V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC |
+ V4L2_AV1_FRAME_FLAG_USE_SUPERRES |
+ V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV |
+ V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE |
+ V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS |
+ V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF |
+ V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION |
+ V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT |
+ V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET |
+ V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED |
+ V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT |
+ V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE |
+ V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT |
+ V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING))
+ return -EINVAL;
+
+ if (f->superres_denom > GENMASK(2, 0) + 9)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_sequence(struct v4l2_ctrl_av1_sequence *s)
+{
+ if (s->flags &
+ ~(V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE |
+ V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION |
+ V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME |
+ V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y |
+ V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT |
+ V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q))
+ return -EINVAL;
+
+ if (s->seq_profile == 1 && s->flags & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME)
+ return -EINVAL;
+
+ /* reserved */
+ if (s->seq_profile > 2)
+ return -EINVAL;
+
+ /* TODO: PROFILES */
+ return 0;
+}
+
/*
* Compound controls validation requires setting unused fields/flags to zero
* in order to properly detect unchanged controls with v4l2_ctrl_type_op_equal's
@@ -911,6 +1149,14 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
case V4L2_CTRL_TYPE_VP9_FRAME:
return validate_vp9_frame(p);
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ return validate_av1_frame(p);
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ return validate_av1_sequence(p);
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ return validate_av1_film_grain(p);
case V4L2_CTRL_TYPE_AREA:
area = p;
@@ -1602,6 +1848,18 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
case V4L2_CTRL_TYPE_VP9_FRAME:
elem_size = sizeof(struct v4l2_ctrl_vp9_frame);
break;
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ elem_size = sizeof(struct v4l2_ctrl_av1_sequence);
+ break;
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ elem_size = sizeof(struct v4l2_ctrl_av1_tile_group_entry);
+ break;
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ elem_size = sizeof(struct v4l2_ctrl_av1_frame);
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ elem_size = sizeof(struct v4l2_ctrl_av1_film_grain);
+ break;
case V4L2_CTRL_TYPE_AREA:
elem_size = sizeof(struct v4l2_area);
break;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index 564fedee2c88..8696eb1cdd61 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -499,6 +499,40 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
NULL,
};
+ static const char * const av1_profile[] = {
+ "Main",
+ "High",
+ "Professional",
+ NULL,
+ };
+ static const char * const av1_level[] = {
+ "2.0",
+ "2.1",
+ "2.2",
+ "2.3",
+ "3.0",
+ "3.1",
+ "3.2",
+ "3.3",
+ "4.0",
+ "4.1",
+ "4.2",
+ "4.3",
+ "5.0",
+ "5.1",
+ "5.2",
+ "5.3",
+ "6.0",
+ "6.1",
+ "6.2",
+ "6.3",
+ "7.0",
+ "7.1",
+ "7.2",
+ "7.3",
+ NULL,
+ };
+
static const char * const hevc_profile[] = {
"Main",
"Main Still Picture",
@@ -704,6 +738,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return hevc_tier;
case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
return hevc_loop_filter_mode;
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE:
+ return av1_profile;
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL:
+ return av1_level;
case V4L2_CID_STATELESS_HEVC_DECODE_MODE:
return hevc_decode_mode;
case V4L2_CID_STATELESS_HEVC_START_CODE:
@@ -1004,6 +1042,10 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame";
case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR";
+ /* AV1 controls */
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE: return "AV1 Profile";
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL: return "AV1 Level";
+
/* CAMERA controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
@@ -1190,6 +1232,10 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return "HEVC Decode Mode";
case V4L2_CID_STATELESS_HEVC_START_CODE: return "HEVC Start Code";
case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: return "HEVC Entry Point Offsets";
+ case V4L2_CID_STATELESS_AV1_SEQUENCE: return "AV1 Sequence Parameters";
+ case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY: return "AV1 Tile Group Entry";
+ case V4L2_CID_STATELESS_AV1_FRAME: return "AV1 Frame Parameters";
+ case V4L2_CID_STATELESS_AV1_FILM_GRAIN: return "AV1 Film Grain";
/* Colorimetry controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1365,6 +1411,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL:
case V4L2_CID_STATELESS_HEVC_DECODE_MODE:
case V4L2_CID_STATELESS_HEVC_START_CODE:
case V4L2_CID_STATELESS_H264_DECODE_MODE:
@@ -1531,6 +1579,19 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_STATELESS_VP9_FRAME:
*type = V4L2_CTRL_TYPE_VP9_FRAME;
break;
+ case V4L2_CID_STATELESS_AV1_SEQUENCE:
+ *type = V4L2_CTRL_TYPE_AV1_SEQUENCE;
+ break;
+ case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY:
+ *type = V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
+ break;
+ case V4L2_CID_STATELESS_AV1_FRAME:
+ *type = V4L2_CTRL_TYPE_AV1_FRAME;
+ break;
+ case V4L2_CID_STATELESS_AV1_FILM_GRAIN:
+ *type = V4L2_CTRL_TYPE_AV1_FILM_GRAIN;
+ break;
case V4L2_CID_UNIT_CELL_SIZE:
*type = V4L2_CTRL_TYPE_AREA;
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index a858acea6547..41d7a8e3712a 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1506,6 +1506,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_QC08C: descr = "QCOM Compressed 8-bit Format"; break;
case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break;
+ case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
default:
if (fmt->description[0])
return;