Commit b6a5f4f0 authored by Mark Brown's avatar Mark Brown

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.
parents 745a8e7c f063eba3
/* 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 @@ ...@@ -12,6 +12,7 @@
#include <sound/sof/header.h> #include <sound/sof/header.h>
#include <sound/sof/dai-intel.h> #include <sound/sof/dai-intel.h>
#include <sound/sof/dai-imx.h> #include <sound/sof/dai-imx.h>
#include <sound/sof/dai-amd.h>
/* /*
* DAI Configuration. * DAI Configuration.
...@@ -66,6 +67,9 @@ enum sof_ipc_dai_type { ...@@ -66,6 +67,9 @@ enum sof_ipc_dai_type {
SOF_DAI_INTEL_ALH, /**< Intel ALH */ SOF_DAI_INTEL_ALH, /**< Intel ALH */
SOF_DAI_IMX_SAI, /**< i.MX SAI */ SOF_DAI_IMX_SAI, /**< i.MX SAI */
SOF_DAI_IMX_ESAI, /**< i.MX ESAI */ 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 */ /* general purpose DAI configuration */
...@@ -90,6 +94,9 @@ struct sof_ipc_dai_config { ...@@ -90,6 +94,9 @@ struct sof_ipc_dai_config {
struct sof_ipc_dai_alh_params alh; struct sof_ipc_dai_alh_params alh;
struct sof_ipc_dai_esai_params esai; struct sof_ipc_dai_esai_params esai;
struct sof_ipc_dai_sai_params sai; 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; } __packed;
......
...@@ -96,4 +96,10 @@ config SND_SOC_AMD_YC_MACH ...@@ -96,4 +96,10 @@ config SND_SOC_AMD_YC_MACH
Say m if you have such a device. Say m if you have such a device.
If unsure select "N". 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" source "sound/soc/amd/acp/Kconfig"
...@@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o ...@@ -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-da7219mx98357-mach-objs := acp-da7219-max98357a.o
snd-soc-acp-rt5645-mach-objs := acp-rt5645.o snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.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_ACP) += acp_audio_dma.o
obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.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/ ...@@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
// 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");
/* 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 ...@@ -233,6 +233,7 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE
When selected, the probe is handled in two steps, for example to When selected, the probe is handled in two steps, for example to
avoid lockdeps if request_module is used in the probe. 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/imx/Kconfig"
source "sound/soc/sof/intel/Kconfig" source "sound/soc/sof/intel/Kconfig"
source "sound/soc/sof/xtensa/Kconfig" source "sound/soc/sof/xtensa/Kconfig"
......
...@@ -22,4 +22,5 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o ...@@ -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_INTEL_TOPLEVEL) += intel/
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/
obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/
# 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
# 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
/* 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
// 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");
// 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);
// 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);
// 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);
// 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);
// 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");
/* 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
// 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);
// 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 ...@@ -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: %d channels_max: %d\n",
channels->min, channels->max); channels->min, channels->max);
break; 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: default:
dev_err(component->dev, "error: invalid DAI type %d\n", dev_err(component->dev, "error: invalid DAI type %d\n",
dai->dai_config->type); dai->dai_config->type);
......
...@@ -376,6 +376,9 @@ static const struct sof_dai_types sof_dais[] = { ...@@ -376,6 +376,9 @@ static const struct sof_dai_types sof_dais[] = {
{"ALH", SOF_DAI_INTEL_ALH}, {"ALH", SOF_DAI_INTEL_ALH},
{"SAI", SOF_DAI_IMX_SAI}, {"SAI", SOF_DAI_IMX_SAI},
{"ESAI", SOF_DAI_IMX_ESAI}, {"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) 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, ...@@ -2992,6 +2995,102 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
return ret; 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, static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dai_link *link, struct snd_soc_dai_link *link,
struct snd_soc_tplg_link_config *cfg, struct snd_soc_tplg_link_config *cfg,
...@@ -3277,6 +3376,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, ...@@ -3277,6 +3376,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
case SOF_DAI_IMX_ESAI: case SOF_DAI_IMX_ESAI:
ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break; 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: default:
dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type);
ret = -EINVAL; ret = -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment