[PATCH 22/45] media: qcom: camss: add IFE statistics output
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Tue Oct 06 2026 - 18:55:23 EST
From: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
Add metadata capture support to CAMSS video nodes.
Expose the SM8250 IFE statistics output through write master 15.
Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
---
.../platform/qcom/camss/camss-ife-pads.c | 34 +++++
drivers/media/platform/qcom/camss/camss-ife.c | 44 ++++++
drivers/media/platform/qcom/camss/camss-ife.h | 3 +
.../media/platform/qcom/camss/camss-video.c | 141 ++++++++++++++++++
.../media/platform/qcom/camss/camss-video.h | 2 +
5 files changed, 224 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
index 926ba96a7603..bd9ac1a91d73 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-pads.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -3,6 +3,7 @@
* Copyright (c) 2023 Qualcomm Technologies, Inc.
*/
+#include <linux/qcom-camss-stats.h>
#include <linux/videodev2.h>
#include <media/v4l2-mediabus.h>
@@ -63,6 +64,11 @@ static const struct camss_format_info ife_vid_pix_fmts_v20x[] = {
{ { 1, 1 } }, { { 2, 3 } }, { 8 } },
};
+static const struct camss_format_info ife_stats_fmts_v20x[] = {
+ { MEDIA_BUS_FMT_METADATA_FIXED, 8, V4L2_META_FMT_QC_IFE_STAT_3A, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+};
+
static const struct ife_pad_limits ife_vid_limits_v20x = {
.min_width = 320,
.min_height = 240,
@@ -72,6 +78,13 @@ static const struct ife_pad_limits ife_vid_limits_v20x = {
.max_v_ratio = 16,
};
+static const struct ife_pad_limits ife_meta_limits_v20x = {
+ .min_width = 16,
+ .min_height = 16,
+ .max_width = 16,
+ .max_height = 16,
+};
+
static const struct ife_pad_limits ife_fd_limits = {
.min_width = 320,
.min_height = 240,
@@ -293,6 +306,27 @@ static const struct ife_pad_desc ife_pad_desc_v20x[] = {
.wm[0] = 8,
.wm[1] = 9,
},
+ {
+ .pad_flags = MEDIA_PAD_FL_SOURCE,
+ .sup_fmts = {
+ .nformats = ARRAY_SIZE(ife_stats_fmts_v20x),
+ .formats = ife_stats_fmts_v20x,
+ },
+ .limits = &ife_meta_limits_v20x,
+ .video_name = "stats",
+ .link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+ .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+ .meta_dataformat = V4L2_META_FMT_QC_IFE_STAT_3A,
+ .meta_buffersize = PAGE_ALIGN(sizeof(struct camss_ife_stats)),
+ .output = CAMSS_IFE_OUTPUT_STATS,
+ .hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+ .packing_fmt = 0xa,
+ .mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+ .rup_group_mask = BIT(6),
+ .comp_group_mask = BIT(12),
+ .num_wms = 1,
+ .wm[0] = 15,
+ },
};
const struct ife_pads_descriptors ife_lite_pads_v20x = {
diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index cf1474e5bce4..f7ec86fb7f02 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -66,6 +66,11 @@ static bool is_stats_type(const struct ife_pad_desc *pad)
return pad->buf_type == V4L2_BUF_TYPE_META_CAPTURE;
}
+static bool is_ife_video_stats(const struct camss_video *video)
+{
+ return video->type == V4L2_BUF_TYPE_META_CAPTURE;
+}
+
static bool is_meta_type(const struct ife_pad_desc *pad)
{
return is_stats_type(pad);
@@ -467,6 +472,17 @@ static int ife_setup_vid_full(struct ife_video_pad *vid_pad,
u8 depth = 0;
int ret;
+ if (is_ife_video_stats(&vid_pad->video)) {
+ wm_cfg[Y_PLANE].mode_config = dsc->mode_config;
+ wm_cfg[Y_PLANE].packing_fmt = dsc->packing_fmt;
+ wm_cfg[Y_PLANE].width = 0;
+ wm_cfg[Y_PLANE].lines = 0;
+ wm_cfg[Y_PLANE].bytes = 1;
+ wm_cfg[Y_PLANE].stride = 1;
+
+ return 0;
+ }
+
wm_cfg[Y_PLANE].mode_config = dsc->mode_config;
wm_cfg[Y_PLANE].packing_fmt = dsc->packing_fmt;
wm_cfg[C_PLANE].mode_config = dsc->mode_config;
@@ -782,6 +798,9 @@ static int ife_pad_try_format(struct ife_device *ife, const struct ife_pad_desc
u32 in_height;
u32 in_width;
+ if (is_meta_type(desc))
+ return -ENOTTY;
+
for (i = 0; i < sup_fmts->nformats; i++)
if (fmt->code == sup_fmts->formats[i].code)
break;
@@ -841,6 +860,11 @@ static int ife_pad_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
pad = &ife->pads[sd_fmt->pad];
+ if (is_meta_type(pad->desc)) {
+ sd_fmt->format = pad->fmt;
+ return 0;
+ }
+
if (sd_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
} else {
@@ -884,6 +908,9 @@ static void ife_update_src_pads_fmt(struct ife_device *ife, struct v4l2_mbus_fra
fi = ife_pad_try_format(ife, pad->desc, &new_fmt);
+ if (vpad && is_ife_video_stats(&vpad->video))
+ vpad = NULL;
+
if (vpad) {
struct v4l2_pix_format_mplane *pix_mp = &vpad->video.active_fmt.fmt.pix_mp;
const struct camss_format_info *info = &pad->desc->sup_fmts.formats[fi];
@@ -913,6 +940,11 @@ static int ife_pad_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
pad = &ife->pads[sd_fmt->pad];
+ if (is_meta_type(pad->desc)) {
+ sd_fmt->format = pad->fmt;
+ return 0;
+ }
+
ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
@@ -959,6 +991,9 @@ static int ife_pad_mbus_enum_code(struct v4l2_subdev *sd, struct v4l2_subdev_sta
pad = &ife->pads[code->pad];
+ if (is_stats_type(pad->desc))
+ return -ENOTTY;
+
if (pad->desc->pad_flags == MEDIA_PAD_FL_SINK ||
pad->desc->hw_pipe == CAMSS_IFE_HW_PIPE_PP) {
if (code->index >= pad->desc->sup_fmts.nformats)
@@ -994,6 +1029,13 @@ static int ife_pad_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_subdev_st
pad = &ife->pads[fse->pad];
+ if (is_meta_type(pad->desc)) {
+ if (is_stats_type(pad->desc))
+ return -ENOTTY;
+
+ return -EINVAL;
+ }
+
for (i = 0; i < pad->desc->sup_fmts.nformats; i++) {
if (pad->desc->sup_fmts.formats[i].code == fse->code)
break;
@@ -1517,6 +1559,8 @@ int ife_register_entities(struct ife_device *ife, struct v4l2_device *v4l2_dev)
pad->vid_pad->video.formats = dsc->sup_fmts.formats;
pad->vid_pad->video.type = dsc->buf_type;
pad->vid_pad->video.ops = &camss_video_ops;
+ pad->vid_pad->video.meta_dataformat = dsc->meta_dataformat;
+ pad->vid_pad->video.meta_buffersize = dsc->meta_buffersize;
pad->vid_pad->video.bpl_alignment = 16;
if (dsc->mode_config == BUSWR_CLIENT_CFG_MODE_LINE_BASED)
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 45fce7e33c4a..168d26d0664b 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -45,6 +45,7 @@ enum camss_ife_output {
CAMSS_IFE_OUTPUT_VID_FULL,
CAMSS_IFE_OUTPUT_DISP_FULL,
CAMSS_IFE_OUTPUT_FD,
+ CAMSS_IFE_OUTPUT_STATS,
};
enum camss_ife_hw_pipe {
@@ -275,6 +276,8 @@ struct ife_pad_desc {
u32 link_flags;
enum v4l2_buf_type buf_type;
+ u32 meta_dataformat;
+ u32 meta_buffersize;
enum camss_ife_output output;
u32 hw_pipe;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index cb9e08e9ff7e..b5d78f84d6a1 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -216,6 +216,9 @@ static int video_check_format(struct camss_video *video)
struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
int ret;
+ if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return 0;
+
sd_pix->pixelformat = pix->pixelformat;
ret = video_get_subdev_format(video, &format);
if (ret < 0)
@@ -421,6 +424,73 @@ static const struct vb2_ops msm_video_vb2_q_ops = {
.unprepare_streaming = video_unprepare_streaming,
};
+static int meta_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ u32 buffersize = video->active_fmt.fmt.meta.buffersize;
+
+ if (*num_planes) {
+ if (*num_planes != 1 || sizes[0] < buffersize)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = 1;
+ sizes[0] = buffersize;
+ q->min_queued_buffers = 1;
+
+ return 0;
+}
+
+static int meta_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, vb);
+ struct sg_table *sgt;
+
+ sgt = vb2_dma_sg_plane_desc(vb, 0);
+ if (!sgt)
+ return -EFAULT;
+
+ buffer->addr[0] = sg_dma_address(sgt->sgl);
+
+ return 0;
+}
+
+static int meta_buf_prepare(struct vb2_buffer *vb)
+{
+ struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE) {
+ dev_err(video->camss->dev, "%s field isn't supported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (vb2_plane_size(vb, 0) < video->active_fmt.fmt.meta.buffersize)
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, video->active_fmt.fmt.meta.buffersize);
+
+ return 0;
+}
+
+static const struct vb2_ops msm_meta_vb2_q_ops = {
+ .queue_setup = meta_queue_setup,
+ .buf_init = meta_buf_init,
+ .buf_prepare = meta_buf_prepare,
+ .buf_queue = video_buf_queue,
+ .prepare_streaming = video_prepare_streaming,
+ .start_streaming = video_start_streaming,
+ .stop_streaming = video_stop_streaming,
+ .unprepare_streaming = video_unprepare_streaming,
+};
+
/* -----------------------------------------------------------------------------
* V4L2 ioctls
*/
@@ -693,6 +763,52 @@ static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
.vidioc_s_input = video_s_input,
};
+static int meta_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct camss_video *video = video_drvdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = video->active_fmt.fmt.meta.dataformat;
+ meta->buffersize = video->active_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int meta_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+ struct camss_video *video = video_drvdata(file);
+
+ if (f->index)
+ return -EINVAL;
+
+ memset(&f->reserved, 0, sizeof(f->reserved));
+
+ f->pixelformat = video->active_fmt.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops msm_meta_ioctl_ops = {
+ .vidioc_querycap = video_querycap,
+ .vidioc_enum_fmt_meta_cap = meta_enum_fmt,
+ .vidioc_g_fmt_meta_cap = meta_try_fmt,
+ .vidioc_s_fmt_meta_cap = meta_try_fmt,
+ .vidioc_try_fmt_meta_cap = meta_try_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
/* -----------------------------------------------------------------------------
* V4L2 file operations
*/
@@ -744,6 +860,18 @@ static int msm_video_init_format(struct camss_video *video)
},
};
+ if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (!video->meta_dataformat || !video->meta_buffersize)
+ return -EINVAL;
+
+ format.type = video->type;
+ format.fmt.meta.dataformat = video->meta_dataformat;
+ format.fmt.meta.buffersize = video->meta_buffersize;
+ video->active_fmt = format;
+
+ return 0;
+ }
+
ret = __video_try_fmt(video, &format);
if (ret < 0)
return ret;
@@ -787,6 +915,14 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
q->buf_struct_size = sizeof(struct camss_buffer);
q->dev = video->camss->dev;
q->lock = &video->q_lock;
+
+ if (video->type == V4L2_BUF_TYPE_META_CAPTURE) {
+ q->ops = &msm_meta_vb2_q_ops;
+ q->io_modes = VB2_DMABUF | VB2_MMAP;
+ } else if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ dev_err(v4l2_dev->dev, "Invalid format type\n");
+ goto error_vb2_init;
+ }
ret = vb2_queue_init(q);
if (ret < 0) {
dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
@@ -813,6 +949,11 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
| V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
vdev->ioctl_ops = &msm_vid_ioctl_ops;
+ if (video->type == V4L2_BUF_TYPE_META_CAPTURE) {
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_IO_MC;
+ vdev->ioctl_ops = &msm_meta_ioctl_ops;
+ }
vdev->release = msm_video_release;
vdev->v4l2_dev = v4l2_dev;
vdev->vfl_dir = VFL_DIR_RX;
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index f62a72c29a5a..cfee619973ef 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -48,6 +48,8 @@ struct camss_video {
struct mutex q_lock;
unsigned int bpl_alignment;
unsigned int line_based;
+ u32 meta_dataformat;
+ u32 meta_buffersize;
const struct camss_format_info *formats;
unsigned int nformats;
};
--
2.34.1