[PATCH v2 1/9] media: qcom: camss: Introduce CPU latency QoS during streaming

From: bryan . odonoghue

Date: Mon Nov 02 2026 - 09:05:13 EST


From: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>

On a quiescent system - for example the X1 CRD booted to a simple ramdisk
rootfs during development of V4L2_EVENT_FRAME_SYNC it was observed that
interrupt for buffer done could be delayed until after the SOF for the next
frame.

Fiddling about with the interrupt bits, clearing them and changing the
logic of V4L2_EVENT_FRAME_SYNC to buffer completion didn't work and besides
"felt wrong".

Tracing it looks like this:
<idle>-0 [000] d.h1. 36.374380: csid_isr: rdi0 sof=1 done=0
<idle>-0 [000] d.h1. 36.406601: csid_isr: rdi0 sof=1 done=1
<idle>-0 [000] d.h1. 36.439910: csid_isr: rdi0 sof=1 done=1
<idle>-0 [000] d.h1. 36.473245: csid_isr: rdi0 sof=1 done=1
<idle>-0 [000] d.h1. 36.504786: csid_isr: rdi0 sof=0 done=1
<idle>-0 [000] d.h1. 36.506350: csid_isr: rdi0 sof=1 done=0

Tracing with /dev/cpu_dma_latency = 0 looks like this:
<idle>-0 [000] d.h1. 556.149115: csid_isr: rdi0 sof=1 done=0
<idle>-0 [000] d.h1. 556.181338: csid_isr: rdi0 sof=0 done=1
<idle>-0 [000] d.h1. 556.182880: csid_isr: rdi0 sof=1 done=0
<idle>-0 [000] d.h1. 556.214643: csid_isr: rdi0 sof=0 done=1
<idle>-0 [000] d.h1. 556.216185: csid_isr: rdi0 sof=1 done=0

The second is the pattern you expect CSI SOF followed by data.

We will require SOF to track and understand which data-frame is correlated
to which camera stastics and parameters frame. On an ordinary system with
several things pinging off at once, WiFi, display, keyboard you will be
unlikely to hit deep idle and hence unlikely to see the long exit. However
we should design for the case where the system is otherwise quiescent with
only the camera streaming.

Choosing a request of 300us - 1000us seemed to work fine but cutting the
margin finer, similar to the value on IPU6.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
---
drivers/media/platform/qcom/camss/camss.c | 11 ++++++++++-
drivers/media/platform/qcom/camss/camss.h | 2 ++
2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index c54185c93a8a9..52bb19f5fbf3b 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -5834,6 +5834,8 @@ static const struct of_device_id camss_dt_match[] = {

MODULE_DEVICE_TABLE(of, camss_dt_match);

+#define CAMSS_CPU_LATENCY_US 300
+
static int __maybe_unused camss_runtime_suspend(struct device *dev)
{
struct camss *camss = dev_get_drvdata(dev);
@@ -5846,6 +5848,8 @@ static int __maybe_unused camss_runtime_suspend(struct device *dev)
return ret;
}

+ cpu_latency_qos_remove_request(&camss->cpu_latency);
+
#if IS_ENABLED(CONFIG_PM_CLK)
return pm_clk_suspend(dev);
#else
@@ -5866,12 +5870,17 @@ static int __maybe_unused camss_runtime_resume(struct device *dev)
return ret;
#endif

+ cpu_latency_qos_add_request(&camss->cpu_latency,
+ CAMSS_CPU_LATENCY_US);
+
for (i = 0; i < camss->res->icc_path_num; i++) {
ret = icc_set_bw(camss->icc_path[i],
icc_res[i].icc_bw_tbl.avg,
icc_res[i].icc_bw_tbl.peak);
- if (ret)
+ if (ret) {
+ cpu_latency_qos_remove_request(&camss->cpu_latency);
return ret;
+ }
}

return 0;
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 7dc2fd9f03e0d..7564a7c46fa24 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -11,6 +11,7 @@
#define QC_MSM_CAMSS_H

#include <linux/device.h>
+#include <linux/pm_qos.h>
#include <linux/types.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
@@ -141,6 +142,7 @@ struct camss {
struct icc_path *icc_path[ICC_SM8250_COUNT];
const struct camss_resources *res;
bool legacy_phy;
+ struct pm_qos_request cpu_latency;
};

struct camss_camera_interface {

--
2.56.0