[PATCH 1/4] media: wave5: handle decoder runtime resume failures

From: Jiale Yao

Date: Sat Oct 03 2026 - 04:09:31 EST


Several decoder callbacks continue into firmware commands after
pm_runtime_resume_and_get() fails. The runtime resume callback can fail
while enabling the VPU clocks, so those commands may access registers
while the device is unavailable. The matching runtime PM puts can also
be issued without a reference.

Check each resume result. Propagate errors from callbacks that can
return them, complete buffers or jobs from void callbacks, and avoid
firmware access and unmatched puts on failure.

Fixes: 2092b3833487 ("media: chips-media: wave5: Support runtime suspend/resume")
Fixes: cbb9c0d50e47 ("media: chips-media: wave5: Fix SError of kernel panic when closed")
Fixes: a52e6f7923c1 ("media: chips-media: wave5: Resume device before setting EOS flag")
Signed-off-by: Jiale Yao <yaojiale02@xxxxxxx>
---
.../chips-media/wave5/wave5-vpu-dec.c | 55 +++++++++++++++++--
1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index 6564cf3ec739..467c68931e8d 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -829,7 +829,10 @@ static int wave5_vpu_dec_stop(struct vpu_instance *inst)
* accesses VPU registers via send_firmware_command(), so the
* device must be resumed first to avoid an asynchronous SError.
*/
- pm_runtime_resume_and_get(inst->dev->dev);
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0)
+ return ret;
+
ret = wave5_vpu_dec_set_eos_on_firmware(inst);
pm_runtime_put_autosuspend(inst->dev->dev);
if (ret)
@@ -1302,8 +1305,14 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return;
+ }

- pm_runtime_resume_and_get(inst->dev->dev);
vbuf->sequence = inst->queued_dst_buf_num++;

if (inst->state == VPU_INST_STATE_PIC_RUN) {
@@ -1386,7 +1395,11 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
int ret = 0;

dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
- pm_runtime_resume_and_get(inst->dev->dev);
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ wave5_return_bufs(q, VB2_BUF_STATE_QUEUED);
+ return ret;
+ }

v4l2_m2m_update_start_streaming_state(m2m_ctx, q);

@@ -1550,9 +1563,28 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
struct vpu_instance *inst = vb2_get_drv_priv(q);
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
unsigned long timeout;
+ int ret;

dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
- pm_runtime_resume_and_get(inst->dev->dev);
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ struct vpu_src_buffer *vpu_buf;
+
+ inst->retry = false;
+ inst->queuing_num = 0;
+ while ((vpu_buf = inst_src_buf_remove(inst)) != NULL)
+ ;
+ inst->eos = false;
+ }
+
+ v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
+ wave5_return_bufs(q, VB2_BUF_STATE_ERROR);
+ inst->empty_queue = false;
+ inst->sent_eos = false;
+ return;
+ }
+
inst->empty_queue = true;

timeout = jiffies + msecs_to_jiffies(VPU_DEC_STOP_TIMEOUT);
@@ -1669,7 +1701,13 @@ static void wave5_vpu_dec_device_run(void *priv)
bool cmd_issued = false;

dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
- pm_runtime_resume_and_get(inst->dev->dev);
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ dev_err(inst->dev->dev, "Failed to resume VPU: %d\n", ret);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ return;
+ }
+
if (!inst->retry) {
ret = fill_ringbuffer(inst);
if (ret < 0) {
@@ -1812,7 +1850,11 @@ static void wave5_vpu_dec_job_abort(void *priv)
* device must be resumed first; otherwise the register access faults
* with an asynchronous SError.
*/
- pm_runtime_resume_and_get(inst->dev->dev);
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ dev_warn(inst->dev->dev, "Failed to resume VPU: %d\n", ret);
+ goto finish_job;
+ }

ret = wave5_vpu_dec_set_eos_on_firmware(inst);
if (ret)
@@ -1821,6 +1863,7 @@ static void wave5_vpu_dec_job_abort(void *priv)

pm_runtime_put_autosuspend(inst->dev->dev);

+finish_job:
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}

--
2.34.1