[PATCH 62/74] media: qcom: camss: vfe: Size the outputs for the PIX line and set up each one
From: bod
Date: Mon Oct 05 2026 - 13:45:33 EST
From: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
The PIX line is described with five outputs, one per source or sink pad
beyond the sink, but struct vfe_line holds MSM_VFE_NUM_OUTPUTS = 1. The
descriptor writes and the enable loop for outputs 1-4 land past the end
of the line.
- size output[] for every pad but the sink
- record the pad each output serves, as vfe_add_output() was passed it
- initialise every output: back pointer, completions, pending list
- start secondary outputs only while their video node streams and they
have write masters; the first output still starts the line
- stop and release every output that ran
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
---
drivers/media/platform/qcom/camss/camss-vfe.c | 70 +++++++++++++++++++--------
drivers/media/platform/qcom/camss/camss-vfe.h | 3 +-
2 files changed, 52 insertions(+), 21 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 588a355b67ff0..8bcacede0f969 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -679,6 +679,18 @@ void vfe_buf_done(struct vfe_device *vfe, int wm, struct camss_buf_done_info *bu
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
+/*
+ * The first output starts the line. The others run only while their video
+ * node is streaming, and only if they write to memory.
+ */
+static bool vfe_output_wanted(struct vfe_line *line, struct vfe_output *output)
+{
+ if (output == &line->output[0])
+ return true;
+
+ return output->wm_num && vb2_is_streaming(&output->video_out.vb2_q);
+}
+
static int vfe_enable_one_output(struct vfe_line *line, struct vfe_output *output)
{
struct vfe_device *vfe = to_vfe(line);
@@ -747,6 +759,9 @@ int vfe_enable_output_v2(struct vfe_line *line)
ops->reg_update_clear(vfe, line->id);
for (i = 0; i < line->num_outputs; i++) {
+ if (!vfe_output_wanted(line, &line->output[i]))
+ continue;
+
ret = vfe_enable_one_output(line, &line->output[i]);
if (ret)
break;
@@ -900,15 +915,17 @@ int vfe_reset(struct vfe_device *vfe)
static void vfe_init_outputs(struct vfe_device *vfe)
{
- int i;
+ int i, o;
for (i = 0; i < vfe->res->line_num; i++) {
- struct vfe_output *output = &vfe->line[i].output[0];
+ for (o = 0; o < MSM_VFE_NUM_OUTPUTS; o++) {
+ struct vfe_output *output = &vfe->line[i].output[o];
- output->state = VFE_OUTPUT_OFF;
- output->buf[0] = NULL;
- output->buf[1] = NULL;
- INIT_LIST_HEAD(&output->pending_bufs);
+ output->state = VFE_OUTPUT_OFF;
+ output->buf[0] = NULL;
+ output->buf[1] = NULL;
+ INIT_LIST_HEAD(&output->pending_bufs);
+ }
}
}
@@ -996,7 +1013,8 @@ int vfe_put_output(struct vfe_line *line)
for (i = 0; i < output->wm_num; i++)
vfe_release_wm(vfe, output->wm[i].bus_client);
- output->state = VFE_OUTPUT_OFF;
+ for (i = 0; i < line->num_outputs; i++)
+ line->output[i].state = VFE_OUTPUT_OFF;
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
@@ -1006,17 +1024,24 @@ static int vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
const struct vfe_hw_ops *ops = vfe->res->hw_ops;
- struct vfe_output *output = &line->output[0];
unsigned long flags;
- unsigned int i;
+ unsigned int i, o;
spin_lock_irqsave(&vfe->output_lock, flags);
- if (ops->vfe_output_stop)
- ops->vfe_output_stop(vfe, output);
- else
- for (i = 0; i < output->wm_num; i++)
- ops->vfe_wm_stop(vfe, output->wm[i].bus_client, line);
- output->gen2.active_num = 0;
+ for (o = 0; o < line->num_outputs; o++) {
+ struct vfe_output *output = &line->output[o];
+
+ /* the first output is always on; the others only if they streamed */
+ if (o && output->state != VFE_OUTPUT_ON)
+ continue;
+
+ if (ops->vfe_output_stop)
+ ops->vfe_output_stop(vfe, output);
+ else
+ for (i = 0; i < output->wm_num; i++)
+ ops->vfe_wm_stop(vfe, output->wm[i].bus_client, line);
+ output->gen2.active_num = 0;
+ }
spin_unlock_irqrestore(&vfe->output_lock, flags);
return vfe_reset(vfe);
@@ -2175,22 +2200,26 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
struct vfe_line *l = &vfe->line[i];
+ unsigned int o;
+
+ for (o = 0; o < MSM_VFE_NUM_OUTPUTS; o++) {
+ l->output[o].line = l;
+ l->output[o].video_out.camss = camss;
+ init_completion(&l->output[o].sof);
+ init_completion(&l->output[o].reg_update);
+ }
l->output[0].video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- l->output[0].video_out.camss = camss;
l->id = i;
l->num_outputs = 1;
- l->output[0].line = l;
+ l->output[0].pad = MSM_VFE_PAD_SRC;
l->output[0].wm_num = 1;
l->output[0].wm[0].bus_client = l->id;
l->num_pads = MSM_VFE_PAD_NUM_RDI;
- init_completion(&l->output[0].sof);
- init_completion(&l->output[0].reg_update);
-
}
@@ -2500,6 +2529,7 @@ void vfe_add_output(struct vfe_output *output, u8 pad, u8 comp_group,
{
unsigned int i;
+ output->pad = pad;
output->comp_group = comp_group;
output->wm_num = wm_num;
for (i = 0; i < wm_num; i++) {
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 2b354d6bcc1f5..11cded2b345eb 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -28,7 +28,7 @@
#define MSM_VFE_PAD_NUM_RDI 2
#define MSM_VFE_PAD_NUM_PIX 6
#define MSM_VFE_MAX_PADS MSM_VFE_PAD_NUM_PIX
-#define MSM_VFE_NUM_OUTPUTS 1
+#define MSM_VFE_NUM_OUTPUTS (MSM_VFE_MAX_PADS - 1) /* every pad but the sink */
#define MSM_VFE_IMAGE_MASTERS_NUM 7
#define MSM_VFE_COMPOSITE_IRQ_NUM 4
@@ -114,6 +114,7 @@ struct vfe_wm {
#define VFE_MAX_WM_PER_OUTPUT 3
struct vfe_output {
+ u8 pad; /* index into line->pads[] this output serves */
u8 comp_group;
u8 wm_num;
struct vfe_wm wm[VFE_MAX_WM_PER_OUTPUT];
--
2.55.0