diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 10986cb11ec0..dafc07dc181f 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -591,15 +591,19 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
 	 * data blob, so make sure it's physically contiguous, 4K aligned and
 	 * non-cachable to avoid XPU violations.
 	 *
-	 * For PIL calls the hypervisor creates SHM Bridges for the blob
-	 * buffers on behalf of Linux so we must not do it ourselves hence
-	 * not using the TZMem allocator here.
+	 * For PIL calls the hypervisor like Gunyah or older QHEE creates SHM
+	 * Bridges for the blob buffers on behalf of Linux so we must not do it
+	 * ourselves hence use TZMem allocator only when these hypervisors are
+	 * not present.
 	 *
 	 * If we pass a buffer that is already part of an SHM Bridge to this
 	 * call, it will fail.
 	 */
-	mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
-				       GFP_KERNEL);
+	if (ctx && ctx->shm_bridge_needed)
+		mdata_buf = qcom_tzmem_alloc(__scm->mempool, size, GFP_KERNEL);
+	else
+		mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, GFP_KERNEL);
+
 	if (!mdata_buf)
 		return -ENOMEM;
 
@@ -613,7 +617,10 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
 	if (ret)
 		goto disable_clk;
 
-	desc.args[1] = mdata_phys;
+	if (ctx && ctx->shm_bridge_needed)
+		desc.args[1] = qcom_tzmem_to_phys(mdata_buf);
+	else
+		desc.args[1] = mdata_phys;
 
 	ret = qcom_scm_call(__scm->dev, &desc, &res);
 	qcom_scm_bw_disable();
@@ -625,8 +632,11 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
 	if (ret < 0 || !ctx) {
 		dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
 	} else if (ctx) {
+		if (ctx->shm_bridge_needed)
+			ctx->phys = qcom_tzmem_to_phys(mdata_buf);
+		else
+			ctx->phys = mdata_phys;
 		ctx->ptr = mdata_buf;
-		ctx->phys = mdata_phys;
 		ctx->size = size;
 	}
 
@@ -643,7 +653,10 @@ void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
 	if (!ctx->ptr)
 		return;
 
-	dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
+	if (ctx->shm_bridge_needed)
+		qcom_tzmem_free(ctx->ptr);
+	else
+		dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
 
 	ctx->ptr = NULL;
 	ctx->phys = 0;
diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c
index 92b365178235..66aba2fc979d 100644
--- a/drivers/firmware/qcom/qcom_tzmem.c
+++ b/drivers/firmware/qcom/qcom_tzmem.c
@@ -22,14 +22,6 @@
 
 #include "qcom_tzmem.h"
 
-struct qcom_tzmem_area {
-	struct list_head list;
-	void *vaddr;
-	dma_addr_t paddr;
-	size_t size;
-	void *priv;
-};
-
 struct qcom_tzmem_pool {
 	struct gen_pool *genpool;
 	struct list_head areas;
@@ -107,7 +99,7 @@ static int qcom_tzmem_init(void)
 	return 0;
 }
 
-static int qcom_tzmem_init_area(struct qcom_tzmem_area *area)
+int qcom_tzmem_init_area(struct qcom_tzmem_area *area)
 {
 	u64 pfn_and_ns_perm, ipfn_and_s_perm, size_and_flags;
 	int ret;
@@ -133,8 +125,9 @@ static int qcom_tzmem_init_area(struct qcom_tzmem_area *area)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(qcom_tzmem_init_area);
 
-static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area)
+void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area)
 {
 	u64 *handle = area->priv;
 
@@ -144,6 +137,7 @@ static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area)
 	qcom_scm_shm_bridge_delete(qcom_tzmem_dev, *handle);
 	kfree(handle);
 }
+EXPORT_SYMBOL_GPL(qcom_tzmem_cleanup_area);
 
 #endif /* CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE */
 
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index bdb071ab5938..ac339145e072 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -20,6 +20,7 @@
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
 #include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_tzmem.h>
 #include <linux/regulator/consumer.h>
 #include <linux/remoteproc.h>
 #include <linux/soc/qcom/mdt_loader.h>
@@ -120,6 +121,7 @@ struct qcom_adsp {
 	struct qcom_scm_pas_metadata dtb_pas_metadata;
 
 	struct qcom_devmem_table *devmem;
+	struct qcom_tzmem_area *tzmem;
 };
 
 static void adsp_segment_dump(struct rproc *rproc, struct rproc_dump_segment *segment,
@@ -262,6 +264,43 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
 	return ret;
 }
 
+static int adsp_create_shmbridge(struct qcom_adsp *adsp)
+{
+	struct qcom_tzmem_area *rproc_tzmem;
+	struct rproc *rproc = adsp->rproc;
+	int ret;
+
+	if (!rproc->has_iommu)
+		return 0;
+
+	rproc_tzmem = devm_kzalloc(adsp->dev, sizeof(*rproc_tzmem), GFP_KERNEL);
+	if (!rproc_tzmem)
+		return -ENOMEM;
+
+	rproc_tzmem->size = PAGE_ALIGN(adsp->mem_size);
+	rproc_tzmem->paddr = adsp->mem_phys;
+	ret = qcom_tzmem_init_area(rproc_tzmem);
+	if (ret) {
+		dev_err(adsp->dev,
+			"failed to create shmbridge for carveout: %d\n", ret);
+		return ret;
+	}
+
+	adsp->tzmem = rproc_tzmem;
+
+	return ret;
+}
+
+static void adsp_delete_shmbridge(struct qcom_adsp *adsp)
+{
+	struct rproc *rproc = adsp->rproc;
+
+	if (!rproc->has_iommu)
+		return;
+
+	qcom_tzmem_cleanup_area(adsp->tzmem);
+}
+
 static int adsp_start(struct rproc *rproc)
 {
 	struct qcom_adsp *adsp = rproc->priv;
@@ -317,6 +356,10 @@ static int adsp_start(struct rproc *rproc)
 
 	qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size);
 
+	ret = adsp_create_shmbridge(adsp);
+	if (ret)
+		goto release_pas_metadata;
+
 	ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
 	if (ret) {
 		dev_err(adsp->dev,
@@ -324,6 +367,7 @@ static int adsp_start(struct rproc *rproc)
 		goto release_pas_metadata;
 	}
 
+	adsp_delete_shmbridge(adsp);
 	ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000));
 	if (ret == -ETIMEDOUT) {
 		dev_err(adsp->dev, "start timed out\n");
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index 9f14976399ab..25243cd889bb 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -70,6 +70,7 @@ struct qcom_scm_pas_metadata {
 	void *ptr;
 	dma_addr_t phys;
 	ssize_t size;
+	bool shm_bridge_needed;
 };
 
 int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
diff --git a/include/linux/firmware/qcom/qcom_tzmem.h b/include/linux/firmware/qcom/qcom_tzmem.h
index b83b63a0c049..e0a57cc8f74b 100644
--- a/include/linux/firmware/qcom/qcom_tzmem.h
+++ b/include/linux/firmware/qcom/qcom_tzmem.h
@@ -39,6 +39,14 @@ struct qcom_tzmem_pool_config {
 	size_t max_size;
 };
 
+struct qcom_tzmem_area {
+	struct list_head list;
+	void *vaddr;
+	dma_addr_t paddr;
+	size_t size;
+	void *priv;
+};
+
 struct qcom_tzmem_pool *
 qcom_tzmem_pool_new(const struct qcom_tzmem_pool_config *config);
 void qcom_tzmem_pool_free(struct qcom_tzmem_pool *pool);
@@ -48,6 +56,8 @@ devm_qcom_tzmem_pool_new(struct device *dev,
 
 void *qcom_tzmem_alloc(struct qcom_tzmem_pool *pool, size_t size, gfp_t gfp);
 void qcom_tzmem_free(void *ptr);
+int qcom_tzmem_init_area(struct qcom_tzmem_area *area);
+void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area);
 
 DEFINE_FREE(qcom_tzmem, void *, if (_T) qcom_tzmem_free(_T))
 
