Commit 9a19aba2 authored by Mark Brown's avatar Mark Brown

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.
parents e0c8a67f 2d7d9f36
...@@ -44,6 +44,7 @@ config SND_SOC_AMD_RV_RT5682_MACH ...@@ -44,6 +44,7 @@ config SND_SOC_AMD_RV_RT5682_MACH
config SND_SOC_AMD_RENOIR config SND_SOC_AMD_RENOIR
tristate "AMD Audio Coprocessor - Renoir support" tristate "AMD Audio Coprocessor - Renoir support"
select SND_AMD_ACP_CONFIG
depends on X86 && PCI depends on X86 && PCI
help help
This option enables ACP support for Renoir platform This option enables ACP support for Renoir platform
......
...@@ -15,6 +15,9 @@ config SND_SOC_AMD_ACP_COMMON ...@@ -15,6 +15,9 @@ config SND_SOC_AMD_ACP_COMMON
if SND_SOC_AMD_ACP_COMMON if SND_SOC_AMD_ACP_COMMON
config SND_SOC_AMD_ACP_PDM
tristate
config SND_SOC_AMD_ACP_I2S config SND_SOC_AMD_ACP_I2S
tristate tristate
...@@ -22,10 +25,17 @@ config SND_SOC_AMD_ACP_PCM ...@@ -22,10 +25,17 @@ config SND_SOC_AMD_ACP_PCM
tristate tristate
select SND_SOC_ACPI if ACPI 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 config SND_AMD_ASOC_RENOIR
tristate "AMD ACP ASOC Renoir Support" tristate "AMD ACP ASOC Renoir Support"
select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_I2S
select SND_SOC_AMD_ACP_PDM
depends on X86 && PCI depends on X86 && PCI
help help
This option enables Renoir I2S support on AMD platform. This option enables Renoir I2S support on AMD platform.
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#common acp driver #common acp driver
snd-acp-pcm-objs := acp-platform.o snd-acp-pcm-objs := acp-platform.o
snd-acp-i2s-objs := acp-i2s.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 #platform specific driver
snd-acp-renoir-objs := acp-renoir.o snd-acp-renoir-objs := acp-renoir.o
...@@ -18,6 +20,8 @@ snd-acp-sof-mach-objs := acp-sof-mach.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_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.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 obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
......
...@@ -23,10 +23,10 @@ ...@@ -23,10 +23,10 @@
static struct acp_card_drvdata rt5682_rt1019_data = { static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_cpu_id = I2S_SP, .hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP, .amp_cpu_id = I2S_SP,
.dmic_cpu_id = NONE, .dmic_cpu_id = DMIC,
.hs_codec_id = RT5682, .hs_codec_id = RT5682,
.amp_codec_id = RT1019, .amp_codec_id = RT1019,
.dmic_codec_id = NONE, .dmic_codec_id = DMIC,
.gpio_spkr_en = EN_SPKR_GPIO_GB, .gpio_spkr_en = EN_SPKR_GPIO_GB,
}; };
......
...@@ -438,6 +438,8 @@ SND_SOC_DAILINK_DEF(sof_sp, ...@@ -438,6 +438,8 @@ SND_SOC_DAILINK_DEF(sof_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp"))); DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
SND_SOC_DAILINK_DEF(sof_dmic, SND_SOC_DAILINK_DEF(sof_dmic,
DAILINK_COMP_ARRAY(COMP_CPU("acp-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) 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) ...@@ -613,6 +615,25 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].ops = &acp_card_maxim_ops; links[i].ops = &acp_card_maxim_ops;
links[i].init = acp_card_maxim_init; 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; card->dai_link = links;
......
// 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);
// 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 @@ ...@@ -25,6 +25,20 @@
#define DRV_NAME "acp_asoc_renoir" #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 = { static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1, .num_codecs = 1,
.codecs = {"10EC1019"} .codecs = {"10EC1019"}
...@@ -97,13 +111,145 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { ...@@ -97,13 +111,145 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = {
.ops = &asoc_acp_cpu_dai_ops, .ops = &asoc_acp_cpu_dai_ops,
.probe = &asoc_acp_i2s_probe, .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) static int renoir_audio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct acp_chip_info *chip;
struct acp_dev_data *adata; struct acp_dev_data *adata;
struct resource *res; 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); adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
if (!adata) if (!adata)
...@@ -142,6 +288,20 @@ static int renoir_audio_probe(struct platform_device *pdev) ...@@ -142,6 +288,20 @@ static int renoir_audio_probe(struct platform_device *pdev)
static int renoir_audio_remove(struct platform_device *pdev) static int renoir_audio_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; 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); acp_platform_unregister(dev);
return 0; return 0;
......
...@@ -12,13 +12,19 @@ ...@@ -12,13 +12,19 @@
#define __AMD_ACP_H #define __AMD_ACP_H
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h> #include <sound/soc-acpi.h>
#include <sound/soc-dai.h>
#include "chip_offset_byte.h" #include "chip_offset_byte.h"
#define ACP3X_DEV 3
#define I2S_SP_INSTANCE 0x00 #define I2S_SP_INSTANCE 0x00
#define I2S_BT_INSTANCE 0x01 #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_START 0x1242400
#define ACP_I2S_REG_END 0x1242810 #define ACP_I2S_REG_END 0x1242810
...@@ -38,6 +44,7 @@ ...@@ -38,6 +44,7 @@
#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100 #define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200 #define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300 #define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
#define ACP_SRAM_PDM_PTE_OFFSET 0x400
#define PAGE_SIZE_4K_ENABLE 0x2 #define PAGE_SIZE_4K_ENABLE 0x2
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000 #define I2S_SP_TX_MEM_WINDOW_START 0x4000000
...@@ -68,6 +75,12 @@ ...@@ -68,6 +75,12 @@
#define ACP_MAX_STREAM 6 #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 acp_stream {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int irq_bit; int irq_bit;
...@@ -96,6 +109,7 @@ struct acp_dev_data { ...@@ -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 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 asoc_acp_i2s_probe(struct snd_soc_dai *dai);
int acp_platform_register(struct device *dev); int acp_platform_register(struct device *dev);
...@@ -103,6 +117,9 @@ int acp_platform_unregister(struct device *dev); ...@@ -103,6 +117,9 @@ int acp_platform_unregister(struct device *dev);
int acp_machine_select(struct acp_dev_data *adata); 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) static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
{ {
u64 byte_count, low = 0, high = 0; 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 ...@@ -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); high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW); low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
break; 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: default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id); dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL; return -EINVAL;
......
...@@ -14,6 +14,12 @@ ...@@ -14,6 +14,12 @@
#define ACPAXI2AXI_ATU_CTRL 0xC40 #define ACPAXI2AXI_ATU_CTRL 0xC40
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 #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_ENB 0x1800
#define ACP_EXTERNAL_INTR_CNTL 0x1804 #define ACP_EXTERNAL_INTR_CNTL 0x1804
#define ACP_EXTERNAL_INTR_STAT 0x1808 #define ACP_EXTERNAL_INTR_STAT 0x1808
...@@ -73,4 +79,24 @@ ...@@ -73,4 +79,24 @@
#define ACP_BTTDM_ITER 0x280C #define ACP_BTTDM_ITER 0x280C
#define ACP_BTTDM_TXFRMT 0x2810 #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 #endif
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define FLAG_AMD_SOF BIT(1) #define FLAG_AMD_SOF BIT(1)
#define FLAG_AMD_SOF_ONLY_DMIC BIT(2) #define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
#define FLAG_AMD_LEGACY BIT(3)
#define ACP_PCI_DEV_ID 0x15E2 #define ACP_PCI_DEV_ID 0x15E2
......
...@@ -212,10 +212,15 @@ static int snd_rn_acp_probe(struct pci_dev *pci, ...@@ -212,10 +212,15 @@ static int snd_rn_acp_probe(struct pci_dev *pci,
acpi_integer dmic_status; acpi_integer dmic_status;
#endif #endif
const struct dmi_system_id *dmi_id; const struct dmi_system_id *dmi_id;
unsigned int irqflags; unsigned int irqflags, flag;
int ret, index; int ret, index;
u32 addr; u32 addr;
/* Return if acp config flag is defined */
flag = snd_amd_acp_find_config(pci);
if (flag)
return -ENODEV;
/* Renoir device check */ /* Renoir device check */
if (pci->revision != 0x01) if (pci->revision != 0x01)
return -ENODEV; return -ENODEV;
......
...@@ -88,3 +88,6 @@ static inline void rn_writel(u32 val, void __iomem *base_addr) ...@@ -88,3 +88,6 @@ static inline void rn_writel(u32 val, void __iomem *base_addr)
{ {
writel(val, base_addr - ACP_PHY_BASE_ADDRESS); writel(val, base_addr - ACP_PHY_BASE_ADDRESS);
} }
/* Machine configuration */
int snd_amd_acp_find_config(struct pci_dev *pci);
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