Commit e8e8c4a5 authored by Mark Brown's avatar Mark Brown

Merge series "ASoC: Add common modules support for ACP hw block" from Ajit...

Merge series "ASoC: Add common modules support for ACP hw block" from Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>:

changes since v3:
- rebase and fixes merge conflict.
- Fixed kernel autobot warning.

Ajit Kumar Pandey (8):
  ASoC: amd: Add common framework to support I2S on ACP SOC
  ASoC: amd: acp: Add I2S support on Renoir platform
  ASoC: amd: acp: Add callback for machine driver on ACP
  ASoC: amd: acp: Add generic machine driver support for ACP cards
  ASoC: amd: acp: Add legacy sound card support for Chrome audio
  ASoC: amd: acp: Add SOF audio support on Chrome board
  ASoC: amd: acp: Add support for Maxim amplifier codec
  ASoC: amd: acp: Add support for RT5682-VS codec

 sound/soc/amd/Kconfig                |   2 +
 sound/soc/amd/Makefile               |   1 +
 sound/soc/amd/acp/Kconfig            |  51 +++
 sound/soc/amd/acp/Makefile           |  26 ++
 sound/soc/amd/acp/acp-i2s.c          | 340 +++++++++++++++
 sound/soc/amd/acp/acp-legacy-mach.c  | 104 +++++
 sound/soc/amd/acp/acp-mach-common.c  | 600 +++++++++++++++++++++++++++
 sound/soc/amd/acp/acp-mach.h         |  57 +++
 sound/soc/amd/acp/acp-platform.c     | 315 ++++++++++++++
 sound/soc/amd/acp/acp-renoir.c       | 144 +++++++
 sound/soc/amd/acp/acp-sof-mach.c     | 131 ++++++
 sound/soc/amd/acp/amd.h              | 146 +++++++
 sound/soc/amd/acp/chip_offset_byte.h |  76 ++++
 13 files changed, 1993 insertions(+)
 create mode 100644 sound/soc/amd/acp/Kconfig
 create mode 100644 sound/soc/amd/acp/Makefile
 create mode 100644 sound/soc/amd/acp/acp-i2s.c
 create mode 100644 sound/soc/amd/acp/acp-legacy-mach.c
 create mode 100644 sound/soc/amd/acp/acp-mach-common.c
 create mode 100644 sound/soc/amd/acp/acp-mach.h
 create mode 100644 sound/soc/amd/acp/acp-platform.c
 create mode 100644 sound/soc/amd/acp/acp-renoir.c
 create mode 100644 sound/soc/amd/acp/acp-sof-mach.c
 create mode 100644 sound/soc/amd/acp/amd.h
 create mode 100644 sound/soc/amd/acp/chip_offset_byte.h

--
2.25.1
parents d96e75bb 8b725626
......@@ -95,3 +95,5 @@ config SND_SOC_AMD_YC_MACH
DMIC can be connected directly to ACP IP.
Say m if you have such a device.
If unsure select "N".
source "sound/soc/amd/acp/Kconfig"
......@@ -12,3 +12,4 @@ obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
# 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_AMD_ACP_COMMON
tristate "AMD Audio ACP Common support"
select SND_AMD_ACP_CONFIG
help
This option enables common modules for Audio-Coprocessor i.e. ACP
IP block on AMD platforms.
config SND_SOC_AMD_ACP_I2S
tristate
config SND_SOC_AMD_ACP_PCM
tristate
config SND_AMD_ASOC_RENOIR
tristate "AMD ACP ASOC Renoir Support"
select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S
depends on X86 && PCI
help
This option enables Renoir I2S support on AMD platform.
config SND_SOC_AMD_MACH_COMMON
tristate
select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
select SND_SOC_RT1019
select SND_SOC_MAX98357A
select SND_SOC_RT5682S
depends on X86 && PCI && I2C
help
This option enables common Machine driver module for ACP.
config SND_SOC_AMD_LEGACY_MACH
tristate "AMD Legacy Machine Driver Support"
select SND_SOC_AMD_MACH_COMMON
help
This option enables legacy sound card support for ACP audio.
config SND_SOC_AMD_SOF_MACH
tristate "AMD SOF Machine Driver Support"
select SND_SOC_AMD_MACH_COMMON
help
This option enables SOF sound card support for ACP audio.
# 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.
#common acp driver
snd-acp-pcm-objs := acp-platform.o
snd-acp-i2s-objs := acp-i2s.o
#platform specific driver
snd-acp-renoir-objs := acp-renoir.o
#machine specific driver
snd-acp-mach-objs := acp-mach-common.o
snd-acp-legacy-mach-objs := acp-legacy-mach.o
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_AMD_ASOC_RENOIR) += snd-acp-renoir.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.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>
//
/*
* Generic Hardware interface for ACP Audio I2S controller
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <linux/dma-mapping.h>
#include "amd.h"
#define DRV_NAME "acp_i2s_playcap"
static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct device *dev = dai->component->dev;
struct acp_dev_data *adata;
u32 val;
u32 xfer_resolution;
u32 reg_val;
adata = snd_soc_dai_get_drvdata(dai);
/* These values are as per Hardware Spec */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
case SNDRV_PCM_FORMAT_S8:
xfer_resolution = 0x0;
break;
case SNDRV_PCM_FORMAT_S16_LE:
xfer_resolution = 0x02;
break;
case SNDRV_PCM_FORMAT_S24_LE:
xfer_resolution = 0x04;
break;
case SNDRV_PCM_FORMAT_S32_LE:
xfer_resolution = 0x05;
break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_ITER;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_ITER;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
} else {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_IRER;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_IRER;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
}
val = readl(adata->acp_base + reg_val);
val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
val = val | (xfer_resolution << 3);
writel(val, adata->acp_base + reg_val);
return 0;
}
static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 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 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
water_val = ACP_BT_TX_INTR_WATERMARK_SIZE;
reg_val = ACP_BTTDM_ITER;
ier_val = ACP_BTTDM_IER;
buf_reg = ACP_BT_TX_RINGBUFSIZE;
break;
case I2S_SP_INSTANCE:
water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE;
reg_val = ACP_I2STDM_ITER;
ier_val = ACP_I2STDM_IER;
buf_reg = ACP_I2S_TX_RINGBUFSIZE;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
} else {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
water_val = ACP_BT_RX_INTR_WATERMARK_SIZE;
reg_val = ACP_BTTDM_IRER;
ier_val = ACP_BTTDM_IER;
buf_reg = ACP_BT_RX_RINGBUFSIZE;
break;
case I2S_SP_INSTANCE:
water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE;
reg_val = ACP_I2STDM_IRER;
ier_val = ACP_I2STDM_IER;
buf_reg = ACP_I2S_RX_RINGBUFSIZE;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
}
writel(period_bytes, adata->acp_base + water_val);
writel(buf_size, adata->acp_base + buf_reg);
val = readl(adata->acp_base + reg_val);
val = val | BIT(0);
writel(val, adata->acp_base + reg_val);
writel(1, adata->acp_base + ier_val);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_ITER;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_ITER;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
} else {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_IRER;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_IRER;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
}
val = readl(adata->acp_base + reg_val);
val = val & ~BIT(0);
writel(val, adata->acp_base + reg_val);
if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) &&
!(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0)))
writel(0, adata->acp_base + ACP_BTTDM_IER);
if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
!(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
writel(0, adata->acp_base + ACP_I2STDM_IER);
return 0;
default:
return -EINVAL;
}
return 0;
}
static int acp_i2s_prepare(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);
struct acp_stream *stream = substream->runtime->private_data;
u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
unsigned int dir = substream->stream;
switch (dai->driver->id) {
case I2S_SP_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
reg_dma_size = ACP_I2S_TX_DMA_SIZE;
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
SP_PB_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
} else {
reg_dma_size = ACP_I2S_RX_DMA_SIZE;
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
SP_CAPT_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
}
break;
case I2S_BT_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
reg_dma_size = ACP_BT_TX_DMA_SIZE;
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
BT_PB_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_BT_TX_FIFOADDR;
reg_fifo_size = ACP_BT_TX_FIFOSIZE;
phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
} else {
reg_dma_size = ACP_BT_RX_DMA_SIZE;
acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
BT_CAPT_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_BT_RX_FIFOADDR;
reg_fifo_size = ACP_BT_RX_FIFOSIZE;
phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
}
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
writel(DMA_SIZE, adata->acp_base + reg_dma_size);
writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl |= BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
| BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD);
writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
return 0;
}
static int acp_i2s_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;
unsigned int dir = substream->stream;
unsigned int irq_bit = 0;
switch (dai->driver->id) {
case I2S_SP_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
irq_bit = BIT(I2S_TX_THRESHOLD);
stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
} else {
irq_bit = BIT(I2S_RX_THRESHOLD);
stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
}
break;
case I2S_BT_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
irq_bit = BIT(BT_TX_THRESHOLD);
stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
} else {
irq_bit = BIT(BT_RX_THRESHOLD);
stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
}
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
/* Save runtime dai configuration in stream */
stream->id = dai->driver->id + dir;
stream->dai_id = dai->driver->id;
stream->irq_bit = irq_bit;
return 0;
}
const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
.startup = acp_i2s_startup,
.hw_params = acp_i2s_hwparams,
.prepare = acp_i2s_prepare,
.trigger = acp_i2s_trigger,
};
EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
{
struct device *dev = dai->component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
unsigned int val;
if (!adata->acp_base) {
dev_err(dev, "I2S base is NULL\n");
return -EINVAL;
}
val = readl(adata->acp_base + ACP_I2S_PIN_CONFIG);
if (val != I2S_MODE) {
dev_err(dev, "I2S Mode not supported val %x\n", val);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON);
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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
//
/*
* Machine Driver Legacy Support for ACP HW block
*/
#include <sound/core.h>
#include <sound/pcm_params.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <linux/module.h>
#include "acp-mach.h"
static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP,
.dmic_cpu_id = NONE,
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = NONE,
};
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Spk"),
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
static const struct snd_soc_dapm_widget acp_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static int acp_asoc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
int ret;
if (!pdev->id_entry)
return -EINVAL;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->dev = dev;
card->owner = THIS_MODULE;
card->name = pdev->id_entry->name;
card->dapm_widgets = acp_widgets;
card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
acp_legacy_dai_links_create(card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
"devm_snd_soc_register_card(%s) failed: %d\n",
card->name, ret);
return ret;
}
return 0;
}
static const struct platform_device_id board_ids[] = {
{
.name = "rn_rt5682_rt1019",
.driver_data = (kernel_ulong_t)&rt5682_rt1019_data,
},
{ }
};
static struct platform_driver acp_asoc_audio = {
.driver = {
.name = "acp_mach",
},
.probe = acp_asoc_probe,
.id_table = board_ids,
};
module_platform_driver(acp_asoc_audio);
MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
MODULE_DESCRIPTION("ACP chrome audio support");
MODULE_ALIAS("platform:rn_rt5682_rt1019");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
/* 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_MACH_H
#define __ACP_MACH_H
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <linux/input.h>
#include <linux/module.h>
#include <sound/soc.h>
enum be_id {
HEADSET_BE_ID = 0,
AMP_BE_ID,
DMIC_BE_ID,
};
enum cpu_endpoints {
NONE = 0,
I2S_SP,
I2S_BT,
DMIC,
};
enum codec_endpoints {
DUMMY = 0,
RT5682,
RT1019,
MAX98360A,
RT5682S,
};
struct acp_card_drvdata {
unsigned int hs_cpu_id;
unsigned int amp_cpu_id;
unsigned int dmic_cpu_id;
unsigned int hs_codec_id;
unsigned int amp_codec_id;
unsigned int dmic_codec_id;
unsigned int dai_fmt;
struct clk *wclk;
struct clk *bclk;
};
int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
int acp_legacy_dai_links_create(struct snd_soc_card *card);
#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: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Generic interface for ACP audio blck PCM component
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include "amd.h"
#define DRV_NAME "acp_i2s_dma"
static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.rate_min = 8000,
.rate_max = 96000,
.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
.periods_min = PLAYBACK_MIN_NUM_PERIODS,
.periods_max = PLAYBACK_MAX_NUM_PERIODS,
};
static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
.periods_min = CAPTURE_MIN_NUM_PERIODS,
.periods_max = CAPTURE_MAX_NUM_PERIODS,
};
int acp_machine_select(struct acp_dev_data *adata)
{
struct snd_soc_acpi_mach *mach;
int size;
size = sizeof(*adata->machines);
mach = snd_soc_acpi_find_machine(adata->machines);
if (!mach) {
dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
return -EINVAL;
}
adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
PLATFORM_DEVID_NONE, mach, size);
if (!adata->mach_dev)
dev_warn(adata->dev, "Unable to register Machine device\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
static irqreturn_t i2s_irq_handler(int irq, void *data)
{
struct acp_dev_data *adata = data;
struct acp_stream *stream;
u16 i2s_flag = 0;
u32 val, i;
if (!adata)
return IRQ_NONE;
val = readl(adata->acp_base + ACP_EXTERNAL_INTR_STAT);
for (i = 0; i < ACP_MAX_STREAM; i++) {
stream = adata->stream[i];
if (stream && (val & stream->irq_bit)) {
writel(stream->irq_bit, adata->acp_base + ACP_EXTERNAL_INTR_STAT);
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
break;
}
}
if (i2s_flag)
return IRQ_HANDLED;
return IRQ_NONE;
}
static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
{
u32 pte_reg, pte_size, reg_val;
/* Use ATU base Group5 */
pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
stream->reg_offset = 0x02000000;
/* Group Enable */
reg_val = ACP_SRAM_PTE_OFFSET;
writel(reg_val | BIT(31), adata->acp_base + pte_reg);
writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
}
static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
{
struct acp_stream *stream = adata->stream[cpu_id];
struct snd_pcm_substream *substream = stream->substream;
dma_addr_t addr = substream->dma_buffer.addr;
int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
u32 low, high, val;
u16 page_idx;
val = stream->pte_offset;
for (page_idx = 0; page_idx < num_pages; page_idx++) {
/* Load the low address of page int ACP SRAM through SRBM */
low = lower_32_bits(addr);
high = upper_32_bits(addr);
writel(low, adata->acp_base + ACP_SCRATCH_REG_0 + val);
high |= BIT(31);
writel(high, adata->acp_base + ACP_SCRATCH_REG_0 + val + 4);
/* Move to next physically contiguous page */
val += 8;
addr += PAGE_SIZE;
}
}
static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct snd_pcm_runtime *runtime = substream->runtime;
struct device *dev = component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_stream *stream;
int stream_id = cpu_dai->driver->id * 2 + substream->stream;
int ret;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
stream->substream = substream;
adata->stream[stream_id] = stream;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = acp_pcm_hardware_playback;
else
runtime->hw = acp_pcm_hardware_capture;
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
dev_err(component->dev, "set integer constraint failed\n");
kfree(stream);
return ret;
}
runtime->private_data = stream;
writel(1, adata->acp_base + ACP_EXTERNAL_INTR_ENB);
return ret;
}
static int acp_dma_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct acp_stream *stream = substream->runtime->private_data;
int stream_id = cpu_dai->driver->id * 2 + substream->stream;
u64 size = params_buffer_bytes(params);
/* Configure ACP DMA block with params */
config_pte_for_stream(adata, stream);
config_acp_dma(adata, stream_id, size);
return 0;
}
static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct device *dev = component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_stream *stream = substream->runtime->private_data;
u32 pos, buffersize;
u64 bytescount;
buffersize = frames_to_bytes(substream->runtime,
substream->runtime->buffer_size);
bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
if (bytescount > stream->bytescount)
bytescount -= stream->bytescount;
pos = do_div(bytescount, buffersize);
return bytes_to_frames(substream->runtime, pos);
}
static int acp_dma_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct device *parent = component->dev->parent;
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
parent, MIN_BUFFER, MAX_BUFFER);
return 0;
}
static int acp_dma_mmap(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return snd_pcm_lib_default_mmap(substream, vma);
}
static int acp_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct device *dev = component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_stream *stream;
int stream_id = cpu_dai->driver->id * 2 + substream->stream;
stream = adata->stream[stream_id];
kfree(stream);
adata->stream[stream_id] = NULL;
return 0;
}
static const struct snd_soc_component_driver acp_pcm_component = {
.name = DRV_NAME,
.open = acp_dma_open,
.close = acp_dma_close,
.hw_params = acp_dma_hw_params,
.pointer = acp_dma_pointer,
.mmap = acp_dma_mmap,
.pcm_construct = acp_dma_new,
};
int acp_platform_register(struct device *dev)
{
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct snd_soc_dai_driver;
unsigned int status;
status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler,
IRQF_SHARED, "ACP_I2S_IRQ", adata);
if (status) {
dev_err(dev, "ACP I2S IRQ request failed\n");
return status;
}
status = devm_snd_soc_register_component(dev, &acp_pcm_component,
adata->dai_driver,
adata->num_dai);
if (status) {
dev_err(dev, "Fail to register acp i2s component\n");
return status;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
int acp_platform_unregister(struct device *dev)
{
struct acp_dev_data *adata = dev_get_drvdata(dev);
if (adata->mach_dev)
platform_device_unregister(adata->mach_dev);
return 0;
}
EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON);
MODULE_DESCRIPTION("AMD ACP PCM 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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
//
/*
* Hardware interface for Renoir ACP block
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <linux/dma-mapping.h>
#include "amd.h"
#define DRV_NAME "acp_asoc_renoir"
static struct snd_soc_dai_driver acp_renoir_dai[] = {
{
.name = "acp-i2s-sp",
.id = I2S_SP_INSTANCE,
.playback = {
.stream_name = "I2S 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 = {
.stream_name = "I2S SP 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,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &asoc_acp_cpu_dai_ops,
.probe = &asoc_acp_i2s_probe,
},
{
.name = "acp-i2s-bt",
.id = I2S_BT_INSTANCE,
.playback = {
.stream_name = "I2S 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 = {
.stream_name = "I2S BT 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,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &asoc_acp_cpu_dai_ops,
.probe = &asoc_acp_i2s_probe,
},
};
static int renoir_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_dev_data *adata;
struct resource *res;
adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
if (!adata)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
if (!res) {
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
return -ENODEV;
}
adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!adata->acp_base)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
if (!res) {
dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
return -ENODEV;
}
adata->i2s_irq = res->start;
adata->dev = dev;
adata->dai_driver = acp_renoir_dai;
adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
adata->machines = snd_soc_acpi_amd_acp_machines;
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
acp_platform_register(dev);
return 0;
}
static int renoir_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
acp_platform_unregister(dev);
return 0;
}
static struct platform_driver renoir_driver = {
.probe = renoir_audio_probe,
.remove = renoir_audio_remove,
.driver = {
.name = "acp_asoc_renoir",
},
};
module_platform_driver(renoir_driver);
MODULE_DESCRIPTION("AMD ACP Renoir Driver");
MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:" 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) 2021 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
//
/*
* SOF Machine Driver Support for ACP HW block
*/
#include <sound/core.h>
#include <sound/pcm_params.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <linux/module.h>
#include "acp-mach.h"
static struct acp_card_drvdata sof_rt5682_rt1019_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP,
.dmic_cpu_id = DMIC,
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
};
static struct acp_card_drvdata sof_rt5682_max_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP,
.dmic_cpu_id = DMIC,
.hs_codec_id = RT5682,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
};
static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP,
.dmic_cpu_id = DMIC,
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
};
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Spk"),
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
static const struct snd_soc_dapm_widget acp_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static int acp_sof_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
int ret;
if (!pdev->id_entry)
return -EINVAL;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->dev = dev;
card->owner = THIS_MODULE;
card->name = pdev->id_entry->name;
card->dapm_widgets = acp_widgets;
card->num_dapm_widgets = ARRAY_SIZE(acp_widgets);
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
acp_sofdsp_dai_links_create(card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
"devm_snd_soc_register_card(%s) failed: %d\n",
card->name, ret);
return ret;
}
return 0;
}
static const struct platform_device_id board_ids[] = {
{
.name = "rt5682-rt1019",
.driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data
},
{
.name = "rt5682-max",
.driver_data = (kernel_ulong_t)&sof_rt5682_max_data
},
{
.name = "rt5682s-max",
.driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
},
{ }
};
static struct platform_driver acp_asoc_audio = {
.driver = {
.name = "sof_mach",
},
.probe = acp_sof_probe,
.id_table = board_ids,
};
module_platform_driver(acp_asoc_audio);
MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
MODULE_DESCRIPTION("ACP chrome SOF audio support");
MODULE_ALIAS("platform:rt5682-rt1019");
MODULE_ALIAS("platform:rt5682-max");
MODULE_ALIAS("platform:rt5682s-max");
MODULE_LICENSE("GPL v2");
/* 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_ACP_H
#define __AMD_ACP_H
#include <sound/pcm.h>
#include <sound/soc-acpi.h>
#include "chip_offset_byte.h"
#define I2S_SP_INSTANCE 0x00
#define I2S_BT_INSTANCE 0x01
#define MEM_WINDOW_START 0x4000000
#define ACP_I2S_REG_START 0x1242400
#define ACP_I2S_REG_END 0x1242810
#define ACP3x_I2STDM_REG_START 0x1242400
#define ACP3x_I2STDM_REG_END 0x1242410
#define ACP3x_BT_TDM_REG_START 0x1242800
#define ACP3x_BT_TDM_REG_END 0x1242810
#define I2S_MODE 0x04
#define I2S_RX_THRESHOLD 27
#define I2S_TX_THRESHOLD 28
#define BT_TX_THRESHOLD 26
#define BT_RX_THRESHOLD 25
#define ACP_SRAM_PTE_OFFSET 0x02052800
#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
#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 PAGE_SIZE_4K_ENABLE 0x2
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
#define SP_PB_FIFO_ADDR_OFFSET 0x500
#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
#define BT_PB_FIFO_ADDR_OFFSET 0x900
#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
#define PLAYBACK_MAX_PERIOD_SIZE 8192
#define PLAYBACK_MIN_PERIOD_SIZE 1024
#define CAPTURE_MIN_NUM_PERIODS 2
#define CAPTURE_MAX_NUM_PERIODS 8
#define CAPTURE_MAX_PERIOD_SIZE 8192
#define CAPTURE_MIN_PERIOD_SIZE 1024
#define MAX_BUFFER 65536
#define MIN_BUFFER MAX_BUFFER
#define FIFO_SIZE 0x100
#define DMA_SIZE 0x40
#define FRM_LEN 0x100
#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
#define ACP_MAX_STREAM 6
struct acp_stream {
struct snd_pcm_substream *substream;
int irq_bit;
int dai_id;
int id;
u64 bytescount;
u32 reg_offset;
u32 pte_offset;
u32 fifo_offset;
};
struct acp_dev_data {
char *name;
struct device *dev;
void __iomem *acp_base;
unsigned int i2s_irq;
/* SOC specific dais */
struct snd_soc_dai_driver *dai_driver;
int num_dai;
struct acp_stream *stream[ACP_MAX_STREAM];
struct snd_soc_acpi_mach *machines;
struct platform_device *mach_dev;
};
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
int asoc_acp_i2s_probe(struct snd_soc_dai *dai);
int acp_platform_register(struct device *dev);
int acp_platform_unregister(struct device *dev);
int acp_machine_select(struct acp_dev_data *adata);
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[];
static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
{
u64 byte_count, low = 0, high = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai_id) {
case I2S_BT_INSTANCE:
high = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW);
break;
case I2S_SP_INSTANCE:
high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL;
}
} else {
switch (dai_id) {
case I2S_BT_INSTANCE:
high = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW);
break;
case I2S_SP_INSTANCE:
high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL;
}
}
/* Get 64 bit value from two 32 bit registers */
byte_count = (high << 32) | low;
return byte_count;
}
#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.
*
* Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
*/
#ifndef _ACP_IP_OFFSET_HEADER
#define _ACP_IP_OFFSET_HEADER
#define ACPAXI2AXI_ATU_CTRL 0xC40
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
#define ACP_EXTERNAL_INTR_ENB 0x1800
#define ACP_EXTERNAL_INTR_CNTL 0x1804
#define ACP_EXTERNAL_INTR_STAT 0x1808
#define ACP_I2S_PIN_CONFIG 0x1400
#define ACP_SCRATCH_REG_0 0x12800
/* Registers from ACP_AUDIO_BUFFERS block */
#define ACP_I2S_RX_RINGBUFADDR 0x2000
#define ACP_I2S_RX_RINGBUFSIZE 0x2004
#define ACP_I2S_RX_LINKPOSITIONCNTR 0x2008
#define ACP_I2S_RX_FIFOADDR 0x200C
#define ACP_I2S_RX_FIFOSIZE 0x2010
#define ACP_I2S_RX_DMA_SIZE 0x2014
#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x2018
#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x201C
#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x2020
#define ACP_I2S_TX_RINGBUFADDR 0x2024
#define ACP_I2S_TX_RINGBUFSIZE 0x2028
#define ACP_I2S_TX_LINKPOSITIONCNTR 0x202C
#define ACP_I2S_TX_FIFOADDR 0x2030
#define ACP_I2S_TX_FIFOSIZE 0x2034
#define ACP_I2S_TX_DMA_SIZE 0x2038
#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x203C
#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x2040
#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x2044
#define ACP_BT_RX_RINGBUFADDR 0x2048
#define ACP_BT_RX_RINGBUFSIZE 0x204C
#define ACP_BT_RX_LINKPOSITIONCNTR 0x2050
#define ACP_BT_RX_FIFOADDR 0x2054
#define ACP_BT_RX_FIFOSIZE 0x2058
#define ACP_BT_RX_DMA_SIZE 0x205C
#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x2060
#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x2064
#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x2068
#define ACP_BT_TX_RINGBUFADDR 0x206C
#define ACP_BT_TX_RINGBUFSIZE 0x2070
#define ACP_BT_TX_LINKPOSITIONCNTR 0x2074
#define ACP_BT_TX_FIFOADDR 0x2078
#define ACP_BT_TX_FIFOSIZE 0x207C
#define ACP_BT_TX_DMA_SIZE 0x2080
#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x2084
#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x2088
#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x208C
#define ACP_I2STDM_IER 0x2400
#define ACP_I2STDM_IRER 0x2404
#define ACP_I2STDM_RXFRMT 0x2408
#define ACP_I2STDM_ITER 0x240C
#define ACP_I2STDM_TXFRMT 0x2410
/* Registers from ACP_BT_TDM block */
#define ACP_BTTDM_IER 0x2800
#define ACP_BTTDM_IRER 0x2804
#define ACP_BTTDM_RXFRMT 0x2808
#define ACP_BTTDM_ITER 0x280C
#define ACP_BTTDM_TXFRMT 0x2810
#endif
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