The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.

Fix the lifetime by moving v4l2_m2m_init() into vdec_probe() and
releasing it in vdec_remove() which resolves passing the global
core->m2m_dev down to session contexts so all sessions share the
same hardware instance.

This change aligns the driver with proper v4l2_m2m usage, ensuring
the hardware device lifetime is tied to the platform driver core,
not individual sessions, and making teardown safe and predictable.

Reported-by: Sashiko <[email protected]>
Closes: 
https://lore.kernel.org/all/[email protected]/#t
Signed-off-by: Anand Moon <[email protected]>
---
 drivers/staging/media/meson/vdec/vdec.c | 36 +++++++++++++------------
 drivers/staging/media/meson/vdec/vdec.h |  4 +--
 2 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/drivers/staging/media/meson/vdec/vdec.c 
b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe5..6ae3471155a87 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -153,7 +153,7 @@ static void vdec_m2m_job_abort(void *priv)
 {
        struct amvdec_session *sess = priv;
 
-       v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
+       v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
 }
 
 static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -873,23 +873,16 @@ static int vdec_open(struct file *file)
 
        sess->core = core;
 
-       sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
-       if (IS_ERR(sess->m2m_dev)) {
-               dev_err(dev, "Fail to v4l2_m2m_init\n");
-               ret = PTR_ERR(sess->m2m_dev);
-               goto err_free_sess;
-       }
-
-       sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
+       sess->m2m_ctx = v4l2_m2m_ctx_init(core->m2m_dev, sess, m2m_queue_init);
        if (IS_ERR(sess->m2m_ctx)) {
                dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
                ret = PTR_ERR(sess->m2m_ctx);
-               goto err_m2m_release;
+               goto err_free_sess;
        }
 
        ret = vdec_init_ctrls(sess);
        if (ret)
-               goto err_m2m_ctx_release;
+               goto err_release_ctx;
 
        sess->pixfmt_cap = formats[0].pixfmts_cap[0];
        sess->fmt_out = &formats[0];
@@ -913,10 +906,8 @@ static int vdec_open(struct file *file)
 
        return 0;
 
-err_m2m_ctx_release:
+err_release_ctx:
        v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
-       v4l2_m2m_release(sess->m2m_dev);
 err_free_sess:
        kfree(sess);
        return ret;
@@ -927,9 +918,9 @@ static int vdec_close(struct file *file)
        struct amvdec_session *sess = file_to_amvdec_session(file);
 
        v4l2_m2m_ctx_release(sess->m2m_ctx);
-       v4l2_m2m_release(sess->m2m_dev);
        v4l2_fh_del(&sess->fh, file);
        v4l2_fh_exit(&sess->fh);
+       v4l2_ctrl_handler_free(&sess->ctrl_handler);
 
        mutex_destroy(&sess->lock);
        mutex_destroy(&sess->bufs_recycle_lock);
@@ -1059,16 +1050,23 @@ static int vdec_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+       if (IS_ERR(core->m2m_dev)) {
+               dev_err(dev, "Failed to initialize v4l2 m2m device\n");
+               return PTR_ERR(core->m2m_dev);
+       }
+
        ret = v4l2_device_register(dev, &core->v4l2_dev);
        if (ret) {
                dev_err(dev, "Couldn't register v4l2 device\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_m2m_release;
        }
 
        vdev = video_device_alloc();
        if (!vdev) {
                ret = -ENOMEM;
-               goto err_vdev_release;
+               goto err_v4l2_unregister;
        }
 
        core->vdev_dec = vdev;
@@ -1096,7 +1094,10 @@ static int vdec_probe(struct platform_device *pdev)
 
 err_vdev_release:
        video_device_release(vdev);
+err_v4l2_unregister:
        v4l2_device_unregister(&core->v4l2_dev);
+err_m2m_release:
+       v4l2_m2m_release(core->m2m_dev);
        return ret;
 }
 
@@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
        struct amvdec_core *core = platform_get_drvdata(pdev);
 
        video_unregister_device(core->vdev_dec);
+       v4l2_m2m_release(core->m2m_dev);
        v4l2_device_unregister(&core->v4l2_dev);
 }
 
diff --git a/drivers/staging/media/meson/vdec/vdec.h 
b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d708..cc0cfafb8a951 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -63,6 +63,7 @@ struct amvdec_session;
  * @vdec_hevcf_clk: VDEC_HEVCF clock
  * @esparser_reset: RESET for the PARSER
  * @vdev_dec: video device for the decoder
+ * @m2m_dev: v4l2 m2m device
  * @v4l2_dev: v4l2 device
  * @cur_sess: current decoding session
  * @lock: video device lock
@@ -87,6 +88,7 @@ struct amvdec_core {
        struct reset_control *esparser_reset;
 
        struct video_device *vdev_dec;
+       struct v4l2_m2m_dev *m2m_dev;
        struct v4l2_device v4l2_dev;
 
        struct amvdec_session *cur_sess;
@@ -183,7 +185,6 @@ enum amvdec_status {
  *
  * @core: reference to the vdec core struct
  * @fh: v4l2 file handle
- * @m2m_dev: v4l2 m2m device
  * @m2m_ctx: v4l2 m2m context
  * @ctrl_handler: V4L2 control handler
  * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
@@ -230,7 +231,6 @@ struct amvdec_session {
        struct amvdec_core *core;
 
        struct v4l2_fh fh;
-       struct v4l2_m2m_dev *m2m_dev;
        struct v4l2_m2m_ctx *m2m_ctx;
        struct v4l2_ctrl_handler ctrl_handler;
        struct v4l2_ctrl *ctrl_min_buf_capture;
-- 
2.50.1

Reply via email to