mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
To: <broonie@kernel.org>
Cc: <alsa-devel@alsa-project.org>, <lgirdwood@gmail.com>,
	<perex@perex.cz>, <tiwai@suse.com>,
	<yung-chuan.liao@linux.intel.com>, <Basavaraj.Hiregoudar@amd.com>,
	<Sunil-kumar.Dommati@amd.com>, <venkataprasad.potturu@amd.com>,
	<Syed.SabaKareem@amd.com>, <Mario.Limonciello@amd.com>,
	<Richard.Gong@amd.com>, <Vijendar.Mukunda@amd.com>,
	<Poojitha.N@amd.com>, <linux-sound@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	<sound-open-firmware@alsa-project.org>
Subject: [PATCH v2 10/10] ASoC: SOF: amd: add ASP carveout firmware loading for ACP7.B/7.F
Date: Wed, 14 Oct 2026 15:29:18 +0530	[thread overview]
Message-ID: <20261014100902.1215212-11-Vijendar.Mukunda@amd.com> (raw)
In-Reply-To: <20261014100902.1215212-1-Vijendar.Mukunda@amd.com>

ACP7.B and ACP7.F require signed firmware to be authenticated by the
AMD Security Processor (ASP) before the DSP can execute it. Implement
a new two-stage firmware load path via the ASP MPASP mailbox.

Stage 1 (probe time): query ASP for the physical base address and ATU
group assignment of a pre-allocated 32 MB MALL carveout region. If
ASP does not support carveout, probe fails immediately.

Stage 2 (load time): load the signed image from disk, send it to ASP
via VALIDATE_IMAGE; ASP authenticates the image and deposits the raw
binary into the carveout. The driver then SHA-DMAs the raw binary from
the carveout into DSP IRAM.

On D3->D0 resume the raw binary is preserved in the carveout; the
driver skips the filesystem load and ASP authentication and restores
directly from the saved carveout address.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/sof/amd/Makefile          |   2 +-
 sound/soc/sof/amd/acp-dsp-offset.h  |  58 +++
 sound/soc/sof/amd/acp-loader.c      |  64 ++++
 sound/soc/sof/amd/acp-stream.c      |   9 -
 sound/soc/sof/amd/acp.c             |  77 +++-
 sound/soc/sof/amd/acp.h             | 105 ++++++
 sound/soc/sof/amd/acp7x.c           |  11 +-
 sound/soc/sof/amd/acp_asp_mailbox.c | 558 ++++++++++++++++++++++++++++
 8 files changed, 868 insertions(+), 16 deletions(-)
 create mode 100644 sound/soc/sof/amd/acp_asp_mailbox.c

diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile
index bb1c0ddef69d7..3a7d230a4c461 100644
--- a/sound/soc/sof/amd/Makefile
+++ b/sound/soc/sof/amd/Makefile
@@ -4,7 +4,7 @@
 #
 # Copyright(c) 2021, 2023, 2024 Advanced Micro Devices, Inc. All rights reserved.
 
-snd-sof-amd-acp-y := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o
+snd-sof-amd-acp-y := acp.o acp-loader.o acp_asp_mailbox.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o
 snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) += acp-probes.o
 snd-sof-amd-renoir-y := pci-rn.o renoir.o
 snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index 3984cd627db6d..0bcccf27d1336 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -37,6 +37,30 @@
 
 /* Registers from ACP_DSP_0 block */
 #define ACP_DSP0_RUNSTALL			0x414
+/*
+ * ATU group registers for groups 9 to 16.
+ * ACPAXI2AXI_ATU_CTRL (0xC40) sits between GRP_8 and GRP_9,
+ * so groups 9 to 16 are offset by 4 bytes relative to a simple stride.
+ */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_9		0xC44
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_9		0xC48
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_10	0xC4C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_10	0xC50
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_11	0xC54
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_11	0xC58
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_12	0xC5C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_12	0xC60
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_13	0xC64
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_13	0xC68
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_14	0xC6C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_14	0xC70
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_15	0xC74
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_15	0xC78
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_16	0xC7C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_16	0xC80
+
+/* ATU page size value for 2 MB pages - used for carveout groups */
+#define PAGE_SIZE_2M_ENABLE			0x0
 
 /* Registers from ACP_AXI2AXIATU block */
 #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1		0xC00
@@ -132,6 +156,40 @@
 #define ACP_SHA_DMA_INCLUDE_HDR         0x1CCC
 #define ACP_SHA_PSP_ACK                         0x1C74
 
+/* ACP7.B/7.F MALL/Carveout configuration registers (ACP MMIO offsets) */
+#define ACP7X_MALL_ADDR_VALID			0x114C
+#define ACP7X_MALL_BASE_ADDR_LOW		0x1150
+#define ACP7X_MALL_BASE_ADDR_HIGH		0x1154
+#define ACP7X_MALL_SIZE				0x1158
+#define ACP7X_MALL_VALID			0x115C
+
+/* ASP MPASP mailbox SMN addresses for ACP7.B/7.F carveout firmware load */
+#define MPASP_C2PMSG_173_REG			0x03810BB4
+#define MPASP_C2PMSG_174_REG			0x03810BB8
+#define MPASP_C2PMSG_175_REG			0x03810BBC
+#define MPASP_C2PMSG_73_REG			0x03810A24
+
+/* ASP mailbox command IDs */
+#define ASP_MBOX_CMD_VALIDATE_IMAGE		0x0001
+#define ASP_MBOX_CMD_CALC_HMAC			0x0002
+#define ASP_MBOX_CMD_VALIDATE_HMAC		0x0003
+#define ASP_MBOX_CMD_GET_CARVEOUT_ADDR		0x0004
+
+/* MPASP_C2PMSG_173 control register bit definitions */
+#define ASP_MBOX_READY_BIT			BIT(31)
+#define ASP_MBOX_CMD_ID_MASK			0xFFFF
+
+/* ASP mailbox timeout: 5 seconds per ASP firmware load specification */
+#define ASP_MBOX_TIMEOUT_US			5000000
+/* Short timeout for the initial GET_CARVEOUT_ADDR probe: fail fast when
+ * ASP does not support carveout rather than blocking probe for 5 seconds.
+ */
+#define ASP_MBOX_PROBE_TIMEOUT_US		50000
+#define ASP_MBOX_POLL_INTERVAL_US		1000
+
+/* ASP mailbox payload cookie "ACPB" */
+#define ASP_MBOX_COOKIE				0x41435042
+
 #define ACP_SCRATCH_REG_0			0x10000
 #define ACP6X_DSP_FUSION_RUNSTALL		0x0644
 #define ACP70_DSP_FUSION_RUNSTALL		ACP6X_DSP_FUSION_RUNSTALL
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
index f1ec856943238..abd080484804c 100644
--- a/sound/soc/sof/amd/acp-loader.c
+++ b/sound/soc/sof/amd/acp-loader.c
@@ -168,12 +168,76 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
 	struct pci_dev *pci = to_pci_dev(sdev->dev);
 	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
 	struct acp_dev_data *adata;
+	u64 carveout_offset;
 	unsigned int src_addr, size_fw, dest_addr;
 	u32 page_count, dma_size;
+	u32 acp_logical_addr;
 	int ret;
 
 	adata = sdev->pdata->hw_pdata;
 
+	if (adata->asp_carveout_base) {
+		size_fw = adata->fw_code_raw_size;
+		page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
+		adata->fw_bin_page_count = page_count;
+
+		if (adata->fw_code_carveout_addr < adata->asp_carveout_base ||
+		    adata->fw_code_carveout_addr - adata->asp_carveout_base +
+		    size_fw > adata->asp_carveout_size) {
+			dev_err(sdev->dev,
+				"carveout addr 0x%llx out of window [0x%llx+0x%x]\n",
+				adata->fw_code_carveout_addr,
+				adata->asp_carveout_base,
+				adata->asp_carveout_size);
+			return -EINVAL;
+		}
+		carveout_offset = adata->fw_code_carveout_addr - adata->asp_carveout_base;
+		acp_logical_addr = ACP_SYSTEM_MEMORY_WINDOW +
+				   (adata->asp_carveout_group_start * 8 * SZ_1M) +
+				   (u32)carveout_offset;
+
+		ret = configure_and_run_sha_dma(adata, NULL,
+						acp_logical_addr,
+						ACP_IRAM_BASE_ADDRESS, size_fw);
+		if (ret < 0) {
+			dev_err(sdev->dev, "SHA DMA from carveout failed: %d\n", ret);
+			return ret;
+		}
+
+		if (adata->is_sram_in_use) {
+			configure_pte_for_fw_loading(FW_SRAM_DATA_BIN,
+						     ACP_SRAM_PAGE_COUNT, adata);
+			src_addr = ACP_SYSTEM_MEMORY_WINDOW + ACP_DEFAULT_SRAM_LENGTH +
+				   (page_count * ACP_PAGE_SIZE);
+			dest_addr = ACP7X_SRAM_BASE_ADDRESS;
+			ret = configure_and_run_dma(adata, src_addr, dest_addr,
+						    adata->fw_sram_data_bin_size);
+			if (ret < 0) {
+				dev_err(sdev->dev, "SRAM DMA failed: %d\n", ret);
+				dma_free_coherent(&pci->dev, ACP_DEFAULT_SRAM_LENGTH,
+						  adata->sram_data_buf, adata->sram_dma_addr);
+				adata->sram_data_buf = NULL;
+				return ret;
+			}
+			ret = acp_dma_status(adata, 0);
+			if (ret < 0)
+				dev_err(sdev->dev, "SRAM DMA status error: %d\n", ret);
+		}
+
+		/* Enable cache window */
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0,
+				  desc->sram_pte_offset);
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0,
+				  SRAM1_SIZE | BIT(31));
+
+		if (adata->is_sram_in_use) {
+			dma_free_coherent(&pci->dev, ACP_DEFAULT_SRAM_LENGTH,
+					  adata->sram_data_buf, adata->sram_dma_addr);
+			adata->sram_data_buf = NULL;
+		}
+		return ret;
+	}
+
 	if (adata->pci_rev >= ACP7B_PCI_ID) {
 		if (adata->acp_sof_signed_firmware_image) {
 			if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) {
diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c
index 9212a3137cfd2..a751ffdb0adf3 100644
--- a/sound/soc/sof/amd/acp-stream.c
+++ b/sound/soc/sof/amd/acp-stream.c
@@ -15,15 +15,6 @@
 #include "acp-dsp-offset.h"
 #include "acp.h"
 
-#define PTE_GRP1_OFFSET		0x00000000
-#define PTE_GRP2_OFFSET		0x00800000
-#define PTE_GRP3_OFFSET		0x01000000
-#define PTE_GRP4_OFFSET		0x01800000
-#define PTE_GRP5_OFFSET		0x02000000
-#define PTE_GRP6_OFFSET		0x02800000
-#define PTE_GRP7_OFFSET		0x03000000
-#define PTE_GRP8_OFFSET		0x03800000
-
 int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream)
 {
 	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 790af65be34a2..86e2402b8ffd2 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -279,11 +279,18 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
 	unsigned int tx_count, fw_qualifier, val;
 	int ret;
 
-	if (!image_addr) {
+	if (!image_addr &&
+	    !adata->asp_carveout_base) {
 		dev_err(sdev->dev, "SHA DMA image address is NULL\n");
 		return -EINVAL;
 	}
 
+	/*
+	 * Flush the ATU cache before programming SHA DMA registers
+	 * to guarantee coherency at SHA DMA start time.
+	 */
+	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
+
 	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD);
 	if (val & ACP_SHA_RUN) {
 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET);
@@ -297,8 +304,13 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
 		}
 	}
 
-	if ((adata->quirks && adata->quirks->signed_fw_image) ||
-	    adata->acp_sof_signed_firmware_image)
+	/*
+	 * The signing header must NOT be included for the carveout path: ASP
+	 * stripped it when depositing the raw binary during VALIDATE_IMAGE.
+	 */
+	if (((adata->quirks && adata->quirks->signed_fw_image) ||
+	     adata->acp_sof_signed_firmware_image) &&
+	    !adata->asp_carveout_base)
 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER);
 
 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr);
@@ -316,7 +328,17 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
 		if (ret)
 			return ret;
 	}
-	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN);
+	/*
+	 * SHA_IOC_En triggers an interrupt-on-completion to ASP for source
+	 * address validation.  Only set it on the carveout path; enabling it
+	 * on the legacy ATU-window path breaks PSP validation.
+	 */
+	if (adata->asp_carveout_base) {
+		reinit_completion(&adata->sha_dma_complete);
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN_WITH_IOC);
+	} else {
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN);
+	}
 
 	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT,
 					    tx_count, tx_count == image_length,
@@ -333,6 +355,23 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
 			return ret;
 	}
 
+	if (adata->asp_carveout_base) {
+		/*
+		 * Wait for the SHA IOC interrupt which fires after ASP validates
+		 * the source address.  Use ASP_MBOX_TIMEOUT_US to match the
+		 * budget used for all other ASP mailbox operations.
+		 * usecs_to_jiffies() can round to 0 at low HZ so clamp to at
+		 * least 1 jiffy.
+		 */
+		unsigned long timeout_jiffies =
+			max(1UL, usecs_to_jiffies(ASP_MBOX_TIMEOUT_US));
+
+		if (!wait_for_completion_timeout(&adata->sha_dma_complete,
+						 timeout_jiffies))
+			dev_warn(sdev->dev,
+				 "SHA DMA interrupt not received within timeout, continuing\n");
+	}
+
 	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER,
 					    fw_qualifier, fw_qualifier & DSP_FW_RUN_ENABLE,
 					    ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
@@ -768,6 +807,20 @@ static irqreturn_t acp7x_irq_handler(int irq, void *dev_id)
 
 	if (adata->sdw)
 		wake_irq_flag = sof_amd_check_and_handle_acp7x_sdw_wake_irq(sdev);
+	/*
+	 * SHA DMA completion interrupt (ACP_SHA_STAT, bit 15 of ext_intr_stat).
+	 * ACP_SHA_STAT has no dedicated mask bit in ACP_EXTERNAL_INTR_CNTL;
+	 * it is always enabled by default and fires only when SHA DMA completes
+	 * with SHA_IOC_En set.  SHA DMA is triggered exclusively during firmware
+	 * loading, so this interrupt is only asserted in that context.
+	 * Acknowledge and signal sha_dma_complete so configure_and_run_sha_dma()
+	 * can proceed to poll ACP_SHA_DSP_FW_QUALIFIER.
+	 */
+	if (ext_intr_stat & ACP_SHA_STAT) {
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SHA_STAT);
+		complete(&adata->sha_dma_complete);
+		irq_flag = 1;
+	}
 
 	if (ext_intr_stat & ACP7X_ERROR_IRQ) {
 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP7X_ERROR_IRQ);
@@ -1200,6 +1253,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
 	adata->subsystem_vendor = pci->subsystem_vendor;
 	adata->subsystem_device = pci->subsystem_device;
 	mutex_init(&adata->acp_lock);
+	init_completion(&adata->sha_dma_complete);
 	sdev->pdata->hw_pdata = adata;
 
 	ret = acp_init(sdev);
@@ -1339,6 +1393,7 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
 	adata->subsystem_vendor = pci->subsystem_vendor;
 	adata->subsystem_device = pci->subsystem_device;
 	mutex_init(&adata->acp_lock);
+	init_completion(&adata->sha_dma_complete);
 	sdev->pdata->hw_pdata = adata;
 
 	ret = acp_init(sdev);
@@ -1411,6 +1466,20 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
 	acp_memory_init(sdev);
 	acp_dsp_stream_init(sdev);
 
+	/*
+	 * Carveout is the only supported signed firmware load path on
+	 * ACP7.B/7.F. Query ASP now when signed firmware is enabled so
+	 * probe fails cleanly if carveout is unavailable.
+	 */
+	if (adata->acp_sof_signed_firmware_image) {
+		ret = acp7x_query_asp_carveout(sdev);
+		if (ret) {
+			dev_err(sdev->dev,
+				"ASP carveout unavailable, cannot load firmware: %d\n", ret);
+			goto free_ipc_irq;
+		}
+	}
+
 	return 0;
 
 free_ipc_irq:
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index e48a6cc64b6df..b17a36b31ee0d 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -49,10 +49,14 @@
 #define ACP_PAGE_SIZE				0x1000
 #define ACP_DMA_CH_RUN				0x02
 #define ACP_MAX_DESC_CNT			0x02
+#define ACP_DSP_RUN				0x00
 #define DSP_FW_RUN_ENABLE			0x01
 #define ACP_SHA_RUN				0x01
 #define ACP_SHA_RESET				0x02
 #define ACP_SHA_HEADER				0x01
+/* SHA_IOC_En (bit 2): interrupt-on-completion to ASP; must be set alongside sha_run */
+#define ACP_SHA_IOC_EN				0x04
+#define ACP_SHA_RUN_WITH_IOC			(ACP_SHA_RUN | ACP_SHA_IOC_EN)
 #define ACP_DMA_CH_RST				0x01
 #define ACP_DMA_CH_GRACEFUL_RST_EN		0x10
 #define ACP_ATU_CACHE_INVALID			0x01
@@ -115,6 +119,28 @@
 #define ACP_FIRMWARE_SIGNATURE			0x100
 #define ACP_IMAGE_HEADER_SIZE			ACP_FIRMWARE_SIGNATURE
 #define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF	0x14
+#define ACP_ASP_SIGNATURE_LENGTH		512
+
+/*
+ * ACP7.B/7.F carveout: 32 MB region mapped at 2 MB page granularity.
+ * Use AXI2AXIATU_PAGE_SIZE_Mask2MB (value 0x0) for carveout ATU
+ * groups giving 32MB / 2MB = 16 PTEs. 4KB pages would require 8192 PTEs and
+ * is not how the hardware carveout path is configured.
+ */
+#define ACP7X_CARVEOUT_MAX_SIZE			(32 * SZ_1M)
+#define ACP7X_CARVEOUT_PAGE_SIZE		(2 * SZ_1M)
+#define ACP7X_CARVEOUT_PAGE_COUNT		(ACP7X_CARVEOUT_MAX_SIZE / ACP7X_CARVEOUT_PAGE_SIZE)
+/*
+ * PTE high-word flags for ATU entries:
+ *   bit[31]: PAGE_Enable  - entry is valid
+ *   bit[30]: MALL_Enable  - route accesses through carveout (MALL) memory,
+ *            not standard DRAM. Required for ASP to recognise the SHA DMA
+ *            source as within the carveout and grant ACP_SHA_DSP_FW_QUALIFIER.
+ *            (PAGE_Enable | MALL_Enable ATU entry flags)
+ */
+#define ACP_ATU_PTE_PAGE_ENABLE			BIT(31)
+#define ACP_ATU_PTE_MALL_ENABLE			BIT(30)
+#define ACP_ATU_PTE_CARVEOUT_FLAGS		(ACP_ATU_PTE_PAGE_ENABLE | ACP_ATU_PTE_MALL_ENABLE)
 
 #define ACP_ERROR_IRQ_MASK			BIT(29)
 #define ACP_SDW0_IRQ_MASK			BIT(21)
@@ -136,6 +162,30 @@
 #define ACP_DSP_MSG_SET				1
 #define ACP_DSP_ACK_SET				1
 
+/*
+ * ATU PTE group ACP logical address offsets.
+ * Each group covers 8 MB (0x00800000).  Groups 1-8 are used for PCM
+ * streams; groups 9-16 are extended groups.
+ *
+ *   GRP_N_OFFSET = (N - 1) * 8 MB
+ */
+#define PTE_GRP1_OFFSET		0x00000000
+#define PTE_GRP2_OFFSET		0x00800000
+#define PTE_GRP3_OFFSET		0x01000000
+#define PTE_GRP4_OFFSET		0x01800000
+#define PTE_GRP5_OFFSET		0x02000000
+#define PTE_GRP6_OFFSET		0x02800000
+#define PTE_GRP7_OFFSET		0x03000000
+#define PTE_GRP8_OFFSET		0x03800000
+#define PTE_GRP9_OFFSET		0x04000000
+#define PTE_GRP10_OFFSET	0x04800000
+#define PTE_GRP11_OFFSET	0x05000000
+#define PTE_GRP12_OFFSET	0x05800000
+#define PTE_GRP13_OFFSET	0x06000000
+#define PTE_GRP14_OFFSET	0x06800000
+#define PTE_GRP15_OFFSET	0x07000000
+#define PTE_GRP16_OFFSET	0x07800000
+
 enum clock_source {
 	ACP_CLOCK_96M = 0,
 	ACP_CLOCK_48M,
@@ -149,6 +199,29 @@ struct  acp_atu_grp_pte {
 	u32 high;
 };
 
+/* ASP mailbox payload for GET_CARVEOUT_ADDR command (0x04) */
+struct asp_get_carveout_payload {
+	u32 cookie;		/* [0x00] Driver: ASP_MBOX_COOKIE */
+	u32 status;		/* [0x04] ASP:    0 = success */
+	u64 carveout_addr;	/* [0x08] ASP:    carveout physical base address */
+	u32 carveout_size;	/* [0x10] ASP:    ACP_MALL_SIZE register value */
+	u32 mall_addr_valid;	/* [0x14] ASP:    ACP_MALL_ADDR_VALID register value */
+	u32 mall_valid;		/* [0x18] ASP:    ACP_MALL_VALID register value */
+};
+
+/*
+ * ASP mailbox payload for VALIDATE_IMAGE command (0x01).
+ * Driver fills src/dest fields; ASP fills carveout_dest_addr and status.
+ */
+struct asp_validate_image_payload {
+	u32 cookie;		/* [0x00] Driver: ASP_MBOX_COOKIE */
+	u32 status;		/* [0x04] ASP:    0 = success */
+	u64 carveout_dest_addr;	/* [0x08] ASP:    physical addr of raw binary in carveout */
+	u64 dest_offset;	/* [0x10] Driver: destination offset within carveout */
+	u64 src_phys_addr;	/* [0x18] Driver: signed FW source physical addr in DDR */
+	u32 fw_image_size;	/* [0x20] Driver: total signed image size in bytes */
+};
+
 union dma_tx_cnt {
 	struct {
 		unsigned int count : 19;
@@ -296,6 +369,33 @@ struct acp_dev_data {
 	u32 subsystem_vendor;
 	u32 subsystem_device;
 	int acp_sof_signed_firmware_image;
+	/* ACP7.F ASP carveout firmware load state */
+	u64  asp_carveout_base;
+	u32  asp_carveout_size;
+	u32  asp_carveout_group_start;  /* first ATU group index for carveout (from ASP) */
+	u32  asp_carveout_group_count;  /* number of consecutive ATU groups for carveout */
+	/*
+	 * Physical address and raw size of each validated DSP code binary
+	 * within the carveout region (returned by ASP VALIDATE_IMAGE).
+	 * Preserved across suspend/resume so that SHA DMA can reload DSP IRAM
+	 * from the carveout without re-authenticating from disk.
+	 */
+	u64  fw_code_carveout_addr;
+	u32  fw_code_raw_size;
+	/* Real CPU physical address of the ASP MPASP mailbox payload page */
+	phys_addr_t asp_mbox_buf_phys;
+	void       *asp_mbox_buf;
+	/*
+	 * SHA DMA completion for the carveout path.
+	 * ACP_SHA_STAT (bit 15 of ACP_EXTERNAL_INTR_STAT) has no dedicated
+	 * mask bit in ACP_EXTERNAL_INTR_CNTL - it is always enabled by default.
+	 * SHA DMA is triggered exclusively during firmware loading, so no
+	 * spurious ACP_SHA_STAT interrupts are expected outside that window.
+	 * acp7x_irq_handler signals sha_dma_complete when ACP_SHA_STAT fires;
+	 * configure_and_run_sha_dma() waits on it before polling
+	 * ACP_SHA_DSP_FW_QUALIFIER to ensure ASP has validated the source.
+	 */
+	struct completion sha_dma_complete;
 };
 
 void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
@@ -318,6 +418,11 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev);
 int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev);
 int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type);
 
+/* ACP7.B/7.F ASP carveout firmware load helper functions */
+int acp7x_query_asp_carveout(struct snd_sof_dev *sdev);
+int acp7x_load_firmware_carveout(struct snd_sof_dev *sdev);
+int acp7x_configure_carveout_pte(struct snd_sof_dev *sdev);
+
 /* Block IO callbacks */
 int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
 			u32 offset, void *src, size_t size);
diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c
index b6722d11168cb..521b7c00c7612 100644
--- a/sound/soc/sof/amd/acp7x.c
+++ b/sound/soc/sof/amd/acp7x.c
@@ -170,8 +170,15 @@ int sof_acp7x_ops_init(struct snd_sof_dev *sdev)
 			acp_sof_post_fw_run_delay = obj->integer.value;
 	}
 
-	if (acp_sof_signed_firmware_image)
-		sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware;
+	if (acp_sof_signed_firmware_image) {
+		/*
+		 * This driver handles only ACP7.B/7.F which both use the ASP
+		 * carveout firmware load path. Assign unconditionally rather
+		 * than rechecking the revision so the correct loader is always
+		 * selected when signed firmware is enabled.
+		 */
+		sof_acp7x_ops.load_firmware = acp7x_load_firmware_carveout;
+	}
 
 	if (acp_sof_post_fw_run_delay)
 		sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay;
diff --git a/sound/soc/sof/amd/acp_asp_mailbox.c b/sound/soc/sof/amd/acp_asp_mailbox.c
new file mode 100644
index 0000000000000..bc183814b9738
--- /dev/null
+++ b/sound/soc/sof/amd/acp_asp_mailbox.c
@@ -0,0 +1,558 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2026 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+
+/*
+ * ASP (AMD Security Processor) mailbox communication and ACP7.B/7.F carveout
+ * memory firmware load/restore for the ACP7.B/7.F platform.
+ */
+
+#include <linux/acpi.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/unaligned.h>
+#include <asm/amd/node.h>
+
+#include "../ops.h"
+#include "acp-dsp-offset.h"
+#include "acp.h"
+
+/*
+ * acp7x_asp_send_cmd - send one ASP mailbox command via the ACPI AMSG method.
+ *
+ * The ACPI method "AMSG" (stored as 'GSMA' LE) is used to
+ * proxy commands to ASP. Direct SMN writes to MPASP_C2PMSG_* do not work
+ * because the BIOS gates host access to those registers and requires all
+ * ASP mailbox traffic to go through this ACPI method.
+ *
+ * The caller must fill adata->asp_mbox_buf with the payload before calling.
+ * On return the same buffer contains ASP's response fields.
+ *
+ * ACPI method signature: AMSG(CommandId, PayloadPhysAddrHi, PayloadPhysAddrLo)
+ * GET_CARVEOUT_ADDR uses a short 50 ms probe timeout; other commands use 5 s.
+ */
+static int acp7x_asp_send_cmd(struct snd_sof_dev *sdev, u16 cmd_id)
+{
+	struct pci_dev *pci = to_pci_dev(sdev->dev);
+	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+	struct acpi_device *adev = ACPI_COMPANION(&pci->dev);
+	union acpi_object args[3];
+	struct acpi_object_list arg_list = { ARRAY_SIZE(args), args };
+	unsigned long timeout;
+	acpi_handle handle;
+	acpi_status status;
+	unsigned int ready_timeout;
+	u32 ctrl;
+	int ret, smn_ret;
+
+	if (!adev) {
+		dev_err(sdev->dev, "ASP cmd 0x%04x: no ACPI companion device\n", cmd_id);
+		return -ENODEV;
+	}
+
+	handle = adev->handle;
+
+	/* Short timeout for GET_CARVEOUT_ADDR to fail fast if ASP lacks carveout support. */
+	ready_timeout = (cmd_id == ASP_MBOX_CMD_GET_CARVEOUT_ADDR) ?
+			ASP_MBOX_PROBE_TIMEOUT_US : ASP_MBOX_TIMEOUT_US;
+
+	timeout = jiffies + usecs_to_jiffies(ready_timeout);
+	ret = -ETIMEDOUT;
+	do {
+		smn_ret = amd_smn_read(0, MPASP_C2PMSG_173_REG, &ctrl);
+		if (smn_ret) {
+			dev_err(sdev->dev, "ASP mailbox SMN read failed: %d\n", smn_ret);
+			return smn_ret;
+		}
+		if (ctrl & ASP_MBOX_READY_BIT) {
+			ret = 0;
+			break;
+		}
+		usleep_range(ASP_MBOX_POLL_INTERVAL_US, ASP_MBOX_POLL_INTERVAL_US + 100);
+	} while (!time_after(jiffies, timeout));
+
+	if (ret) {
+		dev_err(sdev->dev, "ASP mailbox not ready before cmd 0x%04x\n", cmd_id);
+		return -ETIMEDOUT;
+	}
+
+	args[0].type = ACPI_TYPE_INTEGER;
+	args[0].integer.value = cmd_id;
+	args[1].type = ACPI_TYPE_INTEGER;
+	args[1].integer.value = upper_32_bits(adata->asp_mbox_buf_phys);
+	args[2].type = ACPI_TYPE_INTEGER;
+	args[2].integer.value = lower_32_bits(adata->asp_mbox_buf_phys);
+
+	status = acpi_evaluate_object(handle, "AMSG", &arg_list, NULL);
+	if (ACPI_FAILURE(status)) {
+		dev_err(sdev->dev, "ASP AMSG cmd 0x%04x: ACPI method failed: %s\n",
+			cmd_id, acpi_format_exception(status));
+		return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * acp7x_configure_carveout_pte - program ATU groups for ACP7.B/7.F carveout.
+ *
+ * Programs PAGE_SIZE, BASE_ADDR and PTE entries for each ATU group covering
+ * the carveout region using 2 MB pages with PAGE_Enable | MALL_Enable flags.
+ * Must be called after GET_CARVEOUT_ADDR and on every D0 resume since
+ * acp_init() resets the scratch SRAM and ATU registers.
+ */
+int acp7x_configure_carveout_pte(struct snd_sof_dev *sdev)
+{
+	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+	u64 phys_addr;
+	u32 grp_start = adata->asp_carveout_group_start;
+	u32 grp_count = adata->asp_carveout_group_count;
+	u32 total_pages, pages_per_group;
+	u32 grp, page_in_grp, page_idx;
+	u32 pte_scratch_offset, page_size_reg, base_addr_reg, reg_val;
+	u32 pte_lo_addr, pte_hi_addr;
+	u32 low, high;
+	u32 abs_grp;
+
+	if (!adata->asp_carveout_base) {
+		dev_err(sdev->dev, "carveout PTE: no valid carveout address\n");
+		return -EINVAL;
+	}
+
+	if (!grp_count || grp_start < 8 || grp_start + grp_count > 16) {
+		dev_err(sdev->dev, "carveout PTE: invalid ATU group range %u+%u\n",
+			grp_start, grp_count);
+		return -EINVAL;
+	}
+
+	/*
+	 * ASP is expected to allocate exactly four contiguous 8 MB ATU groups
+	 * for the fixed 32 MB carveout. Warn if the response differs so that
+	 * unexpected firmware behaviour is clearly visible in the log.
+	 */
+	if (grp_count != 4)
+		dev_warn(sdev->dev, "carveout PTE: expected 4 ATU groups, got %u\n",
+			 grp_count);
+
+	total_pages = adata->asp_carveout_size / ACP7X_CARVEOUT_PAGE_SIZE;
+	pages_per_group = total_pages / grp_count;
+	page_idx = 0;
+
+	for (grp = 0; grp < grp_count; grp++) {
+		abs_grp = grp_start + grp;
+
+		/*
+		 * asp_carveout_group_start is the 0-based index from
+		 * __ffs(mall_addr_valid): bit N in MallGroupEntries represents
+		 * hardware GRP_(N+1), so abs_grp is 0-based and GRP number is
+		 * (abs_grp + 1). Example: bits 11-14 set -> abs_grp 11..14 ->
+		 * programs GRP_12..GRP_15 (registers 0xC5C..0xC74).
+		 * ATU_CTRL (0xC40) sits between GRP_8 and GRP_9, adding a
+		 * 4-byte gap for abs_grp >= 8 (i.e. GRP_9 and above).
+		 */
+		if (abs_grp < 8)
+			page_size_reg = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 + abs_grp * 8;
+		else
+			page_size_reg = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 + abs_grp * 8 + 4;
+
+		base_addr_reg = page_size_reg + 4;
+
+		pte_scratch_offset = abs_grp * 0x20;
+		reg_val = desc->sram_pte_offset + pte_scratch_offset;
+
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, page_size_reg, PAGE_SIZE_2M_ENABLE);
+		snd_sof_dsp_write(sdev, ACP_DSP_BAR, base_addr_reg, reg_val | BIT(31));
+
+		dev_dbg(sdev->dev,
+			"carveout ATU: abs_grp=%u PAGE_SIZE_reg=0x%x BASE_ADDR_reg=0x%x val=0x%x\n",
+			abs_grp, page_size_reg, base_addr_reg,
+			(u32)(reg_val | BIT(31)));
+
+		for (page_in_grp = 0; page_in_grp < pages_per_group;
+		     page_in_grp++, page_idx++) {
+			pte_lo_addr = ACP_SCRATCH_REG_0 + pte_scratch_offset + (page_in_grp * 8);
+			pte_hi_addr = pte_lo_addr + 4;
+			phys_addr = adata->asp_carveout_base +
+				    ((u64)page_idx * ACP7X_CARVEOUT_PAGE_SIZE);
+			low  = lower_32_bits(phys_addr);
+			high = upper_32_bits(phys_addr) | ACP_ATU_PTE_CARVEOUT_FLAGS;
+
+			snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_lo_addr, low);
+			snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_hi_addr, high);
+		}
+	}
+
+	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
+
+	dev_dbg(sdev->dev,
+		"carveout PTE done: %u groups [%u..%u] covering 0x%llx..0x%llx\n",
+		grp_count, grp_start, grp_start + grp_count - 1,
+		adata->asp_carveout_base,
+		adata->asp_carveout_base + (u64)adata->asp_carveout_size);
+	return 0;
+}
+EXPORT_SYMBOL_NS(acp7x_configure_carveout_pte, "SND_SOC_SOF_AMD_COMMON");
+
+/*
+ * acp7x_alloc_asp_payload - allocate a physically contiguous buffer below 4 GB.
+ *
+ * ASP accesses memory via the SoC fabric, bypassing the IOMMU, so the buffer
+ * must be below 4 GB and page_to_phys() must return the real CPU physical
+ * address.  Use __GFP_DMA32/__GFP_DMA as hard zone constraints (not hints)
+ * to guarantee the physical address fits in 32 bits.
+ */
+static void *acp7x_alloc_asp_payload(struct snd_sof_dev *sdev, phys_addr_t *phys_out)
+{
+	struct page *page;
+	void *vaddr;
+
+	page = alloc_page(GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO);
+	if (!page)
+		page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
+	if (!page) {
+		dev_err(sdev->dev, "ASP mbox: failed to allocate below-4GB payload page\n");
+		return NULL;
+	}
+
+	vaddr = page_address(page);
+	*phys_out = page_to_phys(page);
+
+	if (*phys_out > 0xFFFFFFFFULL) {
+		dev_err(sdev->dev, "ASP mbox: page at phys=0x%llx still exceeds 4 GB\n",
+			(u64)*phys_out);
+		__free_page(page);
+		return NULL;
+	}
+
+	return vaddr;
+}
+
+static void acp7x_free_asp_payload(void *vaddr)
+{
+	if (vaddr)
+		__free_page(virt_to_page(vaddr));
+}
+
+int acp7x_query_asp_carveout(struct snd_sof_dev *sdev)
+{
+	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+	struct asp_get_carveout_payload *co_payload;
+	phys_addr_t phys;
+	u32 group_bits, first_bit, count;
+	int ret = 0;
+
+	co_payload = acp7x_alloc_asp_payload(sdev, &phys);
+	if (!co_payload)
+		return -ENOMEM;
+
+	co_payload->cookie       = ASP_MBOX_COOKIE;
+	/* Sentinel: ASP overwrites on success; 0xFFFFFFFF after the call means no carveout */
+	co_payload->carveout_size   = 0xFFFFFFFF;
+	co_payload->mall_addr_valid = 0xFFFFFFFF;
+	co_payload->mall_valid      = 0xFFFFFFFF;
+
+	adata->asp_mbox_buf     = co_payload;
+	adata->asp_mbox_buf_phys = phys;
+
+	ret = acp7x_asp_send_cmd(sdev, ASP_MBOX_CMD_GET_CARVEOUT_ADDR);
+
+	dev_dbg(sdev->dev,
+		"GET_CARVEOUT response: status=0x%x addr=0x%llx size=0x%x mall_valid=0x%x mall_addr_valid=0x%x\n",
+		 co_payload->status, co_payload->carveout_addr,
+		 co_payload->carveout_size, co_payload->mall_valid,
+		 co_payload->mall_addr_valid);
+
+	if (ret)
+		goto out_free;
+
+	if (co_payload->status || !co_payload->carveout_addr ||
+	    !co_payload->carveout_size || co_payload->carveout_size == 0xFFFFFFFF) {
+		dev_warn(sdev->dev,
+			 "ASP GET_CARVEOUT_ADDR: status=0x%x addr=0x%llx size=0x%x - no carveout\n",
+			 co_payload->status, co_payload->carveout_addr,
+			 co_payload->carveout_size);
+		ret = -ENODATA;
+		goto out_free;
+	}
+
+	if (!IS_ALIGNED(co_payload->carveout_addr, ACP7X_CARVEOUT_PAGE_SIZE) ||
+	    co_payload->carveout_size != ACP7X_CARVEOUT_MAX_SIZE) {
+		dev_warn(sdev->dev,
+			 "ASP carveout: unexpected base alignment or size (addr=0x%llx size=0x%x, expected 0x%x)\n",
+			 co_payload->carveout_addr, co_payload->carveout_size,
+			 ACP7X_CARVEOUT_MAX_SIZE);
+		ret = -ENODATA;
+		goto out_free;
+	}
+	/*
+	 * Extract ATU group start and count from MallGroupEntries bits[15:0].
+	 * Scan to find first set bit (start), then count
+	 * contiguous set bits. Example: 0x7800 -> start=11, count=4.
+	 * Defer committing asp_carveout_base/size until all validation passes
+	 * so adata is never left with a non-zero base but zero group_count.
+	 */
+	group_bits = co_payload->mall_addr_valid & 0xFFFF;
+	first_bit = 0;
+	count = 0;
+
+	if (group_bits) {
+		first_bit = __ffs(group_bits);
+		while ((group_bits >> (first_bit + count)) & 1)
+			count++;
+		/* Validate contiguous run - reject non-contiguous masks */
+		if (group_bits != (((1u << count) - 1) << first_bit)) {
+			dev_warn(sdev->dev,
+				 "ASP carveout: non-contiguous mall_addr_valid 0x%x unsupported\n",
+				 group_bits);
+			ret = -ENODATA;
+			goto out_free;
+		}
+	}
+	if (!count) {
+		dev_warn(sdev->dev, "ASP carveout: no valid ATU groups in mall_addr_valid\n");
+		ret = -ENODATA;
+		goto out_free;
+	}
+	adata->asp_carveout_base = co_payload->carveout_addr;
+	adata->asp_carveout_size = co_payload->carveout_size;
+	adata->asp_carveout_group_start = first_bit;
+	adata->asp_carveout_group_count = count;
+
+	dev_dbg(sdev->dev, "ASP carveout: base=0x%llx size=0x%x grp_start=%u grp_count=%u\n",
+		adata->asp_carveout_base, adata->asp_carveout_size,
+		adata->asp_carveout_group_start, adata->asp_carveout_group_count);
+
+out_free:
+	adata->asp_mbox_buf = NULL;
+	adata->asp_mbox_buf_phys = 0;
+	acp7x_free_asp_payload(co_payload);
+	return ret;
+}
+EXPORT_SYMBOL_NS(acp7x_query_asp_carveout, "SND_SOC_SOF_AMD_COMMON");
+
+int acp7x_load_firmware_carveout(struct snd_sof_dev *sdev)
+{
+	struct snd_sof_pdata *plat_data = sdev->pdata;
+	struct acp_dev_data *adata = plat_data->hw_pdata;
+	struct asp_validate_image_payload *vi_payload;
+	const char *fw_filename;
+	struct page *fw_page;
+	phys_addr_t vi_phys;
+	u64 dest;
+	u32 page_count, dma_size;
+	u32 size_fw_signed;
+	unsigned int order;
+	int ret;
+
+	/*
+	 * Carveout query was already done in amd_sof_acp7x_probe() via
+	 * acp7x_query_asp_carveout(). If ASP did not return a valid carveout
+	 * address, fail immediately - no fallback to legacy path.
+	 */
+
+	/* Program PTEs for the entire carveout region before any DMA into it.
+	 * acp_init() in resume clears the scratch SRAM so PTEs must be reprogrammed
+	 * on every boot including D3->D0 resume.
+	 */
+	ret = acp7x_configure_carveout_pte(sdev);
+	if (ret)
+		return ret;
+
+	/*
+	 * On D3->D0 resume the raw binary is already in the carveout region
+	 * (placed by ASP during the previous VALIDATE_IMAGE call). Skip the
+	 * filesystem load and VALIDATE_IMAGE - go directly to the data binary
+	 * path. acp_dsp_pre_fw_run() will SHA-DMA from the preserved carveout
+	 * address into DSP IRAM.
+	 *
+	 * On first boot fw_code_carveout_addr is 0 so the condition below is
+	 * false and the full firmware load path executes.
+	 */
+	if (adata->fw_code_carveout_addr)
+		goto load_data_binary;
+
+	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
+				plat_data->fw_filename_prefix, adata->fw_code_bin);
+	if (!fw_filename)
+		return -ENOMEM;
+
+	/* Release any previously loaded firmware before requesting again */
+	if (sdev->basefw.fw) {
+		release_firmware(sdev->basefw.fw);
+		sdev->basefw.fw = NULL;
+	}
+
+	ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
+	kfree(fw_filename);
+	if (ret < 0) {
+		dev_err(sdev->dev, "request_firmware %s failed: %d\n",
+			adata->fw_code_bin, ret);
+		return ret;
+	}
+
+	page_count = PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT;
+	dma_size = page_count * ACP_PAGE_SIZE;
+
+	/*
+	 * Allocate the firmware staging buffer below 4 GB using alloc_pages so
+	 * virt_to_phys() gives the real CPU physical address that ASP can read
+	 * via the SoC fabric (bypassing the IOMMU). dma_alloc_coherent() gives
+	 * an IOMMU-remapped DMA address which ASP cannot use.
+	 *
+	 * get_order() gives the smallest power-of-2 number of pages covering
+	 * dma_size; alloc_pages() allocates that contiguous range.
+	 */
+	order = get_order(dma_size);
+	fw_page = alloc_pages(GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO, order);
+	if (!fw_page)
+		fw_page = alloc_pages(GFP_KERNEL | __GFP_DMA | __GFP_ZERO, order);
+	if (!fw_page) {
+		dev_err(sdev->dev, "failed to allocate FW staging buffer\n");
+		release_firmware(sdev->basefw.fw);
+		sdev->basefw.fw = NULL;
+		return -ENOMEM;
+	}
+	adata->bin_buf     = page_address(fw_page);
+	adata->sha_dma_addr = page_to_phys(fw_page);
+
+	if (adata->sha_dma_addr > 0xFFFFFFFFULL) {
+		dev_err(sdev->dev,
+			"FW staging buffer at phys=0x%llx exceeds 4 GB\n",
+			(u64)adata->sha_dma_addr);
+		__free_pages(fw_page, order);
+		adata->bin_buf = NULL;
+		release_firmware(sdev->basefw.fw);
+		sdev->basefw.fw = NULL;
+		return -ENOMEM;
+	}
+
+	memcpy(adata->bin_buf, sdev->basefw.fw->data, sdev->basefw.fw->size);
+	adata->fw_bin_size = sdev->basefw.fw->size;
+
+	if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) {
+		dev_err(sdev->dev, "signed FW too small: %u\n", adata->fw_bin_size);
+		ret = -EINVAL;
+		goto free_bin;
+	}
+	/*
+	 * SizeFWSigned (header[0x14]) is the size of the signed payload
+	 * including the ASP signature trailer.  ASP strips the ACP image
+	 * header (0x100 bytes) but preserves the signature, so the raw
+	 * binary deposited in carveout has size = SizeFWSigned and the
+	 * SHA DMA length must equal SizeFWSigned.
+	 */
+	size_fw_signed = get_unaligned_le32(adata->bin_buf + ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF);
+
+	if (size_fw_signed <= ACP_ASP_SIGNATURE_LENGTH ||
+	    size_fw_signed > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) {
+		dev_err(sdev->dev,
+			"invalid SizeFWSigned 0x%x in carveout firmware header\n",
+			size_fw_signed);
+		ret = -EINVAL;
+		goto free_bin;
+	}
+	adata->fw_code_raw_size = size_fw_signed;
+	vi_payload = acp7x_alloc_asp_payload(sdev, &vi_phys);
+	if (!vi_payload) {
+		ret = -ENOMEM;
+		goto free_bin;
+	}
+
+	vi_payload->cookie = ASP_MBOX_COOKIE;
+	vi_payload->dest_offset = 0;
+	/*
+	 * ASP bypasses the CPU IOMMU and accesses memory via the SoC
+	 * fabric directly, so it must be given a real CPU physical
+	 * address for the staging buffer (not an IOMMU/DMA-translated
+	 * address). Use virt_to_phys(bin_buf) which returns the real
+	 * CPU physical address, same as for mailbox payload buffers.
+	 */
+	vi_payload->src_phys_addr = (u64)virt_to_phys(adata->bin_buf);
+	vi_payload->fw_image_size = adata->fw_bin_size;
+
+	adata->asp_mbox_buf = vi_payload;
+	adata->asp_mbox_buf_phys = vi_phys;
+
+	dev_dbg(sdev->dev, "sending VALIDATE_IMAGE(0x01): src=0x%llx size=0x%x dest_offset=0x%llx phys=0x%llx\n",
+		vi_payload->src_phys_addr, vi_payload->fw_image_size,
+		vi_payload->dest_offset, (u64)adata->asp_mbox_buf_phys);
+
+	ret = acp7x_asp_send_cmd(sdev, ASP_MBOX_CMD_VALIDATE_IMAGE);
+
+	/* Read result before freeing payload */
+	if (!ret && !vi_payload->status) {
+		dest = vi_payload->carveout_dest_addr;
+
+		if (dest < adata->asp_carveout_base ||
+		    dest - adata->asp_carveout_base + adata->fw_code_raw_size >
+		    adata->asp_carveout_size) {
+			dev_err(sdev->dev,
+				"VALIDATE_IMAGE: dest 0x%llx outside carveout window\n",
+				dest);
+			ret = -EIO;
+		} else {
+			adata->fw_code_carveout_addr = dest;
+		}
+	} else if (!ret && vi_payload->status) {
+		ret = -EIO;
+	}
+
+	adata->asp_mbox_buf     = NULL;
+	adata->asp_mbox_buf_phys = 0;
+	acp7x_free_asp_payload(vi_payload);
+
+	if (ret) {
+		if (ret == -EIO)
+			dev_err(sdev->dev, "ASP VALIDATE_IMAGE failed\n");
+		goto free_bin;
+	}
+
+	dev_dbg(sdev->dev, "ASP VALIDATE_IMAGE: raw binary at carveout 0x%llx size 0x%x\n",
+		adata->fw_code_carveout_addr, adata->fw_code_raw_size);
+
+	/* DDR staging buffer is no longer needed - raw binary is in carveout */
+	free_pages((unsigned long)adata->bin_buf, get_order(dma_size));
+	adata->bin_buf = NULL;
+
+load_data_binary:
+	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
+				plat_data->fw_filename_prefix, adata->fw_data_bin);
+	if (!fw_filename) {
+		release_firmware(sdev->basefw.fw);
+		sdev->basefw.fw = NULL;
+		return -ENOMEM;
+	}
+
+	ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev);
+	kfree(fw_filename);
+	if (ret < 0) {
+		dev_err(sdev->dev, "request_firmware %s failed: %d\n",
+			adata->fw_data_bin, ret);
+		release_firmware(sdev->basefw.fw);
+		sdev->basefw.fw = NULL;
+		return ret;
+	}
+
+	ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0,
+				      (void *)adata->fw_dbin->data,
+				      adata->fw_dbin->size);
+	release_firmware(adata->fw_dbin);
+	adata->fw_dbin = NULL;
+	release_firmware(sdev->basefw.fw);
+	sdev->basefw.fw = NULL;
+	return ret;
+
+free_bin:
+	free_pages((unsigned long)adata->bin_buf, get_order(dma_size));
+	adata->bin_buf = NULL;
+	release_firmware(sdev->basefw.fw);
+	sdev->basefw.fw = NULL;
+	return ret;
+}
+EXPORT_SYMBOL_NS(acp7x_load_firmware_carveout, "SND_SOC_SOF_AMD_COMMON");
-- 
2.48.1


  parent reply	other threads:[~2026-10-14 10:10 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14  9:59 [PATCH v2 00/10] ASoC: SOF/amd: ACP7.B support and ASP carveout firmware loading Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 01/10] ASoC: SOF: amd: rename acp7x machine table and descriptor to acp7f Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 02/10] ASoC: SOF: amd: select descriptor per PCI revision in acp7x probe Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 03/10] ASoC: amd: add I2S machine table for ACP7.B Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 04/10] ASoC: SOF: amd: add ACP7.B chip descriptor and wire per-revision probe Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 05/10] ASoC: SOF: amd: Propagate PCI subsystem Vendor and Device IDs Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 06/10] ASoC: SOF: amd: consolidate local variable declarations Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 07/10] ASoC: amd: acp: add acp-mach-common.h for shared machine driver types Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 08/10] ASoC: amd: acp: move ACP7X_PDM_DMIC0/1 to acp-mach-common.h Vijendar Mukunda
2026-10-14  9:59 ` [PATCH v2 09/10] ASoC: SOF: amd: propagate pdm_sel to SDW machine driver via mach->pdata Vijendar Mukunda
2026-10-14  9:59 ` Vijendar Mukunda [this message]
2026-10-14 10:44 ` [PATCH v2 00/10] ASoC: SOF/amd: ACP7.B support and ASP carveout firmware loading Mark Brown
2026-10-14 11:51   ` Mukunda,Vijendar
2026-10-14 11:55     ` Mark Brown
2026-10-14 12:10       ` Mukunda,Vijendar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261014100902.1215212-11-Vijendar.Mukunda@amd.com \
    --to=vijendar.mukunda@amd.com \
    --cc=Basavaraj.Hiregoudar@amd.com \
    --cc=Mario.Limonciello@amd.com \
    --cc=Poojitha.N@amd.com \
    --cc=Richard.Gong@amd.com \
    --cc=Sunil-kumar.Dommati@amd.com \
    --cc=Syed.SabaKareem@amd.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=sound-open-firmware@alsa-project.org \
    --cc=tiwai@suse.com \
    --cc=venkataprasad.potturu@amd.com \
    --cc=yung-chuan.liao@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®