ASoC: SOF: Platform updates for AMD and Mediatek
Merge series from Daniel Baluta <daniel.baluta@oss.nxp.com>: This patchseries adds AMD Renoir ACP HW support.
This commit is contained in:
commit
b6a5f4f055
|
@ -0,0 +1,21 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_AMD_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_AMD_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/* ACP Configuration Request - SOF_IPC_DAI_AMD_CONFIG */
|
||||
struct sof_ipc_dai_acp_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
||||
uint32_t fsync_rate; /* FSYNC frequency in Hz */
|
||||
uint32_t tdm_slots;
|
||||
} __packed;
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/dai-intel.h>
|
||||
#include <sound/sof/dai-imx.h>
|
||||
#include <sound/sof/dai-amd.h>
|
||||
|
||||
/*
|
||||
* DAI Configuration.
|
||||
|
@ -66,6 +67,9 @@ enum sof_ipc_dai_type {
|
|||
SOF_DAI_INTEL_ALH, /**< Intel ALH */
|
||||
SOF_DAI_IMX_SAI, /**< i.MX SAI */
|
||||
SOF_DAI_IMX_ESAI, /**< i.MX ESAI */
|
||||
SOF_DAI_AMD_BT, /**< AMD ACP BT*/
|
||||
SOF_DAI_AMD_SP, /**< AMD ACP SP */
|
||||
SOF_DAI_AMD_DMIC, /**< AMD ACP DMIC */
|
||||
};
|
||||
|
||||
/* general purpose DAI configuration */
|
||||
|
@ -90,6 +94,9 @@ struct sof_ipc_dai_config {
|
|||
struct sof_ipc_dai_alh_params alh;
|
||||
struct sof_ipc_dai_esai_params esai;
|
||||
struct sof_ipc_dai_sai_params sai;
|
||||
struct sof_ipc_dai_acp_params acpbt;
|
||||
struct sof_ipc_dai_acp_params acpsp;
|
||||
struct sof_ipc_dai_acp_params acpdmic;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -96,4 +96,10 @@ config SND_SOC_AMD_YC_MACH
|
|||
Say m if you have such a device.
|
||||
If unsure select "N".
|
||||
|
||||
config SND_AMD_ACP_CONFIG
|
||||
tristate "AMD ACP configuration selection"
|
||||
help
|
||||
This option adds an auto detection to determine which ACP
|
||||
driver modules to use
|
||||
|
||||
source "sound/soc/amd/acp/Kconfig"
|
||||
|
|
|
@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o
|
|||
snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o
|
||||
snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
|
||||
snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o
|
||||
snd-acp-config-objs := acp-config.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
|
||||
|
@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
|
|||
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
|
||||
obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// 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>
|
||||
//
|
||||
|
||||
/* ACP machine configuration module */
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../sof/amd/acp.h"
|
||||
#include "mach-config.h"
|
||||
|
||||
static int acp_quirk_data;
|
||||
|
||||
static const struct config_entry config_table[] = {
|
||||
{
|
||||
.flags = FLAG_AMD_SOF,
|
||||
.device = ACP_PCI_DEV_ID,
|
||||
.dmi_table = (const struct dmi_system_id []) {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int snd_amd_acp_find_config(struct pci_dev *pci)
|
||||
{
|
||||
const struct config_entry *table = config_table;
|
||||
u16 device = pci->device;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
|
||||
if (table->device != device)
|
||||
continue;
|
||||
if (table->dmi_table && !dmi_check_system(table->dmi_table))
|
||||
continue;
|
||||
acp_quirk_data = table->flags;
|
||||
return table->flags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_amd_acp_find_config);
|
||||
|
||||
struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
|
||||
{
|
||||
.id = "AMDI1019",
|
||||
.drv_name = "renoir-dsp",
|
||||
.pdata = (void *)&acp_quirk_data,
|
||||
.fw_filename = "sof-rn.ri",
|
||||
.sof_tplg_filename = "sof-acp.tplg",
|
||||
},
|
||||
{},
|
||||
};
|
||||
EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -0,0 +1,28 @@
|
|||
/* 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.
|
||||
*
|
||||
* Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
|
||||
*/
|
||||
#ifndef __AMD_MACH_CONFIG_H
|
||||
#define __AMD_MACH_CONFIG_H
|
||||
|
||||
#include <sound/soc-acpi.h>
|
||||
|
||||
#define FLAG_AMD_SOF BIT(1)
|
||||
#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
|
||||
|
||||
#define ACP_PCI_DEV_ID 0x15E2
|
||||
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
|
||||
|
||||
struct config_entry {
|
||||
u32 flags;
|
||||
u16 device;
|
||||
const struct dmi_system_id *dmi_table;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -233,6 +233,7 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE
|
|||
When selected, the probe is handled in two steps, for example to
|
||||
avoid lockdeps if request_module is used in the probe.
|
||||
|
||||
source "sound/soc/sof/amd/Kconfig"
|
||||
source "sound/soc/sof/imx/Kconfig"
|
||||
source "sound/soc/sof/intel/Kconfig"
|
||||
source "sound/soc/sof/xtensa/Kconfig"
|
||||
|
|
|
@ -22,4 +22,5 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o
|
|||
|
||||
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
|
||||
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
|
||||
obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/
|
||||
obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# 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.
|
||||
|
||||
config SND_SOC_SOF_AMD_TOPLEVEL
|
||||
tristate "SOF support for AMD audio DSPs"
|
||||
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_PCI_DEV
|
||||
select SND_AMD_ACP_CONFIG
|
||||
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
|
||||
select SND_SOC_SOF_AMD_COMMON
|
||||
help
|
||||
Select this option for SOF support on AMD Renoir platform
|
||||
endif
|
|
@ -0,0 +1,11 @@
|
|||
# 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.
|
||||
|
||||
snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o
|
||||
snd-sof-amd-renoir-objs := pci-rn.o renoir.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
|
|
@ -0,0 +1,78 @@
|
|||
/* 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.
|
||||
*
|
||||
* 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
|
||||
|
||||
/* 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_I2S_PIN_CONFIG 0x1400
|
||||
|
||||
/* Registers from ACP_PGFSM block */
|
||||
#define ACP_PGFSM_CONTROL 0x141C
|
||||
#define ACP_PGFSM_STATUS 0x1420
|
||||
|
||||
/* Registers from ACP_INTR block */
|
||||
#define ACP_EXTERNAL_INTR_ENB 0x1800
|
||||
#define ACP_EXTERNAL_INTR_CNTL 0x1804
|
||||
#define ACP_EXTERNAL_INTR_STAT 0x1808
|
||||
#define ACP_DSP_SW_INTR_CNTL 0x1814
|
||||
#define ACP_DSP_SW_INTR_STAT 0x1818
|
||||
#define ACP_SW_INTR_TRIG 0x181C
|
||||
#define ACP_ERROR_STATUS 0x18C4
|
||||
|
||||
/* 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_PSP_ACK 0x1C74
|
||||
|
||||
#define ACP_SCRATCH_REG_0 0x10000
|
||||
|
||||
#endif
|
|
@ -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: 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;
|
||||
u32 swintr_trigger;
|
||||
|
||||
swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG);
|
||||
swintr_trigger |= 0x01;
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger);
|
||||
}
|
||||
|
||||
static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
|
||||
{
|
||||
unsigned int host_msg = 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 = 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 = 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;
|
||||
unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
|
||||
|
||||
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);
|
||||
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 = offsetof(struct scratch_ipc_conf, sof_in_box);
|
||||
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 {
|
||||
/* 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;
|
||||
unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
|
||||
unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
|
||||
bool ipc_irq = false;
|
||||
int dsp_msg, dsp_ack;
|
||||
|
||||
dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
|
||||
if (dsp_msg) {
|
||||
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) {
|
||||
spin_lock_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);
|
||||
spin_unlock_irq(&sdev->ipc_lock);
|
||||
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_pcm_substream *substream,
|
||||
void *p, size_t sz)
|
||||
{
|
||||
unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
|
||||
|
||||
if (!substream || !sdev->stream_box.size)
|
||||
acp_mailbox_read(sdev, offset, p, sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
|
||||
|
||||
int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
|
||||
const struct sof_ipc_pcm_params_reply *reply)
|
||||
{
|
||||
/* TODO: Implement stream hw params to validate stream offset */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON);
|
||||
|
||||
int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
|
||||
{
|
||||
return ACP_SCRATCH_MEMORY_ADDRESS;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
|
||||
|
||||
MODULE_DESCRIPTION("AMD ACP sof-ipc driver");
|
|
@ -0,0 +1,199 @@
|
|||
// 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 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_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)
|
||||
{
|
||||
switch (blk_type) {
|
||||
case SOF_FW_BLK_TYPE_SRAM:
|
||||
offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
|
||||
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 snd_sof_pdata *plat_data = sdev->pdata;
|
||||
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 = plat_data->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_ATOMIC);
|
||||
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_ATOMIC);
|
||||
if (!adata->data_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
dest = adata->data_buf + offset;
|
||||
adata->fw_data_bin_size = size + offset;
|
||||
break;
|
||||
case SOF_FW_BLK_TYPE_SRAM:
|
||||
offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
|
||||
memcpy_to_scratch(sdev, offset, src, size);
|
||||
return 0;
|
||||
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;
|
||||
unsigned int low, high;
|
||||
dma_addr_t addr;
|
||||
u16 page_idx;
|
||||
u32 offset;
|
||||
|
||||
sdev = adata->dev;
|
||||
|
||||
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;
|
||||
default:
|
||||
dev_err(sdev->dev, "Invalid data type %x\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* pre fw run operations */
|
||||
int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(sdev->dev);
|
||||
struct snd_sof_pdata *plat_data = sdev->pdata;
|
||||
struct acp_dev_data *adata;
|
||||
unsigned int src_addr, size_fw;
|
||||
u32 page_count, dma_size;
|
||||
int ret;
|
||||
|
||||
adata = sdev->pdata->hw_pdata;
|
||||
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;
|
||||
}
|
||||
configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata);
|
||||
|
||||
src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE;
|
||||
ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS,
|
||||
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);
|
||||
|
||||
/* Free memory once DMA is complete */
|
||||
dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
|
||||
dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
|
||||
dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr);
|
||||
adata->bin_buf = NULL;
|
||||
adata->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)
|
||||
{
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON);
|
|
@ -0,0 +1,82 @@
|
|||
// 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 sof_ipc_stream_params *ipc_params)
|
||||
{
|
||||
struct acp_dsp_stream *stream = substream->runtime->private_data;
|
||||
unsigned int buf_offset, index;
|
||||
u32 size;
|
||||
int ret;
|
||||
|
||||
size = ipc_params->buffer.size;
|
||||
stream->num_pages = ipc_params->buffer.pages;
|
||||
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;
|
||||
}
|
||||
|
||||
ipc_params->buffer.phy_addr = stream->reg_offset;
|
||||
ipc_params->stream_tag = stream->stream_tag;
|
||||
|
||||
/* write buffer size of stream in scratch memory */
|
||||
|
||||
buf_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);
|
|
@ -0,0 +1,181 @@
|
|||
// 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)
|
||||
{
|
||||
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 = 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 */
|
||||
reg_val = ACP_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;
|
||||
}
|
||||
|
||||
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);
|
|
@ -0,0 +1,84 @@
|
|||
// 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);
|
||||
|
||||
static int acp_sof_trace_prepare(struct snd_sof_dev *sdev,
|
||||
struct sof_ipc_dma_trace_params_ext *params)
|
||||
{
|
||||
struct acp_dsp_stream *stream;
|
||||
struct acp_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = sdev->pdata->hw_pdata;
|
||||
stream = adata->dtrace_stream;
|
||||
stream->dmab = &sdev->dmatb;
|
||||
stream->num_pages = NUM_PAGES;
|
||||
|
||||
ret = acp_dsp_stream_config(sdev, stream);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "Failed to configure trace stream\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
params->buffer.phy_addr = stream->reg_offset;
|
||||
params->stream_tag = stream->stream_tag;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
|
||||
{
|
||||
struct sof_ipc_dma_trace_params_ext *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;
|
||||
|
||||
adata->dtrace_stream = stream;
|
||||
params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag);
|
||||
ret = acp_sof_trace_prepare(sdev, params);
|
||||
if (ret < 0) {
|
||||
acp_dsp_stream_put(sdev, stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*stream_tag = stream->stream_tag;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON);
|
|
@ -0,0 +1,446 @@
|
|||
// 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: 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 "../ops.h"
|
||||
#include "acp.h"
|
||||
#include "acp-dsp-offset.h"
|
||||
|
||||
static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)
|
||||
{
|
||||
pci_write_config_dword(dev, 0x60, smn_addr);
|
||||
pci_write_config_dword(dev, 0x64, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data)
|
||||
{
|
||||
pci_write_config_dword(dev, 0x60, smn_addr);
|
||||
pci_read_config_dword(dev, 0x64, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void configure_acp_groupregisters(struct acp_dev_data *adata)
|
||||
{
|
||||
struct snd_sof_dev *sdev = adata->dev;
|
||||
|
||||
/* Group Enable */
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
|
||||
ACP_SRAM_PTE_OFFSET | BIT(31));
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
|
||||
PAGE_SIZE_4K_ENABLE);
|
||||
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
|
||||
}
|
||||
|
||||
static void init_dma_descriptor(struct acp_dev_data *adata)
|
||||
{
|
||||
struct snd_sof_dev *sdev = adata->dev;
|
||||
unsigned int addr;
|
||||
|
||||
addr = ACP_SRAM_PTE_OFFSET + offsetof(struct scratch_reg_conf, dma_desc);
|
||||
|
||||
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 + 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;
|
||||
unsigned int val, status;
|
||||
int ret;
|
||||
|
||||
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, ACP_ERROR_STATUS);
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_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;
|
||||
}
|
||||
|
||||
static int psp_fw_validate(struct acp_dev_data *adata)
|
||||
{
|
||||
struct snd_sof_dev *sdev = adata->dev;
|
||||
int timeout;
|
||||
u32 data;
|
||||
|
||||
smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND);
|
||||
|
||||
for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
|
||||
msleep(20);
|
||||
smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data);
|
||||
if (data & MBOX_READY_MASK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
ret = psp_fw_validate(adata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER);
|
||||
if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) {
|
||||
dev_err(sdev->dev, "PSP validation failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
|
||||
{
|
||||
struct snd_sof_dev *sdev = adata->dev;
|
||||
unsigned int val;
|
||||
int ret = 0;
|
||||
|
||||
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_memory_init(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
|
||||
|
||||
snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL,
|
||||
ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
|
||||
configure_acp_groupregisters(adata);
|
||||
init_dma_descriptor(adata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t acp_irq_thread(int irq, void *context)
|
||||
{
|
||||
struct snd_sof_dev *sdev = context;
|
||||
unsigned int val;
|
||||
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT);
|
||||
if (val & ACP_SHA_STAT) {
|
||||
/* Clear SHA interrupt raised by PSP */
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT, val);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT);
|
||||
if (val & ACP_DSP_TO_HOST_IRQ) {
|
||||
sof_ops(sdev)->irq_thread(irq, sdev);
|
||||
val |= ACP_DSP_TO_HOST_IRQ;
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
};
|
||||
|
||||
static irqreturn_t acp_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_id;
|
||||
unsigned int val;
|
||||
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT);
|
||||
if (val)
|
||||
return IRQ_WAKE_THREAD;
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int acp_power_on(struct snd_sof_dev *sdev)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS);
|
||||
|
||||
if (val == ACP_POWERED_ON)
|
||||
return 0;
|
||||
|
||||
if (val & ACP_PGFSM_STATUS_MASK)
|
||||
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_PGFSM_CONTROL,
|
||||
ACP_PGFSM_CNTL_POWER_ON_MASK);
|
||||
|
||||
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_PGFSM_STATUS, 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_init(struct snd_sof_dev *sdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = acp_power_on(sdev);
|
||||
if (ret) {
|
||||
dev_err(sdev->dev, "ACP power on failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* Reset */
|
||||
return acp_reset(sdev);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int addr;
|
||||
int ret;
|
||||
|
||||
adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
|
||||
GFP_KERNEL);
|
||||
if (!adata)
|
||||
return -ENOMEM;
|
||||
|
||||
adata->dev = sdev;
|
||||
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");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
sdev->pdata->hw_pdata = adata;
|
||||
|
||||
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->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
|
||||
if (!adata->smn_dev) {
|
||||
dev_err(sdev->dev, "Failed to get host bridge device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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);
|
||||
pci_dev_put(adata->smn_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = acp_init(sdev);
|
||||
if (ret < 0) {
|
||||
free_irq(sdev->ipc_irq, sdev);
|
||||
pci_dev_put(adata->smn_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
acp_memory_init(sdev);
|
||||
|
||||
acp_dsp_stream_init(sdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
|
||||
|
||||
int amd_sof_acp_remove(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
|
||||
|
||||
if (adata->smn_dev)
|
||||
pci_dev_put(adata->smn_dev);
|
||||
|
||||
if (sdev->ipc_irq)
|
||||
free_irq(sdev->ipc_irq, sdev);
|
||||
|
||||
return acp_reset(sdev);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
|
||||
|
||||
MODULE_DESCRIPTION("AMD ACP sof driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -0,0 +1,226 @@
|
|||
/* 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.
|
||||
*
|
||||
* Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef __SOF_AMD_ACP_H
|
||||
#define __SOF_AMD_ACP_H
|
||||
|
||||
#include "../sof-priv.h"
|
||||
|
||||
#define ACP_MAX_STREAM 8
|
||||
|
||||
#define ACP_DSP_BAR 0
|
||||
|
||||
#define ACP_REG_POLL_INTERVAL 500
|
||||
#define ACP_REG_POLL_TIMEOUT_US 2000
|
||||
#define ACP_DMA_COMPLETE_TIMEOUT_US 5000
|
||||
|
||||
#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
|
||||
#define ACP_PGFSM_STATUS_MASK 0x03
|
||||
#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_INTR_EN_MASK 0x00000001
|
||||
#define ACP_SRAM_PTE_OFFSET 0x02050000
|
||||
#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_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 ACP_SCRATCH_MEMORY_ADDRESS 0x02050000
|
||||
#define ACP_SYSTEM_MEMORY_WINDOW 0x4000000
|
||||
#define ACP_IRAM_BASE_ADDRESS 0x000000
|
||||
#define ACP_DATA_RAM_BASE_ADDRESS 0x01000000
|
||||
#define ACP_DRAM_PAGE_COUNT 128
|
||||
|
||||
#define ACP_DSP_TO_HOST_IRQ 0x04
|
||||
|
||||
#define HOST_BRIDGE_CZN 0x1630
|
||||
#define ACP_SHA_STAT 0x8000
|
||||
#define ACP_PSP_TIMEOUT_COUNTER 5
|
||||
#define ACP_EXT_INTR_ERROR_STAT 0x20000000
|
||||
#define MP0_C2PMSG_26_REG 0x03810570
|
||||
#define MBOX_ACP_SHA_DMA_COMMAND 0x330000
|
||||
#define MBOX_READY_MASK 0x80000000
|
||||
#define MBOX_STATUS_MASK 0xFFFF
|
||||
|
||||
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 {
|
||||
/* DSP mailbox */
|
||||
u8 sof_out_box[512];
|
||||
/* Host mailbox */
|
||||
u8 sof_in_box[512];
|
||||
/* 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;
|
||||
};
|
||||
|
||||
/* Common device data struct for ACP devices */
|
||||
struct acp_dev_data {
|
||||
struct snd_sof_dev *dev;
|
||||
unsigned int fw_bin_size;
|
||||
unsigned int fw_data_bin_size;
|
||||
u32 fw_bin_page_count;
|
||||
dma_addr_t sha_dma_addr;
|
||||
u8 *bin_buf;
|
||||
dma_addr_t dma_addr;
|
||||
u8 *data_buf;
|
||||
struct dma_descriptor dscr_info[ACP_MAX_DESC];
|
||||
struct acp_dsp_stream stream_buf[ACP_MAX_STREAM];
|
||||
struct acp_dsp_stream *dtrace_stream;
|
||||
struct pci_dev *smn_dev;
|
||||
};
|
||||
|
||||
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);
|
||||
int 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_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_pcm_substream *substream,
|
||||
void *p, size_t sz);
|
||||
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);
|
||||
int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
|
||||
const struct sof_ipc_pcm_params_reply *reply);
|
||||
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 sof_ipc_stream_params *ipc_params);
|
||||
|
||||
extern const struct snd_sof_dsp_ops sof_renoir_ops;
|
||||
|
||||
/* Machine configuration */
|
||||
int snd_amd_acp_find_config(struct pci_dev *pci);
|
||||
|
||||
/* Trace */
|
||||
int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
|
||||
int acp_sof_trace_release(struct snd_sof_dev *sdev);
|
||||
|
||||
struct sof_amd_acp_desc {
|
||||
unsigned int host_bridge_id;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,165 @@
|
|||
// 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"
|
||||
|
||||
#define ACP3x_REG_START 0x1240000
|
||||
#define ACP3x_REG_END 0x125C000
|
||||
|
||||
static struct platform_device *dmic_dev;
|
||||
static struct platform_device *pdev;
|
||||
|
||||
static const struct resource renoir_res[] = {
|
||||
{
|
||||
.start = 0,
|
||||
.end = ACP3x_REG_END - ACP3x_REG_START,
|
||||
.name = "acp_mem",
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.name = "acp_dai_irq",
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sof_amd_acp_desc renoir_chip_info = {
|
||||
.host_bridge_id = HOST_BRIDGE_CZN,
|
||||
};
|
||||
|
||||
static const struct sof_dev_desc renoir_desc = {
|
||||
.machines = snd_soc_acpi_amd_sof_machines,
|
||||
.resindex_lpe_base = 0,
|
||||
.resindex_pcicfg_base = -1,
|
||||
.resindex_imr_base = -1,
|
||||
.irqindex_host_ipc = -1,
|
||||
.chip_info = &renoir_chip_info,
|
||||
.default_fw_path = "amd/sof",
|
||||
.default_tplg_path = "amd/sof-tplg",
|
||||
.default_fw_filename = "sof-rn.ri",
|
||||
.nocodec_tplg_filename = "sof-acp.tplg",
|
||||
.ops = &sof_renoir_ops,
|
||||
};
|
||||
|
||||
static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
||||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct device *dev = &pci->dev;
|
||||
const struct resource *res_i2s;
|
||||
struct resource *res;
|
||||
unsigned int flag, i, addr;
|
||||
int ret;
|
||||
|
||||
flag = snd_amd_acp_find_config(pci);
|
||||
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
|
||||
return -ENODEV;
|
||||
|
||||
ret = sof_pci_probe(pci, pci_id);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
|
||||
if (IS_ERR(dmic_dev)) {
|
||||
dev_err(dev, "failed to create DMIC device\n");
|
||||
sof_pci_remove(pci);
|
||||
return PTR_ERR(dmic_dev);
|
||||
}
|
||||
|
||||
/* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
|
||||
if (flag != FLAG_AMD_SOF_ONLY_DMIC)
|
||||
return 0;
|
||||
|
||||
addr = pci_resource_start(pci, 0);
|
||||
res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
|
||||
if (!res) {
|
||||
sof_pci_remove(pci);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res_i2s = renoir_res;
|
||||
for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) {
|
||||
res[i].name = res_i2s->name;
|
||||
res[i].flags = res_i2s->flags;
|
||||
res[i].start = addr + res_i2s->start;
|
||||
res[i].end = addr + res_i2s->end;
|
||||
if (res_i2s->flags == IORESOURCE_IRQ) {
|
||||
res[i].start = pci->irq;
|
||||
res[i].end = res[i].start;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
/*
|
||||
* We have common PCI driver probe for ACP device but we have to support I2S without SOF
|
||||
* for some distributions. Register platform device that will be used to support non dsp
|
||||
* ACP's audio ends points on some machines.
|
||||
*/
|
||||
|
||||
pdevinfo.name = "acp_asoc_renoir";
|
||||
pdevinfo.id = 0;
|
||||
pdevinfo.parent = &pci->dev;
|
||||
pdevinfo.num_res = ARRAY_SIZE(renoir_res);
|
||||
pdevinfo.res = &res[0];
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
|
||||
sof_pci_remove(pci);
|
||||
platform_device_unregister(dmic_dev);
|
||||
ret = PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void acp_pci_rn_remove(struct pci_dev *pci)
|
||||
{
|
||||
if (dmic_dev)
|
||||
platform_device_unregister(dmic_dev);
|
||||
if (pdev)
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
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,
|
||||
};
|
||||
module_pci_driver(snd_sof_pci_amd_rn_driver);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
|
||||
MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
|
|
@ -0,0 +1,185 @@
|
|||
// 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_MODE 0x04
|
||||
|
||||
static int renoir_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
|
||||
unsigned int val;
|
||||
|
||||
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_I2S_PIN_CONFIG);
|
||||
if (val != I2S_MODE) {
|
||||
dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
.probe = &renoir_dai_probe,
|
||||
},
|
||||
|
||||
[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,
|
||||
},
|
||||
.probe = &renoir_dai_probe,
|
||||
},
|
||||
|
||||
[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,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static void amd_sof_machine_select(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct snd_sof_pdata *sof_pdata = sdev->pdata;
|
||||
const struct sof_dev_desc *desc = sof_pdata->desc;
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
|
||||
mach = snd_soc_acpi_find_machine(desc->machines);
|
||||
if (!mach) {
|
||||
dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sof_pdata->tplg_filename = mach->sof_tplg_filename;
|
||||
sof_pdata->fw_filename = mach->fw_filename;
|
||||
sof_pdata->machine = mach;
|
||||
}
|
||||
|
||||
/* AMD Renoir DSP ops */
|
||||
const struct snd_sof_dsp_ops sof_renoir_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,
|
||||
|
||||
/* Module loading */
|
||||
.load_module = snd_sof_parse_module_memcpy,
|
||||
|
||||
/*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,
|
||||
.ipc_pcm_params = acp_sof_ipc_pcm_params,
|
||||
.get_mailbox_offset = acp_sof_ipc_get_mailbox_offset,
|
||||
.irq_thread = acp_sof_ipc_irq_thread,
|
||||
.fw_ready = sof_fw_ready,
|
||||
|
||||
/* DAI drivers */
|
||||
.drv = renoir_sof_dai,
|
||||
.num_drv = ARRAY_SIZE(renoir_sof_dai),
|
||||
|
||||
/* stream callbacks */
|
||||
.pcm_open = acp_pcm_open,
|
||||
.pcm_close = acp_pcm_close,
|
||||
.pcm_hw_params = acp_pcm_hw_params,
|
||||
|
||||
.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,
|
||||
};
|
||||
EXPORT_SYMBOL(sof_renoir_ops);
|
||||
|
||||
MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
|
||||
MODULE_DESCRIPTION("RENOIR SOF Driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -826,6 +826,42 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
|
|||
"channels_min: %d channels_max: %d\n",
|
||||
channels->min, channels->max);
|
||||
break;
|
||||
case SOF_DAI_AMD_BT:
|
||||
rate->min = dai->dai_config->acpbt.fsync_rate;
|
||||
rate->max = dai->dai_config->acpbt.fsync_rate;
|
||||
channels->min = dai->dai_config->acpbt.tdm_slots;
|
||||
channels->max = dai->dai_config->acpbt.tdm_slots;
|
||||
|
||||
dev_dbg(component->dev,
|
||||
"AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
|
||||
dev_dbg(component->dev,
|
||||
"AMD_BT channels_min: %d channels_max: %d\n",
|
||||
channels->min, channels->max);
|
||||
break;
|
||||
case SOF_DAI_AMD_SP:
|
||||
rate->min = dai->dai_config->acpsp.fsync_rate;
|
||||
rate->max = dai->dai_config->acpsp.fsync_rate;
|
||||
channels->min = dai->dai_config->acpsp.tdm_slots;
|
||||
channels->max = dai->dai_config->acpsp.tdm_slots;
|
||||
|
||||
dev_dbg(component->dev,
|
||||
"AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
|
||||
dev_dbg(component->dev,
|
||||
"AMD_SP channels_min: %d channels_max: %d\n",
|
||||
channels->min, channels->max);
|
||||
break;
|
||||
case SOF_DAI_AMD_DMIC:
|
||||
rate->min = dai->dai_config->acpdmic.fsync_rate;
|
||||
rate->max = dai->dai_config->acpdmic.fsync_rate;
|
||||
channels->min = dai->dai_config->acpdmic.tdm_slots;
|
||||
channels->max = dai->dai_config->acpdmic.tdm_slots;
|
||||
|
||||
dev_dbg(component->dev,
|
||||
"AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
|
||||
dev_dbg(component->dev,
|
||||
"AMD_DMIC channels_min: %d channels_max: %d\n",
|
||||
channels->min, channels->max);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "error: invalid DAI type %d\n",
|
||||
dai->dai_config->type);
|
||||
|
|
|
@ -376,6 +376,9 @@ static const struct sof_dai_types sof_dais[] = {
|
|||
{"ALH", SOF_DAI_INTEL_ALH},
|
||||
{"SAI", SOF_DAI_IMX_SAI},
|
||||
{"ESAI", SOF_DAI_IMX_ESAI},
|
||||
{"ACP", SOF_DAI_AMD_BT},
|
||||
{"ACPSP", SOF_DAI_AMD_SP},
|
||||
{"ACPDMIC", SOF_DAI_AMD_DMIC},
|
||||
};
|
||||
|
||||
static enum sof_ipc_dai_type find_dai(const char *name)
|
||||
|
@ -2992,6 +2995,102 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index,
|
||||
struct snd_soc_dai_link *link,
|
||||
struct snd_soc_tplg_link_config *cfg,
|
||||
struct snd_soc_tplg_hw_config *hw_config,
|
||||
struct sof_ipc_dai_config *config)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
|
||||
u32 size = sizeof(*config);
|
||||
int ret;
|
||||
|
||||
/* handle master/slave and inverted clocks */
|
||||
sof_dai_set_format(hw_config, config);
|
||||
|
||||
/* init IPC */
|
||||
memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params));
|
||||
config->hdr.size = size;
|
||||
|
||||
config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
|
||||
config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
|
||||
|
||||
dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n",
|
||||
config->dai_index, config->acpdmic.tdm_slots,
|
||||
config->acpdmic.fsync_rate);
|
||||
|
||||
/* set config for all DAI's with name matching the link name */
|
||||
ret = sof_set_dai_config(sdev, size, link, config);
|
||||
if (ret < 0)
|
||||
dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n",
|
||||
config->dai_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index,
|
||||
struct snd_soc_dai_link *link,
|
||||
struct snd_soc_tplg_link_config *cfg,
|
||||
struct snd_soc_tplg_hw_config *hw_config,
|
||||
struct sof_ipc_dai_config *config)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
|
||||
u32 size = sizeof(*config);
|
||||
int ret;
|
||||
|
||||
/* handle master/slave and inverted clocks */
|
||||
sof_dai_set_format(hw_config, config);
|
||||
|
||||
/* init IPC */
|
||||
memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params));
|
||||
config->hdr.size = size;
|
||||
|
||||
config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
|
||||
config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
|
||||
|
||||
dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
|
||||
config->dai_index, config->acpbt.tdm_slots,
|
||||
config->acpbt.fsync_rate);
|
||||
|
||||
/* set config for all DAI's with name matching the link name */
|
||||
ret = sof_set_dai_config(sdev, size, link, config);
|
||||
if (ret < 0)
|
||||
dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n",
|
||||
config->dai_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index,
|
||||
struct snd_soc_dai_link *link,
|
||||
struct snd_soc_tplg_link_config *cfg,
|
||||
struct snd_soc_tplg_hw_config *hw_config,
|
||||
struct sof_ipc_dai_config *config)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
|
||||
u32 size = sizeof(*config);
|
||||
int ret;
|
||||
|
||||
/* handle master/slave and inverted clocks */
|
||||
sof_dai_set_format(hw_config, config);
|
||||
|
||||
/* init IPC */
|
||||
memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params));
|
||||
config->hdr.size = size;
|
||||
|
||||
config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
|
||||
config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
|
||||
|
||||
dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n",
|
||||
config->dai_index, config->acpsp.tdm_slots,
|
||||
config->acpsp.fsync_rate);
|
||||
|
||||
/* set config for all DAI's with name matching the link name */
|
||||
ret = sof_set_dai_config(sdev, size, link, config);
|
||||
if (ret < 0)
|
||||
dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n",
|
||||
config->dai_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
|
||||
struct snd_soc_dai_link *link,
|
||||
struct snd_soc_tplg_link_config *cfg,
|
||||
|
@ -3277,6 +3376,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
|
|||
case SOF_DAI_IMX_ESAI:
|
||||
ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
|
||||
break;
|
||||
case SOF_DAI_AMD_BT:
|
||||
ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config);
|
||||
break;
|
||||
case SOF_DAI_AMD_SP:
|
||||
ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config);
|
||||
break;
|
||||
case SOF_DAI_AMD_DMIC:
|
||||
ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf,
|
||||
config);
|
||||
break;
|
||||
default:
|
||||
dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type);
|
||||
ret = -EINVAL;
|
||||
|
|
Loading…
Reference in New Issue