ASOC: amd: acp: Add generic PDM and PCI driver support for ACP
Merge series from Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>: These changes add PDM and PCI drivers for AMD ACP hardware.
This commit is contained in:
commit
9a19aba24e
@ -44,6 +44,7 @@ config SND_SOC_AMD_RV_RT5682_MACH
|
||||
|
||||
config SND_SOC_AMD_RENOIR
|
||||
tristate "AMD Audio Coprocessor - Renoir support"
|
||||
select SND_AMD_ACP_CONFIG
|
||||
depends on X86 && PCI
|
||||
help
|
||||
This option enables ACP support for Renoir platform
|
||||
|
@ -15,6 +15,9 @@ config SND_SOC_AMD_ACP_COMMON
|
||||
|
||||
if SND_SOC_AMD_ACP_COMMON
|
||||
|
||||
config SND_SOC_AMD_ACP_PDM
|
||||
tristate
|
||||
|
||||
config SND_SOC_AMD_ACP_I2S
|
||||
tristate
|
||||
|
||||
@ -22,10 +25,17 @@ config SND_SOC_AMD_ACP_PCM
|
||||
tristate
|
||||
select SND_SOC_ACPI if ACPI
|
||||
|
||||
config SND_SOC_AMD_ACP_PCI
|
||||
tristate "AMD ACP PCI Driver Support"
|
||||
depends on X86 && PCI
|
||||
help
|
||||
This options enables generic PCI driver for ACP device.
|
||||
|
||||
config SND_AMD_ASOC_RENOIR
|
||||
tristate "AMD ACP ASOC Renoir Support"
|
||||
select SND_SOC_AMD_ACP_PCM
|
||||
select SND_SOC_AMD_ACP_I2S
|
||||
select SND_SOC_AMD_ACP_PDM
|
||||
depends on X86 && PCI
|
||||
help
|
||||
This option enables Renoir I2S support on AMD platform.
|
||||
|
@ -7,6 +7,8 @@
|
||||
#common acp driver
|
||||
snd-acp-pcm-objs := acp-platform.o
|
||||
snd-acp-i2s-objs := acp-i2s.o
|
||||
snd-acp-pdm-objs := acp-pdm.o
|
||||
snd-acp-pci-objs := acp-pci.o
|
||||
|
||||
#platform specific driver
|
||||
snd-acp-renoir-objs := acp-renoir.o
|
||||
@ -18,6 +20,8 @@ snd-acp-sof-mach-objs := acp-sof-mach.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
|
||||
|
||||
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
|
||||
|
||||
|
@ -23,10 +23,10 @@
|
||||
static struct acp_card_drvdata rt5682_rt1019_data = {
|
||||
.hs_cpu_id = I2S_SP,
|
||||
.amp_cpu_id = I2S_SP,
|
||||
.dmic_cpu_id = NONE,
|
||||
.dmic_cpu_id = DMIC,
|
||||
.hs_codec_id = RT5682,
|
||||
.amp_codec_id = RT1019,
|
||||
.dmic_codec_id = NONE,
|
||||
.dmic_codec_id = DMIC,
|
||||
.gpio_spkr_en = EN_SPKR_GPIO_GB,
|
||||
};
|
||||
|
||||
|
@ -438,6 +438,8 @@ SND_SOC_DAILINK_DEF(sof_sp,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
|
||||
SND_SOC_DAILINK_DEF(sof_dmic,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
|
||||
SND_SOC_DAILINK_DEF(pdm_dmic,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic")));
|
||||
|
||||
int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
|
||||
{
|
||||
@ -613,6 +615,25 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
|
||||
links[i].ops = &acp_card_maxim_ops;
|
||||
links[i].init = acp_card_maxim_init;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (drv_data->dmic_cpu_id == DMIC) {
|
||||
links[i].name = "acp-dmic-codec";
|
||||
links[i].id = DMIC_BE_ID;
|
||||
if (drv_data->dmic_codec_id == DMIC) {
|
||||
links[i].codecs = dmic_codec;
|
||||
links[i].num_codecs = ARRAY_SIZE(dmic_codec);
|
||||
} else {
|
||||
/* Use dummy codec if codec id not specified */
|
||||
links[i].codecs = dummy_codec;
|
||||
links[i].num_codecs = ARRAY_SIZE(dummy_codec);
|
||||
}
|
||||
links[i].cpus = pdm_dmic;
|
||||
links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
|
||||
links[i].platforms = platform_component;
|
||||
links[i].num_platforms = ARRAY_SIZE(platform_component);
|
||||
links[i].dpcm_capture = 1;
|
||||
}
|
||||
|
||||
card->dai_link = links;
|
||||
|
160
sound/soc/amd/acp/acp-pci.c
Normal file
160
sound/soc/amd/acp/acp-pci.c
Normal file
@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
|
||||
//
|
||||
// This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
// redistributing this file, you may do so under either license.
|
||||
//
|
||||
// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
|
||||
|
||||
/*
|
||||
* Generic PCI interface for ACP device
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "amd.h"
|
||||
#include "../mach-config.h"
|
||||
|
||||
#define DRV_NAME "acp_pci"
|
||||
|
||||
#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 acp3x_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 int acp_pci_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_acp;
|
||||
struct acp_chip_info *chip;
|
||||
struct resource *res;
|
||||
unsigned int flag, addr, num_res, i;
|
||||
int ret;
|
||||
|
||||
flag = snd_amd_acp_find_config(pci);
|
||||
if (flag != FLAG_AMD_LEGACY)
|
||||
return -ENODEV;
|
||||
|
||||
chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pci_enable_device(pci)) {
|
||||
dev_err(&pci->dev, "pci_enable_device failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci_request_regions(pci, "AMD ACP3x audio");
|
||||
if (ret < 0) {
|
||||
dev_err(&pci->dev, "pci_request_regions failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
switch (pci->revision) {
|
||||
case 0x01:
|
||||
res_acp = acp3x_res;
|
||||
num_res = ARRAY_SIZE(acp3x_res);
|
||||
chip->name = "acp_asoc_renoir";
|
||||
chip->acp_rev = ACP3X_DEV;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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");
|
||||
return PTR_ERR(dmic_dev);
|
||||
}
|
||||
|
||||
addr = pci_resource_start(pci, 0);
|
||||
chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
|
||||
|
||||
res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL);
|
||||
if (!res) {
|
||||
platform_device_unregister(dmic_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_res; i++, res_acp++) {
|
||||
res[i].name = res_acp->name;
|
||||
res[i].flags = res_acp->flags;
|
||||
res[i].start = addr + res_acp->start;
|
||||
res[i].end = addr + res_acp->end;
|
||||
if (res_acp->flags == IORESOURCE_IRQ) {
|
||||
res[i].start = pci->irq;
|
||||
res[i].end = res[i].start;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
pdevinfo.name = chip->name;
|
||||
pdevinfo.id = 0;
|
||||
pdevinfo.parent = &pci->dev;
|
||||
pdevinfo.num_res = num_res;
|
||||
pdevinfo.res = &res[0];
|
||||
pdevinfo.data = chip;
|
||||
pdevinfo.size_data = sizeof(*chip);
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
|
||||
platform_device_unregister(dmic_dev);
|
||||
ret = PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void acp_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
if (dmic_dev)
|
||||
platform_device_unregister(dmic_dev);
|
||||
if (pdev)
|
||||
platform_device_unregister(pdev);
|
||||
}
|
||||
|
||||
/* PCI IDs */
|
||||
static const struct pci_device_id acp_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, acp_pci_ids);
|
||||
|
||||
/* pci_driver definition */
|
||||
static struct pci_driver snd_amd_acp_pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = acp_pci_ids,
|
||||
.probe = acp_pci_probe,
|
||||
.remove = acp_pci_remove,
|
||||
};
|
||||
module_pci_driver(snd_amd_acp_pci_driver);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS(DRV_NAME);
|
193
sound/soc/amd/acp/acp-pdm.c
Normal file
193
sound/soc/amd/acp/acp-pdm.c
Normal file
@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
|
||||
//
|
||||
// This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
// redistributing this file, you may do so under either license.
|
||||
//
|
||||
// Copyright(c) 2022 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
|
||||
// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
|
||||
//
|
||||
|
||||
/*
|
||||
* Generic Hardware interface for ACP Audio PDM controller
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#include "amd.h"
|
||||
|
||||
#define DRV_NAME "acp-pdm"
|
||||
|
||||
#define PDM_DMA_STAT 0x10
|
||||
#define PDM_DMA_INTR_MASK 0x10000
|
||||
#define PDM_DEC_64 0x2
|
||||
#define PDM_CLK_FREQ_MASK 0x07
|
||||
#define PDM_MISC_CTRL_MASK 0x10
|
||||
#define PDM_ENABLE 0x01
|
||||
#define PDM_DISABLE 0x00
|
||||
#define DMA_EN_MASK 0x02
|
||||
#define DELAY_US 5
|
||||
#define PDM_TIMEOUT 1000
|
||||
#define ACP_REGION2_OFFSET 0x02000000
|
||||
|
||||
static int acp_dmic_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct acp_stream *stream = substream->runtime->private_data;
|
||||
struct device *dev = dai->component->dev;
|
||||
struct acp_dev_data *adata = dev_get_drvdata(dev);
|
||||
u32 physical_addr, size_dmic, period_bytes;
|
||||
unsigned int dmic_ctrl;
|
||||
|
||||
/* Enable default DMIC clk */
|
||||
writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL);
|
||||
dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL);
|
||||
dmic_ctrl |= PDM_MISC_CTRL_MASK;
|
||||
writel(dmic_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL);
|
||||
|
||||
period_bytes = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->period_size);
|
||||
size_dmic = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->buffer_size);
|
||||
|
||||
physical_addr = stream->reg_offset + MEM_WINDOW_START;
|
||||
|
||||
/* Init DMIC Ring buffer */
|
||||
writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR);
|
||||
writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE);
|
||||
writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
|
||||
writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->component->dev;
|
||||
struct acp_dev_data *adata = dev_get_drvdata(dev);
|
||||
unsigned int dma_enable;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
if (!(dma_enable & DMA_EN_MASK)) {
|
||||
writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
|
||||
writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE,
|
||||
dma_enable, (dma_enable & DMA_EN_MASK),
|
||||
DELAY_US, PDM_TIMEOUT);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
if ((dma_enable & DMA_EN_MASK)) {
|
||||
writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
|
||||
writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE,
|
||||
dma_enable, !(dma_enable & DMA_EN_MASK),
|
||||
DELAY_US, PDM_TIMEOUT);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->component->dev;
|
||||
struct acp_dev_data *adata = dev_get_drvdata(dev);
|
||||
unsigned int channels, ch_mask;
|
||||
|
||||
channels = params_channels(hwparams);
|
||||
switch (channels) {
|
||||
case 2:
|
||||
ch_mask = 0;
|
||||
break;
|
||||
case 4:
|
||||
ch_mask = 1;
|
||||
break;
|
||||
case 6:
|
||||
ch_mask = 2;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid channels %d\n", channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
|
||||
dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
|
||||
writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct acp_stream *stream = substream->runtime->private_data;
|
||||
struct device *dev = dai->component->dev;
|
||||
struct acp_dev_data *adata = dev_get_drvdata(dev);
|
||||
u32 ext_int_ctrl;
|
||||
|
||||
stream->dai_id = DMIC_INSTANCE;
|
||||
stream->irq_bit = BIT(PDM_DMA_STAT);
|
||||
stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET;
|
||||
stream->reg_offset = ACP_REGION2_OFFSET;
|
||||
|
||||
/* Enable DMIC Interrupts */
|
||||
ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_int_ctrl |= PDM_DMA_INTR_MASK;
|
||||
writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->component->dev;
|
||||
struct acp_dev_data *adata = dev_get_drvdata(dev);
|
||||
u32 ext_int_ctrl;
|
||||
|
||||
/* Disable DMIC interrrupts */
|
||||
ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
|
||||
writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
const struct snd_soc_dai_ops acp_dmic_dai_ops = {
|
||||
.prepare = acp_dmic_prepare,
|
||||
.hw_params = acp_dmic_hwparams,
|
||||
.trigger = acp_dmic_dai_trigger,
|
||||
.startup = acp_dmic_dai_startup,
|
||||
.shutdown = acp_dmic_dai_shutdown,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS(DRV_NAME);
|
@ -25,6 +25,20 @@
|
||||
|
||||
#define DRV_NAME "acp_asoc_renoir"
|
||||
|
||||
#define ACP_SOFT_RST_DONE_MASK 0x00010001
|
||||
|
||||
#define ACP_PWR_ON_MASK 0x01
|
||||
#define ACP_PWR_OFF_MASK 0x00
|
||||
#define ACP_PGFSM_STAT_MASK 0x03
|
||||
#define ACP_POWERED_ON 0x00
|
||||
#define ACP_PWR_ON_IN_PROGRESS 0x01
|
||||
#define ACP_POWERED_OFF 0x02
|
||||
#define DELAY_US 5
|
||||
#define ACP_TIMEOUT 500
|
||||
|
||||
#define ACP_ERROR_MASK 0x20000000
|
||||
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
|
||||
|
||||
static struct snd_soc_acpi_codecs amp_rt1019 = {
|
||||
.num_codecs = 1,
|
||||
.codecs = {"10EC1019"}
|
||||
@ -97,13 +111,145 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = {
|
||||
.ops = &asoc_acp_cpu_dai_ops,
|
||||
.probe = &asoc_acp_i2s_probe,
|
||||
},
|
||||
{
|
||||
.name = "acp-pdm-dmic",
|
||||
.id = DMIC_INSTANCE,
|
||||
.capture = {
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &acp_dmic_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int acp3x_power_on(void __iomem *base)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(base + ACP_PGFSM_STATUS);
|
||||
|
||||
if (val == ACP_POWERED_ON)
|
||||
return 0;
|
||||
|
||||
if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS)
|
||||
writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL);
|
||||
|
||||
return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
|
||||
}
|
||||
|
||||
static int acp3x_power_off(void __iomem *base)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL);
|
||||
|
||||
return readl_poll_timeout(base + ACP_PGFSM_STATUS, val,
|
||||
(val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF,
|
||||
DELAY_US, ACP_TIMEOUT);
|
||||
}
|
||||
|
||||
static int acp3x_reset(void __iomem *base)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
writel(1, base + ACP_SOFT_RESET);
|
||||
|
||||
ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
|
||||
DELAY_US, ACP_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0, base + ACP_SOFT_RESET);
|
||||
|
||||
return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
|
||||
}
|
||||
|
||||
static void acp3x_enable_interrupts(void __iomem *base)
|
||||
{
|
||||
u32 ext_intr_ctrl;
|
||||
|
||||
writel(0x01, base + ACP_EXTERNAL_INTR_ENB);
|
||||
ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_intr_ctrl |= ACP_ERROR_MASK;
|
||||
writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static void acp3x_disable_interrupts(void __iomem *base)
|
||||
{
|
||||
writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT);
|
||||
writel(0x00, base + ACP_EXTERNAL_INTR_ENB);
|
||||
}
|
||||
|
||||
static int rn_acp_init(void __iomem *base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = acp3x_power_on(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0x01, base + ACP_CONTROL);
|
||||
|
||||
/* Reset */
|
||||
ret = acp3x_reset(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acp3x_enable_interrupts(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn_acp_deinit(void __iomem *base)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
acp3x_disable_interrupts(base);
|
||||
|
||||
/* Reset */
|
||||
ret = acp3x_reset(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0x00, base + ACP_CONTROL);
|
||||
|
||||
/* power off */
|
||||
ret = acp3x_power_off(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int renoir_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct acp_chip_info *chip;
|
||||
struct acp_dev_data *adata;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
chip = dev_get_platdata(&pdev->dev);
|
||||
if (!chip || !chip->base) {
|
||||
dev_err(&pdev->dev, "ACP chip data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (chip->acp_rev != ACP3X_DEV) {
|
||||
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = rn_acp_init(chip->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ACP Init failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
|
||||
if (!adata)
|
||||
@ -142,6 +288,20 @@ static int renoir_audio_probe(struct platform_device *pdev)
|
||||
static int renoir_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct acp_chip_info *chip;
|
||||
int ret;
|
||||
|
||||
chip = dev_get_platdata(&pdev->dev);
|
||||
if (!chip || !chip->base) {
|
||||
dev_err(&pdev->dev, "ACP chip data is NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = rn_acp_deinit(chip->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ACP de-init Failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
acp_platform_unregister(dev);
|
||||
return 0;
|
||||
|
@ -12,13 +12,19 @@
|
||||
#define __AMD_ACP_H
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#include "chip_offset_byte.h"
|
||||
|
||||
#define ACP3X_DEV 3
|
||||
|
||||
#define I2S_SP_INSTANCE 0x00
|
||||
#define I2S_BT_INSTANCE 0x01
|
||||
#define DMIC_INSTANCE 0x02
|
||||
|
||||
#define MEM_WINDOW_START 0x4000000
|
||||
#define MEM_WINDOW_START 0x4080000
|
||||
|
||||
#define ACP_I2S_REG_START 0x1242400
|
||||
#define ACP_I2S_REG_END 0x1242810
|
||||
@ -38,6 +44,7 @@
|
||||
#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
|
||||
#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
|
||||
#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
|
||||
#define ACP_SRAM_PDM_PTE_OFFSET 0x400
|
||||
#define PAGE_SIZE_4K_ENABLE 0x2
|
||||
|
||||
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
|
||||
@ -68,6 +75,12 @@
|
||||
|
||||
#define ACP_MAX_STREAM 6
|
||||
|
||||
struct acp_chip_info {
|
||||
char *name; /* Platform name */
|
||||
unsigned int acp_rev; /* ACP Revision id */
|
||||
void __iomem *base; /* ACP memory PCI base */
|
||||
};
|
||||
|
||||
struct acp_stream {
|
||||
struct snd_pcm_substream *substream;
|
||||
int irq_bit;
|
||||
@ -96,6 +109,7 @@ struct acp_dev_data {
|
||||
};
|
||||
|
||||
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
|
||||
extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
|
||||
|
||||
int asoc_acp_i2s_probe(struct snd_soc_dai *dai);
|
||||
int acp_platform_register(struct device *dev);
|
||||
@ -103,6 +117,9 @@ int acp_platform_unregister(struct device *dev);
|
||||
|
||||
int acp_machine_select(struct acp_dev_data *adata);
|
||||
|
||||
/* Machine configuration */
|
||||
int snd_amd_acp_find_config(struct pci_dev *pci);
|
||||
|
||||
static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
|
||||
{
|
||||
u64 byte_count, low = 0, high = 0;
|
||||
@ -131,6 +148,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
|
||||
high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
|
||||
break;
|
||||
case DMIC_INSTANCE:
|
||||
high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
|
||||
break;
|
||||
default:
|
||||
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
|
||||
return -EINVAL;
|
||||
|
@ -14,6 +14,12 @@
|
||||
#define ACPAXI2AXI_ATU_CTRL 0xC40
|
||||
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
|
||||
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
|
||||
|
||||
#define ACP_PGFSM_CONTROL 0x141C
|
||||
#define ACP_PGFSM_STATUS 0x1420
|
||||
#define ACP_SOFT_RESET 0x1000
|
||||
#define ACP_CONTROL 0x1004
|
||||
|
||||
#define ACP_EXTERNAL_INTR_ENB 0x1800
|
||||
#define ACP_EXTERNAL_INTR_CNTL 0x1804
|
||||
#define ACP_EXTERNAL_INTR_STAT 0x1808
|
||||
@ -73,4 +79,24 @@
|
||||
#define ACP_BTTDM_ITER 0x280C
|
||||
#define ACP_BTTDM_TXFRMT 0x2810
|
||||
|
||||
/* Registers from ACP_WOV_PDM block */
|
||||
|
||||
#define ACP_WOV_PDM_ENABLE 0x2C04
|
||||
#define ACP_WOV_PDM_DMA_ENABLE 0x2C08
|
||||
#define ACP_WOV_RX_RINGBUFADDR 0x2C0C
|
||||
#define ACP_WOV_RX_RINGBUFSIZE 0x2C10
|
||||
#define ACP_WOV_RX_LINKPOSITIONCNTR 0x2C14
|
||||
#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x2C18
|
||||
#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x2C1C
|
||||
#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x2C20
|
||||
#define ACP_WOV_PDM_FIFO_FLUSH 0x2C24
|
||||
#define ACP_WOV_PDM_NO_OF_CHANNELS 0x2C28
|
||||
#define ACP_WOV_PDM_DECIMATION_FACTOR 0x2C2C
|
||||
#define ACP_WOV_PDM_VAD_CTRL 0x2C30
|
||||
#define ACP_WOV_BUFFER_STATUS 0x2C58
|
||||
#define ACP_WOV_MISC_CTRL 0x2C5C
|
||||
#define ACP_WOV_CLK_CTRL 0x2C60
|
||||
#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64
|
||||
#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68
|
||||
|
||||
#endif
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#define FLAG_AMD_SOF BIT(1)
|
||||
#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
|
||||
#define FLAG_AMD_LEGACY BIT(3)
|
||||
|
||||
#define ACP_PCI_DEV_ID 0x15E2
|
||||
|
||||
|
@ -212,10 +212,15 @@ static int snd_rn_acp_probe(struct pci_dev *pci,
|
||||
acpi_integer dmic_status;
|
||||
#endif
|
||||
const struct dmi_system_id *dmi_id;
|
||||
unsigned int irqflags;
|
||||
unsigned int irqflags, flag;
|
||||
int ret, index;
|
||||
u32 addr;
|
||||
|
||||
/* Return if acp config flag is defined */
|
||||
flag = snd_amd_acp_find_config(pci);
|
||||
if (flag)
|
||||
return -ENODEV;
|
||||
|
||||
/* Renoir device check */
|
||||
if (pci->revision != 0x01)
|
||||
return -ENODEV;
|
||||
|
@ -88,3 +88,6 @@ static inline void rn_writel(u32 val, void __iomem *base_addr)
|
||||
{
|
||||
writel(val, base_addr - ACP_PHY_BASE_ADDRESS);
|
||||
}
|
||||
|
||||
/* Machine configuration */
|
||||
int snd_amd_acp_find_config(struct pci_dev *pci);
|
||||
|
Loading…
x
Reference in New Issue
Block a user