Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
# 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) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
config SND_SOC_SOF_AMD_TOPLEVEL
tristate "SOF support for AMD audio DSPs"
depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
depends on X86 || COMPILE_TEST
help
This adds support for Sound Open Firmware for AMD platforms.
Say Y if you have such a device.
If unsure select "N".
if SND_SOC_SOF_AMD_TOPLEVEL
config SND_SOC_SOF_AMD_COMMON
tristate
select SND_SOC_SOF
select SND_SOC_SOF_IPC3
select SND_SOC_SOF_PCI_DEV
select SND_AMD_ACP_CONFIG
select SND_SOC_SOF_XTENSA
select SND_SOC_SOF_ACP_PROBES
select SND_SOC_ACPI_AMD_MATCH
select SND_SOC_ACPI if ACPI
help
This option is not user-selectable but automatically handled by
'select' statements at a higher level
config SND_SOC_SOF_AMD_RENOIR
tristate "SOF support for RENOIR"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
help
Select this option for SOF support on AMD Renoir platform
config SND_SOC_SOF_AMD_VANGOGH
tristate "SOF support for VANGOGH"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
help
Select this option for SOF support
on AMD Vangogh platform.
Say Y if you want to enable SOF on Vangogh.
If unsure select "N".
config SND_SOC_SOF_AMD_REMBRANDT
tristate "SOF support for REMBRANDT"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
help
Select this option for SOF support on AMD Rembrandt platform
Say Y if you want to enable SOF on Rembrandt.
If unsure select "N".
config SND_SOC_SOF_ACP_PROBES
tristate
select SND_SOC_SOF_DEBUG_PROBES
help
This option is not user-selectable but automatically handled by
'select' statements at a higher level
config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
tristate
select SND_AMD_SOUNDWIRE_ACPI if ACPI
config SND_SOC_SOF_AMD_SOUNDWIRE
tristate "SOF support for SoundWire based AMD platforms"
default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
depends on ACPI
depends on SOUNDWIRE_AMD
help
This adds support for SoundWire with Sound Open Firmware
for AMD platforms.
Say Y if you want to enable SoundWire links with SOF.
If unsure select "N".
config SND_SOC_SOF_AMD_ACP63
tristate "SOF support for ACP6.3 platform"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
help
Select this option for SOF support on
AMD ACP6.3 version based platforms.
Say Y if you want to enable SOF on ACP6.3 based platform.
If unsure select "N".
config SND_SOC_SOF_AMD_ACP70
tristate "SOF support for ACP7.0/ACP7.1 platforms"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
help
Select this option for SOF support on
AMD ACP7.0/ACP7.1 version based platforms.
Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms.
endif
+20
View File
@@ -0,0 +1,20 @@
# 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) 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-$(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
snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o
snd-sof-amd-acp63-y := pci-acp63.o acp63.o
snd-sof-amd-acp70-y := pci-acp70.o acp70.o
obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o
obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o
obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o
obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o
obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o
obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o
+268
View File
@@ -0,0 +1,268 @@
// 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) 2022 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
// V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
/* ACP-specific Common code */
#include "../sof-priv.h"
#include "../sof-audio.h"
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#include <sound/sof/xtensa.h>
/**
* amd_sof_ipc_dump() - This function is called when IPC tx times out.
* @sdev: SOF device.
*/
void amd_sof_ipc_dump(struct snd_sof_dev *sdev)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
u32 base = desc->dsp_intr_base;
u32 dsp_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
u32 dsp_ack_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
u32 host_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_host_msg_write);
u32 host_ack_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_host_ack_write);
u32 dsp_msg, dsp_ack, host_msg, host_ack, irq_stat;
dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
host_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg_write);
host_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_ack_write);
irq_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
dev_err(sdev->dev,
"dsp_msg = %#x dsp_ack = %#x host_msg = %#x host_ack = %#x irq_stat = %#x\n",
dsp_msg, dsp_ack, host_msg, host_ack, irq_stat);
}
/**
* amd_get_registers() - This function is called in case of DSP oops
* in order to gather information about the registers, filename and
* linenumber and stack.
* @sdev: SOF device.
* @xoops: Stores information about registers.
* @panic_info: Stores information about filename and line number.
* @stack: Stores the stack dump.
* @stack_words: Size of the stack dump.
*/
static void amd_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_dsp_oops_xtensa *xoops,
struct sof_ipc_panic_info *panic_info,
u32 *stack, size_t stack_words)
{
u32 offset = sdev->dsp_oops_offset;
/* first read registers */
acp_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
/* then get panic info */
if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
xoops->arch_hdr.totalsize);
return;
}
offset += xoops->arch_hdr.totalsize;
acp_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
/* then get the stack */
offset += sizeof(*panic_info);
acp_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
}
/**
* amd_sof_dump() - This function is called when a panic message is
* received from the firmware.
* @sdev: SOF device.
* @flags: parameter not used but required by ops prototype
*/
void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags)
{
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[AMD_STACK_DUMP_SIZE];
u32 status;
/* Get information about the panic status from the debug box area.
* Compute the trace point based on the status.
*/
if (sdev->dsp_oops_offset > sdev->debug_box.offset) {
acp_mailbox_read(sdev, sdev->debug_box.offset, &status, sizeof(u32));
} else {
/* Read DSP Panic status from dsp_box.
* As window information for exception box offset and size is not available
* before FW_READY
*/
acp_mailbox_read(sdev, sdev->dsp_box.offset, &status, sizeof(u32));
sdev->dsp_oops_offset = sdev->dsp_box.offset + sizeof(status);
}
/* Get information about the registers, the filename and line
* number and the stack.
*/
amd_get_registers(sdev, &xoops, &panic_info, stack, AMD_STACK_DUMP_SIZE);
/* Print the information to the console */
sof_print_oops_and_stack(sdev, KERN_ERR, status, status, &xoops,
&panic_info, stack, AMD_STACK_DUMP_SIZE);
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
static int amd_sof_sdw_get_slave_info(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
return sdw_amd_get_slave_info(acp_data->sdw);
}
static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
{
struct snd_soc_acpi_mach *mach;
const struct snd_soc_acpi_link_adr *link;
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
int ret, i;
if (acp_data->info.count) {
ret = amd_sof_sdw_get_slave_info(sdev);
if (ret) {
dev_info(sdev->dev, "failed to read slave information\n");
return NULL;
}
for (mach = sdev->pdata->desc->alt_machines; mach; mach++) {
if (!mach->links)
break;
link = mach->links;
for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link,
acp_data->sdw->peripherals))
break;
}
if (i == acp_data->info.count || !link->num_adr)
if (!mach->machine_check || mach->machine_check(acp_data->sdw))
break;
}
if (mach && mach->link_mask) {
mach->mach_params.subsystem_rev = acp_data->pci_rev;
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
mach->mach_params.platform = dev_name(sdev->dev);
return mach;
}
}
dev_info(sdev->dev, "No SoundWire machine driver found\n");
return NULL;
}
#else
static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
{
return NULL;
}
#endif
struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_soc_acpi_mach *mach = NULL;
if (desc->machines)
mach = snd_soc_acpi_find_machine(desc->machines);
if (!mach) {
mach = amd_sof_sdw_machine_select(sdev);
if (!mach) {
dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
return NULL;
}
}
mach->mach_params.subsystem_rev = acp_data->pci_rev;
sof_pdata->tplg_filename = mach->sof_tplg_filename;
sof_pdata->fw_filename = mach->fw_filename;
return mach;
}
/* AMD Common DSP ops */
const struct snd_sof_dsp_ops sof_acp_common_ops = {
/* probe and remove */
.probe = amd_sof_acp_probe,
.remove = amd_sof_acp_remove,
/* Register IO */
.write = sof_io_write,
.read = sof_io_read,
/* Block IO */
.block_read = acp_dsp_block_read,
.block_write = acp_dsp_block_write,
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
.pre_fw_run = acp_dsp_pre_fw_run,
.get_bar_index = acp_get_bar_index,
/* DSP core boot */
.run = acp_sof_dsp_run,
/*IPC */
.send_msg = acp_sof_ipc_send_msg,
.ipc_msg_data = acp_sof_ipc_msg_data,
.set_stream_data_offset = acp_set_stream_data_offset,
.get_mailbox_offset = acp_sof_ipc_get_mailbox_offset,
.get_window_offset = acp_sof_ipc_get_window_offset,
.irq_thread = acp_sof_ipc_irq_thread,
/* stream callbacks */
.pcm_open = acp_pcm_open,
.pcm_close = acp_pcm_close,
.pcm_hw_params = acp_pcm_hw_params,
.pcm_pointer = acp_pcm_pointer,
.hw_info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
/* Machine driver callbacks */
.machine_select = amd_sof_machine_select,
.machine_register = sof_machine_register,
.machine_unregister = sof_machine_unregister,
/* Trace Logger */
.trace_init = acp_sof_trace_init,
.trace_release = acp_sof_trace_release,
/* PM */
.suspend = amd_sof_acp_suspend,
.resume = amd_sof_acp_resume,
.ipc_dump = amd_sof_ipc_dump,
.dbg_dump = amd_sof_dump,
.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
.dsp_arch_ops = &sof_xtensa_arch_ops,
/* probe client device registation */
.register_ipc_clients = acp_probes_register,
.unregister_ipc_clients = acp_probes_unregister,
};
EXPORT_SYMBOL_NS(sof_acp_common_ops, "SND_SOC_SOF_AMD_COMMON");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("ACP SOF COMMON Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
@@ -0,0 +1,143 @@
/* 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) 2021, 2023, 2024 Advanced Micro Devices, Inc. All rights reserved.
*
* Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
*/
#ifndef _ACP_DSP_IP_OFFSET_H
#define _ACP_DSP_IP_OFFSET_H
/* Registers from ACP_DMA_0 block */
#define ACP_DMA_CNTL_0 0x00
#define ACP_DMA_DSCR_STRT_IDX_0 0x20
#define ACP_DMA_DSCR_CNT_0 0x40
#define ACP_DMA_PRIO_0 0x60
#define ACP_DMA_CUR_DSCR_0 0x80
#define ACP_DMA_ERR_STS_0 0xC0
#define ACP_DMA_DESC_BASE_ADDR 0xE0
#define ACP_DMA_DESC_MAX_NUM_DSCR 0xE4
#define ACP_DMA_CH_STS 0xE8
#define ACP_DMA_CH_GROUP 0xEC
#define ACP_DMA_CH_RST_STS 0xF0
#define ACP70_DMA_CNTL_0 0x00
#define ACP70_DMA_DSCR_STRT_IDX_0 0x28
#define ACP70_DMA_DSCR_CNT_0 0x50
#define ACP70_DMA_PRIO_0 0x78
#define ACP70_DMA_CUR_DSCR_0 0xA0
#define ACP70_DMA_ERR_STS_0 0xF0
#define ACP70_DMA_DESC_BASE_ADDR 0x118
#define ACP70_DMA_DESC_MAX_NUM_DSCR 0x11C
#define ACP70_DMA_CH_STS 0x120
#define ACP70_DMA_CH_GROUP 0x124
#define ACP70_DMA_CH_RST_STS 0x128
/* Registers from ACP_DSP_0 block */
#define ACP_DSP0_RUNSTALL 0x414
/* Registers from ACP_AXI2AXIATU block */
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0xC10
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0xC14
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0xC18
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0xC1C
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0xC28
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0xC2C
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0xC30
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0xC34
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0xC38
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0xC3C
#define ACPAXI2AXI_ATU_CTRL 0xC40
#define ACP_SOFT_RESET 0x1000
#define ACP_CONTROL 0x1004
#define ACP3X_I2S_PIN_CONFIG 0x1400
#define ACP5X_I2S_PIN_CONFIG 0x1400
#define ACP6X_I2S_PIN_CONFIG 0x1440
/* Registers offsets from ACP_PGFSM block */
#define ACP3X_PGFSM_BASE 0x141C
#define ACP5X_PGFSM_BASE 0x1424
#define ACP6X_PGFSM_BASE 0x1024
#define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE
#define PGFSM_CONTROL_OFFSET 0x0
#define PGFSM_STATUS_OFFSET 0x4
#define ACP3X_CLKMUX_SEL 0x1424
#define ACP5X_CLKMUX_SEL 0x142C
#define ACP6X_CLKMUX_SEL 0x102C
#define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL
/* Registers from ACP_INTR block */
#define ACP3X_EXT_INTR_STAT 0x1808
#define ACP5X_EXT_INTR_STAT 0x1808
#define ACP6X_EXTERNAL_INTR_ENB 0x1A00
#define ACP6X_EXTERNAL_INTR_CNTL 0x1A04
#define ACP6X_EXT_INTR_STAT 0x1A0C
#define ACP6X_EXT_INTR_STAT1 0x1A10
#define ACP70_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB
#define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL
#define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT
#define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1
#define ACP3X_DSP_SW_INTR_BASE 0x1814
#define ACP5X_DSP_SW_INTR_BASE 0x1814
#define ACP6X_DSP_SW_INTR_BASE 0x1808
#define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE
#define DSP_SW_INTR_CNTL_OFFSET 0x0
#define DSP_SW_INTR_STAT_OFFSET 0x4
#define DSP_SW_INTR_TRIG_OFFSET 0x8
#define ACP3X_ERROR_STATUS 0x18C4
#define ACP6X_ERROR_STATUS 0x1A4C
#define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS
#define ACP3X_AXI2DAGB_SEM_0 0x1880
#define ACP5X_AXI2DAGB_SEM_0 0x1884
#define ACP6X_AXI2DAGB_SEM_0 0x1874
#define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0
/* ACP common registers to report errors related to I2S & SoundWire interfaces */
#define ACP3X_SW_I2S_ERROR_REASON 0x18C8
#define ACP6X_SW0_I2S_ERROR_REASON 0x18B4
#define ACP7X_SW0_I2S_ERROR_REASON ACP6X_SW0_I2S_ERROR_REASON
#define ACP_SW1_I2S_ERROR_REASON 0x1A50
/* Registers from ACP_SHA block */
#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70
#define ACP_SHA_DMA_CMD 0x1CB0
#define ACP_SHA_MSG_LENGTH 0x1CB4
#define ACP_SHA_DMA_STRT_ADDR 0x1CB8
#define ACP_SHA_DMA_DESTINATION_ADDR 0x1CBC
#define ACP_SHA_DMA_CMD_STS 0x1CC0
#define ACP_SHA_DMA_ERR_STATUS 0x1CC4
#define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8
#define ACP_SHA_DMA_INCLUDE_HDR 0x1CCC
#define ACP_SHA_PSP_ACK 0x1C74
#define ACP_SCRATCH_REG_0 0x10000
#define ACP6X_DSP_FUSION_RUNSTALL 0x0644
#define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL
/* Cache window registers */
#define ACP_DSP0_CACHE_OFFSET0 0x0420
#define ACP_DSP0_CACHE_SIZE0 0x0424
#define ACP_SW0_EN 0x3000
#define ACP_SW1_EN 0x3C00
#define ACP70_PME_EN 0x1400
#define ACP70_EXTERNAL_INTR_CNTL1 0x1A08
#define ACP70_SW0_WAKE_EN 0x1458
#define ACP70_SW1_WAKE_EN 0x1460
#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
#define ACP70_SDW0_PME_STAT BIT(26)
#define ACP70_SDW1_PME_STAT BIT(27)
#endif
+308
View File
@@ -0,0 +1,308 @@
// 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) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Balakishore Pati <Balakishore.pati@amd.com>
// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/* ACP-specific SOF IPC code */
#include <linux/module.h>
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
{
memcpy_to_scratch(sdev, offset, message, bytes);
}
EXPORT_SYMBOL_NS(acp_mailbox_write, "SND_SOC_SOF_AMD_COMMON");
void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
{
memcpy_from_scratch(sdev, offset, message, bytes);
}
EXPORT_SYMBOL_NS(acp_mailbox_read, "SND_SOC_SOF_AMD_COMMON");
static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata)
{
struct snd_sof_dev *sdev = adata->dev;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
u32 swintr_trigger;
swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base +
DSP_SW_INTR_TRIG_OFFSET);
swintr_trigger |= 0x01;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET,
swintr_trigger);
}
static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
{
unsigned int host_msg = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_host_msg_write);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
}
static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
{
unsigned int dsp_msg = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
}
static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
{
unsigned int dsp_ack = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
}
int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int offset = sdev->host_box.offset;
unsigned int count = ACP_HW_SEM_RETRY_COUNT;
while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
/* Wait until acquired HW Semaphore Lock or timeout*/
count--;
if (!count) {
dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
return -EINVAL;
}
}
acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
acp_ipc_host_msg_set(sdev);
/* Trigger host to dsp interrupt for the msg */
acpbus_trigger_host_to_dsp_swintr(adata);
/* Unlock or Release HW Semaphore */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, "SND_SOC_SOF_AMD_COMMON");
static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
unsigned int offset = sdev->host_box.offset;
int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
* ipc belongs to none of the ipcs sent from driver.
* In this case, the driver must ignore the ipc.
*/
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
hdr = msg->msg_data;
if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
/*
* memory windows are powered off before sending IPC reply,
* so we can't read the mailbox for CTX_SAVE and PM_GATE
* replies.
*/
reply.error = 0;
reply.hdr.cmd = SOF_IPC_GLB_REPLY;
reply.hdr.size = sizeof(reply);
memcpy(msg->reply_data, &reply, sizeof(reply));
goto out;
}
/* get IPC reply from DSP in the mailbox */
acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
/*
* To support an IPC tx_message with a
* reply_size set to zero.
*/
if (!msg->reply_size)
goto out;
/* reply correct size ? */
if (reply.hdr.size != msg->reply_size &&
!(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
}
/* read the message */
if (msg->reply_size > 0)
acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
}
out:
msg->reply_error = ret;
}
irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int dsp_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
unsigned int dsp_ack_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
bool ipc_irq = false;
int dsp_msg, dsp_ack;
unsigned int status;
if (unlikely(sdev->first_boot && sdev->fw_state != SOF_FW_BOOT_COMPLETE)) {
acp_mailbox_read(sdev, sdev->dsp_box.offset, &status, sizeof(status));
if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
snd_sof_dsp_panic(sdev, sdev->dsp_box.offset + sizeof(status),
true);
status = 0;
acp_mailbox_write(sdev, sdev->dsp_box.offset, &status, sizeof(status));
return IRQ_HANDLED;
}
snd_sof_ipc_msgs_rx(sdev);
acp_dsp_ipc_host_done(sdev);
return IRQ_HANDLED;
}
dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
if (dsp_msg == ACP_DSP_MSG_SET) {
snd_sof_ipc_msgs_rx(sdev);
acp_dsp_ipc_host_done(sdev);
ipc_irq = true;
}
dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
if (dsp_ack == ACP_DSP_ACK_SET) {
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
guard(spinlock_irq)(&sdev->ipc_lock);
/* handle immediate reply from DSP core */
acp_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, 0);
/* set the done bit */
acp_dsp_ipc_dsp_done(sdev);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n",
dsp_ack);
}
ipc_irq = true;
}
acp_mailbox_read(sdev, sdev->debug_box.offset, &status, sizeof(u32));
if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
snd_sof_dsp_panic(sdev, sdev->dsp_oops_offset, true);
status = 0;
acp_mailbox_write(sdev, sdev->debug_box.offset, &status, sizeof(status));
return IRQ_HANDLED;
}
if (desc->probe_reg_offset) {
u32 val;
u32 posn;
/* Probe register consists of two parts
* (0-30) bit has cumulative position value
* 31 bit is a synchronization flag between DSP and CPU
* for the position update
*/
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->probe_reg_offset);
if (val & PROBE_STATUS_BIT) {
posn = val & ~PROBE_STATUS_BIT;
if (adata->probe_stream) {
/* Probe related posn value is of 31 bits limited to 2GB
* once wrapped DSP won't send posn interrupt.
*/
adata->probe_stream->cstream_posn = posn;
snd_compr_fragment_elapsed(adata->probe_stream->cstream);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->probe_reg_offset, posn);
ipc_irq = true;
}
}
}
if (!ipc_irq)
dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
return IRQ_HANDLED;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
unsigned int offset = sdev->dsp_box.offset;
if (!sps || !sdev->stream_box.size) {
acp_mailbox_read(sdev, offset, p, sz);
} else {
struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream;
if (!substream || !substream->runtime)
return -ESTRPIPE;
stream = substream->runtime->private_data;
if (!stream)
return -ESTRPIPE;
acp_mailbox_read(sdev, stream->posn_offset, p, sz);
}
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, "SND_SOC_SOF_AMD_COMMON");
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream = substream->runtime->private_data;
/* check for unaligned offset or overflow */
if (posn_offset > sdev->stream_box.size ||
posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
return -EINVAL;
stream->posn_offset = sdev->stream_box.offset + posn_offset;
dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
substream->stream, stream->posn_offset);
return 0;
}
EXPORT_SYMBOL_NS(acp_set_stream_data_offset, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
return desc->sram_pte_offset;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
{
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_get_window_offset, "SND_SOC_SOF_AMD_COMMON");
MODULE_DESCRIPTION("AMD ACP sof-ipc driver");
+320
View File
@@ -0,0 +1,320 @@
// 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) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for ACP DSP Firmware binaries loader
*/
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "../ops.h"
#include "acp-dsp-offset.h"
#include "acp.h"
#define FW_BIN 0
#define FW_DATA_BIN 1
#define FW_SRAM_DATA_BIN 2
#define FW_BIN_PTE_OFFSET 0x00
#define FW_DATA_BIN_PTE_OFFSET 0x08
#define ACP_DSP_RUN 0x00
int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
u32 offset, void *dest, size_t size)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
switch (blk_type) {
case SOF_FW_BLK_TYPE_SRAM:
offset = offset - desc->sram_pte_offset;
memcpy_from_scratch(sdev, offset, dest, size);
break;
default:
dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_NS(acp_dsp_block_read, "SND_SOC_SOF_AMD_COMMON");
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)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct acp_dev_data *adata;
void *dest;
u32 dma_size, page_count;
unsigned int size_fw;
adata = sdev->pdata->hw_pdata;
switch (blk_type) {
case SOF_FW_BLK_TYPE_IRAM:
if (!adata->bin_buf) {
size_fw = sdev->basefw.fw->size;
page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
dma_size = page_count * ACP_PAGE_SIZE;
adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size,
&adata->sha_dma_addr,
GFP_KERNEL);
if (!adata->bin_buf)
return -ENOMEM;
}
adata->fw_bin_size = size + offset;
dest = adata->bin_buf + offset;
break;
case SOF_FW_BLK_TYPE_DRAM:
if (!adata->data_buf) {
adata->data_buf = dma_alloc_coherent(&pci->dev,
ACP_DEFAULT_DRAM_LENGTH,
&adata->dma_addr,
GFP_KERNEL);
if (!adata->data_buf)
return -ENOMEM;
}
dest = adata->data_buf + offset;
adata->fw_data_bin_size = size + offset;
adata->is_dram_in_use = true;
break;
case SOF_FW_BLK_TYPE_SRAM:
if (!adata->sram_data_buf) {
adata->sram_data_buf = dma_alloc_coherent(&pci->dev,
ACP_DEFAULT_SRAM_LENGTH,
&adata->sram_dma_addr,
GFP_KERNEL);
if (!adata->sram_data_buf)
return -ENOMEM;
}
adata->fw_sram_data_bin_size = size + offset;
dest = adata->sram_data_buf + offset;
adata->is_sram_in_use = true;
break;
default:
dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
return -EINVAL;
}
memcpy(dest, src, size);
return 0;
}
EXPORT_SYMBOL_NS(acp_dsp_block_write, "SND_SOC_SOF_AMD_COMMON");
int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
{
return type;
}
EXPORT_SYMBOL_NS(acp_get_bar_index, "SND_SOC_SOF_AMD_COMMON");
static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata)
{
struct snd_sof_dev *sdev = adata->dev;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int low, high;
dma_addr_t addr;
u16 page_idx;
u32 offset;
switch (type) {
case FW_BIN:
offset = FW_BIN_PTE_OFFSET;
addr = adata->sha_dma_addr;
break;
case FW_DATA_BIN:
offset = adata->fw_bin_page_count * 8;
addr = adata->dma_addr;
break;
case FW_SRAM_DATA_BIN:
offset = (adata->fw_bin_page_count + ACP_DRAM_PAGE_COUNT) * 8;
addr = adata->sram_dma_addr;
break;
default:
dev_err(sdev->dev, "Invalid data type %x\n", type);
return;
}
/* Group Enable */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
desc->sram_pte_offset | BIT(31));
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
PAGE_SIZE_4K_ENABLE);
for (page_idx = 0; page_idx < num_pages; page_idx++) {
low = lower_32_bits(addr);
high = upper_32_bits(addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
high |= BIT(31);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
offset += 8;
addr += PAGE_SIZE;
}
/* Flush ATU Cache after PTE Update */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
}
/* pre fw run operations */
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;
unsigned int src_addr, size_fw, dest_addr;
u32 page_count, dma_size;
int ret;
adata = sdev->pdata->hw_pdata;
if (adata->quirks && adata->quirks->signed_fw_image)
size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE;
else
size_fw = adata->fw_bin_size;
page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
adata->fw_bin_page_count = page_count;
configure_pte_for_fw_loading(FW_BIN, page_count, adata);
ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW,
ACP_IRAM_BASE_ADDRESS, size_fw);
if (ret < 0) {
dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret);
return ret;
}
if (adata->is_dram_in_use) {
configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata);
src_addr = ACP_SYSTEM_MEMORY_WINDOW + (page_count * ACP_PAGE_SIZE);
dest_addr = ACP_DRAM_BASE_ADDRESS;
ret = configure_and_run_dma(adata, src_addr, dest_addr, adata->fw_data_bin_size);
if (ret < 0) {
dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret);
return ret;
}
ret = acp_dma_status(adata, 0);
if (ret < 0)
dev_err(sdev->dev, "acp dma transfer status: %d\n", 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);
if (adata->pci_rev > ACP63_PCI_ID)
dest_addr = ACP7X_SRAM_BASE_ADDRESS;
else
dest_addr = ACP_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, "acp dma configuration failed: %d\n", ret);
return ret;
}
ret = acp_dma_status(adata, 0);
if (ret < 0)
dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
}
if (adata->pci_rev > ACP_RN_PCI_ID) {
/* Cache Window enable */
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));
}
/* Free memory once DMA is complete */
dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
adata->bin_buf = NULL;
if (adata->is_dram_in_use) {
dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf,
adata->dma_addr);
adata->data_buf = NULL;
}
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;
}
EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_dsp_run(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);
int val;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN);
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL);
dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val);
/* Some platforms won't support fusion DSP,keep offset zero for no support */
if (desc->fusion_dsp_offset && adata->enable_fw_debug) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset, ACP_DSP_RUN);
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset);
dev_dbg(sdev->dev, "ACP_DSP0_FUSION_RUNSTALL : 0x%0x\n", val);
}
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_dsp_run, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
struct acp_dev_data *adata = plat_data->hw_pdata;
const char *fw_filename;
int ret;
fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
plat_data->fw_filename_prefix,
adata->fw_code_bin);
if (!fw_filename)
return -ENOMEM;
ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);
if (ret < 0) {
kfree(fw_filename);
dev_err(sdev->dev, "sof signed firmware code bin is missing\n");
return ret;
} else {
dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename);
}
kfree(fw_filename);
ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0,
(void *)sdev->basefw.fw->data,
sdev->basefw.fw->size);
if (ret < 0)
return ret;
fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
plat_data->fw_filename_prefix,
adata->fw_data_bin);
if (!fw_filename)
return -ENOMEM;
ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev);
if (ret < 0) {
kfree(fw_filename);
dev_err(sdev->dev, "sof signed firmware data bin is missing\n");
return ret;
} else {
dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename);
}
kfree(fw_filename);
ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0,
(void *)adata->fw_dbin->data,
adata->fw_dbin->size);
return ret;
}
EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON");
+120
View File
@@ -0,0 +1,120 @@
// 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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* PCM interface for generic AMD audio ACP DSP block
*/
#include <sound/pcm_params.h>
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct acp_dsp_stream *stream = runtime->private_data;
unsigned int buf_offset, index;
u32 size;
int ret;
size = runtime->dma_bytes;
stream->num_pages = PFN_UP(runtime->dma_bytes);
stream->dmab = substream->runtime->dma_buffer_p;
ret = acp_dsp_stream_config(sdev, stream);
if (ret < 0) {
dev_err(sdev->dev, "stream configuration failed\n");
return ret;
}
platform_params->use_phy_address = true;
platform_params->phy_addr = stream->reg_offset;
platform_params->stream_tag = stream->stream_tag;
platform_params->cont_update_posn = 1;
/* write buffer size of stream in scratch memory */
buf_offset = sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, buf_size);
index = stream->stream_tag - 1;
buf_offset = buf_offset + index * 4;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size);
return 0;
}
EXPORT_SYMBOL_NS(acp_pcm_hw_params, "SND_SOC_SOF_AMD_COMMON");
int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
{
struct acp_dsp_stream *stream;
stream = acp_dsp_stream_get(sdev, 0);
if (!stream)
return -ENODEV;
substream->runtime->private_data = stream;
stream->substream = substream;
return 0;
}
EXPORT_SYMBOL_NS(acp_pcm_open, "SND_SOC_SOF_AMD_COMMON");
int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
{
struct acp_dsp_stream *stream;
stream = substream->runtime->private_data;
if (!stream) {
dev_err(sdev->dev, "No open stream\n");
return -EINVAL;
}
stream->substream = NULL;
substream->runtime->private_data = NULL;
return acp_dsp_stream_put(sdev, stream);
}
EXPORT_SYMBOL_NS(acp_pcm_close, "SND_SOC_SOF_AMD_COMMON");
snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_component *scomp = sdev->component;
struct snd_sof_pcm_stream *stream;
struct sof_ipc_stream_posn posn;
struct snd_sof_pcm *spcm;
snd_pcm_uframes_t pos;
int ret;
spcm = snd_sof_find_spcm_dai(scomp, rtd);
if (!spcm) {
dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n",
rtd->dai_link->id);
return 0;
}
stream = &spcm->stream[substream->stream];
ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return 0;
}
memcpy(&stream->posn, &posn, sizeof(posn));
pos = spcm->stream[substream->stream].posn.host_posn;
pos = bytes_to_frames(substream->runtime, pos);
return pos;
}
EXPORT_SYMBOL_NS(acp_pcm_pointer, "SND_SOC_SOF_AMD_COMMON");
+147
View File
@@ -0,0 +1,147 @@
// 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) 2023 Advanced Micro Devices, Inc.
//
// Authors: V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com>
/*
* Probe interface for generic AMD audio ACP DSP block
*/
#include <linux/module.h>
#include <sound/soc.h>
#include "../sof-priv.h"
#include "../sof-client-probes.h"
#include "../sof-client.h"
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
static int acp_probes_compr_startup(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai, u32 *stream_id)
{
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct acp_dsp_stream *stream;
struct acp_dev_data *adata;
adata = sdev->pdata->hw_pdata;
stream = acp_dsp_stream_get(sdev, 0);
if (!stream)
return -ENODEV;
stream->cstream = cstream;
cstream->runtime->private_data = stream;
adata->probe_stream = stream;
*stream_id = stream->stream_tag;
return 0;
}
static int acp_probes_compr_shutdown(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai)
{
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct acp_dsp_stream *stream = cstream->runtime->private_data;
struct acp_dev_data *adata;
int ret;
ret = acp_dsp_stream_put(sdev, stream);
if (ret < 0) {
dev_err(sdev->dev, "Failed to release probe compress stream\n");
return ret;
}
adata = sdev->pdata->hw_pdata;
stream->cstream = NULL;
cstream->runtime->private_data = NULL;
adata->probe_stream = NULL;
return 0;
}
static int acp_probes_compr_set_params(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai)
{
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct acp_dsp_stream *stream = cstream->runtime->private_data;
unsigned int buf_offset, index;
u32 size;
int ret;
stream->dmab = cstream->runtime->dma_buffer_p;
stream->num_pages = PFN_UP(cstream->runtime->dma_bytes);
size = cstream->runtime->buffer_size;
ret = acp_dsp_stream_config(sdev, stream);
if (ret < 0) {
acp_dsp_stream_put(sdev, stream);
return ret;
}
/* write buffer size of stream in scratch memory */
buf_offset = sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, buf_size);
index = stream->stream_tag - 1;
buf_offset = buf_offset + index * 4;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size);
return 0;
}
static int acp_probes_compr_trigger(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
int cmd, struct snd_soc_dai *dai)
{
/* Nothing to do here, as it is a mandatory callback just defined */
return 0;
}
static int acp_probes_compr_pointer(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp64 *tstamp,
struct snd_soc_dai *dai)
{
struct acp_dsp_stream *stream = cstream->runtime->private_data;
struct snd_soc_pcm_stream *pstream;
pstream = &dai->driver->capture;
tstamp->copied_total = stream->cstream_posn;
tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
return 0;
}
/* SOF client implementation */
static const struct sof_probes_host_ops acp_probes_ops = {
.startup = acp_probes_compr_startup,
.shutdown = acp_probes_compr_shutdown,
.set_params = acp_probes_compr_set_params,
.trigger = acp_probes_compr_trigger,
.pointer = acp_probes_compr_pointer,
};
int acp_probes_register(struct snd_sof_dev *sdev)
{
return sof_client_dev_register(sdev, "acp-probes", 0, &acp_probes_ops,
sizeof(acp_probes_ops));
}
EXPORT_SYMBOL_NS(acp_probes_register, "SND_SOC_SOF_AMD_COMMON");
void acp_probes_unregister(struct snd_sof_dev *sdev)
{
sof_client_dev_unregister(sdev, "acp-probes", 0);
}
EXPORT_SYMBOL_NS(acp_probes_unregister, "SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");
+187
View File
@@ -0,0 +1,187 @@
// 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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for generic AMD audio DSP ACP IP
*/
#include "../ops.h"
#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);
unsigned int pte_reg, pte_size, phy_addr_offset, index;
int stream_tag = stream->stream_tag;
u32 low, high, offset, reg_val;
dma_addr_t addr;
int page_idx;
switch (stream_tag) {
case 1:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1;
offset = offsetof(struct scratch_reg_conf, grp1_pte);
stream->reg_offset = PTE_GRP1_OFFSET;
break;
case 2:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2;
offset = offsetof(struct scratch_reg_conf, grp2_pte);
stream->reg_offset = PTE_GRP2_OFFSET;
break;
case 3:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3;
offset = offsetof(struct scratch_reg_conf, grp3_pte);
stream->reg_offset = PTE_GRP3_OFFSET;
break;
case 4:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4;
offset = offsetof(struct scratch_reg_conf, grp4_pte);
stream->reg_offset = PTE_GRP4_OFFSET;
break;
case 5:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
offset = offsetof(struct scratch_reg_conf, grp5_pte);
stream->reg_offset = PTE_GRP5_OFFSET;
break;
case 6:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6;
offset = offsetof(struct scratch_reg_conf, grp6_pte);
stream->reg_offset = PTE_GRP6_OFFSET;
break;
case 7:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7;
offset = offsetof(struct scratch_reg_conf, grp7_pte);
stream->reg_offset = PTE_GRP7_OFFSET;
break;
case 8:
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8;
offset = offsetof(struct scratch_reg_conf, grp8_pte);
stream->reg_offset = PTE_GRP8_OFFSET;
break;
default:
dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag);
return -EINVAL;
}
/* write phy_addr in scratch memory */
phy_addr_offset = sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, reg_offset);
index = stream_tag - 1;
phy_addr_offset = phy_addr_offset + index * 4;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 +
phy_addr_offset, stream->reg_offset);
/* Group Enable */
offset = offset + sdev->debug_box.offset;
reg_val = desc->sram_pte_offset + offset;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31));
snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE);
for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE);
/* Load the low address of page int ACP SRAM through SRBM */
low = lower_32_bits(addr);
high = upper_32_bits(addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
high |= BIT(31);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
/* Move to next physically contiguous page */
offset += 8;
}
/* Flush ATU Cache after PTE Update */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
return 0;
}
struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
struct acp_dsp_stream *stream = adata->stream_buf;
int i;
for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
if (stream->active)
continue;
/* return stream if tag not specified*/
if (!tag) {
stream->active = 1;
return stream;
}
/* check if this is the requested stream tag */
if (stream->stream_tag == tag) {
stream->active = 1;
return stream;
}
}
dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag);
return NULL;
}
EXPORT_SYMBOL_NS(acp_dsp_stream_get, "SND_SOC_SOF_AMD_COMMON");
int acp_dsp_stream_put(struct snd_sof_dev *sdev,
struct acp_dsp_stream *acp_stream)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
struct acp_dsp_stream *stream = adata->stream_buf;
int i;
/* Free an active stream */
for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
if (stream == acp_stream) {
stream->active = 0;
return 0;
}
}
dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag);
return -EINVAL;
}
EXPORT_SYMBOL_NS(acp_dsp_stream_put, "SND_SOC_SOF_AMD_COMMON");
int acp_dsp_stream_init(struct snd_sof_dev *sdev)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
int i;
for (i = 0; i < ACP_MAX_STREAM; i++) {
adata->stream_buf[i].sdev = sdev;
adata->stream_buf[i].active = 0;
adata->stream_buf[i].stream_tag = i + 1;
}
return 0;
}
EXPORT_SYMBOL_NS(acp_dsp_stream_init, "SND_SOC_SOF_AMD_COMMON");
+64
View File
@@ -0,0 +1,64 @@
// 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Vishnuvardhanrao Ravuapati <vishnuvardhanrao.ravulapati@amd.com>
// V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com>
/*This file support Host TRACE Logger driver callback for SOF FW */
#include "acp.h"
#define ACP_LOGGER_STREAM 8
#define NUM_PAGES 16
int acp_sof_trace_release(struct snd_sof_dev *sdev)
{
struct acp_dsp_stream *stream;
struct acp_dev_data *adata;
int ret;
adata = sdev->pdata->hw_pdata;
stream = adata->dtrace_stream;
ret = acp_dsp_stream_put(sdev, stream);
if (ret < 0) {
dev_err(sdev->dev, "Failed to release trace stream\n");
return ret;
}
adata->dtrace_stream = NULL;
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_trace_release, "SND_SOC_SOF_AMD_COMMON");
int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct acp_dsp_stream *stream;
struct acp_dev_data *adata;
int ret;
adata = sdev->pdata->hw_pdata;
stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM);
if (!stream)
return -ENODEV;
stream->dmab = dmab;
stream->num_pages = NUM_PAGES;
ret = acp_dsp_stream_config(sdev, stream);
if (ret < 0) {
acp_dsp_stream_put(sdev, stream);
return ret;
}
adata->dtrace_stream = stream;
dtrace_params->stream_tag = stream->stream_tag;
dtrace_params->buffer.phy_addr = stream->reg_offset;
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_trace_init, "SND_SOC_SOF_AMD_COMMON");
+996
View File
@@ -0,0 +1,996 @@
// 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) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for generic AMD ACP processor
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/amd/node.h>
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
static bool enable_fw_debug;
module_param(enable_fw_debug, bool, 0444);
MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug");
static struct acp_quirk_entry quirk_valve_galileo = {
.signed_fw_image = true,
.skip_iram_dram_size_mod = true,
.post_fw_run_delay = true,
};
const struct dmi_system_id acp_sof_quirk_table[] = {
{
/* Steam Deck OLED device */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
},
.driver_data = &quirk_valve_galileo,
},
{}
};
EXPORT_SYMBOL_GPL(acp_sof_quirk_table);
static void init_dma_descriptor(struct acp_dev_data *adata)
{
struct snd_sof_dev *sdev = adata->dev;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
unsigned int addr;
unsigned int acp_dma_desc_base_addr, acp_dma_desc_max_num_dscr;
addr = desc->sram_pte_offset + sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, dma_desc);
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR;
acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR;
break;
default:
acp_dma_desc_base_addr = ACP_DMA_DESC_BASE_ADDR;
acp_dma_desc_max_num_dscr = ACP_DMA_DESC_MAX_NUM_DSCR;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_desc_base_addr, addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_desc_max_num_dscr, ACP_MAX_DESC_CNT);
}
static void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx,
struct dma_descriptor *dscr_info)
{
struct snd_sof_dev *sdev = adata->dev;
unsigned int offset;
offset = ACP_SCRATCH_REG_0 + sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, dma_desc) +
idx * sizeof(struct dma_descriptor);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all);
}
static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch,
unsigned int idx, unsigned int dscr_count)
{
struct snd_sof_dev *sdev = adata->dev;
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int val, status;
unsigned int acp_dma_cntl_0, acp_dma_ch_rst_sts, acp_dma_dscr_err_sts_0;
unsigned int acp_dma_dscr_cnt_0, acp_dma_prio_0, acp_dma_dscr_strt_idx_0;
int ret;
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_dma_cntl_0 = ACP70_DMA_CNTL_0;
acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS;
acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0;
acp_dma_dscr_cnt_0 = ACP70_DMA_DSCR_CNT_0;
acp_dma_prio_0 = ACP70_DMA_PRIO_0;
acp_dma_dscr_strt_idx_0 = ACP70_DMA_DSCR_STRT_IDX_0;
break;
default:
acp_dma_cntl_0 = ACP_DMA_CNTL_0;
acp_dma_ch_rst_sts = ACP_DMA_CH_RST_STS;
acp_dma_dscr_err_sts_0 = ACP_DMA_ERR_STS_0;
acp_dma_dscr_cnt_0 = ACP_DMA_DSCR_CNT_0;
acp_dma_prio_0 = ACP_DMA_PRIO_0;
acp_dma_dscr_strt_idx_0 = ACP_DMA_DSCR_STRT_IDX_0;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_cntl_0 + ch * sizeof(u32),
ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, acp_dma_ch_rst_sts, val,
val & (1 << ch), ACP_REG_POLL_INTERVAL,
ACP_REG_POLL_TIMEOUT_US);
if (ret < 0) {
status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->acp_error_stat);
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, acp_dma_dscr_err_sts_0 +
ch * sizeof(u32));
dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status);
return ret;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, (acp_dma_cntl_0 + ch * sizeof(u32)), 0);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_dscr_cnt_0 + ch * sizeof(u32), dscr_count);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_dscr_strt_idx_0 + ch * sizeof(u32), idx);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_prio_0 + ch * sizeof(u32), 0);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_dma_cntl_0 + ch * sizeof(u32), ACP_DMA_CH_RUN);
return ret;
}
static int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch,
unsigned int dscr_count, struct dma_descriptor *dscr_info)
{
struct snd_sof_dev *sdev = adata->dev;
int ret;
u16 dscr;
if (!dscr_info || !dscr_count)
return -EINVAL;
for (dscr = 0; dscr < dscr_count; dscr++)
configure_dma_descriptor(adata, dscr, dscr_info++);
ret = config_dma_channel(adata, ch, 0, dscr_count);
if (ret < 0)
dev_err(sdev->dev, "config dma ch failed:%d\n", ret);
return ret;
}
int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
unsigned int dest_addr, int dsp_data_size)
{
struct snd_sof_dev *sdev = adata->dev;
unsigned int desc_count, index;
int ret;
for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0;
desc_count++, dsp_data_size -= ACP_PAGE_SIZE) {
adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE;
adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE;
adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE;
if (dsp_data_size < ACP_PAGE_SIZE)
adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size;
}
ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info);
if (ret)
dev_err(sdev->dev, "acpbus_dma_start failed\n");
/* Clear descriptor array */
for (index = 0; index < desc_count; index++)
memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor));
return ret;
}
/*
* psp_mbox_ready- function to poll ready bit of psp mbox
* @adata: acp device data
* @ack: bool variable to check ready bit status or psp ack
*/
static int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
{
struct snd_sof_dev *sdev = adata->dev;
int ret, data;
ret = read_poll_timeout(smn_read_register, data, data > 0 && data & MBOX_READY_MASK,
MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_114_REG);
if (!ret)
return 0;
dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK);
if (ack)
return -ETIMEDOUT;
return -EBUSY;
}
/*
* psp_send_cmd - function to send psp command over mbox
* @adata: acp device data
* @cmd: non zero integer value for command type
*/
static int psp_send_cmd(struct acp_dev_data *adata, int cmd)
{
struct snd_sof_dev *sdev = adata->dev;
int ret;
int data;
if (!cmd)
return -EINVAL;
/* Get a non-zero Doorbell value from PSP */
ret = read_poll_timeout(smn_read_register, data, data > 0, MBOX_DELAY_US,
ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_73_REG);
if (ret) {
dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);
return ret;
}
/* Check if PSP is ready for new command */
ret = psp_mbox_ready(adata, 0);
if (ret)
return ret;
ret = amd_smn_write(0, MP0_C2PMSG_114_REG, cmd);
if (ret)
return ret;
/* Ring the Doorbell for PSP */
ret = amd_smn_write(0, MP0_C2PMSG_73_REG, data);
if (ret)
return ret;
/* Check MBOX ready as PSP ack */
ret = psp_mbox_ready(adata, 1);
return ret;
}
int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
unsigned int start_addr, unsigned int dest_addr,
unsigned int image_length)
{
struct snd_sof_dev *sdev = adata->dev;
unsigned int tx_count, fw_qualifier, val;
int ret;
if (!image_addr) {
dev_err(sdev->dev, "SHA DMA image address is NULL\n");
return -EINVAL;
}
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);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS,
val, val & ACP_SHA_RESET,
ACP_REG_POLL_INTERVAL,
ACP_REG_POLL_TIMEOUT_US);
if (ret < 0) {
dev_err(sdev->dev, "SHA DMA Failed to Reset\n");
return ret;
}
}
if (adata->quirks && adata->quirks->signed_fw_image)
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);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length);
/* psp_send_cmd only required for vangogh platform */
if (adata->pci_rev == ACP_VANGOGH_PCI_ID &&
!(adata->quirks && adata->quirks->skip_iram_dram_size_mod)) {
/* Modify IRAM and DRAM size */
ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | IRAM_DRAM_FENCE_2);
if (ret)
return ret;
ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | MBOX_ISREADY_FLAG);
if (ret)
return ret;
}
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,
ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
if (ret < 0) {
dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count);
return ret;
}
/* psp_send_cmd only required for renoir platform*/
if (adata->pci_rev == ACP_RN_PCI_ID) {
ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
if (ret)
return ret;
}
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);
if (ret < 0) {
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_PSP_ACK);
dev_err(sdev->dev, "PSP validation failed: fw_qualifier = %#x, ACP_SHA_PSP_ACK = %#x\n",
fw_qualifier, val);
return ret;
}
return 0;
}
int acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
{
struct snd_sof_dev *sdev = adata->dev;
unsigned int val;
unsigned int acp_dma_ch_sts;
int ret = 0;
switch (adata->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_dma_ch_sts = ACP70_DMA_CH_STS;
break;
default:
acp_dma_ch_sts = ACP_DMA_CH_STS;
}
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32));
if (val & ACP_DMA_CH_RUN) {
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, acp_dma_ch_sts, val, !val,
ACP_REG_POLL_INTERVAL,
ACP_DMA_COMPLETE_TIMEOUT_US);
if (ret < 0)
dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch);
}
return ret;
}
void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes)
{
unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
int i, j;
for (i = 0, j = 0; i < bytes; i = i + 4, j++)
dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i);
}
void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes)
{
unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
int i, j;
for (i = 0, j = 0; i < bytes; i = i + 4, j++)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]);
}
static int acp_init_scratch_mem_ipc_flags(struct snd_sof_dev *sdev)
{
u32 dsp_msg_write, dsp_ack_write, host_msg_write, host_ack_write;
dsp_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
dsp_ack_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
host_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_host_msg_write);
host_ack_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_host_ack_write);
/* Initialize host message write flag */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg_write, 0);
/* Initialize host ack write flag */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_ack_write, 0);
/* Initialize DSP message write flag */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write, 0);
/* Initialize DSP ack write flag */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write, 0);
return 0;
}
static int acp_memory_init(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);
snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_CNTL_OFFSET,
ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
acp_init_scratch_mem_ipc_flags(sdev);
init_dma_descriptor(adata);
return 0;
}
static void amd_sof_handle_acp70_sdw_wake_event(struct acp_dev_data *adata)
{
struct amd_sdw_manager *amd_manager;
if (adata->acp70_sdw0_wake_event) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
if (amd_manager)
pm_request_resume(amd_manager->dev);
adata->acp70_sdw0_wake_event = 0;
}
if (adata->acp70_sdw1_wake_event) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
if (amd_manager)
pm_request_resume(amd_manager->dev);
adata->acp70_sdw1_wake_event = 0;
}
}
static int amd_sof_check_and_handle_acp70_sdw_wake_irq(struct snd_sof_dev *sdev)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
u32 ext_intr_stat1;
int irq_flag = 0;
bool sdw_wake_irq = false;
ext_intr_stat1 = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
ACP70_SDW0_HOST_WAKE_STAT);
adata->acp70_sdw0_wake_event = true;
sdw_wake_irq = true;
}
if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
ACP70_SDW1_HOST_WAKE_STAT);
adata->acp70_sdw1_wake_event = true;
sdw_wake_irq = true;
}
if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN, 0);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW0_PME_STAT);
adata->acp70_sdw0_wake_event = true;
sdw_wake_irq = true;
}
if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN, 0);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW1_PME_STAT);
adata->acp70_sdw1_wake_event = true;
sdw_wake_irq = true;
}
if (sdw_wake_irq) {
amd_sof_handle_acp70_sdw_wake_event(adata);
irq_flag = 1;
}
return irq_flag;
}
static irqreturn_t acp_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int count = ACP_HW_SEM_RETRY_COUNT;
spin_lock_irq(&sdev->ipc_lock);
/* Wait until acquired HW Semaphore lock or timeout */
while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset) && --count)
;
spin_unlock_irq(&sdev->ipc_lock);
if (!count) {
dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
return IRQ_NONE;
}
sof_ops(sdev)->irq_thread(irq, sdev);
/* Unlock or Release HW Semaphore */
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
return IRQ_HANDLED;
};
static irqreturn_t acp_irq_handler(int irq, void *dev_id)
{
struct amd_sdw_manager *amd_manager;
struct snd_sof_dev *sdev = dev_id;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int base = desc->dsp_intr_base;
unsigned int val;
int irq_flag = 0, wake_irq_flag = 0;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
if (val & ACP_DSP_TO_HOST_IRQ) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET,
ACP_DSP_TO_HOST_IRQ);
return IRQ_WAKE_THREAD;
}
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
if (val & ACP_SDW0_IRQ_MASK) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SDW0_IRQ_MASK);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
}
if (val & ACP_ERROR_IRQ_MASK) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0);
/* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */
if (adata->pci_rev >= ACP_RMB_PCI_ID)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0);
irq_flag = 1;
}
if (desc->ext_intr_stat1) {
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
if (val & ACP_SDW1_IRQ_MASK) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
ACP_SDW1_IRQ_MASK);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
}
switch (adata->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
wake_irq_flag = amd_sof_check_and_handle_acp70_sdw_wake_irq(sdev);
break;
}
}
if (irq_flag || wake_irq_flag)
return IRQ_HANDLED;
else
return IRQ_NONE;
}
static int acp_power_on(struct snd_sof_dev *sdev)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int base = desc->pgfsm_base;
unsigned int val;
unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask;
int ret;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET);
if (val == ACP_POWERED_ON)
return 0;
switch (adata->pci_rev) {
case ACP_RN_PCI_ID:
case ACP_VANGOGH_PCI_ID:
acp_pgfsm_status_mask = ACP3X_PGFSM_STATUS_MASK;
acp_pgfsm_cntl_mask = ACP3X_PGFSM_CNTL_POWER_ON_MASK;
break;
case ACP_RMB_PCI_ID:
case ACP63_PCI_ID:
acp_pgfsm_status_mask = ACP6X_PGFSM_STATUS_MASK;
acp_pgfsm_cntl_mask = ACP6X_PGFSM_CNTL_POWER_ON_MASK;
break;
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK;
acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK;
break;
default:
return -EINVAL;
}
if (val & acp_pgfsm_status_mask)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET,
acp_pgfsm_cntl_mask);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val,
!val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
if (ret < 0)
dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n");
return ret;
}
static int acp_reset(struct snd_sof_dev *sdev)
{
unsigned int val;
int ret;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_ASSERT_RESET);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
val & ACP_SOFT_RESET_DONE_MASK,
ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
if (ret < 0) {
dev_err(sdev->dev, "timeout asserting reset\n");
return ret;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_RELEASE_RESET);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
if (ret < 0)
dev_err(sdev->dev, "timeout in releasing reset\n");
return ret;
}
static int acp_dsp_reset(struct snd_sof_dev *sdev)
{
unsigned int val;
int ret;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_ASSERT_RESET);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
val & ACP_DSP_SOFT_RESET_DONE_MASK,
ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
if (ret < 0) {
dev_err(sdev->dev, "timeout asserting reset\n");
return ret;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_RELEASE_RESET);
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
if (ret < 0)
dev_err(sdev->dev, "timeout in releasing reset\n");
return ret;
}
static int acp_init(struct snd_sof_dev *sdev)
{
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *acp_data;
unsigned int sdw0_wake_en, sdw1_wake_en;
int ret;
/* power on */
acp_data = sdev->pdata->hw_pdata;
ret = acp_power_on(sdev);
if (ret) {
dev_err(sdev->dev, "ACP power on failed\n");
return ret;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, 0x01);
/* Reset */
ret = acp_reset(sdev);
if (ret)
return ret;
if (desc->acp_clkmux_sel)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK);
if (desc->ext_intr_enb)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
if (desc->ext_intr_cntl)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK);
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
sdw0_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN);
sdw1_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN);
if (sdw0_wake_en || sdw1_wake_en)
snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP70_EXTERNAL_INTR_CNTL1,
ACP70_SDW_HOST_WAKE_MASK, ACP70_SDW_HOST_WAKE_MASK);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1);
break;
}
return 0;
}
static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
u32 sdw0_en, sdw1_en;
acp_data = sdev->pdata->hw_pdata;
if (!acp_data->sdw)
return false;
sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN);
sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN);
acp_data->sdw_en_stat = sdw0_en || sdw1_en;
return acp_data->sdw_en_stat;
}
int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state)
{
struct acp_dev_data *acp_data;
int ret;
bool enable = false;
acp_data = sdev->pdata->hw_pdata;
/* When acp_reset() function is invoked, it will apply ACP SOFT reset and
* DSP reset. ACP Soft reset sequence will cause all ACP IP registers will
* be reset to default values which will break the ClockStop Mode functionality.
* Add a condition check to apply DSP reset when SoundWire ClockStop mode
* is selected. For the rest of the scenarios, apply acp reset sequence.
*/
if (check_acp_sdw_enable_status(sdev))
return acp_dsp_reset(sdev);
ret = acp_reset(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Reset failed\n");
return ret;
}
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
enable = true;
break;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable);
return 0;
}
EXPORT_SYMBOL_NS(amd_sof_acp_suspend, "SND_SOC_SOF_AMD_COMMON");
int amd_sof_acp_resume(struct snd_sof_dev *sdev)
{
int ret;
struct acp_dev_data *acp_data;
acp_data = sdev->pdata->hw_pdata;
if (!acp_data->sdw_en_stat) {
ret = acp_init(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Init failed\n");
return ret;
}
return acp_memory_init(sdev);
}
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1);
break;
}
return acp_dsp_reset(sdev);
}
EXPORT_SYMBOL_NS(amd_sof_acp_resume, "SND_SOC_SOF_AMD_COMMON");
#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
{
struct acpi_device *sdw_dev;
struct acp_dev_data *acp_data;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
if (!addr)
return -ENODEV;
acp_data = sdev->pdata->hw_pdata;
sdw_dev = acpi_find_child_device(ACPI_COMPANION(sdev->dev), addr, 0);
if (!sdw_dev)
return -ENODEV;
acp_data->info.handle = sdw_dev->handle;
acp_data->info.count = desc->sdw_max_link_count;
return amd_sdw_scan_controller(&acp_data->info);
}
static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
struct sdw_amd_res sdw_res;
int ret;
acp_data = sdev->pdata->hw_pdata;
memset(&sdw_res, 0, sizeof(sdw_res));
sdw_res.addr = acp_data->addr;
sdw_res.reg_range = acp_data->reg_range;
sdw_res.handle = acp_data->info.handle;
sdw_res.parent = sdev->dev;
sdw_res.dev = sdev->dev;
sdw_res.acp_lock = &acp_data->acp_lock;
sdw_res.count = acp_data->info.count;
sdw_res.link_mask = acp_data->info.link_mask;
sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR];
sdw_res.acp_rev = acp_data->pci_rev;
ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
if (ret)
dev_err(sdev->dev, "SoundWire probe failed\n");
return ret;
}
static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
acp_data = sdev->pdata->hw_pdata;
if (acp_data->sdw)
sdw_amd_exit(acp_data->sdw);
acp_data->sdw = NULL;
return 0;
}
#else
static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
{
return 0;
}
static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
{
return 0;
}
static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
{
return 0;
}
#endif
int amd_sof_acp_probe(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct acp_dev_data *adata;
const struct sof_amd_acp_desc *chip;
const struct dmi_system_id *dmi_id;
unsigned int addr;
int ret;
chip = get_chip_info(sdev->pdata);
if (!chip) {
dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
return -EIO;
}
adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
GFP_KERNEL);
if (!adata)
return -ENOMEM;
adata->dev = sdev;
adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
PLATFORM_DEVID_NONE, NULL, 0);
if (IS_ERR(adata->dmic_dev)) {
dev_err(sdev->dev, "failed to register platform for dmic codec\n");
return PTR_ERR(adata->dmic_dev);
}
addr = pci_resource_start(pci, ACP_DSP_BAR);
sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR));
if (!sdev->bar[ACP_DSP_BAR]) {
dev_err(sdev->dev, "ioremap error\n");
ret = -ENXIO;
goto unregister_dev;
}
pci_set_master(pci);
adata->addr = addr;
adata->reg_range = chip->reg_end_addr - chip->reg_start_addr;
adata->pci_rev = pci->revision;
mutex_init(&adata->acp_lock);
sdev->pdata->hw_pdata = adata;
ret = acp_init(sdev);
if (ret < 0)
goto unregister_dev;
sdev->ipc_irq = pci->irq;
ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread,
IRQF_SHARED, "AudioDSP", sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to register IRQ %d\n",
sdev->ipc_irq);
goto unregister_dev;
}
/* scan SoundWire capabilities exposed by DSDT */
ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr);
if (ret < 0) {
dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n");
goto skip_soundwire;
}
ret = amd_sof_sdw_probe(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: SoundWire probe error\n");
free_irq(sdev->ipc_irq, sdev);
return ret;
}
skip_soundwire:
sdev->dsp_box.offset = 0;
sdev->dsp_box.size = BOX_SIZE_512;
sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size;
sdev->host_box.size = BOX_SIZE_512;
sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size;
sdev->debug_box.size = BOX_SIZE_1024;
dmi_id = dmi_first_match(acp_sof_quirk_table);
if (dmi_id) {
adata->quirks = dmi_id->driver_data;
if (adata->quirks->signed_fw_image) {
adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
"sof-%s-code.bin",
chip->name);
if (!adata->fw_code_bin) {
ret = -ENOMEM;
goto free_ipc_irq;
}
adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
"sof-%s-data.bin",
chip->name);
if (!adata->fw_data_bin) {
ret = -ENOMEM;
goto free_ipc_irq;
}
}
}
adata->enable_fw_debug = enable_fw_debug;
acp_memory_init(sdev);
acp_dsp_stream_init(sdev);
return 0;
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
unregister_dev:
platform_device_unregister(adata->dmic_dev);
return ret;
}
EXPORT_SYMBOL_NS(amd_sof_acp_probe, "SND_SOC_SOF_AMD_COMMON");
void amd_sof_acp_remove(struct snd_sof_dev *sdev)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
if (adata->sdw)
amd_sof_sdw_exit(sdev);
if (sdev->ipc_irq)
free_irq(sdev->ipc_irq, sdev);
if (adata->dmic_dev)
platform_device_unregister(adata->dmic_dev);
acp_reset(sdev);
}
EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("AMD ACP sof driver");
MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
+375
View File
@@ -0,0 +1,375 @@
/* 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) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
*
* Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
*/
#ifndef __SOF_AMD_ACP_H
#define __SOF_AMD_ACP_H
#include <linux/dmi.h>
#include <linux/soundwire/sdw_amd.h>
#include "../sof-priv.h"
#include "../sof-audio.h"
#define ACP_MAX_STREAM 8
#define ACP_DSP_BAR 0
#define ACP_HW_SEM_RETRY_COUNT 10000
#define ACP_REG_POLL_INTERVAL 500
#define ACP_REG_POLL_TIMEOUT_US 2000
#define ACP_DMA_COMPLETE_TIMEOUT_US 5000
#define ACP3X_PGFSM_CNTL_POWER_ON_MASK 0x01
#define ACP3X_PGFSM_STATUS_MASK 0x03
#define ACP6X_PGFSM_CNTL_POWER_ON_MASK 0x07
#define ACP6X_PGFSM_STATUS_MASK 0x0F
#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
#define ACP70_PGFSM_STATUS_MASK 0xFF
#define ACP_POWERED_ON 0x00
#define ACP_ASSERT_RESET 0x01
#define ACP_RELEASE_RESET 0x00
#define ACP_SOFT_RESET_DONE_MASK 0x00010001
#define ACP_DSP_ASSERT_RESET 0x04
#define ACP_DSP_RELEASE_RESET 0x00
#define ACP_DSP_SOFT_RESET_DONE_MASK 0x00050004
#define ACP_DSP_INTR_EN_MASK 0x00000001
#define ACP3X_SRAM_PTE_OFFSET 0x02050000
#define ACP5X_SRAM_PTE_OFFSET 0x02050000
#define ACP6X_SRAM_PTE_OFFSET 0x03800000
#define ACP70_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET
#define PAGE_SIZE_4K_ENABLE 0x2
#define ACP_PAGE_SIZE 0x1000
#define ACP_DMA_CH_RUN 0x02
#define ACP_MAX_DESC_CNT 0x02
#define DSP_FW_RUN_ENABLE 0x01
#define ACP_SHA_RUN 0x01
#define ACP_SHA_RESET 0x02
#define ACP_SHA_HEADER 0x01
#define ACP_DMA_CH_RST 0x01
#define ACP_DMA_CH_GRACEFUL_RST_EN 0x10
#define ACP_ATU_CACHE_INVALID 0x01
#define ACP_MAX_DESC 128
#define ACPBUS_REG_BASE_OFFSET ACP_DMA_CNTL_0
#define ACP_DEFAULT_DRAM_LENGTH 0x00080000
#define ACP3X_SCRATCH_MEMORY_ADDRESS 0x02050000
#define ACP_SYSTEM_MEMORY_WINDOW 0x4000000
#define ACP_IRAM_BASE_ADDRESS 0x000000
#define ACP_DRAM_BASE_ADDRESS 0x01000000
#define ACP_DRAM_PAGE_COUNT 128
#define ACP_SRAM_BASE_ADDRESS 0x3806000
#define ACP7X_SRAM_BASE_ADDRESS 0x380C000
#define ACP_DSP_TO_HOST_IRQ 0x04
#define ACP_RN_PCI_ID 0x01
#define ACP_VANGOGH_PCI_ID 0x50
#define ACP_RMB_PCI_ID 0x6F
#define ACP63_PCI_ID 0x63
#define ACP70_PCI_ID 0x70
#define ACP71_PCI_ID 0x71
#define ACP72_PCI_ID 0x72
#define HOST_BRIDGE_CZN 0x1630
#define HOST_BRIDGE_VGH 0x1645
#define HOST_BRIDGE_RMB 0x14B5
#define HOST_BRIDGE_ACP63 0x14E8
#define HOST_BRIDGE_ACP70 0x1507
#define ACP_SHA_STAT 0x8000
#define ACP_PSP_TIMEOUT_US 1000000
#define ACP_EXT_INTR_ERROR_STAT 0x20000000
#define MP0_C2PMSG_114_REG 0x3810AC8
#define MP0_C2PMSG_73_REG 0x3810A24
#define MBOX_ACP_SHA_DMA_COMMAND 0x70000
#define MBOX_ACP_IRAM_DRAM_FENCE_COMMAND 0x80000
#define MBOX_DELAY_US 1000
#define MBOX_READY_MASK 0x80000000
#define MBOX_STATUS_MASK 0xFFFF
#define MBOX_ISREADY_FLAG 0x40000000
#define IRAM_DRAM_FENCE_0 0X0
#define IRAM_DRAM_FENCE_1 0X01
#define IRAM_DRAM_FENCE_2 0X02
#define BOX_SIZE_512 0x200
#define BOX_SIZE_1024 0x400
#define EXCEPT_MAX_HDR_SIZE 0x400
#define AMD_STACK_DUMP_SIZE 32
#define SRAM1_SIZE 0x280000
#define PROBE_STATUS_BIT BIT(31)
#define ACP_FIRMWARE_SIGNATURE 0x100
#define ACP_ERROR_IRQ_MASK BIT(29)
#define ACP_SDW0_IRQ_MASK BIT(21)
#define ACP_SDW1_IRQ_MASK BIT(2)
#define SDW_ACPI_ADDR_ACP63 5
#define SDW_ACPI_ADDR_ACP70 SDW_ACPI_ADDR_ACP63
#define ACP_DEFAULT_SRAM_LENGTH 0x00080000
#define ACP_SRAM_PAGE_COUNT 128
#define ACP6X_SDW_MAX_MANAGER_COUNT 2
#define ACP70_SDW_MAX_MANAGER_COUNT ACP6X_SDW_MAX_MANAGER_COUNT
#define ACP_DSP_MSG_SET 1
#define ACP_DSP_ACK_SET 1
enum clock_source {
ACP_CLOCK_96M = 0,
ACP_CLOCK_48M,
ACP_CLOCK_24M,
ACP_CLOCK_ACLK,
ACP_CLOCK_MCLK,
};
struct acp_atu_grp_pte {
u32 low;
u32 high;
};
union dma_tx_cnt {
struct {
unsigned int count : 19;
unsigned int reserved : 12;
unsigned ioc : 1;
} bitfields, bits;
unsigned int u32_all;
signed int i32_all;
};
struct dma_descriptor {
unsigned int src_addr;
unsigned int dest_addr;
union dma_tx_cnt tx_cnt;
unsigned int reserved;
};
/* Scratch memory structure for communication b/w host and dsp */
struct scratch_ipc_conf {
/* Debug memory */
u8 sof_debug_box[1024];
/* Exception memory*/
u8 sof_except_box[1024];
/* Stream buffer */
u8 sof_stream_box[1024];
/* Trace buffer */
u8 sof_trace_box[1024];
/* Host msg flag */
u32 sof_host_msg_write;
/* Host ack flag*/
u32 sof_host_ack_write;
/* DSP msg flag */
u32 sof_dsp_msg_write;
/* Dsp ack flag */
u32 sof_dsp_ack_write;
};
struct scratch_reg_conf {
struct scratch_ipc_conf info;
struct acp_atu_grp_pte grp1_pte[16];
struct acp_atu_grp_pte grp2_pte[16];
struct acp_atu_grp_pte grp3_pte[16];
struct acp_atu_grp_pte grp4_pte[16];
struct acp_atu_grp_pte grp5_pte[16];
struct acp_atu_grp_pte grp6_pte[16];
struct acp_atu_grp_pte grp7_pte[16];
struct acp_atu_grp_pte grp8_pte[16];
struct dma_descriptor dma_desc[64];
unsigned int reg_offset[8];
unsigned int buf_size[8];
u8 acp_tx_fifo_buf[256];
u8 acp_rx_fifo_buf[256];
unsigned int reserve[];
};
struct acp_dsp_stream {
struct list_head list;
struct snd_sof_dev *sdev;
struct snd_pcm_substream *substream;
struct snd_dma_buffer *dmab;
int num_pages;
int stream_tag;
int active;
unsigned int reg_offset;
size_t posn_offset;
struct snd_compr_stream *cstream;
u64 cstream_posn;
};
struct sof_amd_acp_desc {
const char *name;
u32 pgfsm_base;
u32 ext_intr_enb;
u32 ext_intr_cntl;
u32 ext_intr_stat;
u32 ext_intr_stat1;
u32 dsp_intr_base;
u32 sram_pte_offset;
u32 hw_semaphore_offset;
u32 acp_clkmux_sel;
u32 fusion_dsp_offset;
u32 probe_reg_offset;
u32 reg_start_addr;
u32 reg_end_addr;
u32 acp_error_stat;
u32 acp_sw0_i2s_err_reason;
u32 sdw_max_link_count;
u64 sdw_acpi_dev_addr;
};
struct acp_quirk_entry {
bool signed_fw_image;
bool skip_iram_dram_size_mod;
bool post_fw_run_delay;
};
/* Common device data struct for ACP devices */
struct acp_dev_data {
struct snd_sof_dev *dev;
const struct firmware *fw_dbin;
/* DMIC device */
struct platform_device *dmic_dev;
/* mutex lock to protect ACP common registers access */
struct mutex acp_lock;
/* ACPI information stored between scan and probe steps */
struct sdw_amd_acpi_info info;
/* sdw context allocated by SoundWire driver */
struct sdw_amd_ctx *sdw;
unsigned int fw_bin_size;
unsigned int fw_data_bin_size;
unsigned int fw_sram_data_bin_size;
const char *fw_code_bin;
const char *fw_data_bin;
const char *fw_sram_data_bin;
u32 fw_bin_page_count;
u32 fw_data_bin_page_count;
u32 addr;
u32 reg_range;
u32 blk_type;
dma_addr_t sha_dma_addr;
u8 *bin_buf;
dma_addr_t dma_addr;
u8 *data_buf;
dma_addr_t sram_dma_addr;
u8 *sram_data_buf;
struct acp_quirk_entry *quirks;
struct dma_descriptor dscr_info[ACP_MAX_DESC];
struct acp_dsp_stream stream_buf[ACP_MAX_STREAM];
struct acp_dsp_stream *dtrace_stream;
struct acp_dsp_stream *probe_stream;
bool enable_fw_debug;
bool is_dram_in_use;
bool is_sram_in_use;
bool sdw_en_stat;
/* acp70_sdw0_wake_event flag set to true when wake irq asserted for SW0 instance */
bool acp70_sdw0_wake_event;
/* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */
bool acp70_sdw1_wake_event;
unsigned int pci_rev;
};
void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes);
int acp_dma_status(struct acp_dev_data *adata, unsigned char ch);
int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
unsigned int dest_addr, int dsp_data_size);
int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
unsigned int start_addr, unsigned int dest_addr,
unsigned int image_length);
/* ACP device probe/remove */
int amd_sof_acp_probe(struct snd_sof_dev *sdev);
void amd_sof_acp_remove(struct snd_sof_dev *sdev);
/* DSP Loader callbacks */
int acp_sof_dsp_run(struct snd_sof_dev *sdev);
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);
/* 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);
int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
u32 offset, void *dest, size_t size);
/* IPC callbacks */
irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context);
int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
struct snd_sof_pcm_stream *sps,
size_t posn_offset);
int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg);
int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev);
int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
/* ACP - DSP stream callbacks */
int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream);
int acp_dsp_stream_init(struct snd_sof_dev *sdev);
struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag);
int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stream);
/*
* DSP PCM Operations.
*/
int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params);
snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
extern const struct snd_sof_dsp_ops sof_acp_common_ops;
extern struct snd_sof_dsp_ops sof_renoir_ops;
int sof_renoir_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_vangogh_ops;
int sof_vangogh_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_rembrandt_ops;
int sof_rembrandt_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_acp63_ops;
int sof_acp63_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_acp70_ops;
int sof_acp70_ops_init(struct snd_sof_dev *sdev);
struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev);
/* Machine configuration */
int snd_amd_acp_find_config(struct pci_dev *pci);
/* Trace */
int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params);
int acp_sof_trace_release(struct snd_sof_dev *sdev);
/* PM Callbacks */
int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state);
int amd_sof_acp_resume(struct snd_sof_dev *sdev);
void amd_sof_ipc_dump(struct snd_sof_dev *sdev);
void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags);
static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata *pdata)
{
const struct sof_dev_desc *desc = pdata->desc;
return desc->chip_info;
}
int acp_probes_register(struct snd_sof_dev *sdev);
void acp_probes_unregister(struct snd_sof_dev *sdev);
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[];
extern const struct dmi_system_id acp_sof_quirk_table[];
#endif
+142
View File
@@ -0,0 +1,142 @@
// 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) 2023 Advanced Micro Devices, Inc.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
/*
* Hardware interface for Audio DSP on ACP6.3 version based platform
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include "../ops.h"
#include "../sof-audio.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define I2S_HS_INSTANCE 0
#define I2S_BT_INSTANCE 1
#define I2S_SP_INSTANCE 2
#define PDM_DMIC_INSTANCE 3
#define I2S_HS_VIRTUAL_INSTANCE 4
static struct snd_soc_dai_driver acp63_sof_dai[] = {
[I2S_HS_INSTANCE] = {
.id = I2S_HS_INSTANCE,
.name = "acp-sof-hs",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S HS controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_BT_INSTANCE] = {
.id = I2S_BT_INSTANCE,
.name = "acp-sof-bt",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S BT controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_INSTANCE] = {
.id = I2S_SP_INSTANCE,
.name = "acp-sof-sp",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S SP controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[PDM_DMIC_INSTANCE] = {
.id = PDM_DMIC_INSTANCE,
.name = "acp-sof-dmic",
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 4,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_HS_VIRTUAL_INSTANCE] = {
.id = I2S_HS_VIRTUAL_INSTANCE,
.name = "acp-sof-hs-virtual",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
},
};
/* Phoenix ops */
struct snd_sof_dsp_ops sof_acp63_ops;
EXPORT_SYMBOL_NS(sof_acp63_ops, "SND_SOC_SOF_AMD_COMMON");
int sof_acp63_ops_init(struct snd_sof_dev *sdev)
{
/* common defaults */
memcpy(&sof_acp63_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops));
sof_acp63_ops.drv = acp63_sof_dai;
sof_acp63_ops.num_drv = ARRAY_SIZE(acp63_sof_dai);
return 0;
}
+142
View File
@@ -0,0 +1,142 @@
// 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) 2024 Advanced Micro Devices, Inc.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
/*
* Hardware interface for Audio DSP on ACP7.0 version based platform
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include "../ops.h"
#include "../sof-audio.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define I2S_HS_INSTANCE 0
#define I2S_BT_INSTANCE 1
#define I2S_SP_INSTANCE 2
#define PDM_DMIC_INSTANCE 3
#define I2S_HS_VIRTUAL_INSTANCE 4
static struct snd_soc_dai_driver acp70_sof_dai[] = {
[I2S_HS_INSTANCE] = {
.id = I2S_HS_INSTANCE,
.name = "acp-sof-hs",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S HS controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_BT_INSTANCE] = {
.id = I2S_BT_INSTANCE,
.name = "acp-sof-bt",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S BT controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_INSTANCE] = {
.id = I2S_SP_INSTANCE,
.name = "acp-sof-sp",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S SP controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[PDM_DMIC_INSTANCE] = {
.id = PDM_DMIC_INSTANCE,
.name = "acp-sof-dmic",
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 4,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_HS_VIRTUAL_INSTANCE] = {
.id = I2S_HS_VIRTUAL_INSTANCE,
.name = "acp-sof-hs-virtual",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
},
};
/* Phoenix ops */
struct snd_sof_dsp_ops sof_acp70_ops;
EXPORT_SYMBOL_NS(sof_acp70_ops, "SND_SOC_SOF_AMD_COMMON");
int sof_acp70_ops_init(struct snd_sof_dev *sdev)
{
/* common defaults */
memcpy(&sof_acp70_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops));
sof_acp70_ops.drv = acp70_sof_dai;
sof_acp70_ops.num_drv = ARRAY_SIZE(acp70_sof_dai);
return 0;
}
+115
View File
@@ -0,0 +1,115 @@
// 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) 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
/*.
* PCI interface for ACP6.3 device
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <sound/sof.h>
#include <sound/soc-acpi.h>
#include "../ops.h"
#include "../sof-pci-dev.h"
#include "../../amd/mach-config.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define ACP6X_FUTURE_REG_ACLK_0 0x1854
#define ACP6x_REG_START 0x1240000
#define ACP6x_REG_END 0x125C000
static const struct sof_amd_acp_desc acp63_chip_info = {
.pgfsm_base = ACP6X_PGFSM_BASE,
.ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB,
.ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL,
.ext_intr_stat = ACP6X_EXT_INTR_STAT,
.ext_intr_stat1 = ACP6X_EXT_INTR_STAT1,
.acp_error_stat = ACP6X_ERROR_STATUS,
.acp_sw0_i2s_err_reason = ACP6X_SW0_I2S_ERROR_REASON,
.dsp_intr_base = ACP6X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
.fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0,
.sdw_max_link_count = ACP6X_SDW_MAX_MANAGER_COUNT,
.sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP63,
.reg_start_addr = ACP6x_REG_START,
.reg_end_addr = ACP6x_REG_END,
};
static const struct sof_dev_desc acp63_desc = {
.machines = snd_soc_acpi_amd_acp63_sof_machines,
.alt_machines = snd_soc_acpi_amd_acp63_sof_sdw_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &acp63_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-acp_6_3.ri",
},
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_acp63_ops,
.ops_init = sof_acp63_ops_init,
};
static int acp63_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
unsigned int flag;
if (pci->revision != ACP63_PCI_ID)
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);
};
static void acp63_pci_remove(struct pci_dev *pci)
{
sof_pci_remove(pci);
}
/* PCI IDs */
static const struct pci_device_id acp63_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
.driver_data = (unsigned long)&acp63_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, acp63_pci_ids);
/* pci_driver definition */
static struct pci_driver snd_sof_pci_amd_acp63_driver = {
.name = KBUILD_MODNAME,
.id_table = acp63_pci_ids,
.probe = acp63_pci_probe,
.remove = acp63_pci_remove,
.driver = {
.pm = pm_ptr(&sof_pci_pm),
},
};
module_pci_driver(snd_sof_pci_amd_acp63_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("ACP63 SOF Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+121
View File
@@ -0,0 +1,121 @@
// 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) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
/*.
* PCI interface for ACP7.0 device
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <sound/sof.h>
#include <sound/soc-acpi.h>
#include "../ops.h"
#include "../sof-pci-dev.h"
#include "../../amd/mach-config.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define ACP70_FUTURE_REG_ACLK_0 0x1854
#define ACP70_REG_START 0x1240000
#define ACP70_REG_END 0x125C000
static const struct sof_amd_acp_desc acp70_chip_info = {
.pgfsm_base = ACP70_PGFSM_BASE,
.ext_intr_enb = ACP70_EXTERNAL_INTR_ENB,
.ext_intr_cntl = ACP70_EXTERNAL_INTR_CNTL,
.ext_intr_stat = ACP70_EXT_INTR_STAT,
.ext_intr_stat1 = ACP70_EXT_INTR_STAT1,
.acp_error_stat = ACP70_ERROR_STATUS,
.dsp_intr_base = ACP70_DSP_SW_INTR_BASE,
.acp_sw0_i2s_err_reason = ACP7X_SW0_I2S_ERROR_REASON,
.sram_pte_offset = ACP70_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP70_AXI2DAGB_SEM_0,
.fusion_dsp_offset = ACP70_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP70_FUTURE_REG_ACLK_0,
.sdw_max_link_count = ACP70_SDW_MAX_MANAGER_COUNT,
.sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP70,
.reg_start_addr = ACP70_REG_START,
.reg_end_addr = ACP70_REG_END,
};
static const struct sof_dev_desc acp70_desc = {
.machines = snd_soc_acpi_amd_acp70_sof_machines,
.alt_machines = snd_soc_acpi_amd_acp70_sof_sdw_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &acp70_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-acp_7_0.ri",
},
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_acp70_ops,
.ops_init = sof_acp70_ops_init,
};
static int acp70_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
unsigned int flag;
switch (pci->revision) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_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);
};
static void acp70_pci_remove(struct pci_dev *pci)
{
sof_pci_remove(pci);
}
/* PCI IDs */
static const struct pci_device_id acp70_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
.driver_data = (unsigned long)&acp70_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, acp70_pci_ids);
/* pci_driver definition */
static struct pci_driver snd_sof_pci_amd_acp70_driver = {
.name = KBUILD_MODNAME,
.id_table = acp70_pci_ids,
.probe = acp70_pci_probe,
.remove = acp70_pci_remove,
.driver = {
.pm = pm_ptr(&sof_pci_pm),
},
};
module_pci_driver(snd_sof_pci_amd_acp70_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("ACP70 SOF Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+104
View File
@@ -0,0 +1,104 @@
// 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) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*.
* PCI interface for Rembrandt ACP device
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <sound/sof.h>
#include <sound/soc-acpi.h>
#include "../ops.h"
#include "../sof-pci-dev.h"
#include "../../amd/mach-config.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define ACP6x_REG_START 0x1240000
#define ACP6x_REG_END 0x125C000
#define ACP6X_FUTURE_REG_ACLK_0 0x1854
static const struct sof_amd_acp_desc rembrandt_chip_info = {
.pgfsm_base = ACP6X_PGFSM_BASE,
.ext_intr_stat = ACP6X_EXT_INTR_STAT,
.dsp_intr_base = ACP6X_DSP_SW_INTR_BASE,
.acp_error_stat = ACP6X_ERROR_STATUS,
.acp_sw0_i2s_err_reason = ACP6X_SW0_I2S_ERROR_REASON,
.sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
.fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0,
};
static const struct sof_dev_desc rembrandt_desc = {
.machines = snd_soc_acpi_amd_rmb_sof_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &rembrandt_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-rmb.ri",
},
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_rembrandt_ops,
.ops_init = sof_rembrandt_ops_init,
};
static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
unsigned int flag;
if (pci->revision != ACP_RMB_PCI_ID)
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);
};
static void acp_pci_rmb_remove(struct pci_dev *pci)
{
sof_pci_remove(pci);
}
/* PCI IDs */
static const struct pci_device_id rmb_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
.driver_data = (unsigned long)&rembrandt_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, rmb_pci_ids);
/* pci_driver definition */
static struct pci_driver snd_sof_pci_amd_rmb_driver = {
.name = KBUILD_MODNAME,
.id_table = rmb_pci_ids,
.probe = acp_pci_rmb_probe,
.remove = acp_pci_rmb_remove,
};
module_pci_driver(snd_sof_pci_amd_rmb_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("REMBRANDT SOF Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+108
View File
@@ -0,0 +1,108 @@
// 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* PCI interface for Renoir ACP device
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <sound/sof.h>
#include <sound/soc-acpi.h>
#include "../ops.h"
#include "../sof-pci-dev.h"
#include "../../amd/mach-config.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define ACP3x_REG_START 0x1240000
#define ACP3x_REG_END 0x125C000
#define ACP3X_FUTURE_REG_ACLK_0 0x1860
static const struct sof_amd_acp_desc renoir_chip_info = {
.pgfsm_base = ACP3X_PGFSM_BASE,
.ext_intr_stat = ACP3X_EXT_INTR_STAT,
.dsp_intr_base = ACP3X_DSP_SW_INTR_BASE,
.acp_error_stat = ACP3X_ERROR_STATUS,
.acp_sw0_i2s_err_reason = ACP3X_SW_I2S_ERROR_REASON,
.sram_pte_offset = ACP3X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0,
.acp_clkmux_sel = ACP3X_CLKMUX_SEL,
.probe_reg_offset = ACP3X_FUTURE_REG_ACLK_0,
};
static const struct sof_dev_desc renoir_desc = {
.machines = snd_soc_acpi_amd_sof_machines,
.use_acpi_target_states = true,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &renoir_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-rn.ri",
},
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_renoir_ops,
.ops_init = sof_renoir_ops_init,
};
static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
unsigned int flag;
if (pci->revision != ACP_RN_PCI_ID)
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);
};
static void acp_pci_rn_remove(struct pci_dev *pci)
{
return sof_pci_remove(pci);
}
/* PCI IDs */
static const struct pci_device_id rn_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
.driver_data = (unsigned long)&renoir_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, rn_pci_ids);
/* pci_driver definition */
static struct pci_driver snd_sof_pci_amd_rn_driver = {
.name = KBUILD_MODNAME,
.id_table = rn_pci_ids,
.probe = acp_pci_rn_probe,
.remove = acp_pci_rn_remove,
.driver = {
.pm = pm_ptr(&sof_pci_pm),
},
};
module_pci_driver(snd_sof_pci_amd_rn_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("RENOIR SOF Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+101
View File
@@ -0,0 +1,101 @@
// 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) 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
/*.
* PCI interface for Vangogh ACP device
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <sound/sof.h>
#include <sound/soc-acpi.h>
#include "../sof-pci-dev.h"
#include "../../amd/mach-config.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define ACP5X_FUTURE_REG_ACLK_0 0x1864
static const struct sof_amd_acp_desc vangogh_chip_info = {
.name = "vangogh",
.pgfsm_base = ACP5X_PGFSM_BASE,
.ext_intr_stat = ACP5X_EXT_INTR_STAT,
.dsp_intr_base = ACP5X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP5X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP5X_AXI2DAGB_SEM_0,
.probe_reg_offset = ACP5X_FUTURE_REG_ACLK_0,
};
static const struct sof_dev_desc vangogh_desc = {
.machines = snd_soc_acpi_amd_vangogh_sof_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &vangogh_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-vangogh.ri",
},
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_vangogh_ops,
.ops_init = sof_vangogh_ops_init,
};
static int acp_pci_vgh_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
unsigned int flag;
if (pci->revision != ACP_VANGOGH_PCI_ID)
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);
};
static void acp_pci_vgh_remove(struct pci_dev *pci)
{
sof_pci_remove(pci);
}
/* PCI IDs */
static const struct pci_device_id vgh_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
.driver_data = (unsigned long)&vangogh_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, vgh_pci_ids);
/* pci_driver definition */
static struct pci_driver snd_sof_pci_amd_vgh_driver = {
.name = KBUILD_MODNAME,
.id_table = vgh_pci_ids,
.probe = acp_pci_vgh_probe,
.remove = acp_pci_vgh_remove,
.driver = {
.pm = pm_ptr(&sof_pci_pm),
},
};
module_pci_driver(snd_sof_pci_amd_vgh_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("VANGOGH SOF Driver");
MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
+142
View File
@@ -0,0 +1,142 @@
// 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) 2022 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for Audio DSP on Rembrandt platform
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include "../ops.h"
#include "../sof-audio.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define I2S_HS_INSTANCE 0
#define I2S_BT_INSTANCE 1
#define I2S_SP_INSTANCE 2
#define PDM_DMIC_INSTANCE 3
#define I2S_HS_VIRTUAL_INSTANCE 4
static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
[I2S_HS_INSTANCE] = {
.id = I2S_HS_INSTANCE,
.name = "acp-sof-hs",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S HS controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_BT_INSTANCE] = {
.id = I2S_BT_INSTANCE,
.name = "acp-sof-bt",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S BT controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_INSTANCE] = {
.id = I2S_SP_INSTANCE,
.name = "acp-sof-sp",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S SP controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[PDM_DMIC_INSTANCE] = {
.id = PDM_DMIC_INSTANCE,
.name = "acp-sof-dmic",
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 4,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_HS_VIRTUAL_INSTANCE] = {
.id = I2S_HS_VIRTUAL_INSTANCE,
.name = "acp-sof-hs-virtual",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
},
};
/* Rembrandt ops */
struct snd_sof_dsp_ops sof_rembrandt_ops;
EXPORT_SYMBOL_NS(sof_rembrandt_ops, "SND_SOC_SOF_AMD_COMMON");
int sof_rembrandt_ops_init(struct snd_sof_dev *sdev)
{
/* common defaults */
memcpy(&sof_rembrandt_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops));
sof_rembrandt_ops.drv = rembrandt_sof_dai;
sof_rembrandt_ops.num_drv = ARRAY_SIZE(rembrandt_sof_dai);
return 0;
}
+117
View File
@@ -0,0 +1,117 @@
// 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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for Audio DSP on Renoir platform
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include "../ops.h"
#include "../sof-audio.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#define I2S_BT_INSTANCE 0
#define I2S_SP_INSTANCE 1
#define PDM_DMIC_INSTANCE 2
#define I2S_SP_VIRTUAL_INSTANCE 3
static struct snd_soc_dai_driver renoir_sof_dai[] = {
[I2S_BT_INSTANCE] = {
.id = I2S_BT_INSTANCE,
.name = "acp-sof-bt",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S BT controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_INSTANCE] = {
.id = I2S_SP_INSTANCE,
.name = "acp-sof-sp",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S SP controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[PDM_DMIC_INSTANCE] = {
.id = PDM_DMIC_INSTANCE,
.name = "acp-sof-dmic",
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 4,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_VIRTUAL_INSTANCE] = {
.id = I2S_SP_VIRTUAL_INSTANCE,
.name = "acp-sof-sp-virtual",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
},
};
/* Renoir ops */
struct snd_sof_dsp_ops sof_renoir_ops;
EXPORT_SYMBOL_NS(sof_renoir_ops, "SND_SOC_SOF_AMD_COMMON");
int sof_renoir_ops_init(struct snd_sof_dev *sdev)
{
/* common defaults */
memcpy(&sof_renoir_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops));
sof_renoir_ops.drv = renoir_sof_dai;
sof_renoir_ops.num_drv = ARRAY_SIZE(renoir_sof_dai);
return 0;
}
+177
View File
@@ -0,0 +1,177 @@
// 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) 2023 Advanced Micro Devices, Inc.
//
// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
/*
* Hardware interface for Audio DSP on Vangogh platform
*/
#include <linux/delay.h>
#include <linux/module.h>
#include "acp.h"
#define I2S_HS_INSTANCE 0
#define I2S_BT_INSTANCE 1
#define I2S_SP_INSTANCE 2
#define PDM_DMIC_INSTANCE 3
#define I2S_HS_VIRTUAL_INSTANCE 4
static struct snd_soc_dai_driver vangogh_sof_dai[] = {
[I2S_HS_INSTANCE] = {
.id = I2S_HS_INSTANCE,
.name = "acp-sof-hs",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S HS controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_BT_INSTANCE] = {
.id = I2S_BT_INSTANCE,
.name = "acp-sof-bt",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S BT controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_SP_INSTANCE] = {
.id = I2S_SP_INSTANCE,
.name = "acp-sof-sp",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S SP controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
[PDM_DMIC_INSTANCE] = {
.id = PDM_DMIC_INSTANCE,
.name = "acp-sof-dmic",
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 4,
.rate_min = 8000,
.rate_max = 48000,
},
},
[I2S_HS_VIRTUAL_INSTANCE] = {
.id = I2S_HS_VIRTUAL_INSTANCE,
.name = "acp-sof-hs-virtual",
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
},
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
/* Supporting only stereo for I2S HS-Virtual controller capture */
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
},
};
static int sof_vangogh_post_fw_run_delay(struct snd_sof_dev *sdev)
{
/*
* Resuming from suspend in some cases my cause the DSP firmware
* to enter an unrecoverable faulty state. Delaying a bit any host
* to DSP transmission right after firmware boot completion seems
* to resolve the issue.
*/
if (!sdev->first_boot)
usleep_range(100, 150);
return 0;
}
/* Vangogh ops */
struct snd_sof_dsp_ops sof_vangogh_ops;
EXPORT_SYMBOL_NS(sof_vangogh_ops, "SND_SOC_SOF_AMD_COMMON");
int sof_vangogh_ops_init(struct snd_sof_dev *sdev)
{
const struct dmi_system_id *dmi_id;
struct acp_quirk_entry *quirks;
/* common defaults */
memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops));
sof_vangogh_ops.drv = vangogh_sof_dai;
sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai);
dmi_id = dmi_first_match(acp_sof_quirk_table);
if (dmi_id) {
quirks = dmi_id->driver_data;
if (quirks->signed_fw_image)
sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware;
if (quirks->post_fw_run_delay)
sof_vangogh_ops.post_fw_run = sof_vangogh_post_fw_run_delay;
}
return 0;
}