diff options
Diffstat (limited to 'sound/soc/amd/vangogh')
-rw-r--r-- | sound/soc/amd/vangogh/Makefile | 11 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/acp5x-i2s.c | 416 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/acp5x-mach.c | 404 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/acp5x-pcm-dma.c | 515 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/acp5x.h | 224 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/pci-acp5x.c | 338 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/vg_chip_offset_byte.h | 337 |
7 files changed, 2245 insertions, 0 deletions
diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile new file mode 100644 index 000000000..c9e53e04e --- /dev/null +++ b/sound/soc/amd/vangogh/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Vangogh platform Support +snd-pci-acp5x-objs := pci-acp5x.o +snd-acp5x-i2s-objs := acp5x-i2s.o +snd-acp5x-pcm-dma-objs := acp5x-pcm-dma.o +snd-soc-acp5x-mach-objs := acp5x-mach.o + +obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o +obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o +obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o +obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c new file mode 100644 index 000000000..773e96f1b --- /dev/null +++ b/sound/soc/amd/vangogh/acp5x-i2s.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ALSA SoC PCM Driver +// +// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. + +#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 "acp5x.h" + +#define DRV_NAME "acp5x_i2s_playcap" + +static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct i2s_dev_data *adata; + int mode; + + adata = snd_soc_dai_get_drvdata(cpu_dai); + mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + switch (mode) { + case SND_SOC_DAIFMT_I2S: + adata->tdm_mode = TDM_DISABLE; + break; + case SND_SOC_DAIFMT_DSP_A: + adata->tdm_mode = TDM_ENABLE; + break; + default: + return -EINVAL; + } + mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + switch (mode) { + case SND_SOC_DAIFMT_BP_FP: + adata->master_mode = I2S_MASTER_MODE_ENABLE; + break; + case SND_SOC_DAIFMT_BC_FC: + adata->master_mode = I2S_MASTER_MODE_DISABLE; + break; + } + return 0; +} + +static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, + u32 tx_mask, u32 rx_mask, + int slots, int slot_width) +{ + struct i2s_dev_data *adata; + u32 frm_len; + u16 slot_len; + + adata = snd_soc_dai_get_drvdata(cpu_dai); + + /* These values are as per Hardware Spec */ + switch (slot_width) { + case SLOT_WIDTH_8: + slot_len = 8; + break; + case SLOT_WIDTH_16: + slot_len = 16; + break; + case SLOT_WIDTH_24: + slot_len = 24; + break; + case SLOT_WIDTH_32: + slot_len = 0; + break; + default: + return -EINVAL; + } + frm_len = FRM_LEN | (slots << 15) | (slot_len << 18); + adata->tdm_fmt = frm_len; + return 0; +} + +static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct i2s_stream_instance *rtd; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp5x_platform_info *pinfo; + struct i2s_dev_data *adata; + + u32 val; + u32 reg_val, frmt_reg; + u32 lrclk_div_val, bclk_div_val; + + lrclk_div_val = 0; + bclk_div_val = 0; + prtd = asoc_substream_to_rtd(substream); + rtd = substream->runtime->private_data; + card = prtd->card; + adata = snd_soc_dai_get_drvdata(dai); + pinfo = snd_soc_card_get_drvdata(card); + if (pinfo) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->i2s_instance = pinfo->play_i2s_instance; + else + rtd->i2s_instance = pinfo->cap_i2s_instance; + } + + /* These values are as per Hardware Spec */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + rtd->xfer_resolution = 0x0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + rtd->xfer_resolution = 0x02; + break; + case SNDRV_PCM_FORMAT_S24_LE: + rtd->xfer_resolution = 0x04; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rtd->xfer_resolution = 0x05; + break; + default: + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_ITER; + frmt_reg = ACP_HSTDM_TXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = ACP_I2STDM_ITER; + frmt_reg = ACP_I2STDM_TXFRMT; + } + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_IRER; + frmt_reg = ACP_HSTDM_RXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = ACP_I2STDM_IRER; + frmt_reg = ACP_I2STDM_RXFRMT; + } + } + if (adata->tdm_mode) { + val = acp_readl(rtd->acp5x_base + reg_val); + acp_writel(val | 0x2, rtd->acp5x_base + reg_val); + acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg); + } + val = acp_readl(rtd->acp5x_base + reg_val); + val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK; + val = val | (rtd->xfer_resolution << 3); + acp_writel(val, rtd->acp5x_base + reg_val); + + if (adata->master_mode) { + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + switch (params_rate(params)) { + case 8000: + bclk_div_val = 768; + break; + case 16000: + bclk_div_val = 384; + break; + case 24000: + bclk_div_val = 256; + break; + case 32000: + bclk_div_val = 192; + break; + case 44100: + case 48000: + bclk_div_val = 128; + break; + case 88200: + case 96000: + bclk_div_val = 64; + break; + case 192000: + bclk_div_val = 32; + break; + default: + return -EINVAL; + } + lrclk_div_val = 32; + break; + case SNDRV_PCM_FORMAT_S32_LE: + switch (params_rate(params)) { + case 8000: + bclk_div_val = 384; + break; + case 16000: + bclk_div_val = 192; + break; + case 24000: + bclk_div_val = 128; + break; + case 32000: + bclk_div_val = 96; + break; + case 44100: + case 48000: + bclk_div_val = 64; + break; + case 88200: + case 96000: + bclk_div_val = 32; + break; + case 192000: + bclk_div_val = 16; + break; + default: + return -EINVAL; + } + lrclk_div_val = 64; + break; + default: + return -EINVAL; + } + rtd->lrclk_div = lrclk_div_val; + rtd->bclk_div = bclk_div_val; + } + return 0; +} + +static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct i2s_stream_instance *rtd; + struct i2s_dev_data *adata; + u32 ret, val, period_bytes, reg_val, ier_val, water_val; + u32 buf_size, buf_reg; + + adata = snd_soc_dai_get_drvdata(dai); + rtd = substream->runtime->private_data; + 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: + rtd->bytescount = acp_get_byte_count(rtd, + substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + water_val = + ACP_HS_TX_INTR_WATERMARK_SIZE; + reg_val = ACP_HSTDM_ITER; + ier_val = ACP_HSTDM_IER; + buf_reg = ACP_HS_TX_RINGBUFSIZE; + break; + case I2S_SP_INSTANCE: + default: + water_val = + ACP_I2S_TX_INTR_WATERMARK_SIZE; + reg_val = ACP_I2STDM_ITER; + ier_val = ACP_I2STDM_IER; + buf_reg = ACP_I2S_TX_RINGBUFSIZE; + } + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + water_val = + ACP_HS_RX_INTR_WATERMARK_SIZE; + reg_val = ACP_HSTDM_IRER; + ier_val = ACP_HSTDM_IER; + buf_reg = ACP_HS_RX_RINGBUFSIZE; + break; + case I2S_SP_INSTANCE: + default: + water_val = + ACP_I2S_RX_INTR_WATERMARK_SIZE; + reg_val = ACP_I2STDM_IRER; + ier_val = ACP_I2STDM_IER; + buf_reg = ACP_I2S_RX_RINGBUFSIZE; + } + } + acp_writel(period_bytes, rtd->acp5x_base + water_val); + acp_writel(buf_size, rtd->acp5x_base + buf_reg); + if (adata->master_mode) + acp5x_set_i2s_clk(adata, rtd); + val = acp_readl(rtd->acp5x_base + reg_val); + val = val | BIT(0); + acp_writel(val, rtd->acp5x_base + reg_val); + acp_writel(1, rtd->acp5x_base + ier_val); + ret = 0; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_ITER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = ACP_I2STDM_ITER; + } + + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_IRER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = ACP_I2STDM_IRER; + } + } + val = acp_readl(rtd->acp5x_base + reg_val); + val = val & ~BIT(0); + acp_writel(val, rtd->acp5x_base + reg_val); + + if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) && + !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0))) + acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER); + if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) && + !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0))) + acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { + .hw_params = acp5x_i2s_hwparams, + .trigger = acp5x_i2s_trigger, + .set_fmt = acp5x_i2s_set_fmt, + .set_tdm_slot = acp5x_i2s_set_tdm_slot, +}; + +static const struct snd_soc_component_driver acp5x_dai_component = { + .name = "acp5x-i2s", + .legacy_dai_naming = 1, +}; + +static struct snd_soc_dai_driver acp5x_i2s_dai = { + .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 = 2, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .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 = 2, + .rate_min = 8000, + .rate_max = 96000, + }, + .ops = &acp5x_i2s_dai_ops, +}; + +static int acp5x_dai_probe(struct platform_device *pdev) +{ + struct resource *res; + struct i2s_dev_data *adata; + int ret; + + adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), + GFP_KERNEL); + if (!adata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); + return -ENOMEM; + } + adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!adata->acp5x_base) + return -ENOMEM; + + adata->master_mode = I2S_MASTER_MODE_ENABLE; + dev_set_drvdata(&pdev->dev, adata); + ret = devm_snd_soc_register_component(&pdev->dev, + &acp5x_dai_component, + &acp5x_i2s_dai, 1); + if (ret) + dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); + return ret; +} + +static struct platform_driver acp5x_dai_driver = { + .probe = acp5x_dai_probe, + .driver = { + .name = "acp5x_i2s_playcap", + }, +}; + +module_platform_driver(acp5x_dai_driver); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c new file mode 100644 index 000000000..eebf2650a --- /dev/null +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41 + * codecs. + * + * Copyright 2021 Advanced Micro Devices, Inc. + */ + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/module.h> +#include <linux/io.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include <sound/jack.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/acpi.h> +#include <linux/dmi.h> + +#include "../../codecs/nau8821.h" +#include "../../codecs/cs35l41.h" + +#include "acp5x.h" + +#define DRV_NAME "acp5x_mach" +#define DUAL_CHANNEL 2 +#define ACP5X_NUVOTON_CODEC_DAI "nau8821-hifi" +#define VG_JUPITER 1 +#define ACP5X_NUVOTON_BCLK 3072000 +#define ACP5X_NAU8821_FREQ_OUT 12288000 + +static unsigned long acp5x_machine_id; +static struct snd_soc_jack vg_headset; + +static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_card *card = rtd->card; + struct snd_soc_component *component = + asoc_rtd_to_codec(rtd, 0)->component; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &vg_headset, acp5x_nau8821_jack_pins, + ARRAY_SIZE(acp5x_nau8821_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA); + nau8821_enable_jack_detect(component, &vg_headset); + return ret; +} + +static int acp5x_cs35l41_init(struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static const unsigned int acp5x_nau8821_format[] = {32}; + +static struct snd_pcm_hw_constraint_list constraints_sample_bits = { + .list = acp5x_nau8821_format, + .count = ARRAY_SIZE(acp5x_nau8821_format), +}; + +static int acp5x_8821_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->play_i2s_instance = I2S_SP_INSTANCE; + machine->cap_i2s_instance = I2S_SP_INSTANCE; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constraints_sample_bits); + return 0; +} + +static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = + snd_soc_card_get_codec_dai(card, + ACP5X_NUVOTON_CODEC_DAI); + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(card->dev, "can't set FS clock %d\n", ret); + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params), + params_rate(params) * 256); + if (ret < 0) + dev_err(card->dev, "can't set FLL: %d\n", ret); + + return ret; +} + +static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->play_i2s_instance = I2S_HS_INSTANCE; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + return 0; +} + +static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai; + int ret, i; + unsigned int num_codecs = rtd->dai_link->num_codecs; + unsigned int bclk_val; + + ret = 0; + for (i = 0; i < num_codecs; i++) { + codec_dai = asoc_rtd_to_codec(rtd, i); + if (strcmp(codec_dai->name, "cs35l41-pcm") == 0) { + switch (params_rate(params)) { + case 48000: + bclk_val = 1536000; + break; + default: + dev_err(card->dev, "Invalid Samplerate:0x%x\n", + params_rate(params)); + return -EINVAL; + } + ret = snd_soc_component_set_sysclk(codec_dai->component, + 0, 0, bclk_val, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "failed to set sysclk for CS35l41 dai\n"); + return ret; + } + } + } + + return ret; +} + +static const struct snd_soc_ops acp5x_8821_ops = { + .startup = acp5x_8821_startup, + .hw_params = acp5x_nau8821_hw_params, +}; + +static const struct snd_soc_ops acp5x_cs35l41_play_ops = { + .startup = acp5x_cs35l41_startup, + .hw_params = acp5x_cs35l41_hw_params, +}; + +static struct snd_soc_codec_conf cs35l41_conf[] = { + { + .dlc = COMP_CODEC_CONF("spi-VLV1776:00"), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF("spi-VLV1776:01"), + .name_prefix = "Right", + }, +}; + +SND_SOC_DAILINK_DEF(acp5x_i2s, + DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0"))); + +SND_SOC_DAILINK_DEF(acp5x_bt, + DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1"))); + +SND_SOC_DAILINK_DEF(nau8821, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00", + "nau8821-hifi"))); + +SND_SOC_DAILINK_DEF(cs35l41, + DAILINK_COMP_ARRAY(COMP_CODEC("spi-VLV1776:00", "cs35l41-pcm"), + COMP_CODEC("spi-VLV1776:01", "cs35l41-pcm"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0"))); + +static struct snd_soc_dai_link acp5x_dai[] = { + { + .name = "acp5x-8821-play", + .stream_name = "Playback/Capture", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &acp5x_8821_ops, + .init = acp5x_8821_init, + SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform), + }, + { + .name = "acp5x-CS35L41-Stereo", + .stream_name = "CS35L41 Stereo Playback", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + .playback_only = 1, + .ops = &acp5x_cs35l41_play_ops, + .init = acp5x_cs35l41_init, + SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform), + }, +}; + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, ACP5X_NUVOTON_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(codec_dai->dev, "can't set BLK clock %d\n", ret); + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, ACP5X_NUVOTON_BCLK, + ACP5X_NAU8821_FREQ_OUT); + if (ret < 0) + dev_err(codec_dai->dev, "can't set FLL: %d\n", ret); + } + return ret; +} + +static const struct snd_kcontrol_new acp5x_8821_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static const struct snd_soc_dapm_widget acp5x_8821_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone", NULL, "HPOL" }, + { "Headphone", NULL, "HPOR" }, + { "MICL", NULL, "Headset Mic" }, + { "MICR", NULL, "Headset Mic" }, + { "DMIC", NULL, "Int Mic" }, + + { "Headphone", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, + { "Int Mic", NULL, "Platform Clock" }, +}; + +static struct snd_soc_card acp5x_card = { + .name = "acp5x", + .owner = THIS_MODULE, + .dai_link = acp5x_dai, + .num_links = ARRAY_SIZE(acp5x_dai), + .dapm_widgets = acp5x_8821_widgets, + .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_widgets), + .dapm_routes = acp5x_8821_audio_route, + .num_dapm_routes = ARRAY_SIZE(acp5x_8821_audio_route), + .codec_conf = cs35l41_conf, + .num_configs = ARRAY_SIZE(cs35l41_conf), + .controls = acp5x_8821_controls, + .num_controls = ARRAY_SIZE(acp5x_8821_controls), +}; + +static int acp5x_vg_quirk_cb(const struct dmi_system_id *id) +{ + acp5x_machine_id = VG_JUPITER; + return 1; +} + +static const struct dmi_system_id acp5x_vg_quirk_table[] = { + { + .callback = acp5x_vg_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"), + } + }, + {} +}; + +static int acp5x_probe(struct platform_device *pdev) +{ + int ret; + struct acp5x_platform_info *machine; + struct snd_soc_card *card; + + machine = devm_kzalloc(&pdev->dev, sizeof(struct acp5x_platform_info), + GFP_KERNEL); + if (!machine) + return -ENOMEM; + + dmi_check_system(acp5x_vg_quirk_table); + switch (acp5x_machine_id) { + case VG_JUPITER: + card = &acp5x_card; + acp5x_card.dev = &pdev->dev; + break; + default: + return -ENODEV; + } + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card(%s) failed\n", + acp5x_card.name); + } + return 0; +} + +static struct platform_driver acp5x_mach_driver = { + .driver = { + .name = "acp5x_mach", + .pm = &snd_soc_pm_ops, + }, + .probe = acp5x_probe, +}; + +module_platform_driver(acp5x_mach_driver); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c new file mode 100644 index 000000000..d36bb7183 --- /dev/null +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ALSA SoC PCM Driver +// +// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "acp5x.h" + +#define DRV_NAME "acp5x_i2s_dma" + +static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + 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_S32_LE, + .channels_min = 2, + .channels_max = 2, + .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 acp5x_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + 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_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 96000, + .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, +}; + +static irqreturn_t i2s_irq_handler(int irq, void *dev_id) +{ + struct i2s_dev_data *vg_i2s_data; + u16 irq_flag; + u32 val; + + vg_i2s_data = dev_id; + if (!vg_i2s_data) + return IRQ_NONE; + + irq_flag = 0; + val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); + if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) { + acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base + + ACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(vg_i2s_data->play_stream); + irq_flag = 1; + } + if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) { + acp_writel(BIT(I2S_TX_THRESHOLD), + vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream); + irq_flag = 1; + } + + if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) { + acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base + + ACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(vg_i2s_data->capture_stream); + irq_flag = 1; + } + if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) { + acp_writel(BIT(I2S_RX_THRESHOLD), + vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream); + irq_flag = 1; + } + + if (irq_flag) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction) +{ + u16 page_idx; + u32 low, high, val, acp_fifo_addr, reg_fifo_addr; + u32 reg_dma_size, reg_fifo_size; + dma_addr_t addr; + + addr = rtd->dma_addr; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + val = ACP_SRAM_HS_PB_PTE_OFFSET; + break; + case I2S_SP_INSTANCE: + default: + val = ACP_SRAM_SP_PB_PTE_OFFSET; + } + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + val = ACP_SRAM_HS_CP_PTE_OFFSET; + break; + case I2S_SP_INSTANCE: + default: + val = ACP_SRAM_SP_CP_PTE_OFFSET; + } + } + /* Group Enable */ + acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base + + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); + acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base + + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); + + for (page_idx = 0; page_idx < rtd->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); + + acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val); + high |= BIT(31); + acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4); + /* Move to next physically contiguous page */ + val += 8; + addr += PAGE_SIZE; + } + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_dma_size = ACP_HS_TX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + HS_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_TX_FIFOADDR; + reg_fifo_size = ACP_HS_TX_FIFOSIZE; + acp_writel(I2S_HS_TX_MEM_WINDOW_START, + rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR); + break; + + case I2S_SP_INSTANCE: + default: + 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; + acp_writel(I2S_SP_TX_MEM_WINDOW_START, + rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR); + } + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + reg_dma_size = ACP_HS_RX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + HS_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_RX_FIFOADDR; + reg_fifo_size = ACP_HS_RX_FIFOSIZE; + acp_writel(I2S_HS_RX_MEM_WINDOW_START, + rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR); + break; + + case I2S_SP_INSTANCE: + default: + 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; + acp_writel(I2S_SP_RX_MEM_WINDOW_START, + rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR); + } + } + acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size); + acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr); + acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size); + acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD) + | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD), + rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL); +} + +static int acp5x_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *prtd; + struct i2s_dev_data *adata; + struct i2s_stream_instance *i2s_data; + int ret; + + runtime = substream->runtime; + prtd = asoc_substream_to_rtd(substream); + component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + adata = dev_get_drvdata(component->dev); + + i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL); + if (!i2s_data) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = acp5x_pcm_hardware_playback; + else + runtime->hw = acp5x_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(i2s_data); + return ret; + } + i2s_data->acp5x_base = adata->acp5x_base; + runtime->private_data = i2s_data; + return ret; +} + +static int acp5x_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct i2s_stream_instance *rtd; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp5x_platform_info *pinfo; + struct i2s_dev_data *adata; + u64 size; + + prtd = asoc_substream_to_rtd(substream); + card = prtd->card; + pinfo = snd_soc_card_get_drvdata(card); + adata = dev_get_drvdata(component->dev); + rtd = substream->runtime->private_data; + + if (!rtd) + return -EINVAL; + + if (pinfo) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rtd->i2s_instance = pinfo->play_i2s_instance; + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + adata->play_stream = substream; + break; + case I2S_SP_INSTANCE: + default: + adata->i2ssp_play_stream = substream; + } + } else { + rtd->i2s_instance = pinfo->cap_i2s_instance; + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + adata->capture_stream = substream; + break; + case I2S_SP_INSTANCE: + default: + adata->i2ssp_capture_stream = substream; + } + } + } else { + dev_err(component->dev, "pinfo failed\n"); + return -EINVAL; + } + size = params_buffer_bytes(params); + rtd->dma_addr = substream->runtime->dma_addr; + rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + config_acp5x_dma(rtd, substream->stream); + return 0; +} + +static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct i2s_stream_instance *rtd; + u32 pos; + u32 buffersize; + u64 bytescount; + + rtd = substream->runtime->private_data; + buffersize = frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + bytescount = acp_get_byte_count(rtd, substream->stream); + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; + pos = do_div(bytescount, buffersize); + return bytes_to_frames(substream->runtime, pos); +} + +static int acp5x_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 acp5x_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *prtd; + struct i2s_dev_data *adata; + struct i2s_stream_instance *ins; + + prtd = asoc_substream_to_rtd(substream); + component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + adata = dev_get_drvdata(component->dev); + ins = substream->runtime->private_data; + if (!ins) + return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (ins->i2s_instance) { + case I2S_HS_INSTANCE: + adata->play_stream = NULL; + break; + case I2S_SP_INSTANCE: + default: + adata->i2ssp_play_stream = NULL; + } + } else { + switch (ins->i2s_instance) { + case I2S_HS_INSTANCE: + adata->capture_stream = NULL; + break; + case I2S_SP_INSTANCE: + default: + adata->i2ssp_capture_stream = NULL; + } + } + kfree(ins); + return 0; +} + +static const struct snd_soc_component_driver acp5x_i2s_component = { + .name = DRV_NAME, + .open = acp5x_dma_open, + .close = acp5x_dma_close, + .hw_params = acp5x_dma_hw_params, + .pointer = acp5x_dma_pointer, + .pcm_construct = acp5x_dma_new, +}; + +static int acp5x_audio_probe(struct platform_device *pdev) +{ + struct resource *res; + struct i2s_dev_data *adata; + unsigned int irqflags; + int status; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "platform_data not retrieved\n"); + return -ENODEV; + } + irqflags = *((unsigned int *)(pdev->dev.platform_data)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); + return -ENODEV; + } + + adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL); + if (!adata) + return -ENOMEM; + + adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!adata->acp5x_base) + return -ENOMEM; + + status = platform_get_irq(pdev, 0); + if (status < 0) + return status; + adata->i2s_irq = status; + + dev_set_drvdata(&pdev->dev, adata); + status = devm_snd_soc_register_component(&pdev->dev, + &acp5x_i2s_component, + NULL, 0); + if (status) { + dev_err(&pdev->dev, "Fail to register acp i2s component\n"); + return status; + } + status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, + irqflags, "ACP5x_I2S_IRQ", adata); + if (status) { + dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n"); + return status; + } + pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + return 0; +} + +static int acp5x_audio_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int __maybe_unused acp5x_pcm_resume(struct device *dev) +{ + struct i2s_dev_data *adata; + struct i2s_stream_instance *rtd; + u32 val; + + adata = dev_get_drvdata(dev); + + if (adata->play_stream && adata->play_stream->runtime) { + rtd = adata->play_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT); + val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER); + } + } + if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) { + rtd = adata->i2ssp_play_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT); + val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER); + } + } + + if (adata->capture_stream && adata->capture_stream->runtime) { + rtd = adata->capture_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT); + val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER); + } + } + if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) { + rtd = adata->i2ssp_capture_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT); + val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER); + } + } + acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); + return 0; +} + +static int __maybe_unused acp5x_pcm_suspend(struct device *dev) +{ + struct i2s_dev_data *adata; + + adata = dev_get_drvdata(dev); + acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); + return 0; +} + +static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev) +{ + struct i2s_dev_data *adata; + + adata = dev_get_drvdata(dev); + acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); + return 0; +} + +static const struct dev_pm_ops acp5x_pm_ops = { + SET_RUNTIME_PM_OPS(acp5x_pcm_suspend, + acp5x_pcm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume) +}; + +static struct platform_driver acp5x_dma_driver = { + .probe = acp5x_audio_probe, + .remove = acp5x_audio_remove, + .driver = { + .name = "acp5x_i2s_dma", + .pm = &acp5x_pm_ops, + }, +}; + +module_platform_driver(acp5x_dma_driver); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h new file mode 100644 index 000000000..ac1936a8c --- /dev/null +++ b/sound/soc/amd/vangogh/acp5x.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. + */ + +#include "vg_chip_offset_byte.h" +#include <sound/pcm.h> + +#define ACP5x_PHY_BASE_ADDRESS 0x1240000 +#define ACP_DEVICE_ID 0x15E2 +#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 + +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_POWER_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 +#define ACP_POWER_OFF_IN_PROGRESS 0x03 + +#define ACP_ERR_INTR_MASK 0x20000000 +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF + +#define ACP5x_DEVS 4 +#define ACP5x_REG_START 0x1240000 +#define ACP5x_REG_END 0x1250200 +#define ACP5x_I2STDM_REG_START 0x1242400 +#define ACP5x_I2STDM_REG_END 0x1242410 +#define ACP5x_HS_TDM_REG_START 0x1242814 +#define ACP5x_HS_TDM_REG_END 0x1242824 +#define I2S_MODE 0 +#define ACP5x_I2S_MODE 1 +#define ACP5x_RES 4 +#define I2S_RX_THRESHOLD 27 +#define I2S_TX_THRESHOLD 28 +#define HS_TX_THRESHOLD 24 +#define HS_RX_THRESHOLD 23 + +#define I2S_SP_INSTANCE 1 +#define I2S_HS_INSTANCE 2 + +#define ACP_SRAM_PTE_OFFSET 0x02050000 +#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0 +#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100 +#define ACP_SRAM_HS_PB_PTE_OFFSET 0x200 +#define ACP_SRAM_HS_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_HS_TX_MEM_WINDOW_START 0x4040000 +#define I2S_HS_RX_MEM_WINDOW_START 0x4060000 + +#define SP_PB_FIFO_ADDR_OFFSET 0x500 +#define SP_CAPT_FIFO_ADDR_OFFSET 0x700 +#define HS_PB_FIFO_ADDR_OFFSET 0x900 +#define HS_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 (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) +#define MIN_BUFFER MAX_BUFFER +#define FIFO_SIZE 0x100 +#define DMA_SIZE 0x40 +#define FRM_LEN 0x100 + +#define I2S_MASTER_MODE_ENABLE 1 +#define I2S_MASTER_MODE_DISABLE 0 + +#define SLOT_WIDTH_8 8 +#define SLOT_WIDTH_16 16 +#define SLOT_WIDTH_24 24 +#define SLOT_WIDTH_32 32 +#define TDM_ENABLE 1 +#define TDM_DISABLE 0 +#define ACP5x_ITER_IRER_SAMP_LEN_MASK 0x38 + +struct i2s_dev_data { + bool tdm_mode; + bool master_mode; + int i2s_irq; + u16 i2s_instance; + u32 tdm_fmt; + void __iomem *acp5x_base; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; + struct snd_pcm_substream *i2ssp_play_stream; + struct snd_pcm_substream *i2ssp_capture_stream; +}; + +struct i2s_stream_instance { + u16 num_pages; + u16 i2s_instance; + u16 direction; + u16 channels; + u32 xfer_resolution; + u32 val; + dma_addr_t dma_addr; + u64 bytescount; + void __iomem *acp5x_base; + u32 lrclk_div; + u32 bclk_div; +}; + +union acp_dma_count { + struct { + u32 low; + u32 high; + } bcount; + u64 bytescount; +}; + +struct acp5x_platform_info { + u16 play_i2s_instance; + u16 cap_i2s_instance; +}; + +union acp_i2stdm_mstrclkgen { + struct { + u32 i2stdm_master_mode : 1; + u32 i2stdm_format_mode : 1; + u32 i2stdm_lrclk_div_val : 9; + u32 i2stdm_bclk_div_val : 11; + u32:10; + } bitfields, bits; + u32 u32_all; +}; + +/* common header file uses exact offset rather than relative + * offset which requires subtraction logic from base_addr + * for accessing ACP5x MMIO space registers + */ +static inline u32 acp_readl(void __iomem *base_addr) +{ + return readl(base_addr - ACP5x_PHY_BASE_ADDRESS); +} + +static inline void acp_writel(u32 val, void __iomem *base_addr) +{ + writel(val, base_addr - ACP5x_PHY_BASE_ADDRESS); +} + +int snd_amd_acp_find_config(struct pci_dev *pci); + +static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd, + int direction) +{ + union acp_dma_count byte_count; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + byte_count.bcount.high = + acp_readl(rtd->acp5x_base + + ACP_HS_TX_LINEARPOSCNTR_HIGH); + byte_count.bcount.low = + acp_readl(rtd->acp5x_base + + ACP_HS_TX_LINEARPOSCNTR_LOW); + break; + case I2S_SP_INSTANCE: + default: + byte_count.bcount.high = + acp_readl(rtd->acp5x_base + + ACP_I2S_TX_LINEARPOSCNTR_HIGH); + byte_count.bcount.low = + acp_readl(rtd->acp5x_base + + ACP_I2S_TX_LINEARPOSCNTR_LOW); + } + } else { + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + byte_count.bcount.high = + acp_readl(rtd->acp5x_base + + ACP_HS_RX_LINEARPOSCNTR_HIGH); + byte_count.bcount.low = + acp_readl(rtd->acp5x_base + + ACP_HS_RX_LINEARPOSCNTR_LOW); + break; + case I2S_SP_INSTANCE: + default: + byte_count.bcount.high = + acp_readl(rtd->acp5x_base + + ACP_I2S_RX_LINEARPOSCNTR_HIGH); + byte_count.bcount.low = + acp_readl(rtd->acp5x_base + + ACP_I2S_RX_LINEARPOSCNTR_LOW); + } + } + return byte_count.bytescount; +} + +static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata, + struct i2s_stream_instance *rtd) +{ + union acp_i2stdm_mstrclkgen mclkgen; + u32 master_reg; + + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + master_reg = ACP_I2STDM2_MSTRCLKGEN; + break; + case I2S_SP_INSTANCE: + default: + master_reg = ACP_I2STDM0_MSTRCLKGEN; + break; + } + + mclkgen.bits.i2stdm_master_mode = 0x1; + if (adata->tdm_mode) + mclkgen.bits.i2stdm_format_mode = 0x01; + else + mclkgen.bits.i2stdm_format_mode = 0x00; + + mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div; + mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div; + acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg); +} diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c new file mode 100644 index 000000000..c4634a8a1 --- /dev/null +++ b/sound/soc/amd/vangogh/pci-acp5x.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD Vangogh ACP PCI Driver +// +// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. + +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> + +#include "acp5x.h" + +struct acp5x_dev_data { + void __iomem *acp5x_base; + bool acp5x_audio_mode; + struct resource *res; + struct platform_device *pdev[ACP5x_DEVS]; +}; + +static int acp5x_power_on(void __iomem *acp5x_base) +{ + u32 val; + int timeout; + + val = acp_readl(acp5x_base + ACP_PGFSM_STATUS); + + if (val == 0) + return val; + + if ((val & ACP_PGFSM_STATUS_MASK) != + ACP_POWER_ON_IN_PROGRESS) + acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, + acp5x_base + ACP_PGFSM_CONTROL); + timeout = 0; + while (++timeout < 500) { + val = acp_readl(acp5x_base + ACP_PGFSM_STATUS); + if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON) + return 0; + udelay(1); + } + return -ETIMEDOUT; +} + +static int acp5x_reset(void __iomem *acp5x_base) +{ + u32 val; + int timeout; + + acp_writel(1, acp5x_base + ACP_SOFT_RESET); + timeout = 0; + while (++timeout < 500) { + val = acp_readl(acp5x_base + ACP_SOFT_RESET); + if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) + break; + cpu_relax(); + } + acp_writel(0, acp5x_base + ACP_SOFT_RESET); + timeout = 0; + while (++timeout < 500) { + val = acp_readl(acp5x_base + ACP_SOFT_RESET); + if (!val) + return 0; + cpu_relax(); + } + return -ETIMEDOUT; +} + +static void acp5x_enable_interrupts(void __iomem *acp5x_base) +{ + acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB); +} + +static void acp5x_disable_interrupts(void __iomem *acp5x_base) +{ + acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base + + ACP_EXTERNAL_INTR_STAT); + acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL); + acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB); +} + +static int acp5x_init(void __iomem *acp5x_base) +{ + int ret; + + /* power on */ + ret = acp5x_power_on(acp5x_base); + if (ret) { + pr_err("ACP5x power on failed\n"); + return ret; + } + acp_writel(0x01, acp5x_base + ACP_CONTROL); + /* Reset */ + ret = acp5x_reset(acp5x_base); + if (ret) { + pr_err("ACP5x reset failed\n"); + return ret; + } + acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL); + acp5x_enable_interrupts(acp5x_base); + return 0; +} + +static int acp5x_deinit(void __iomem *acp5x_base) +{ + int ret; + + acp5x_disable_interrupts(acp5x_base); + /* Reset */ + ret = acp5x_reset(acp5x_base); + if (ret) { + pr_err("ACP5x reset failed\n"); + return ret; + } + acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL); + acp_writel(0x00, acp5x_base + ACP_CONTROL); + return 0; +} + +static int snd_acp5x_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct acp5x_dev_data *adata; + struct platform_device_info pdevinfo[ACP5x_DEVS]; + unsigned int irqflags, flag; + int ret, i; + u32 addr, val; + + /* Return if acp config flag is defined */ + flag = snd_amd_acp_find_config(pci); + if (flag) + return -ENODEV; + + irqflags = IRQF_SHARED; + if (pci->revision != 0x50) + return -ENODEV; + + if (pci_enable_device(pci)) { + dev_err(&pci->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + + ret = pci_request_regions(pci, "AMD ACP5x audio"); + if (ret < 0) { + dev_err(&pci->dev, "pci_request_regions failed\n"); + goto disable_pci; + } + + adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data), + GFP_KERNEL); + if (!adata) { + ret = -ENOMEM; + goto release_regions; + } + addr = pci_resource_start(pci, 0); + adata->acp5x_base = devm_ioremap(&pci->dev, addr, + pci_resource_len(pci, 0)); + if (!adata->acp5x_base) { + ret = -ENOMEM; + goto release_regions; + } + pci_set_master(pci); + pci_set_drvdata(pci, adata); + ret = acp5x_init(adata->acp5x_base); + if (ret) + goto release_regions; + + val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG); + switch (val) { + case I2S_MODE: + adata->res = devm_kzalloc(&pci->dev, + sizeof(struct resource) * ACP5x_RES, + GFP_KERNEL); + if (!adata->res) { + ret = -ENOMEM; + goto de_init; + } + + adata->res[0].name = "acp5x_i2s_iomem"; + adata->res[0].flags = IORESOURCE_MEM; + adata->res[0].start = addr; + adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START); + + adata->res[1].name = "acp5x_i2s_sp"; + adata->res[1].flags = IORESOURCE_MEM; + adata->res[1].start = addr + ACP5x_I2STDM_REG_START; + adata->res[1].end = addr + ACP5x_I2STDM_REG_END; + + adata->res[2].name = "acp5x_i2s_hs"; + adata->res[2].flags = IORESOURCE_MEM; + adata->res[2].start = addr + ACP5x_HS_TDM_REG_START; + adata->res[2].end = addr + ACP5x_HS_TDM_REG_END; + + adata->res[3].name = "acp5x_i2s_irq"; + adata->res[3].flags = IORESOURCE_IRQ; + adata->res[3].start = pci->irq; + adata->res[3].end = adata->res[3].start; + + adata->acp5x_audio_mode = ACP5x_I2S_MODE; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo[0].name = "acp5x_i2s_dma"; + pdevinfo[0].id = 0; + pdevinfo[0].parent = &pci->dev; + pdevinfo[0].num_res = 4; + pdevinfo[0].res = &adata->res[0]; + pdevinfo[0].data = &irqflags; + pdevinfo[0].size_data = sizeof(irqflags); + + pdevinfo[1].name = "acp5x_i2s_playcap"; + pdevinfo[1].id = 0; + pdevinfo[1].parent = &pci->dev; + pdevinfo[1].num_res = 1; + pdevinfo[1].res = &adata->res[1]; + + pdevinfo[2].name = "acp5x_i2s_playcap"; + pdevinfo[2].id = 1; + pdevinfo[2].parent = &pci->dev; + pdevinfo[2].num_res = 1; + pdevinfo[2].res = &adata->res[2]; + + pdevinfo[3].name = "acp5x_mach"; + pdevinfo[3].id = 0; + pdevinfo[3].parent = &pci->dev; + for (i = 0; i < ACP5x_DEVS; i++) { + adata->pdev[i] = + platform_device_register_full(&pdevinfo[i]); + if (IS_ERR(adata->pdev[i])) { + dev_err(&pci->dev, "cannot register %s device\n", + pdevinfo[i].name); + ret = PTR_ERR(adata->pdev[i]); + goto unregister_devs; + } + } + break; + default: + dev_info(&pci->dev, "ACP audio mode : %d\n", val); + } + pm_runtime_set_autosuspend_delay(&pci->dev, 2000); + pm_runtime_use_autosuspend(&pci->dev); + pm_runtime_put_noidle(&pci->dev); + pm_runtime_allow(&pci->dev); + return 0; + +unregister_devs: + for (--i; i >= 0; i--) + platform_device_unregister(adata->pdev[i]); +de_init: + if (acp5x_deinit(adata->acp5x_base)) + dev_err(&pci->dev, "ACP de-init failed\n"); +release_regions: + pci_release_regions(pci); +disable_pci: + pci_disable_device(pci); + + return ret; +} + +static int __maybe_unused snd_acp5x_suspend(struct device *dev) +{ + int ret; + struct acp5x_dev_data *adata; + + adata = dev_get_drvdata(dev); + ret = acp5x_deinit(adata->acp5x_base); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + else + dev_dbg(dev, "ACP de-initialized\n"); + + return ret; +} + +static int __maybe_unused snd_acp5x_resume(struct device *dev) +{ + int ret; + struct acp5x_dev_data *adata; + + adata = dev_get_drvdata(dev); + ret = acp5x_init(adata->acp5x_base); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; + } + return 0; +} + +static const struct dev_pm_ops acp5x_pm = { + SET_RUNTIME_PM_OPS(snd_acp5x_suspend, + snd_acp5x_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume) +}; + +static void snd_acp5x_remove(struct pci_dev *pci) +{ + struct acp5x_dev_data *adata; + int i, ret; + + adata = pci_get_drvdata(pci); + if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) { + for (i = 0; i < ACP5x_DEVS; i++) + platform_device_unregister(adata->pdev[i]); + } + ret = acp5x_deinit(adata->acp5x_base); + if (ret) + dev_err(&pci->dev, "ACP de-init failed\n"); + pm_runtime_forbid(&pci->dev); + pm_runtime_get_noresume(&pci->dev); + pci_release_regions(pci); + pci_disable_device(pci); +} + +static const struct pci_device_id snd_acp5x_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID), + .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, + .class_mask = 0xffffff }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, snd_acp5x_ids); + +static struct pci_driver acp5x_driver = { + .name = KBUILD_MODNAME, + .id_table = snd_acp5x_ids, + .probe = snd_acp5x_probe, + .remove = snd_acp5x_remove, + .driver = { + .pm = &acp5x_pm, + } +}; + +module_pci_driver(acp5x_driver); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/vangogh/vg_chip_offset_byte.h b/sound/soc/amd/vangogh/vg_chip_offset_byte.h new file mode 100644 index 000000000..b1165ae14 --- /dev/null +++ b/sound/soc/amd/vangogh/vg_chip_offset_byte.h @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMD ACP 5.x Register Documentation + * + * Copyright 2021 Advanced Micro Devices, Inc. + */ + +#ifndef _acp_ip_OFFSET_HEADER +#define _acp_ip_OFFSET_HEADER + +/* Registers from ACP_DMA block */ +#define ACP_DMA_CNTL_0 0x1240000 +#define ACP_DMA_CNTL_1 0x1240004 +#define ACP_DMA_CNTL_2 0x1240008 +#define ACP_DMA_CNTL_3 0x124000C +#define ACP_DMA_CNTL_4 0x1240010 +#define ACP_DMA_CNTL_5 0x1240014 +#define ACP_DMA_CNTL_6 0x1240018 +#define ACP_DMA_CNTL_7 0x124001C +#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020 +#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024 +#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028 +#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C +#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030 +#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034 +#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038 +#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C +#define ACP_DMA_DSCR_CNT_0 0x1240040 +#define ACP_DMA_DSCR_CNT_1 0x1240044 +#define ACP_DMA_DSCR_CNT_2 0x1240048 +#define ACP_DMA_DSCR_CNT_3 0x124004C +#define ACP_DMA_DSCR_CNT_4 0x1240050 +#define ACP_DMA_DSCR_CNT_5 0x1240054 +#define ACP_DMA_DSCR_CNT_6 0x1240058 +#define ACP_DMA_DSCR_CNT_7 0x124005C +#define ACP_DMA_PRIO_0 0x1240060 +#define ACP_DMA_PRIO_1 0x1240064 +#define ACP_DMA_PRIO_2 0x1240068 +#define ACP_DMA_PRIO_3 0x124006C +#define ACP_DMA_PRIO_4 0x1240070 +#define ACP_DMA_PRIO_5 0x1240074 +#define ACP_DMA_PRIO_6 0x1240078 +#define ACP_DMA_PRIO_7 0x124007C +#define ACP_DMA_CUR_DSCR_0 0x1240080 +#define ACP_DMA_CUR_DSCR_1 0x1240084 +#define ACP_DMA_CUR_DSCR_2 0x1240088 +#define ACP_DMA_CUR_DSCR_3 0x124008C +#define ACP_DMA_CUR_DSCR_4 0x1240090 +#define ACP_DMA_CUR_DSCR_5 0x1240094 +#define ACP_DMA_CUR_DSCR_6 0x1240098 +#define ACP_DMA_CUR_DSCR_7 0x124009C +#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0 +#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4 +#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8 +#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC +#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0 +#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4 +#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8 +#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC +#define ACP_DMA_ERR_STS_0 0x12400C0 +#define ACP_DMA_ERR_STS_1 0x12400C4 +#define ACP_DMA_ERR_STS_2 0x12400C8 +#define ACP_DMA_ERR_STS_3 0x12400CC +#define ACP_DMA_ERR_STS_4 0x12400D0 +#define ACP_DMA_ERR_STS_5 0x12400D4 +#define ACP_DMA_ERR_STS_6 0x12400D8 +#define ACP_DMA_ERR_STS_7 0x12400DC +#define ACP_DMA_DESC_BASE_ADDR 0x12400E0 +#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4 +#define ACP_DMA_CH_STS 0x12400E8 +#define ACP_DMA_CH_GROUP 0x12400EC +#define ACP_DMA_CH_RST_STS 0x12400F0 + +/* Registers from ACP_AXI2AXIATU block */ +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34 +#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38 +#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C +#define ACPAXI2AXI_ATU_CTRL 0x1240C40 + +/* Registers from ACP_CLKRST block */ +#define ACP_SOFT_RESET 0x1241000 +#define ACP_CONTROL 0x1241004 +#define ACP_STATUS 0x1241008 +#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010 + +/* Registers from ACP_MISC block */ +#define ACP_EXTERNAL_INTR_ENB 0x1241800 +#define ACP_EXTERNAL_INTR_CNTL 0x1241804 +#define ACP_EXTERNAL_INTR_STAT 0x1241808 +#define ACP_ERROR_STATUS 0x12418C4 +#define ACP_SW_I2S_ERROR_REASON 0x12418C8 +#define ACP_MEM_PG_STS 0x12418CC +#define ACP_PGMEM_DEEP_SLEEP_CTRL 0x12418D0 +#define ACP_PGMEM_SHUT_DOWN_CTRL 0x12418D4 + +/* Registers from ACP_PGFSM block */ +#define ACP_PIN_CONFIG 0x1241400 +#define ACP_PAD_PULLUP_CTRL 0x1241404 +#define ACP_PAD_PULLDOWN_CTRL 0x1241408 +#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124140C +#define ACP_PAD_SCHMEN_CTRL 0x1241410 +#define ACP_SW_PAD_KEEPER_EN 0x1241414 +#define ACP_SW_WAKE_EN 0x1241418 +#define ACP_I2S_WAKE_EN 0x124141C +#define ACP_PME_EN 0x1241420 +#define ACP_PGFSM_CONTROL 0x1241424 +#define ACP_PGFSM_STATUS 0x1241428 +#define ACP_CLKMUX_SEL 0x124142C +#define ACP_DEVICE_STATE 0x1241430 +#define AZ_DEVICE_STATE 0x1241434 +#define ACP_INTR_URGENCY_TIMER 0x1241438 +#define AZ_INTR_URGENCY_TIMER 0x124143C +#define ACP_AON_SW_INTR_TRIG 0x1241440 + +/* Registers from ACP_SCRATCH block */ +#define ACP_SCRATCH_REG_0 0x1250000 +#define ACP_SCRATCH_REG_1 0x1250004 +#define ACP_SCRATCH_REG_2 0x1250008 +#define ACP_SCRATCH_REG_3 0x125000C +#define ACP_SCRATCH_REG_4 0x1250010 +#define ACP_SCRATCH_REG_5 0x1250014 +#define ACP_SCRATCH_REG_6 0x1250018 +#define ACP_SCRATCH_REG_7 0x125001C +#define ACP_SCRATCH_REG_8 0x1250020 +#define ACP_SCRATCH_REG_9 0x1250024 +#define ACP_SCRATCH_REG_10 0x1250028 +#define ACP_SCRATCH_REG_11 0x125002C +#define ACP_SCRATCH_REG_12 0x1250030 +#define ACP_SCRATCH_REG_13 0x1250034 +#define ACP_SCRATCH_REG_14 0x1250038 +#define ACP_SCRATCH_REG_15 0x125003C +#define ACP_SCRATCH_REG_16 0x1250040 +#define ACP_SCRATCH_REG_17 0x1250044 +#define ACP_SCRATCH_REG_18 0x1250048 +#define ACP_SCRATCH_REG_19 0x125004C +#define ACP_SCRATCH_REG_20 0x1250050 +#define ACP_SCRATCH_REG_21 0x1250054 +#define ACP_SCRATCH_REG_22 0x1250058 +#define ACP_SCRATCH_REG_23 0x125005C +#define ACP_SCRATCH_REG_24 0x1250060 +#define ACP_SCRATCH_REG_25 0x1250064 +#define ACP_SCRATCH_REG_26 0x1250068 +#define ACP_SCRATCH_REG_27 0x125006C +#define ACP_SCRATCH_REG_28 0x1250070 +#define ACP_SCRATCH_REG_29 0x1250074 +#define ACP_SCRATCH_REG_30 0x1250078 +#define ACP_SCRATCH_REG_31 0x125007C +#define ACP_SCRATCH_REG_32 0x1250080 +#define ACP_SCRATCH_REG_33 0x1250084 +#define ACP_SCRATCH_REG_34 0x1250088 +#define ACP_SCRATCH_REG_35 0x125008C +#define ACP_SCRATCH_REG_36 0x1250090 +#define ACP_SCRATCH_REG_37 0x1250094 +#define ACP_SCRATCH_REG_38 0x1250098 +#define ACP_SCRATCH_REG_39 0x125009C +#define ACP_SCRATCH_REG_40 0x12500A0 +#define ACP_SCRATCH_REG_41 0x12500A4 +#define ACP_SCRATCH_REG_42 0x12500A8 +#define ACP_SCRATCH_REG_43 0x12500AC +#define ACP_SCRATCH_REG_44 0x12500B0 +#define ACP_SCRATCH_REG_45 0x12500B4 +#define ACP_SCRATCH_REG_46 0x12500B8 +#define ACP_SCRATCH_REG_47 0x12500BC +#define ACP_SCRATCH_REG_48 0x12500C0 +#define ACP_SCRATCH_REG_49 0x12500C4 +#define ACP_SCRATCH_REG_50 0x12500C8 +#define ACP_SCRATCH_REG_51 0x12500CC +#define ACP_SCRATCH_REG_52 0x12500D0 +#define ACP_SCRATCH_REG_53 0x12500D4 +#define ACP_SCRATCH_REG_54 0x12500D8 +#define ACP_SCRATCH_REG_55 0x12500DC +#define ACP_SCRATCH_REG_56 0x12500E0 +#define ACP_SCRATCH_REG_57 0x12500E4 +#define ACP_SCRATCH_REG_58 0x12500E8 +#define ACP_SCRATCH_REG_59 0x12500EC +#define ACP_SCRATCH_REG_60 0x12500F0 +#define ACP_SCRATCH_REG_61 0x12500F4 +#define ACP_SCRATCH_REG_62 0x12500F8 +#define ACP_SCRATCH_REG_63 0x12500FC +#define ACP_SCRATCH_REG_64 0x1250100 +#define ACP_SCRATCH_REG_65 0x1250104 +#define ACP_SCRATCH_REG_66 0x1250108 +#define ACP_SCRATCH_REG_67 0x125010C +#define ACP_SCRATCH_REG_68 0x1250110 +#define ACP_SCRATCH_REG_69 0x1250114 +#define ACP_SCRATCH_REG_70 0x1250118 +#define ACP_SCRATCH_REG_71 0x125011C +#define ACP_SCRATCH_REG_72 0x1250120 +#define ACP_SCRATCH_REG_73 0x1250124 +#define ACP_SCRATCH_REG_74 0x1250128 +#define ACP_SCRATCH_REG_75 0x125012C +#define ACP_SCRATCH_REG_76 0x1250130 +#define ACP_SCRATCH_REG_77 0x1250134 +#define ACP_SCRATCH_REG_78 0x1250138 +#define ACP_SCRATCH_REG_79 0x125013C +#define ACP_SCRATCH_REG_80 0x1250140 +#define ACP_SCRATCH_REG_81 0x1250144 +#define ACP_SCRATCH_REG_82 0x1250148 +#define ACP_SCRATCH_REG_83 0x125014C +#define ACP_SCRATCH_REG_84 0x1250150 +#define ACP_SCRATCH_REG_85 0x1250154 +#define ACP_SCRATCH_REG_86 0x1250158 +#define ACP_SCRATCH_REG_87 0x125015C +#define ACP_SCRATCH_REG_88 0x1250160 +#define ACP_SCRATCH_REG_89 0x1250164 +#define ACP_SCRATCH_REG_90 0x1250168 +#define ACP_SCRATCH_REG_91 0x125016C +#define ACP_SCRATCH_REG_92 0x1250170 +#define ACP_SCRATCH_REG_93 0x1250174 +#define ACP_SCRATCH_REG_94 0x1250178 +#define ACP_SCRATCH_REG_95 0x125017C +#define ACP_SCRATCH_REG_96 0x1250180 +#define ACP_SCRATCH_REG_97 0x1250184 +#define ACP_SCRATCH_REG_98 0x1250188 +#define ACP_SCRATCH_REG_99 0x125018C +#define ACP_SCRATCH_REG_100 0x1250190 +#define ACP_SCRATCH_REG_101 0x1250194 +#define ACP_SCRATCH_REG_102 0x1250198 +#define ACP_SCRATCH_REG_103 0x125019C +#define ACP_SCRATCH_REG_104 0x12501A0 +#define ACP_SCRATCH_REG_105 0x12501A4 +#define ACP_SCRATCH_REG_106 0x12501A8 +#define ACP_SCRATCH_REG_107 0x12501AC +#define ACP_SCRATCH_REG_108 0x12501B0 +#define ACP_SCRATCH_REG_109 0x12501B4 +#define ACP_SCRATCH_REG_110 0x12501B8 +#define ACP_SCRATCH_REG_111 0x12501BC +#define ACP_SCRATCH_REG_112 0x12501C0 +#define ACP_SCRATCH_REG_113 0x12501C4 +#define ACP_SCRATCH_REG_114 0x12501C8 +#define ACP_SCRATCH_REG_115 0x12501CC +#define ACP_SCRATCH_REG_116 0x12501D0 +#define ACP_SCRATCH_REG_117 0x12501D4 +#define ACP_SCRATCH_REG_118 0x12501D8 +#define ACP_SCRATCH_REG_119 0x12501DC +#define ACP_SCRATCH_REG_120 0x12501E0 +#define ACP_SCRATCH_REG_121 0x12501E4 +#define ACP_SCRATCH_REG_122 0x12501E8 +#define ACP_SCRATCH_REG_123 0x12501EC +#define ACP_SCRATCH_REG_124 0x12501F0 +#define ACP_SCRATCH_REG_125 0x12501F4 +#define ACP_SCRATCH_REG_126 0x12501F8 +#define ACP_SCRATCH_REG_127 0x12501FC +#define ACP_SCRATCH_REG_128 0x1250200 + +/* Registers from ACP_AUDIO_BUFFERS block */ +#define ACP_I2S_RX_RINGBUFADDR 0x1242000 +#define ACP_I2S_RX_RINGBUFSIZE 0x1242004 +#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008 +#define ACP_I2S_RX_FIFOADDR 0x124200C +#define ACP_I2S_RX_FIFOSIZE 0x1242010 +#define ACP_I2S_RX_DMA_SIZE 0x1242014 +#define ACP_I2S_RX_LINEARPOSCNTR_HIGH 0x1242018 +#define ACP_I2S_RX_LINEARPOSCNTR_LOW 0x124201C +#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020 +#define ACP_I2S_TX_RINGBUFADDR 0x1242024 +#define ACP_I2S_TX_RINGBUFSIZE 0x1242028 +#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C +#define ACP_I2S_TX_FIFOADDR 0x1242030 +#define ACP_I2S_TX_FIFOSIZE 0x1242034 +#define ACP_I2S_TX_DMA_SIZE 0x1242038 +#define ACP_I2S_TX_LINEARPOSCNTR_HIGH 0x124203C +#define ACP_I2S_TX_LINEARPOSCNTR_LOW 0x1242040 +#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044 +#define ACP_BT_RX_RINGBUFADDR 0x1242048 +#define ACP_BT_RX_RINGBUFSIZE 0x124204C +#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050 +#define ACP_BT_RX_FIFOADDR 0x1242054 +#define ACP_BT_RX_FIFOSIZE 0x1242058 +#define ACP_BT_RX_DMA_SIZE 0x124205C +#define ACP_BT_RX_LINEARPOSCNTR_HIGH 0x1242060 +#define ACP_BT_RX_LINEARPOSCNTR_LOW 0x1242064 +#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068 +#define ACP_BT_TX_RINGBUFADDR 0x124206C +#define ACP_BT_TX_RINGBUFSIZE 0x1242070 +#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074 +#define ACP_BT_TX_FIFOADDR 0x1242078 +#define ACP_BT_TX_FIFOSIZE 0x124207C +#define ACP_BT_TX_DMA_SIZE 0x1242080 +#define ACP_BT_TX_LINEARPOSCNTR_HIGH 0x1242084 +#define ACP_BT_TX_LINEARPOSCNTR_LOW 0x1242088 +#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C +#define ACP_HS_RX_RINGBUFADDR 0x1242090 +#define ACP_HS_RX_RINGBUFSIZE 0x1242094 +#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098 +#define ACP_HS_RX_FIFOADDR 0x124209C +#define ACP_HS_RX_FIFOSIZE 0x12420A0 +#define ACP_HS_RX_DMA_SIZE 0x12420A4 +#define ACP_HS_RX_LINEARPOSCNTR_HIGH 0x12420A8 +#define ACP_HS_RX_LINEARPOSCNTR_LOW 0x12420AC +#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0 +#define ACP_HS_TX_RINGBUFADDR 0x12420B4 +#define ACP_HS_TX_RINGBUFSIZE 0x12420B8 +#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC +#define ACP_HS_TX_FIFOADDR 0x12420C0 +#define ACP_HS_TX_FIFOSIZE 0x12420C4 +#define ACP_HS_TX_DMA_SIZE 0x12420C8 +#define ACP_HS_TX_LINEARPOSCNTR_HIGH 0x12420CC +#define ACP_HS_TX_LINEARPOSCNTR_LOW 0x12420D0 +#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4 + +/* Registers from ACP_I2S_TDM block */ +#define ACP_I2STDM_IER 0x1242400 +#define ACP_I2STDM_IRER 0x1242404 +#define ACP_I2STDM_RXFRMT 0x1242408 +#define ACP_I2STDM_ITER 0x124240C +#define ACP_I2STDM_TXFRMT 0x1242410 +#define ACP_I2STDM0_MSTRCLKGEN 0x1242414 +#define ACP_I2STDM1_MSTRCLKGEN 0x1242418 +#define ACP_I2STDM2_MSTRCLKGEN 0x124241C +#define ACP_I2STDM_REFCLKGEN 0x1242420 + +/* Registers from ACP_BT_TDM block */ +#define ACP_BTTDM_IER 0x1242800 +#define ACP_BTTDM_IRER 0x1242804 +#define ACP_BTTDM_RXFRMT 0x1242808 +#define ACP_BTTDM_ITER 0x124280C +#define ACP_BTTDM_TXFRMT 0x1242810 +#define ACP_HSTDM_IER 0x1242814 +#define ACP_HSTDM_IRER 0x1242818 +#define ACP_HSTDM_RXFRMT 0x124281C +#define ACP_HSTDM_ITER 0x1242820 +#define ACP_HSTDM_TXFRMT 0x1242824 +#endif |