mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ASoC: SOF/amd: ACP7.B support and ASP carveout firmware loading
Vijendar Mukunda <Vijendar.Mukunda@amd.com> says: This series adds ACP7.B platform support to the AMD SOF driver stack and implements the ASP-authenticated firmware load path required by both ACP7.B and ACP7.F. Patches 1-2 are preparatory renames and restructuring. The existing acp7x machine table and chip descriptor are renamed to acp7f to accurately reflect the hardware they target (ACP7.F). The probe callback is restructured to select the per-revision descriptor via a dedicated static pci_device_id entry rather than encoding it in driver_data of the shared ID table. Patches 3-4 add ACP7.B hardware support. Patch 3 adds the ACP7.B I2S machine table with its driver name, firmware image, and topology file. Patch 4 adds the acp7b chip descriptor and extends acp7x_pci_probe() to select it for ACP7B_PCI_ID revisions alongside the existing acp7f_desc path. Patch 5 propagates the PCI subsystem Vendor and Device IDs from the AMD SOF machine driver into mach_params, enabling downstream SDW machine drivers to use them as an SSID. Patch 6 is a cleanup: register variable declarations in the ACP7.x SDW wake and PME handlers are consolidated to u32. Patches 7-8 address PDM controller selection for ACP7.B/7.F. Patch 7 introduces acp-mach-common.h as a minimal shared header for types used across all AMD ACP machine driver variants (I2S, SoundWire, and SOF), and moves struct amd_pdm_pdata there from the SoundWire-specific soc_amd_sdw_common.h. Patch 8 fixes a gap in the SDW machine select path where pdm_sel was not propagated to the machine driver, preventing acp-sdw-sof-mach.c from configuring the correct DMIC DAI link on ACP7.B/7.F platforms. Patch 9 implements the two-stage ASP carveout firmware load path for ACP7.B and ACP7.F. At probe time the driver queries ASP for the physical base address and ATU group of a pre-allocated 32 MB MALL carveout region; probe fails immediately if ASP does not support carveout. At load time the signed firmware image is sent to ASP via the MPASP mailbox VALIDATE_IMAGE command. ASP authenticates the image and deposits the raw binary into the carveout; the driver then SHA-DMAs from the carveout into DSP IRAM. On D3->D0 resume the carveout content is preserved, so the ASP authentication step is skipped and the driver restores directly from the saved carveout address. Link: https://patch.msgid.link/20261013154644.3788064-1-Vijendar.Mukunda@amd.com
This commit is contained in:
commit
bcff584bd2
|
|
@ -428,17 +428,29 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = {
|
|||
};
|
||||
EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines);
|
||||
|
||||
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = {
|
||||
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7f_sof_machines[] = {
|
||||
{
|
||||
.id = "AMDI1010",
|
||||
.drv_name = "acp7x-dsp",
|
||||
.drv_name = "acp7f-dsp",
|
||||
.pdata = &acp_quirk_data,
|
||||
.fw_filename = "sof-acp7x.ri",
|
||||
.sof_tplg_filename = "sof-acp7x.tplg",
|
||||
.fw_filename = "sof-acp7f.ri",
|
||||
.sof_tplg_filename = "sof-acp7f.tplg",
|
||||
},
|
||||
{},
|
||||
};
|
||||
EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines);
|
||||
EXPORT_SYMBOL(snd_soc_acpi_amd_acp7f_sof_machines);
|
||||
|
||||
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7b_sof_machines[] = {
|
||||
{
|
||||
.id = "AMDI1010",
|
||||
.drv_name = "acp7b-dsp",
|
||||
.pdata = &acp_quirk_data,
|
||||
.fw_filename = "sof-acp7b.ri",
|
||||
.sof_tplg_filename = "sof-acp7b.tplg",
|
||||
},
|
||||
{},
|
||||
};
|
||||
EXPORT_SYMBOL(snd_soc_acpi_amd_acp7b_sof_machines);
|
||||
|
||||
MODULE_DESCRIPTION("AMD ACP Machine Configuration Module");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
|
|||
25
sound/soc/amd/acp/acp-mach-common.h
Normal file
25
sound/soc/amd/acp/acp-mach-common.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only
|
||||
* Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved
|
||||
*/
|
||||
/*
|
||||
* acp-mach-common.h - Structures shared across all AMD ACP machine drivers
|
||||
* (I2S, SoundWire, and SOF paths).
|
||||
*/
|
||||
|
||||
#ifndef __ACP_MACH_COMMON_H
|
||||
#define __ACP_MACH_COMMON_H
|
||||
|
||||
/**
|
||||
* struct amd_pdm_pdata - PDM controller platform data passed via mach->pdata
|
||||
* @pdm_sel: active PDM controller (ACP7X_PDM_DMIC0 or ACP7X_PDM_DMIC1),
|
||||
* non-zero when a PDM controller was identified via ACPI _DSD for
|
||||
* ACP7.B/7.F platforms.
|
||||
*
|
||||
* Passed via mach->pdata by machine select logic to both I2S and SoundWire
|
||||
* machine drivers so each can configure the correct DMIC DAI link.
|
||||
*/
|
||||
struct amd_pdm_pdata {
|
||||
unsigned int pdm_sel;
|
||||
};
|
||||
|
||||
#endif /* __ACP_MACH_COMMON_H */
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc_sdw_utils.h>
|
||||
#include "acp-mach-common.h"
|
||||
|
||||
#define ACP63_SDW_MAX_CPU_DAIS 8
|
||||
#define ACP63_SDW_MAX_LINKS 2
|
||||
|
|
@ -23,19 +24,6 @@
|
|||
#define ACP71_PCI_REV 0x71
|
||||
#define ACP72_PCI_REV 0x72
|
||||
|
||||
/**
|
||||
* struct amd_pdm_pdata - platform data passed via mach->pdata to machine driver
|
||||
* @pdm_sel: active PDM controller (ACP7X_PDM_DMIC0 or ACP7X_PDM_DMIC1),
|
||||
* non-zero when a PDM controller was identified via ACPI _DSD
|
||||
*
|
||||
* Carries the PDM controller selection for ACP7.B/7.F platforms, derived
|
||||
* from the acp-audio-ep-port ACPI _DSD property and passed via mach->pdata
|
||||
* to the machine driver.
|
||||
*/
|
||||
struct amd_pdm_pdata {
|
||||
unsigned int pdm_sel;
|
||||
};
|
||||
|
||||
#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
|
||||
#define ASOC_SDW_FOUR_SPK BIT(4)
|
||||
#define ASOC_SDW_ACP_DMIC BIT(5)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
|
|||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7b_sof_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7f_sof_machines[];
|
||||
|
||||
struct config_entry {
|
||||
u32 flags;
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ static int snd_acp6x_suspend(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int snd_acp6x_resume(struct device *dev)
|
||||
static int snd_acp6x_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct acp6x_dev_data *adata;
|
||||
int ret;
|
||||
|
|
@ -313,8 +313,42 @@ static int snd_acp6x_resume(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void acp6x_disable_msi_on_resume(struct pci_dev *pdev)
|
||||
{
|
||||
u16 control;
|
||||
|
||||
if (!pdev->msi_cap)
|
||||
return;
|
||||
|
||||
pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control);
|
||||
if (control & PCI_MSI_FLAGS_ENABLE) {
|
||||
dev_warn(&pdev->dev,
|
||||
"ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n",
|
||||
control);
|
||||
control &= ~PCI_MSI_FLAGS_ENABLE;
|
||||
pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_acp6x_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
/*
|
||||
* BIOS/firmware may re-enable MSI in PCI config space during
|
||||
* system resume even though this driver only uses legacy INTx
|
||||
* interrupts. If MSI is left enabled with stale address/data
|
||||
* registers, the device will write interrupts to a bogus address
|
||||
* causing IOMMU IO_PAGE_FAULT and interrupt delivery failure.
|
||||
* Explicitly clear the MSI Enable bit before reinitializing
|
||||
* the ACP hardware.
|
||||
*/
|
||||
acp6x_disable_msi_on_resume(pdev);
|
||||
return snd_acp6x_runtime_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops acp6x_pm = {
|
||||
RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
|
||||
RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ static const char * const cs42l52_ng_type_text[] = {
|
|||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(ng_type_enum,
|
||||
CS42L52_NOISE_GATE_CTL, 6,
|
||||
CS42L52_NOISE_GATE_CTL, 7,
|
||||
cs42l52_ng_type_text);
|
||||
|
||||
static const char * const left_swap_text[] = {
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
|
|||
0, 0x3f, 0, 0),
|
||||
SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL,
|
||||
5, 0x07, 1, alc_tlv),
|
||||
SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD,
|
||||
SOC_SINGLE_TLV("Limiter Cushion", CS42L56_LIM_THRESHOLD_CTL,
|
||||
2, 0x07, 1, alc_tlv),
|
||||
|
||||
SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1),
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ static const struct snd_kcontrol_new max98371_snd_controls[] = {
|
|||
0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
|
||||
max98371_dht_max_gain),
|
||||
SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
|
||||
0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
|
||||
4, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
|
||||
max98371_dht_min_gain),
|
||||
SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
|
||||
0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
|
||||
|
|
|
|||
|
|
@ -383,9 +383,9 @@ static const struct snd_kcontrol_new nau8822_snd_controls[] = {
|
|||
NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv),
|
||||
|
||||
SOC_SINGLE("DAC 128x Oversampling Switch",
|
||||
NAU8822_REG_DAC_CONTROL, 5, 1, 0),
|
||||
NAU8822_REG_DAC_CONTROL, 3, 1, 0),
|
||||
SOC_SINGLE("ADC 128x Oversampling Switch",
|
||||
NAU8822_REG_ADC_CONTROL, 5, 1, 0),
|
||||
NAU8822_REG_ADC_CONTROL, 3, 1, 0),
|
||||
};
|
||||
|
||||
/* LMAIN and RMAIN Mixer */
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
|
|||
static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
|
||||
PCM3168A_DAC_ZREV_SHIFT, pcm3168a_pol);
|
||||
|
||||
static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
|
||||
|
||||
|
|
|
|||
|
|
@ -255,9 +255,9 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
|
|||
|
||||
/* DAC / ADC oversampling */
|
||||
SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
|
||||
5, 1, 0),
|
||||
3, 1, 0),
|
||||
SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
|
||||
5, 1, 0),
|
||||
3, 1, 0),
|
||||
};
|
||||
|
||||
/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
|
||||
|
|
|
|||
|
|
@ -756,7 +756,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
|
|||
data = snd_soc_dai_get_dma_data(dai, substream);
|
||||
host_stream = data->host_stream;
|
||||
|
||||
if (runtime->state == SNDRV_PCM_STATE_XRUN)
|
||||
if (runtime->state != SNDRV_PCM_STATE_PREPARED)
|
||||
hdac_stream(host_stream)->prepared = false;
|
||||
if (hdac_stream(host_stream)->prepared)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *
|
|||
struct snd_soc_acpi_mach *mach;
|
||||
const struct snd_soc_acpi_link_adr *link;
|
||||
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
|
||||
struct amd_pdm_pdata *pdm_pdata;
|
||||
int ret, i;
|
||||
|
||||
if (acp_data->info.count) {
|
||||
|
|
@ -155,9 +156,25 @@ static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *
|
|||
}
|
||||
if (mach && mach->link_mask) {
|
||||
mach->mach_params.subsystem_rev = acp_data->pci_rev;
|
||||
mach->mach_params.subsystem_vendor = acp_data->subsystem_vendor;
|
||||
mach->mach_params.subsystem_device = acp_data->subsystem_device;
|
||||
mach->mach_params.subsystem_id_set = true;
|
||||
mach->mach_params.links = mach->links;
|
||||
mach->mach_params.link_mask = mach->link_mask;
|
||||
mach->mach_params.platform = dev_name(sdev->dev);
|
||||
|
||||
if (acp_data->pdm_sel) {
|
||||
pdm_pdata = devm_kzalloc(sdev->dev,
|
||||
sizeof(*pdm_pdata),
|
||||
GFP_KERNEL);
|
||||
if (!pdm_pdata)
|
||||
return NULL;
|
||||
pdm_pdata->pdm_sel = acp_data->pdm_sel;
|
||||
mach->pdata = pdm_pdata;
|
||||
}
|
||||
|
||||
dev_dbg(sdev->dev, "SSID %x%04x\n", mach->mach_params.subsystem_vendor,
|
||||
mach->mach_params.subsystem_device);
|
||||
return mach;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -674,9 +713,8 @@ static int sof_amd_check_and_handle_acp7x_sdw_wake_irq(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);
|
||||
unsigned int ext_intr_stat1;
|
||||
unsigned int sdw_pme_stat, sdw_wake_en;
|
||||
unsigned int i;
|
||||
u32 ext_intr_stat1, sdw_pme_stat, sdw_wake_en;
|
||||
u32 i;
|
||||
bool sdw_wake_irq = false;
|
||||
|
||||
ext_intr_stat1 = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
|
||||
|
|
@ -769,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);
|
||||
|
|
@ -1198,7 +1250,10 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
|
|||
adata->addr = addr;
|
||||
adata->reg_range = chip->reg_end_addr - chip->reg_start_addr;
|
||||
adata->pci_rev = pci->revision;
|
||||
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);
|
||||
|
|
@ -1335,7 +1390,10 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
|
|||
adata->addr = addr;
|
||||
adata->reg_range = chip->reg_end_addr - chip->reg_start_addr;
|
||||
adata->pci_rev = pci->revision;
|
||||
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);
|
||||
|
|
@ -1408,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:
|
||||
|
|
@ -1441,10 +1513,7 @@ static void handle_amd_sof_acp7x_sdw_pme_event(struct snd_sof_dev *sdev)
|
|||
{
|
||||
struct acp_dev_data *adata;
|
||||
struct amd_sdw_manager *amd_manager;
|
||||
u32 sdw_pme_stat;
|
||||
u32 sdw_wake_en;
|
||||
u32 pme_reg;
|
||||
u32 wake_mask;
|
||||
u32 sdw_pme_stat, sdw_wake_en, pme_reg, wake_mask;
|
||||
unsigned int instance;
|
||||
|
||||
adata = sdev->pdata->hw_pdata;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -139,6 +165,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,
|
||||
|
|
@ -152,6 +202,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,7 +369,36 @@ struct acp_dev_data {
|
|||
unsigned int pdm_sel;
|
||||
bool is_sdw_dev;
|
||||
unsigned int pci_rev;
|
||||
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);
|
||||
|
|
@ -319,6 +421,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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
559
sound/soc/sof/amd/acp_asp_mailbox.c
Normal file
559
sound/soc/sof/amd/acp_asp_mailbox.c
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
// 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");
|
||||
|
||||
|
|
@ -26,8 +26,8 @@
|
|||
#define ACP7X_REG_START 0x1240000
|
||||
#define ACP7X_REG_END 0x125C000
|
||||
|
||||
static const struct sof_amd_acp_desc acp7x_chip_info = {
|
||||
.name = "acp7x",
|
||||
static const struct sof_amd_acp_desc acp7b_chip_info = {
|
||||
.name = "acp7b",
|
||||
.pgfsm_base = ACP7X_PGFSM_BASE,
|
||||
.ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB,
|
||||
.ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL,
|
||||
|
|
@ -45,13 +45,13 @@ static const struct sof_amd_acp_desc acp7x_chip_info = {
|
|||
.reg_end_addr = ACP7X_REG_END,
|
||||
};
|
||||
|
||||
static const struct sof_dev_desc acp7x_desc = {
|
||||
.machines = snd_soc_acpi_amd_acp7x_sof_machines,
|
||||
static const struct sof_dev_desc acp7b_desc = {
|
||||
.machines = snd_soc_acpi_amd_acp7b_sof_machines,
|
||||
.resindex_lpe_base = 0,
|
||||
.resindex_pcicfg_base = -1,
|
||||
.resindex_imr_base = -1,
|
||||
.irqindex_host_ipc = -1,
|
||||
.chip_info = &acp7x_chip_info,
|
||||
.chip_info = &acp7b_chip_info,
|
||||
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
|
||||
.ipc_default = SOF_IPC_TYPE_3,
|
||||
.default_fw_path = {
|
||||
|
|
@ -61,30 +61,81 @@ static const struct sof_dev_desc acp7x_desc = {
|
|||
[SOF_IPC_TYPE_3] = "amd/sof-tplg",
|
||||
},
|
||||
.default_fw_filename = {
|
||||
[SOF_IPC_TYPE_3] = "sof-acp7x.ri",
|
||||
[SOF_IPC_TYPE_3] = "sof-acp7b.ri",
|
||||
},
|
||||
.nocodec_tplg_filename = "sof-acp.tplg",
|
||||
.ops = &sof_acp7x_ops,
|
||||
.ops_init = sof_acp7x_ops_init,
|
||||
};
|
||||
|
||||
static const struct sof_amd_acp_desc acp7f_chip_info = {
|
||||
.name = "acp7f",
|
||||
.pgfsm_base = ACP7X_PGFSM_BASE,
|
||||
.ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB,
|
||||
.ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL,
|
||||
.ext_intr_stat = ACP7X_EXT_INTR_STAT,
|
||||
.ext_intr_stat1 = ACP7X_EXT_INTR_STAT1,
|
||||
.dsp_intr_base = ACP7X_DSP_SW_INTR_BASE,
|
||||
.acp_error_stat = ACP7X_ERROR_STATUS,
|
||||
.sram_pte_offset = ACP7X_SRAM_PTE_OFFSET,
|
||||
.hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0,
|
||||
.fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL,
|
||||
.probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0,
|
||||
.reg_start_addr = ACP7X_REG_START,
|
||||
.sdw_max_link_count = ACP7X_SDW_MAX_MANAGER_COUNT,
|
||||
.sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP7X,
|
||||
.reg_end_addr = ACP7X_REG_END,
|
||||
};
|
||||
|
||||
static const struct sof_dev_desc acp7f_desc = {
|
||||
.machines = snd_soc_acpi_amd_acp7f_sof_machines,
|
||||
.resindex_lpe_base = 0,
|
||||
.resindex_pcicfg_base = -1,
|
||||
.resindex_imr_base = -1,
|
||||
.irqindex_host_ipc = -1,
|
||||
.chip_info = &acp7f_chip_info,
|
||||
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
|
||||
.ipc_default = SOF_IPC_TYPE_3,
|
||||
.default_fw_path = {
|
||||
[SOF_IPC_TYPE_3] = "amd/sof",
|
||||
},
|
||||
.default_tplg_path = {
|
||||
[SOF_IPC_TYPE_3] = "amd/sof-tplg",
|
||||
},
|
||||
.default_fw_filename = {
|
||||
[SOF_IPC_TYPE_3] = "sof-acp7f.ri",
|
||||
},
|
||||
.nocodec_tplg_filename = "sof-acp.tplg",
|
||||
.ops = &sof_acp7x_ops,
|
||||
.ops_init = sof_acp7x_ops_init,
|
||||
};
|
||||
|
||||
static const struct pci_device_id acp7b_pci_id = {
|
||||
PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
|
||||
.driver_data = (unsigned long)&acp7b_desc,
|
||||
};
|
||||
|
||||
static const struct pci_device_id acp7f_pci_id = {
|
||||
PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
|
||||
.driver_data = (unsigned long)&acp7f_desc,
|
||||
};
|
||||
|
||||
static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
||||
{
|
||||
unsigned int flag;
|
||||
|
||||
switch (pci->revision) {
|
||||
case ACP7B_PCI_ID:
|
||||
case ACP7F_PCI_ID:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
flag = snd_amd_acp_find_config(pci);
|
||||
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
|
||||
return -ENODEV;
|
||||
|
||||
return sof_pci_probe(pci, pci_id);
|
||||
switch (pci->revision) {
|
||||
case ACP7B_PCI_ID:
|
||||
return sof_pci_probe(pci, &acp7b_pci_id);
|
||||
case ACP7F_PCI_ID:
|
||||
return sof_pci_probe(pci, &acp7f_pci_id);
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static void acp7x_pci_remove(struct pci_dev *pci)
|
||||
|
|
@ -94,8 +145,7 @@ static void acp7x_pci_remove(struct pci_dev *pci)
|
|||
|
||||
/* PCI IDs */
|
||||
static const struct pci_device_id acp7x_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
|
||||
.driver_data = (unsigned long)&acp7x_desc},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID) },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, acp7x_pci_ids);
|
||||
|
|
|
|||
|
|
@ -2476,11 +2476,14 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
|
|||
ch_map >>= 4;
|
||||
}
|
||||
|
||||
if (swidget->id == snd_soc_dapm_dai_in && ch_count == out_ref_channels) {
|
||||
if ((swidget->id == snd_soc_dapm_dai_in && ch_count == out_ref_channels) ||
|
||||
ch_count < blob->alh_cfg.device_count) {
|
||||
/*
|
||||
* For playback DAI widgets where the channel number is equal to
|
||||
* the output reference channels, set the step = 0 to ensure all
|
||||
* the ch_mask is applied to all alh mappings.
|
||||
* the output reference channels, or when there are fewer
|
||||
* channels than aggregated devices (e.g. a mono capture
|
||||
* stream split across multiple links), set step = 0 to
|
||||
* ensure all the ch_mask is applied to all alh mappings.
|
||||
*/
|
||||
mask = ch_mask;
|
||||
step = 0;
|
||||
|
|
|
|||
|
|
@ -576,7 +576,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
|
|||
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
|
||||
struct sof_ipc_event_entry *event;
|
||||
|
||||
guard(mutex)(&sdev->ipc_client_mutex);
|
||||
guard(mutex)(&sdev->client_event_handler_mutex);
|
||||
|
||||
list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
|
||||
if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
|
||||
|
|
@ -593,7 +593,7 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
|
|||
{
|
||||
struct sof_state_event_entry *event;
|
||||
|
||||
guard(mutex)(&sdev->ipc_client_mutex);
|
||||
guard(mutex)(&sdev->client_event_handler_mutex);
|
||||
|
||||
list_for_each_entry(event, &sdev->fw_state_handler_list, list)
|
||||
event->callback(event->cdev, sdev->fw_state);
|
||||
|
|
@ -628,7 +628,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
|
|||
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
|
||||
struct sof_state_event_entry *event;
|
||||
|
||||
guard(mutex)(&sdev->ipc_client_mutex);
|
||||
guard(mutex)(&sdev->client_event_handler_mutex);
|
||||
|
||||
list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
|
||||
if (event->cdev == cdev) {
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
|
|||
|
||||
if (err) {
|
||||
regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr);
|
||||
sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) &&
|
||||
sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) ==
|
||||
SPDIFRX_SPDIFEN_SYNC;
|
||||
|
||||
/* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user