On Tue Aug 18 20:43:23 2026 +0530, Vishnu Reddy wrote:
> The VPU issues DMA through several SMMU streams, and the hardware does
> not give every stream the same addressable range. The non-pixel stream
> cannot address the low 600MB of IOVA space, while the pixel stream can
> address the full range:
>     +-----------------------------------------------------------+
>     | non-pixel stream addressable range (600 MB - 3.5 GB)      |
>     | 0x25800000 - 0xe0000000                                   |
>     +-----------------------------------------------------------+
>     | pixel stream addressable range (0 - 3.5 GB)               |
>     | 0x00000000 - 0xe0000000                                   |
>     +-----------------------------------------------------------+
> 
> A single "iommus" property on the video-codec node puts every stream
> in one IOMMU domain sharing one IOVA space, so nothing stops a
> non-pixel buffer from landing below 600MB. Once an allocation lands
> below that boundary the hardware faults, which shows up as unhandled
> SMMU page faults and spontaneous reboots.
> https://gitlab.freedesktop.org/drm/msm/-/work_items/100
> 
> Fix this by reserving the 0-600MB range, below the boundary the
> non-pixel stream cannot address, using dma_iova_try_alloc() so the
> IOMMU-DMA core never hands that range out to a real DMA mapping. This
> reserves only IOVA space, it does not allocate any physical memory.
> 
> Since sub-nodes for non-pixel, pixel, and secure streams do not exist
> yet and only a single device is available, the restriction is applied
> to both non-pixel and pixel stream IDs.
> 
> Fixes: af2c3834c8ca ("[media] media: venus: adding core part and helper 
> functions")
> Cc: [email protected]
> Reviewed-by: Vikash Garodia <[email protected]>
> Reviewed-by: Dmitry Baryshkov <[email protected]>
> Signed-off-by: Vishnu Reddy <[email protected]>
> [bod: took iova_state description from iris and applied to venus in core.h]
> Signed-off-by: Bryan O'Donoghue <[email protected]>

Patch committed.

Thanks,
Bryan O'Donoghue

 drivers/media/platform/qcom/venus/core.c | 66 ++++++++++++++++++++++++++++++--
 drivers/media/platform/qcom/venus/core.h |  6 +++
 2 files changed, 69 insertions(+), 3 deletions(-)

---

diff --git a/drivers/media/platform/qcom/venus/core.c 
b/drivers/media/platform/qcom/venus/core.c
index 243e342b0ae7..2a0ab553c451 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -377,6 +377,57 @@ static int venus_add_dynamic_nodes(struct venus_core *core)
 static void venus_remove_dynamic_nodes(struct venus_core *core) {}
 #endif
 
+static void venus_unreserve_iova_region(struct device *dev, struct 
dma_iova_state *state)
+{
+       unsigned int i;
+
+       for (i = 0; dma_iova_size(&state[i]); i++)
+               dma_iova_free(dev, &state[i]);
+}
+
+static int venus_reserve_iova_region(struct device *dev, struct dma_iova_state 
**iova_state,
+                                    unsigned long start, unsigned long size)
+{
+       unsigned long dma_limit = dev->bus_dma_limit;
+       unsigned long end, rem, chunk;
+       struct dma_iova_state *state;
+       unsigned int count = 0;
+       int ret = -ENOMEM;
+
+       state = devm_kcalloc(dev, BITS_PER_TYPE(dma_addr_t) + 1, 
sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return ret;
+
+       end = start + size;
+       rem = end - max(start, PAGE_SIZE);
+       dev->bus_dma_limit = end - 1;
+
+       while (rem) {
+               chunk = min(end & -end, (u64)1 << (fls64(rem) - 1));
+
+               if (!dma_iova_try_alloc(dev, &state[count], 0, chunk))
+                       goto err_free_iova;
+
+               if (state[count].addr != end - chunk || state[count].__size != 
chunk)
+                       goto err_free_iova;
+
+               rem -= chunk;
+               end -= chunk;
+               count++;
+       }
+
+       *iova_state = state;
+       dev->bus_dma_limit = dma_limit;
+
+       return 0;
+
+err_free_iova:
+       venus_unreserve_iova_region(dev, state);
+       dev->bus_dma_limit = dma_limit;
+
+       return ret;
+}
+
 static int venus_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -421,10 +472,15 @@ static int venus_probe(struct platform_device *pdev)
                        return ret;
        }
 
-       ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
+       ret = venus_reserve_iova_region(dev, &core->iova_state, 
VENUS_NP_RESERVE_IOVA_START,
+                                       VENUS_NP_RESERVE_IOVA_SIZE);
        if (ret)
                goto err_core_put;
 
+       ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
+       if (ret)
+               goto err_unresv_iova_region;
+
        dma_set_max_seg_size(dev, UINT_MAX);
 
        INIT_LIST_HEAD(&core->instances);
@@ -434,13 +490,13 @@ static int venus_probe(struct platform_device *pdev)
 
        ret = hfi_create(core, &venus_core_ops);
        if (ret)
-               goto err_core_put;
+               goto err_unresv_iova_region;
 
        ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, 
venus_isr_thread,
                                        IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
                                        "venus", core);
        if (ret)
-               goto err_core_put;
+               goto err_unresv_iova_region;
 
        venus_assign_register_offsets(core);
 
@@ -525,6 +581,8 @@ err_runtime_disable:
        v4l2_device_unregister(&core->v4l2_dev);
 err_hfi_destroy:
        hfi_destroy(core);
+err_unresv_iova_region:
+       venus_unreserve_iova_region(dev, core->iova_state);
 err_core_put:
        if (core->pm_ops->core_put)
                core->pm_ops->core_put(core);
@@ -562,6 +620,8 @@ static void venus_remove(struct platform_device *pdev)
 
        hfi_destroy(core);
 
+       venus_unreserve_iova_region(dev, core->iova_state);
+
        mutex_destroy(&core->pm_lock);
        mutex_destroy(&core->lock);
        venus_dbgfs_deinit(core);
diff --git a/drivers/media/platform/qcom/venus/core.h 
b/drivers/media/platform/qcom/venus/core.h
index 46705a666776..93fa77f05834 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -8,6 +8,7 @@
 #define __VENUS_CORE_H_
 
 #include <linux/bitops.h>
+#include <linux/dma-mapping.h>
 #include <linux/list.h>
 #include <media/videobuf2-v4l2.h>
 #include <media/v4l2-ctrls.h>
@@ -30,6 +31,9 @@
 
 #define VENUS_MAX_FPS                  240
 
+#define VENUS_NP_RESERVE_IOVA_START    0x0
+#define VENUS_NP_RESERVE_IOVA_SIZE     0x25800000
+
 extern int venus_fw_debug;
 
 struct freq_tbl {
@@ -189,6 +193,7 @@ struct venus_format {
  * @dump_core: a flag indicating that a core dump is required
  * @ocs:       OF changeset pointer
  * @hwmode_dev:        a flag indicating that HW_CTRL_TRIGGER is used in clock 
driver
+ * @iova_state: a pointer to an array of dma_iova_state entries reserved for 
restricted IOVA region
  */
 struct venus_core {
        void __iomem *base;
@@ -250,6 +255,7 @@ struct venus_core {
        unsigned long dump_core;
        struct of_changeset *ocs;
        bool hwmode_dev;
+       struct dma_iova_state *iova_state;
 };
 
 struct vdec_controls {
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to