Commit 61b23b6b authored by Mark Brown's avatar Mark Brown

ADD legacy audio driver support for rembrandt

Merge series from V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>:

Add Generic driver to support multiple platform,
ADD HS control instance for Rembrandt platform.
Add nau8825,max98560 and rt5682s,rt1019 combination support for legacy
platform.
parents 3585da93 e8a33a94
......@@ -40,6 +40,17 @@ config SND_AMD_ASOC_RENOIR
help
This option enables Renoir I2S support on AMD platform.
config SND_AMD_ASOC_REMBRANDT
tristate "AMD ACP ASOC Rembrandt Support"
select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S
select SND_SOC_AMD_ACP_PDM
depends on X86 && PCI
help
This option enables Rembrandt I2S support on AMD platform.
Say Y if you want to enable AUDIO on Rembrandt
If unsure select "N".
config SND_SOC_AMD_MACH_COMMON
tristate
depends on X86 && PCI && I2C
......
......@@ -12,6 +12,7 @@ snd-acp-pci-objs := acp-pci.o
#platform specific driver
snd-acp-renoir-objs := acp-renoir.o
snd-acp-rembrandt-objs := acp-rembrandt.o
#machine specific driver
snd-acp-mach-objs := acp-mach-common.o
......@@ -24,6 +25,7 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
......
This diff is collapsed.
......@@ -47,6 +47,28 @@ static struct acp_card_drvdata rt5682s_rt1019_data = {
.dmic_codec_id = DMIC,
};
static struct acp_card_drvdata max_nau8825_data = {
.hs_cpu_id = I2S_HS,
.amp_cpu_id = I2S_HS,
.dmic_cpu_id = DMIC,
.hs_codec_id = NAU8825,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
};
static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
.hs_cpu_id = I2S_HS,
.amp_cpu_id = I2S_HS,
.dmic_cpu_id = DMIC,
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
};
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
......@@ -112,6 +134,14 @@ static const struct platform_device_id board_ids[] = {
.name = "acp3xalc5682s1019",
.driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
},
{
.name = "rmb-nau8825-max",
.driver_data = (kernel_ulong_t)&max_nau8825_data,
},
{
.name = "rmb-rt5682s-rt1019",
.driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data,
},
{ }
};
static struct platform_driver acp_asoc_audio = {
......@@ -130,4 +160,6 @@ MODULE_DESCRIPTION("ACP chrome audio support");
MODULE_ALIAS("platform:acp3xalc56821019");
MODULE_ALIAS("platform:acp3xalc5682sm98360");
MODULE_ALIAS("platform:acp3xalc5682s1019");
MODULE_ALIAS("platform:rmb-nau8825-max");
MODULE_ALIAS("platform:rmb-rt5682s-rt1019");
MODULE_LICENSE("GPL v2");
......@@ -313,9 +313,6 @@ static const struct snd_soc_ops acp_card_dmic_ops = {
SND_SOC_DAILINK_DEF(rt1019,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
SND_SOC_DAILINK_DEF(rt1019_1,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:02", "rt1019-aif"),
COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
static const struct snd_soc_dapm_route rt1019_map_lr[] = {
{ "Left Spk", NULL, "Left SPO" },
......@@ -333,17 +330,6 @@ static struct snd_soc_codec_conf rt1019_conf[] = {
},
};
static struct snd_soc_codec_conf rt1019_1_conf[] = {
{
.dlc = COMP_CODEC_CONF("i2c-10EC1019:02"),
.name_prefix = "Left",
},
{
.dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
.name_prefix = "Right",
},
};
static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
......@@ -559,6 +545,12 @@ static struct snd_soc_dai_link_component platform_component[] = {
}
};
static struct snd_soc_dai_link_component platform_rmb_component[] = {
{
.name = "acp_asoc_rembrandt.0",
}
};
static struct snd_soc_dai_link_component sof_component[] = {
{
.name = "0000:04:00.5",
......@@ -567,6 +559,8 @@ static struct snd_soc_dai_link_component sof_component[] = {
SND_SOC_DAILINK_DEF(i2s_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
SND_SOC_DAILINK_DEF(i2s_hs,
DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
SND_SOC_DAILINK_DEF(sof_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
SND_SOC_DAILINK_DEF(sof_hs,
......@@ -716,10 +710,6 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].init = acp_card_rt1019_init;
card->codec_conf = rt1019_conf;
card->num_configs = ARRAY_SIZE(rt1019_conf);
links[i].codecs = rt1019_1;
links[i].num_codecs = ARRAY_SIZE(rt1019_1);
card->codec_conf = rt1019_1_conf;
card->num_configs = ARRAY_SIZE(rt1019_1_conf);
}
i++;
}
......@@ -792,6 +782,40 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
i++;
}
if (drv_data->hs_cpu_id == I2S_HS) {
links[i].name = "acp-headset-codec";
links[i].id = HEADSET_BE_ID;
links[i].cpus = i2s_hs;
links[i].num_cpus = ARRAY_SIZE(i2s_hs);
if (drv_data->platform == REMBRANDT) {
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else {
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
}
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = dummy_codec;
links[i].num_codecs = ARRAY_SIZE(dummy_codec);
}
if (drv_data->hs_codec_id == NAU8825) {
links[i].codecs = nau8825;
links[i].num_codecs = ARRAY_SIZE(nau8825);
links[i].init = acp_card_nau8825_init;
links[i].ops = &acp_card_nau8825_ops;
}
if (drv_data->hs_codec_id == RT5682S) {
links[i].codecs = rt5682s;
links[i].num_codecs = ARRAY_SIZE(rt5682s);
links[i].init = acp_card_rt5682s_init;
links[i].ops = &acp_card_rt5682s_ops;
}
i++;
}
if (drv_data->amp_cpu_id == I2S_SP) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
......@@ -822,6 +846,41 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
i++;
}
if (drv_data->amp_cpu_id == I2S_HS) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
links[i].cpus = i2s_hs;
links[i].num_cpus = ARRAY_SIZE(i2s_hs);
if (drv_data->platform == REMBRANDT) {
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else {
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
}
links[i].dpcm_playback = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
links[i].codecs = dummy_codec;
links[i].num_codecs = ARRAY_SIZE(dummy_codec);
}
if (drv_data->amp_codec_id == MAX98360A) {
links[i].codecs = max98360a;
links[i].num_codecs = ARRAY_SIZE(max98360a);
links[i].ops = &acp_card_maxim_ops;
links[i].init = acp_card_maxim_init;
}
if (drv_data->amp_codec_id == RT1019) {
links[i].codecs = rt1019;
links[i].num_codecs = ARRAY_SIZE(rt1019);
links[i].ops = &acp_card_rt1019_ops;
links[i].init = acp_card_rt1019_init;
card->codec_conf = rt1019_conf;
card->num_configs = ARRAY_SIZE(rt1019_conf);
}
i++;
}
if (drv_data->dmic_cpu_id == DMIC) {
links[i].name = "acp-dmic-codec";
links[i].id = DMIC_BE_ID;
......@@ -835,8 +894,13 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
}
links[i].cpus = pdm_dmic;
links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
if (drv_data->platform == REMBRANDT) {
links[i].platforms = platform_rmb_component;
links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
} else {
links[i].platforms = platform_component;
links[i].num_platforms = ARRAY_SIZE(platform_component);
}
links[i].ops = &acp_card_dmic_ops;
links[i].dpcm_capture = 1;
}
......
......@@ -41,6 +41,11 @@ enum codec_endpoints {
NAU8825,
};
enum platform_end_point {
RENOIR = 0,
REMBRANDT,
};
struct acp_card_drvdata {
unsigned int hs_cpu_id;
unsigned int amp_cpu_id;
......@@ -49,6 +54,7 @@ struct acp_card_drvdata {
unsigned int amp_codec_id;
unsigned int dmic_codec_id;
unsigned int dai_fmt;
unsigned int platform;
struct clk *wclk;
struct clk *bclk;
bool soc_mclk;
......
......@@ -82,6 +82,12 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
chip->name = "acp_asoc_renoir";
chip->acp_rev = ACP3X_DEV;
break;
case 0x6f:
res_acp = acp3x_res;
num_res = ARRAY_SIZE(acp3x_res);
chip->name = "acp_asoc_rembrandt";
chip->acp_rev = ACP6X_DEV;
break;
default:
dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
return -EINVAL;
......
......@@ -160,9 +160,9 @@ static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
stream->reg_offset = ACP_REGION2_OFFSET;
/* Enable DMIC Interrupts */
ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
ext_int_ctrl |= PDM_DMA_INTR_MASK;
writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
return 0;
}
......@@ -175,9 +175,9 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
u32 ext_int_ctrl;
/* Disable DMIC interrupts */
ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
}
const struct snd_soc_dai_ops acp_dmic_dai_ops = {
......
......@@ -91,25 +91,38 @@ 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_resource *rsrc = adata->rsrc;
struct acp_stream *stream;
u16 i2s_flag = 0;
u32 val, i;
u32 val, val1, i;
if (!adata)
return IRQ_NONE;
val = readl(adata->acp_base + ACP_EXTERNAL_INTR_STAT);
if (adata->rsrc->no_of_ctrls == 2)
val1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
val = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
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);
writel(stream->irq_bit,
ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
break;
}
if (adata->rsrc->no_of_ctrls == 2) {
if (stream && (val1 & stream->irq_bit)) {
writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
(rsrc->irqp_used - 1)));
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
break;
}
}
}
if (i2s_flag)
return IRQ_HANDLED;
......@@ -118,6 +131,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
{
struct acp_resource *rsrc = adata->rsrc;
u32 pte_reg, pte_size, reg_val;
/* Use ATU base Group5 */
......@@ -126,15 +140,17 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream
stream->reg_offset = 0x02000000;
/* Group Enable */
reg_val = ACP_SRAM_PTE_OFFSET;
reg_val = rsrc->sram_pte_offset;
writel(reg_val | BIT(31), adata->acp_base + pte_reg);
writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
}
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;
struct acp_resource *rsrc = adata->rsrc;
dma_addr_t addr = substream->dma_buffer.addr;
int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
u32 low, high, val;
......@@ -146,9 +162,9 @@ static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
/* 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);
writel(low, adata->acp_base + rsrc->scratch_reg_offset + val);
high |= BIT(31);
writel(high, adata->acp_base + ACP_SCRATCH_REG_0 + val + 4);
writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4);
/* Move to next physically contiguous page */
val += 8;
......@@ -187,7 +203,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs
}
runtime->private_data = stream;
writel(1, adata->acp_base + ACP_EXTERNAL_INTR_ENB);
writel(1, ACP_EXTERNAL_INTR_ENB(adata));
return ret;
}
......
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2022 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
// V sujith kumar Reddy <Vsujithkumar.Reddy@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_rembrandt"
#define ACP6X_PGFSM_CONTROL 0x1024
#define ACP6X_PGFSM_STATUS 0x1028
#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_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
static int rmb_acp_init(void __iomem *base);
static int rmb_acp_deinit(void __iomem *base);
static struct acp_resource rsrc = {
.offset = 0,
.no_of_ctrls = 2,
.irqp_used = 1,
.soc_mclk = true,
.irq_reg_offset = 0x1a00,
.i2s_pin_cfg_offset = 0x1440,
.i2s_mode = 0x0a,
.scratch_reg_offset = 0x12800,
.sram_pte_offset = 0x03802800,
};
static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1,
.codecs = {"10EC1019"}
};
static struct snd_soc_acpi_codecs amp_max = {
.num_codecs = 1,
.codecs = {"MX98360A"}
};
static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
{
.id = "10508825",
.drv_name = "rmb-nau8825-max",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &amp_max,
},
{
.id = "AMDI0007",
.drv_name = "rembrandt-acp",
},
{
.id = "RTL5682",
.drv_name = "rmb-rt5682s-rt1019",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &amp_rt1019,
},
{},
};
static struct snd_soc_dai_driver acp_rmb_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,
},
{
.name = "acp-i2s-hs",
.id = I2S_HS_INSTANCE,
.playback = {
.stream_name = "I2S HS 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 HS 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 = 8,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &asoc_acp_cpu_dai_ops,
.probe = &asoc_acp_i2s_probe,
},
{
.name = "acp-pdm-dmic",
.id = DMIC_INSTANCE,
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &acp_dmic_dai_ops,
},
};
static int acp6x_power_on(void __iomem *base)
{
u32 val;
int timeout;
val = readl(base + ACP6X_PGFSM_STATUS);
if (val == ACP_POWERED_ON)
return 0;
if ((val & ACP_PGFSM_STATUS_MASK) !=
ACP_POWER_ON_IN_PROGRESS)
writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
base + ACP6X_PGFSM_CONTROL);
timeout = 0;
while (++timeout < 500) {
val = readl(base + ACP6X_PGFSM_STATUS);
if (!val)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int acp6x_power_off(void __iomem *base)
{
u32 val;
int timeout;
writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
base + ACP6X_PGFSM_CONTROL);
timeout = 0;
while (++timeout < 500) {
val = readl(base + ACP6X_PGFSM_STATUS);
if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int acp6x_reset(void __iomem *base)
{
u32 val;
int timeout;
writel(1, base + ACP_SOFT_RESET);
timeout = 0;
while (++timeout < 500) {
val = readl(base + ACP_SOFT_RESET);
if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
break;
cpu_relax();
}
writel(0, base + ACP_SOFT_RESET);
timeout = 0;
while (++timeout < 500) {
val = readl(base + ACP_SOFT_RESET);
if (!val)
return 0;
cpu_relax();
}
return -ETIMEDOUT;
}
static void acp6x_enable_interrupts(struct acp_dev_data *adata)
{
struct acp_resource *rsrc = adata->rsrc;
u32 ext_intr_ctrl;
writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
ext_intr_ctrl |= ACP_ERROR_MASK;
writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
}
static void acp6x_disable_interrupts(struct acp_dev_data *adata)
{
struct acp_resource *rsrc = adata->rsrc;
writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
}
static int rmb_acp_init(void __iomem *base)
{
int ret;
/* power on */
ret = acp6x_power_on(base);
if (ret) {
pr_err("ACP power on failed\n");
return ret;
}
writel(0x01, base + ACP_CONTROL);
/* Reset */
ret = acp6x_reset(base);
if (ret) {
pr_err("ACP reset failed\n");
return ret;
}
return 0;
}
static int rmb_acp_deinit(void __iomem *base)
{
int ret = 0;
/* Reset */
ret = acp6x_reset(base);
if (ret) {
pr_err("ACP reset failed\n");
return ret;
}
writel(0x00, base + ACP_CONTROL);
/* power off */
ret = acp6x_power_off(base);
if (ret) {
pr_err("ACP power off failed\n");
return ret;
}
return 0;
}
static int rembrandt_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_chip_info *chip;
struct acp_dev_data *adata;
struct resource *res;
chip = dev_get_platdata(&pdev->dev);
if (!chip || !chip->base) {
dev_err(&pdev->dev, "ACP chip data is NULL\n");
return -ENODEV;
}
if (chip->acp_rev != ACP6X_DEV) {
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
return -ENODEV;
}
rmb_acp_init(chip->base);
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_rmb_dai;
adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
adata->rsrc = &rsrc;
adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
acp6x_enable_interrupts(adata);
acp_platform_register(dev);
return 0;
}
static int rembrandt_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_chip_info *chip;
chip = dev_get_platdata(&pdev->dev);
if (!chip || !chip->base) {
dev_err(&pdev->dev, "ACP chip data is NULL\n");
return -ENODEV;
}
rmb_acp_deinit(chip->base);
acp6x_disable_interrupts(adata);
acp_platform_unregister(dev);
return 0;
}
static struct platform_driver rembrandt_driver = {
.probe = rembrandt_audio_probe,
.remove = rembrandt_audio_remove,
.driver = {
.name = "acp_asoc_rembrandt",
},
};
module_platform_driver(rembrandt_driver);
MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:" DRV_NAME);
......@@ -39,6 +39,17 @@
#define ACP_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
static struct acp_resource rsrc = {
.offset = 20,
.no_of_ctrls = 1,
.irqp_used = 0,
.irq_reg_offset = 0x1800,
.i2s_pin_cfg_offset = 0x1400,
.i2s_mode = 0x04,
.scratch_reg_offset = 0x12800,
.sram_pte_offset = 0x02052800,
};
static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1,
.codecs = {"10EC1019"}
......@@ -186,20 +197,24 @@ static int acp3x_reset(void __iomem *base)
return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
}
static void acp3x_enable_interrupts(void __iomem *base)
static void acp3x_enable_interrupts(struct acp_dev_data *adata)
{
struct acp_resource *rsrc = adata->rsrc;
u32 ext_intr_ctrl;
writel(0x01, base + ACP_EXTERNAL_INTR_ENB);
ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL);
writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
ext_intr_ctrl |= ACP_ERROR_MASK;
writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL);
writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
}
static void acp3x_disable_interrupts(void __iomem *base)
static void acp3x_disable_interrupts(struct acp_dev_data *adata)
{
writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT);
writel(0x00, base + ACP_EXTERNAL_INTR_ENB);
struct acp_resource *rsrc = adata->rsrc;
writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
}
static int rn_acp_init(void __iomem *base)
......@@ -218,8 +233,6 @@ static int rn_acp_init(void __iomem *base)
if (ret)
return ret;
acp3x_enable_interrupts(base);
return 0;
}
......@@ -227,8 +240,6 @@ static int rn_acp_deinit(void __iomem *base)
{
int ret = 0;
acp3x_disable_interrupts(base);
/* Reset */
ret = acp3x_reset(base);
if (ret)
......@@ -290,11 +301,13 @@ static int renoir_audio_probe(struct platform_device *pdev)
adata->dev = dev;
adata->dai_driver = acp_renoir_dai;
adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
adata->rsrc = &rsrc;
adata->machines = snd_soc_acpi_amd_acp_machines;
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
acp3x_enable_interrupts(adata);
acp_platform_register(dev);
return 0;
......@@ -303,11 +316,14 @@ static int renoir_audio_probe(struct platform_device *pdev)
static int renoir_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_chip_info *chip;
int ret;
chip = dev_get_platdata(&pdev->dev);
acp3x_disable_interrupts(adata);
ret = rn_acp_deinit(chip->base);
if (ret)
dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret));
......
......@@ -19,10 +19,12 @@
#include "chip_offset_byte.h"
#define ACP3X_DEV 3
#define ACP6X_DEV 6
#define I2S_SP_INSTANCE 0x00
#define I2S_BT_INSTANCE 0x01
#define DMIC_INSTANCE 0x02
#define I2S_HS_INSTANCE 0x03
#define MEM_WINDOW_START 0x4080000
......@@ -32,30 +34,37 @@
#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 THRESHOLD(bit, base) ((bit) + (base))
#define I2S_RX_THRESHOLD(base) THRESHOLD(7, base)
#define I2S_TX_THRESHOLD(base) THRESHOLD(8, base)
#define BT_TX_THRESHOLD(base) THRESHOLD(6, base)
#define BT_RX_THRESHOLD(base) THRESHOLD(5, base)
#define HS_TX_THRESHOLD(base) THRESHOLD(4, base)
#define HS_RX_THRESHOLD(base) THRESHOLD(3, base)
#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 ACP_SRAM_PDM_PTE_OFFSET 0x400
#define ACP_SRAM_HS_PB_PTE_OFFSET 0x500
#define ACP_SRAM_HS_CP_PTE_OFFSET 0x600
#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 I2S_HS_TX_MEM_WINDOW_START 0x40A0000
#define I2S_HS_RX_MEM_WINDOW_START 0x40C0000
#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 HS_PB_FIFO_ADDR_OFFSET 0xD00
#define HS_CAPT_FIFO_ADDR_OFFSET 0xF00
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
#define PLAYBACK_MAX_PERIOD_SIZE 8192
......@@ -73,7 +82,7 @@
#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
#define ACP_MAX_STREAM 6
#define ACP_MAX_STREAM 8
struct acp_chip_info {
char *name; /* Platform name */
......@@ -92,6 +101,18 @@ struct acp_stream {
u32 fifo_offset;
};
struct acp_resource {
int offset;
int no_of_ctrls;
int irqp_used;
bool soc_mclk;
u32 irq_reg_offset;
u32 i2s_pin_cfg_offset;
int i2s_mode;
u64 scratch_reg_offset;
u64 sram_pte_offset;
};
struct acp_dev_data {
char *name;
struct device *dev;
......@@ -106,6 +127,22 @@ struct acp_dev_data {
struct snd_soc_acpi_mach *machines;
struct platform_device *mach_dev;
u32 bclk_div;
u32 lrclk_div;
struct acp_resource *rsrc;
};
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;
};
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
......@@ -134,6 +171,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
break;
case I2S_HS_INSTANCE:
high = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL;
......@@ -148,6 +189,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
break;
case I2S_HS_INSTANCE:
high = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
break;
case DMIC_INSTANCE:
high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
......@@ -163,4 +208,31 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
return byte_count;
}
static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
{
union acp_i2stdm_mstrclkgen mclkgen;
u32 master_reg;
switch (dai_id) {
case I2S_SP_INSTANCE:
master_reg = ACP_I2STDM0_MSTRCLKGEN;
break;
case I2S_BT_INSTANCE:
master_reg = ACP_I2STDM1_MSTRCLKGEN;
break;
case I2S_HS_INSTANCE:
master_reg = ACP_I2STDM2_MSTRCLKGEN;
break;
default:
master_reg = ACP_I2STDM0_MSTRCLKGEN;
break;
}
mclkgen.bits.i2stdm_master_mode = 0x1;
mclkgen.bits.i2stdm_format_mode = 0x00;
mclkgen.bits.i2stdm_bclk_div_val = adata->bclk_div;
mclkgen.bits.i2stdm_lrclk_div_val = adata->lrclk_div;
writel(mclkgen.u32_all, adata->acp_base + master_reg);
}
#endif
......@@ -20,11 +20,13 @@
#define ACP_SOFT_RESET 0x1000
#define ACP_CONTROL 0x1004
#define ACP_EXTERNAL_INTR_ENB 0x1800
#define ACP_EXTERNAL_INTR_CNTL 0x1804
#define ACP_EXTERNAL_INTR_STAT 0x1808
#define ACP_I2S_PIN_CONFIG 0x1400
#define ACP_SCRATCH_REG_0 0x12800
#define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \
(adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
#define ACP_EXTERNAL_INTR_ENB(adata) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x0, 0x0)
#define ACP_EXTERNAL_INTR_CNTL(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x4, ctrl)
#define ACP_EXTERNAL_INTR_STAT(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, \
(0x4 + (adata->rsrc->no_of_ctrls * 0x04)), ctrl)
/* Registers from ACP_AUDIO_BUFFERS block */
......@@ -64,6 +66,24 @@
#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x2084
#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x2088
#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x208C
#define ACP_HS_RX_RINGBUFADDR 0x3A90
#define ACP_HS_RX_RINGBUFSIZE 0x3A94
#define ACP_HS_RX_LINKPOSITIONCNTR 0x3A98
#define ACP_HS_RX_FIFOADDR 0x3A9C
#define ACP_HS_RX_FIFOSIZE 0x3AA0
#define ACP_HS_RX_DMA_SIZE 0x3AA4
#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x3AA8
#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x3AAC
#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x3AB0
#define ACP_HS_TX_RINGBUFADDR 0x3AB4
#define ACP_HS_TX_RINGBUFSIZE 0x3AB8
#define ACP_HS_TX_LINKPOSITIONCNTR 0x3ABC
#define ACP_HS_TX_FIFOADDR 0x3AC0
#define ACP_HS_TX_FIFOSIZE 0x3AC4
#define ACP_HS_TX_DMA_SIZE 0x3AC8
#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x3ACC
#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x3AD0
#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x3AD4
#define ACP_I2STDM_IER 0x2400
#define ACP_I2STDM_IRER 0x2404
......@@ -79,6 +99,13 @@
#define ACP_BTTDM_ITER 0x280C
#define ACP_BTTDM_TXFRMT 0x2810
/* Registers from ACP_HS_TDM block */
#define ACP_HSTDM_IER 0x2814
#define ACP_HSTDM_IRER 0x2818
#define ACP_HSTDM_RXFRMT 0x281C
#define ACP_HSTDM_ITER 0x2820
#define ACP_HSTDM_TXFRMT 0x2824
/* Registers from ACP_WOV_PDM block */
#define ACP_WOV_PDM_ENABLE 0x2C04
......@@ -99,4 +126,7 @@
#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64
#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68
#define ACP_I2STDM0_MSTRCLKGEN 0x2414
#define ACP_I2STDM1_MSTRCLKGEN 0x2418
#define ACP_I2STDM2_MSTRCLKGEN 0x241C
#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