diff options
Diffstat (limited to 'sound/soc/amd/acp')
-rw-r--r-- | sound/soc/amd/acp/Kconfig | 18 | ||||
-rw-r--r-- | sound/soc/amd/acp/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-i2s.c | 45 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-legacy-common.c | 79 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-legacy-mach.c | 114 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-mach-common.c | 86 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-mach.h | 69 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-pci.c | 23 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-platform.c | 29 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-rembrandt.c | 17 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-renoir.c | 7 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c | 444 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h | 12 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp63.c | 322 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp70.c | 254 | ||||
-rw-r--r-- | sound/soc/amd/acp/amd.h | 71 | ||||
-rw-r--r-- | sound/soc/amd/acp/chip_offset_byte.h | 1 |
17 files changed, 1492 insertions, 103 deletions
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 631cdf96d637..5fb322212938 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -30,13 +30,15 @@ config SND_SOC_AMD_ACP_PCM config SND_SOC_AMD_ACP_PCI tristate "AMD ACP PCI Driver Support" - select SND_SOC_AMD_ACP_LEGACY_COMMON depends on X86 && PCI + depends on ACPI + select SND_SOC_AMD_ACP_LEGACY_COMMON help This options enables generic PCI driver for ACP device. config SND_AMD_ASOC_RENOIR tristate "AMD ACP ASOC Renoir Support" + depends on ACPI select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM @@ -47,6 +49,7 @@ config SND_AMD_ASOC_RENOIR config SND_AMD_ASOC_REMBRANDT tristate "AMD ACP ASOC Rembrandt Support" + depends on ACPI select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM @@ -57,6 +60,19 @@ config SND_AMD_ASOC_REMBRANDT Say Y if you want to enable AUDIO on Rembrandt If unsure select "N". +config SND_AMD_ASOC_ACP63 + tristate "AMD ACP ASOC ACP6.3 Support" + depends on X86 && PCI + depends on ACPI + select SND_SOC_AMD_ACP_PCM + select SND_SOC_AMD_ACP_I2S + select SND_SOC_AMD_ACP_PDM + select SND_SOC_AMD_ACP_LEGACY_COMMON + help + This option enables Acp6.3 I2S support on AMD platform. + Say Y if you want to enable AUDIO on ACP6.3 + If unsure select "N". + config SND_SOC_AMD_MACH_COMMON tristate depends on X86 && PCI && I2C diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index 4e65fdbc8dca..dd85700f1c5f 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -14,10 +14,11 @@ snd-acp-pci-objs := acp-pci.o #platform specific driver snd-acp-renoir-objs := acp-renoir.o snd-acp-rembrandt-objs := acp-rembrandt.o +snd-acp63-objs := acp63.o #machine specific driver snd-acp-mach-objs := acp-mach-common.o -snd-acp-legacy-mach-objs := acp-legacy-mach.o +snd-acp-legacy-mach-objs := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o snd-acp-sof-mach-objs := acp-sof-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o @@ -28,6 +29,7 @@ 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_AMD_ASOC_ACP63) += snd-acp63.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index df350014966a..1185e5aac523 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -20,10 +20,55 @@ #include <sound/soc.h> #include <sound/soc-dai.h> #include <linux/dma-mapping.h> +#include <linux/bitfield.h> #include "amd.h" #define DRV_NAME "acp_i2s_playcap" +#define I2S_MASTER_MODE_ENABLE 1 +#define I2S_MODE_ENABLE 0 +#define LRCLK_DIV_FIELD GENMASK(10, 2) +#define BCLK_DIV_FIELD GENMASK(23, 11) +#define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2) +#define ACP63_BCLK_DIV_FIELD GENMASK(23, 13) + +static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id) +{ + u32 i2s_clk_reg, val; + struct acp_chip_info *chip; + struct device *dev; + + dev = adata->dev; + chip = dev_get_platdata(dev); + switch (dai_id) { + case I2S_SP_INSTANCE: + i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN; + break; + case I2S_BT_INSTANCE: + i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN; + break; + case I2S_HS_INSTANCE: + i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN; + break; + default: + i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN; + break; + } + + val = I2S_MASTER_MODE_ENABLE; + val |= I2S_MODE_ENABLE & BIT(1); + + switch (chip->acp_rev) { + case ACP63_DEV: + val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, adata->lrclk_div); + val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, adata->bclk_div); + break; + default: + val |= FIELD_PREP(LRCLK_DIV_FIELD, adata->lrclk_div); + val |= FIELD_PREP(BCLK_DIV_FIELD, adata->bclk_div); + } + writel(val, adata->acp_base + i2s_clk_reg); +} static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index ba58165cc6e6..b5aff3f230be 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -16,6 +16,11 @@ #include <linux/pci.h> #include <linux/export.h> +#define ACP_RENOIR_PDM_ADDR 0x02 +#define ACP_REMBRANDT_PDM_ADDR 0x03 +#define ACP63_PDM_ADDR 0x02 +#define ACP70_PDM_ADDR 0x02 + void acp_enable_interrupts(struct acp_dev_data *adata) { struct acp_resource *rsrc = adata->rsrc; @@ -80,8 +85,8 @@ void restore_acp_pdm_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *soc_runtime; u32 ext_int_ctrl; - soc_runtime = asoc_substream_to_rtd(substream); - dai = asoc_rtd_to_cpu(soc_runtime, 0); + soc_runtime = snd_soc_substream_to_rtd(substream); + dai = snd_soc_rtd_to_cpu(soc_runtime, 0); /* Programming channel mask and sampling rate */ writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); @@ -192,8 +197,8 @@ int restore_acp_i2s_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *soc_runtime; u32 tdm_fmt, reg_val, fmt_reg, val; - soc_runtime = asoc_substream_to_rtd(substream); - dai = asoc_rtd_to_cpu(soc_runtime, 0); + soc_runtime = snd_soc_substream_to_rtd(substream); + dai = snd_soc_rtd_to_cpu(soc_runtime, 0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1]; switch (stream->dai_id) { @@ -260,6 +265,14 @@ static int acp_power_on(struct acp_chip_info *chip) acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS; acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL; break; + case ACP63_DEV: + acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL; + break; + case ACP70_DEV: + acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL; + break; default: return -EINVAL; } @@ -312,16 +325,17 @@ int acp_init(struct acp_chip_info *chip) } EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON); -int acp_deinit(void __iomem *base) +int acp_deinit(struct acp_chip_info *chip) { int ret; /* Reset */ - ret = acp_reset(base); + ret = acp_reset(chip->base); if (ret) return ret; - writel(0, base + ACP_CONTROL); + if (chip->acp_rev != ACP70_DEV) + writel(0, chip->base + ACP_CONTROL); return 0; } EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON); @@ -344,4 +358,55 @@ int smn_read(struct pci_dev *dev, u32 smn_addr) } EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON); +int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip) +{ + struct acpi_device *pdm_dev; + const union acpi_object *obj; + u32 pdm_addr, val; + + val = readl(chip->base + ACP_PIN_CONFIG); + switch (val) { + case ACP_CONFIG_4: + case ACP_CONFIG_5: + case ACP_CONFIG_6: + case ACP_CONFIG_7: + case ACP_CONFIG_8: + case ACP_CONFIG_10: + case ACP_CONFIG_11: + case ACP_CONFIG_12: + case ACP_CONFIG_13: + case ACP_CONFIG_14: + break; + default: + return -EINVAL; + } + + switch (chip->acp_rev) { + case ACP3X_DEV: + pdm_addr = ACP_RENOIR_PDM_ADDR; + break; + case ACP6X_DEV: + pdm_addr = ACP_REMBRANDT_PDM_ADDR; + break; + case ACP63_DEV: + pdm_addr = ACP63_PDM_ADDR; + break; + case ACP70_DEV: + pdm_addr = ACP70_PDM_ADDR; + break; + default: + return -EINVAL; + } + + pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0); + if (pdm_dev) { + if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type", + ACPI_TYPE_INTEGER, &obj) && + obj->integer.value == pdm_addr) + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL_NS_GPL(check_acp_pdm, SND_SOC_ACP_COMMON); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 6d57d17ddfd7..47c3b5f167f5 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include "acp-mach.h" +#include "acp3x-es83xx/acp3x-es83xx.h" static struct acp_card_drvdata rt5682_rt1019_data = { .hs_cpu_id = I2S_SP, @@ -51,6 +52,14 @@ static struct acp_card_drvdata rt5682s_rt1019_data = { .tdm_mode = false, }; +static struct acp_card_drvdata es83xx_rn_data = { + .hs_cpu_id = I2S_SP, + .dmic_cpu_id = DMIC, + .hs_codec_id = ES83XX, + .dmic_codec_id = DMIC, + .platform = RENOIR, +}; + static struct acp_card_drvdata max_nau8825_data = { .hs_cpu_id = I2S_HS, .amp_cpu_id = I2S_HS, @@ -75,6 +84,44 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = { .tdm_mode = false, }; +static struct acp_card_drvdata acp_dmic_data = { + .dmic_cpu_id = DMIC, + .dmic_codec_id = DMIC, +}; + +static bool acp_asoc_init_ops(struct acp_card_drvdata *priv) +{ + bool has_ops = false; + + if (priv->hs_codec_id == ES83XX) { + has_ops = true; + acp3x_es83xx_init_ops(&priv->ops); + } + return has_ops; +} + +static int acp_asoc_suspend_pre(struct snd_soc_card *card) +{ + int ret; + + ret = acp_ops_suspend_pre(card); + if (ret == 1) + return 0; + else + return ret; +} + +static int acp_asoc_resume_post(struct snd_soc_card *card) +{ + int ret; + + ret = acp_ops_resume_post(card); + if (ret == 1) + return 0; + else + return ret; +} + static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -83,35 +130,70 @@ static int acp_asoc_probe(struct platform_device *pdev) struct acp_card_drvdata *acp_card_drvdata; int ret; - if (!pdev->id_entry) - return -EINVAL; + if (!pdev->id_entry) { + ret = -EINVAL; + goto out; + } card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; + if (!card) { + ret = -ENOMEM; + goto out; + } + card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + acp_card_drvdata = card->drvdata; + acp_card_drvdata->acpi_mach = (struct snd_soc_acpi_mach *)pdev->dev.platform_data; card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; - /* Widgets and controls added per-codec in acp-mach-common.c */ - acp_card_drvdata = card->drvdata; + acp_asoc_init_ops(card->drvdata); + + /* If widgets and controls are not set in specific callback, + * they will be added per-codec in acp-mach-common.c + */ + ret = acp_ops_configure_widgets(card); + if (ret < 0) { + dev_err(&pdev->dev, + "Cannot configure widgets for card (%s): %d\n", + card->name, ret); + goto out; + } + card->suspend_pre = acp_asoc_suspend_pre; + card->resume_post = acp_asoc_resume_post; + + ret = acp_ops_probe(card); + if (ret < 0) { + dev_err(&pdev->dev, + "Cannot probe card (%s): %d\n", + card->name, ret); + goto out; + } + if (!strcmp(pdev->name, "acp-pdm-mach")) + acp_card_drvdata->platform = *((int *)dev->platform_data); + dmi_id = dmi_first_match(acp_quirk_table); if (dmi_id && dmi_id->driver_data) acp_card_drvdata->tdm_mode = dmi_id->driver_data; - acp_legacy_dai_links_create(card); + ret = acp_legacy_dai_links_create(card); + if (ret) { + dev_err(&pdev->dev, + "Cannot create dai links for card (%s): %d\n", + card->name, ret); + goto out; + } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "devm_snd_soc_register_card(%s) failed: %d\n", card->name, ret); - return ret; + goto out; } - - return 0; +out: + return ret; } static const struct platform_device_id board_ids[] = { @@ -128,6 +210,10 @@ static const struct platform_device_id board_ids[] = { .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data, }, { + .name = "acp3x-es83xx", + .driver_data = (kernel_ulong_t)&es83xx_rn_data, + }, + { .name = "rmb-nau8825-max", .driver_data = (kernel_ulong_t)&max_nau8825_data, }, @@ -135,6 +221,10 @@ static const struct platform_device_id board_ids[] = { .name = "rmb-rt5682s-rt1019", .driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data, }, + { + .name = "acp-pdm-mach", + .driver_data = (kernel_ulong_t)&acp_dmic_data, + }, { } }; static struct platform_driver acp_asoc_audio = { @@ -153,6 +243,8 @@ MODULE_DESCRIPTION("ACP chrome audio support"); MODULE_ALIAS("platform:acp3xalc56821019"); MODULE_ALIAS("platform:acp3xalc5682sm98360"); MODULE_ALIAS("platform:acp3xalc5682s1019"); +MODULE_ALIAS("platform:acp3x-es83xx"); MODULE_ALIAS("platform:rmb-nau8825-max"); MODULE_ALIAS("platform:rmb-rt5682s-rt1019"); +MODULE_ALIAS("platform:acp-pdm-mach"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index a06af82b8056..34b14f2611ba 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -117,7 +117,7 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; int ret; @@ -172,10 +172,10 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int acp_card_hs_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); int ret; unsigned int fmt; @@ -206,7 +206,7 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream) static void acp_card_shutdown(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; @@ -220,8 +220,8 @@ static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); int ret; unsigned int fmt, srate, ch, format; @@ -342,7 +342,7 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; int ret; @@ -402,8 +402,8 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); int ret; unsigned int fmt, srate, ch, format; @@ -573,7 +573,7 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream, struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); int i, ret = 0; unsigned int fmt, srate, ch, format; @@ -737,7 +737,7 @@ static int acp_card_maxim_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); unsigned int fmt, srate, ch, format; int ret; @@ -928,7 +928,7 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; int ret; @@ -980,11 +980,11 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) static int acp_nau8825_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); int ret; unsigned int fmt; @@ -1142,7 +1142,7 @@ static struct snd_pcm_hw_constraint_list constraints_sample_bits = { static int acp_8821_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; int ret; @@ -1204,10 +1204,10 @@ static int acp_8821_startup(struct snd_pcm_substream *substream) static int acp_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); int ret; unsigned int fmt; @@ -1260,6 +1260,18 @@ static struct snd_soc_dai_link_component platform_rmb_component[] = { } }; +static struct snd_soc_dai_link_component platform_acp63_component[] = { + { + .name = "acp_asoc_acp63.0", + } +}; + +static struct snd_soc_dai_link_component platform_acp70_component[] = { + { + .name = "acp_asoc_acp70.0", + } +}; + static struct snd_soc_dai_link_component sof_component[] = { { .name = "0000:04:00.5", @@ -1358,7 +1370,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].no_pcm = 1; if (!drv_data->hs_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->hs_codec_id == RT5682) { @@ -1395,7 +1407,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].no_pcm = 1; if (!drv_data->hs_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->hs_codec_id == NAU8825) { @@ -1425,7 +1437,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].no_pcm = 1; if (!drv_data->amp_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->amp_codec_id == RT1019) { @@ -1457,7 +1469,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].no_pcm = 1; if (!drv_data->amp_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->amp_codec_id == MAX98360A) { @@ -1513,6 +1525,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) struct device *dev = card->dev; struct acp_card_drvdata *drv_data = card->drvdata; int i = 0, num_links = 0; + int rc; if (drv_data->hs_cpu_id) num_links++; @@ -1536,7 +1549,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].dpcm_capture = 1; if (!drv_data->hs_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->hs_codec_id == RT5682) { @@ -1551,6 +1564,13 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].init = acp_card_rt5682s_init; links[i].ops = &acp_card_rt5682s_ops; } + if (drv_data->hs_codec_id == ES83XX) { + rc = acp_ops_configure_link(card, &links[i]); + if (rc != 0) { + dev_err(dev, "Failed to configure link for ES83XX: %d\n", rc); + return rc; + } + } i++; } @@ -1562,6 +1582,9 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) if (drv_data->platform == REMBRANDT) { links[i].platforms = platform_rmb_component; links[i].num_platforms = ARRAY_SIZE(platform_rmb_component); + } else if (drv_data->platform == ACP63) { + links[i].platforms = platform_acp63_component; + links[i].num_platforms = ARRAY_SIZE(platform_acp63_component); } else { links[i].platforms = platform_component; links[i].num_platforms = ARRAY_SIZE(platform_component); @@ -1570,7 +1593,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].dpcm_capture = 1; if (!drv_data->hs_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->hs_codec_id == NAU8825) { @@ -1598,7 +1621,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].dpcm_playback = 1; if (!drv_data->amp_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->amp_codec_id == RT1019) { @@ -1626,6 +1649,9 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) if (drv_data->platform == REMBRANDT) { links[i].platforms = platform_rmb_component; links[i].num_platforms = ARRAY_SIZE(platform_rmb_component); + } else if (drv_data->platform == ACP63) { + links[i].platforms = platform_acp63_component; + links[i].num_platforms = ARRAY_SIZE(platform_acp63_component); } else { links[i].platforms = platform_component; links[i].num_platforms = ARRAY_SIZE(platform_component); @@ -1633,7 +1659,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].dpcm_playback = 1; if (!drv_data->amp_codec_id) { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } if (drv_data->amp_codec_id == MAX98360A) { @@ -1661,7 +1687,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].num_codecs = ARRAY_SIZE(dmic_codec); } else { /* Use dummy codec if codec id not specified */ - links[i].codecs = &asoc_dummy_dlc; + links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } links[i].cpus = pdm_dmic; @@ -1669,6 +1695,12 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) if (drv_data->platform == REMBRANDT) { links[i].platforms = platform_rmb_component; links[i].num_platforms = ARRAY_SIZE(platform_rmb_component); + } else if (drv_data->platform == ACP63) { + links[i].platforms = platform_acp63_component; + links[i].num_platforms = ARRAY_SIZE(platform_acp63_component); + } else if (drv_data->platform == ACP70) { + links[i].platforms = platform_acp70_component; + links[i].num_platforms = ARRAY_SIZE(platform_acp70_component); } else { links[i].platforms = platform_component; links[i].num_platforms = ARRAY_SIZE(platform_component); diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 2b3ec6594023..cd681101bea7 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -20,6 +20,10 @@ #define TDM_CHANNELS 8 +#define ACP_OPS(priv, cb) ((priv)->ops.cb) + +#define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata) + enum be_id { HEADSET_BE_ID = 0, AMP_BE_ID, @@ -43,11 +47,22 @@ enum codec_endpoints { NAU8825, NAU8821, MAX98388, + ES83XX, }; enum platform_end_point { RENOIR = 0, REMBRANDT, + ACP63, + ACP70, +}; + +struct acp_mach_ops { + int (*probe)(struct snd_soc_card *card); + int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); + int (*configure_widgets)(struct snd_soc_card *card); + int (*suspend_pre)(struct snd_soc_card *card); + int (*resume_post)(struct snd_soc_card *card); }; struct acp_card_drvdata { @@ -61,6 +76,9 @@ struct acp_card_drvdata { unsigned int platform; struct clk *wclk; struct clk *bclk; + struct acp_mach_ops ops; + struct snd_soc_acpi_mach *acpi_mach; + void *mach_priv; bool soc_mclk; bool tdm_mode; }; @@ -69,4 +87,55 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); extern const struct dmi_system_id acp_quirk_table[]; +static inline int acp_ops_probe(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, probe)) + ret = ACP_OPS(priv, probe)(card); + return ret; +} + +static inline int acp_ops_configure_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, configure_link)) + ret = ACP_OPS(priv, configure_link)(card, dai_link); + return ret; +} + +static inline int acp_ops_configure_widgets(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, configure_widgets)) + ret = ACP_OPS(priv, configure_widgets)(card); + return ret; +} + +static inline int acp_ops_suspend_pre(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, suspend_pre)) + ret = ACP_OPS(priv, suspend_pre)(card); + return ret; +} + +static inline int acp_ops_resume_post(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, resume_post)) + ret = ACP_OPS(priv, resume_post)(card); + return ret; +} + #endif diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index a32c14a109b7..8c8b1dcac628 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -55,7 +55,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id int ret; flag = snd_amd_acp_find_config(pci); - if (flag != FLAG_AMD_LEGACY) + if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC) return -ENODEV; chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL); @@ -87,6 +87,14 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id chip->name = "acp_asoc_rembrandt"; chip->acp_rev = ACP6X_DEV; break; + case 0x63: + chip->name = "acp_asoc_acp63"; + chip->acp_rev = ACP63_DEV; + break; + case 0x70: + chip->name = "acp_asoc_acp70"; + chip->acp_rev = ACP70_DEV; + break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); ret = -EINVAL; @@ -125,6 +133,13 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } } + if (flag == FLAG_AMD_LEGACY_ONLY_DMIC) { + ret = check_acp_pdm(pci, chip); + if (ret < 0) + goto skip_pdev_creation; + } + + chip->flag = flag; memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.name = chip->name; @@ -141,6 +156,8 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id ret = PTR_ERR(pdev); goto unregister_dmic_dev; } + +skip_pdev_creation: chip->chip_pdev = pdev; dev_set_drvdata(&pci->dev, chip); pm_runtime_set_autosuspend_delay(&pci->dev, 2000); @@ -165,7 +182,7 @@ static int __maybe_unused snd_acp_suspend(struct device *dev) int ret; chip = dev_get_drvdata(dev); - ret = acp_deinit(chip->base); + ret = acp_deinit(chip); if (ret) dev_err(dev, "ACP de-init failed\n"); return ret; @@ -206,7 +223,7 @@ static void acp_pci_remove(struct pci_dev *pci) platform_device_unregister(dmic_dev); if (pdev) platform_device_unregister(pdev); - ret = acp_deinit(chip->base); + ret = acp_deinit(chip); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); } diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index f516daf6fef4..aaac8aa744cb 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -21,6 +21,8 @@ #include <linux/dma-mapping.h> #include "amd.h" +#include "../mach-config.h" +#include "acp-mach.h" #define DRV_NAME "acp_i2s_dma" @@ -69,20 +71,25 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = { int acp_machine_select(struct acp_dev_data *adata) { struct snd_soc_acpi_mach *mach; - int size; - - size = sizeof(*adata->machines); - mach = snd_soc_acpi_find_machine(adata->machines); - if (!mach) { - dev_err(adata->dev, "warning: No matching ASoC machine driver found\n"); - return -EINVAL; + int size, platform; + + if (adata->flag == FLAG_AMD_LEGACY_ONLY_DMIC) { + platform = adata->platform; + adata->mach_dev = platform_device_register_data(adata->dev, "acp-pdm-mach", + PLATFORM_DEVID_NONE, &platform, + sizeof(platform)); + } else { + size = sizeof(*adata->machines); + mach = snd_soc_acpi_find_machine(adata->machines); + if (!mach) { + dev_err(adata->dev, "warning: No matching ASoC machine driver found\n"); + return -EINVAL; + } + adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name, + PLATFORM_DEVID_NONE, mach, size); } - - adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name, - PLATFORM_DEVID_NONE, mach, size); if (IS_ERR(adata->mach_dev)) dev_warn(adata->dev, "Unable to register Machine device\n"); - return 0; } EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON); diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 1bf7b2e68a11..158f819f8da4 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -23,6 +23,8 @@ #include <linux/pm_runtime.h> #include "amd.h" +#include "../mach-config.h" +#include "acp-mach.h" #define DRV_NAME "acp_asoc_rembrandt" @@ -189,6 +191,7 @@ static int rembrandt_audio_probe(struct platform_device *pdev) struct acp_chip_info *chip; struct acp_dev_data *adata; struct resource *res; + u32 ret; chip = dev_get_platdata(&pdev->dev); if (!chip || !chip->base) { @@ -226,12 +229,18 @@ static int rembrandt_audio_probe(struct platform_device *pdev) adata->dai_driver = acp_rmb_dai; adata->num_dai = ARRAY_SIZE(acp_rmb_dai); adata->rsrc = &rsrc; - + adata->platform = REMBRANDT; + adata->flag = chip->flag; adata->machines = snd_soc_acpi_amd_rmb_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - acp6x_master_clock_generate(dev); + + if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + ret = acp6x_master_clock_generate(dev); + if (ret) + return ret; + } acp_enable_interrupts(adata); acp_platform_register(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); @@ -260,7 +269,9 @@ static int __maybe_unused rmb_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - acp6x_master_clock_generate(dev); + if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + acp6x_master_clock_generate(dev); + spin_lock(&adata->acp_lock); list_for_each_entry(stream, &adata->stream_list, list) { substream = stream->substream; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 54235cad9cc9..a591482a0726 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -22,6 +22,7 @@ #include <linux/dma-mapping.h> #include "amd.h" +#include "acp-mach.h" #define DRV_NAME "acp_asoc_renoir" @@ -69,6 +70,10 @@ static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { .id = "AMDI1019", .drv_name = "renoir-acp", }, + { + .id = "ESSX8336", + .drv_name = "acp3x-es83xx", + }, {}, }; @@ -181,6 +186,8 @@ static int renoir_audio_probe(struct platform_device *pdev) adata->dai_driver = acp_renoir_dai; adata->num_dai = ARRAY_SIZE(acp_renoir_dai); adata->rsrc = &rsrc; + adata->platform = RENOIR; + adata->flag = chip->flag; adata->machines = snd_soc_acpi_amd_acp_machines; acp_machine_select(adata); diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c new file mode 100644 index 000000000000..6cd3352dc38d --- /dev/null +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Machine driver for AMD ACP Audio engine using ES8336 codec. +// +// Copyright 2023 Marian Postevca <posteuca@mutex.one> +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <sound/soc-acpi.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/acpi.h> +#include <linux/dmi.h> +#include "../acp-mach.h" +#include "acp3x-es83xx.h" + +#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv)) + +#define DUAL_CHANNEL 2 + +#define ES83XX_ENABLE_DMIC BIT(4) +#define ES83XX_48_MHZ_MCLK BIT(5) + +struct acp3x_es83xx_private { + bool speaker_on; + bool headphone_on; + unsigned long quirk; + struct snd_soc_component *codec; + struct device *codec_dev; + struct gpio_desc *gpio_speakers, *gpio_headphone; + struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio; + struct acpi_gpio_mapping gpio_mapping[3]; + struct snd_soc_dapm_route mic_map[2]; +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256) +#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000) + +static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct acp3x_es83xx_private *priv; + unsigned int freq; + int ret; + + runtime = substream->runtime; + rtd = snd_soc_substream_to_rtd(substream); + codec_dai = snd_soc_rtd_to_codec(rtd, 0); + priv = get_mach_priv(rtd->card); + + if (priv->quirk & ES83XX_48_MHZ_MCLK) { + dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n"); + freq = ES83xx_48_MHZ_MCLK_FREQ; + } else { + dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n"); + freq = ES83xx_12288_KHZ_MCLK_FREQ; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + return 0; +} + +static struct snd_soc_jack es83xx_jack; + +static struct snd_soc_jack_pin es83xx_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), + + SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0, + acp3x_es83xx_headphone_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, + acp3x_es83xx_speaker_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +}; + +static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = { + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headphone", NULL, "Headphone Power"}, + + /* + * There is no separate speaker output instead the speakers are muxed to + * the HP outputs. The mux is controlled Speaker and/or headphone switch. + */ + {"Speaker", NULL, "HPOL"}, + {"Speaker", NULL, "HPOR"}, + {"Speaker", NULL, "Speaker Power"}, +}; + + +static const struct snd_kcontrol_new acp3x_es83xx_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), +}; + +static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card) +{ + card->dapm_widgets = acp3x_es83xx_widgets; + card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets); + card->controls = acp3x_es83xx_controls; + card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls); + card->dapm_routes = acp3x_es83xx_audio_map; + card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map); + + return 0; +} + +static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card); + + dev_dbg(priv->codec_dev, "headphone power event = %d\n", event); + if (SND_SOC_DAPM_EVENT_ON(event)) + priv->headphone_on = true; + else + priv->headphone_on = false; + + gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on); + gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on); + + return 0; +} + +static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card); + + dev_dbg(priv->codec_dev, "speaker power event: %d\n", event); + if (SND_SOC_DAPM_EVENT_ON(event)) + priv->speaker_on = true; + else + priv->speaker_on = false; + + gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on); + gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on); + + return 0; +} + +static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(card); + + /* We need to disable the jack in the machine driver suspend + * callback so that the CODEC suspend callback actually gets + * called. Without doing it, the CODEC suspend/resume + * callbacks do not get called if headphones are plugged in. + * This is because plugging in headphones keeps some supplies + * active, this in turn means that the lowest bias level + * that the CODEC can go to is SND_SOC_BIAS_STANDBY. + * If components do not set idle_bias_on to true then + * their suspend/resume callbacks do not get called. + */ + dev_dbg(priv->codec_dev, "card suspend\n"); + snd_soc_component_set_jack(priv->codec, NULL, NULL); + return 0; +} + +static int acp3x_es83xx_resume_post(struct snd_soc_card *card) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(card); + + /* We disabled jack detection in suspend callback, + * enable it back. + */ + dev_dbg(priv->codec_dev, "card resume\n"); + snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL); + return 0; +} + +static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) +{ + int ret = 0; + + priv->enable_spk_gpio.crs_entry_index = 0; + priv->enable_hp_gpio.crs_entry_index = 1; + + priv->enable_spk_gpio.active_low = false; + priv->enable_hp_gpio.active_low = false; + + priv->gpio_mapping[0].name = "speakers-enable-gpios"; + priv->gpio_mapping[0].data = &priv->enable_spk_gpio; + priv->gpio_mapping[0].size = 1; + priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; + + priv->gpio_mapping[1].name = "headphone-enable-gpios"; + priv->gpio_mapping[1].data = &priv->enable_hp_gpio; + priv->gpio_mapping[1].size = 1; + priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; + + dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n", + priv->enable_spk_gpio.crs_entry_index, + priv->enable_spk_gpio.active_low ? "low" : "high", + priv->enable_hp_gpio.crs_entry_index, + priv->enable_hp_gpio.active_low ? "low" : "high"); + return ret; +} + +static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv) +{ + int num_routes = 0; + int i; + + if (!(priv->quirk & ES83XX_ENABLE_DMIC)) { + priv->mic_map[num_routes].sink = "MIC1"; + priv->mic_map[num_routes].source = "Internal Mic"; + num_routes++; + } + + priv->mic_map[num_routes].sink = "MIC2"; + priv->mic_map[num_routes].source = "Headset Mic"; + num_routes++; + + for (i = 0; i < num_routes; i++) + dev_info(priv->codec_dev, "%s is %s\n", + priv->mic_map[i].source, priv->mic_map[i].sink); + + return num_routes; +} + +static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component; + struct snd_soc_card *card = runtime->card; + struct acp3x_es83xx_private *priv = get_mach_priv(card); + int ret = 0; + int num_routes; + + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &es83xx_jack, es83xx_jack_pins, + ARRAY_SIZE(es83xx_jack_pins)); + if (ret) { + dev_err(card->dev, "jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + + snd_soc_component_set_jack(codec, &es83xx_jack, NULL); + + priv->codec = codec; + acp3x_es83xx_configure_gpios(priv); + + ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping); + if (ret) + dev_warn(priv->codec_dev, "failed to add speaker gpio\n"); + + priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable", + priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_speakers)) { + dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n"); + return PTR_ERR(priv->gpio_speakers); + } + + priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable", + priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_headphone)) { + dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n"); + return PTR_ERR(priv->gpio_headphone); + } + + num_routes = acp3x_es83xx_configure_mics(priv); + if (num_routes > 0) { + ret = snd_soc_dapm_add_routes(&card->dapm, priv->mic_map, num_routes); + if (ret != 0) + device_remove_software_node(priv->codec_dev); + } + + return ret; +} + +static const struct snd_soc_ops acp3x_es83xx_ops = { + .startup = acp3x_es83xx_codec_startup, +}; + + +SND_SOC_DAILINK_DEF(codec, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi"))); + +static const struct dmi_system_id acp3x_es83xx_dmi_table[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + {} +}; + +static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->codecs = codec; + link->num_codecs = ARRAY_SIZE(codec); + link->init = acp3x_es83xx_init; + link->ops = &acp3x_es83xx_ops; + link->dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + return 0; +} + +static int acp3x_es83xx_probe(struct snd_soc_card *card) +{ + int ret = 0; + struct device *dev = card->dev; + const struct dmi_system_id *dmi_id; + + dmi_id = dmi_first_match(acp3x_es83xx_dmi_table); + if (dmi_id && dmi_id->driver_data) { + struct acp3x_es83xx_private *priv; + struct acp_card_drvdata *acp_drvdata; + struct acpi_device *adev; + struct device *codec_dev; + + acp_drvdata = (struct acp_card_drvdata *)card->drvdata; + + dev_info(dev, "matched DMI table with this system, trying to register sound card\n"); + + adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1); + if (!adev) { + dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id); + return -ENXIO; + } + + codec_dev = acpi_get_first_physical_node(adev); + acpi_dev_put(adev); + if (!codec_dev) { + dev_warn(dev, "Error cannot find codec device, will defer probe\n"); + return -EPROBE_DEFER; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + put_device(codec_dev); + return -ENOMEM; + } + + priv->codec_dev = codec_dev; + priv->quirk = (unsigned long)dmi_id->driver_data; + acp_drvdata->mach_priv = priv; + dev_info(dev, "successfully probed the sound card\n"); + } else { + ret = -ENODEV; + dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n"); + } + return ret; +} + + +void acp3x_es83xx_init_ops(struct acp_mach_ops *ops) +{ + ops->probe = acp3x_es83xx_probe; + ops->configure_widgets = acp3x_es83xx_configure_widgets; + ops->configure_link = acp3x_es83xx_configure_link; + ops->suspend_pre = acp3x_es83xx_suspend_pre; + ops->resume_post = acp3x_es83xx_resume_post; +} diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h new file mode 100644 index 000000000000..03551ffdd9da --- /dev/null +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 Marian Postevca <posteuca@mutex.one> + */ + +#ifndef __ACP3X_ES83XX_H +#define __ACP3X_ES83XX_H + +void acp3x_es83xx_init_ops(struct acp_mach_ops *ops); + +#endif + diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c new file mode 100644 index 000000000000..b871a216a6af --- /dev/null +++ b/sound/soc/amd/acp/acp63.c @@ -0,0 +1,322 @@ +// 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) 2023 Advanced Micro Devices, Inc. +// +// Authors: Syed Saba kareem <syed.sabakareem@amd.com> +/* + * Hardware interface for ACP6.3 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 <linux/pm_runtime.h> +#include <linux/pci.h> +#include "amd.h" +#include "acp-mach.h" +#include "../mach-config.h" + +#define DRV_NAME "acp_asoc_acp63" + +#define CLK_PLL_PWR_REQ_N0 0X0006C2C0 +#define CLK_SPLL_FIELD_2_N0 0X0006C114 +#define CLK_PLL_REQ_N0 0X0006C0DC +#define CLK_DFSBYPASS_CONTR 0X0006C2C8 +#define CLK_DFS_CNTL_N0 0X0006C1A4 + +#define PLL_AUTO_STOP_REQ BIT(4) +#define PLL_AUTO_START_REQ BIT(0) +#define PLL_FRANCE_EN BIT(4) +#define EXIT_DPF_BYPASS_0 BIT(16) +#define EXIT_DPF_BYPASS_1 BIT(17) +#define CLK0_DIVIDER 0X30 + +union clk_pll_req_no { + struct { + u32 fb_mult_int : 9; + u32 reserved : 3; + u32 pll_spine_div : 4; + u32 gb_mult_frac : 16; + } bitfields, bits; + u32 clk_pll_req_no_reg; +}; + +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_mach snd_soc_acpi_amd_acp63_acp_machines[] = { + { + .id = "AMDI0052", + .drv_name = "acp63-acp", + }, + {}, +}; + +static struct snd_soc_dai_driver acp63_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, +}, +{ + .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, +}, +{ + .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, +}, +{ + .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 acp63_i2s_master_clock_generate(struct acp_dev_data *adata) +{ + u32 data; + union clk_pll_req_no clk_pll; + struct pci_dev *smn_dev; + + smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x14E8, NULL); + if (!smn_dev) + return -ENODEV; + + /* Clk5 pll register values to get mclk as 196.6MHz*/ + clk_pll.bits.fb_mult_int = 0x31; + clk_pll.bits.pll_spine_div = 0; + clk_pll.bits.gb_mult_frac = 0x26E9; + + data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0); + smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ); + + data = smn_read(smn_dev, CLK_SPLL_FIELD_2_N0); + if (data & PLL_FRANCE_EN) + smn_write(smn_dev, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN); + + smn_write(smn_dev, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg); + + data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0); + smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ); + + data = smn_read(smn_dev, CLK_DFSBYPASS_CONTR); + smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0); + smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1); + + smn_write(smn_dev, CLK_DFS_CNTL_N0, CLK0_DIVIDER); + return 0; +} + +static int acp63_audio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acp_chip_info *chip; + struct acp_dev_data *adata; + struct resource *res; + int ret; + + chip = dev_get_platdata(&pdev->dev); + if (!chip || !chip->base) { + dev_err(&pdev->dev, "ACP chip data is NULL\n"); + return -ENODEV; + } + + if (chip->acp_rev != ACP63_DEV) { + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); + return -ENODEV; + } + + 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 = acp63_dai; + adata->num_dai = ARRAY_SIZE(acp63_dai); + adata->rsrc = &rsrc; + adata->platform = ACP63; + adata->flag = chip->flag; + adata->machines = snd_soc_acpi_amd_acp63_acp_machines; + acp_machine_select(adata); + dev_set_drvdata(dev, adata); + + if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + ret = acp63_i2s_master_clock_generate(adata); + if (ret) + return ret; + } + acp_enable_interrupts(adata); + acp_platform_register(dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; +} + +static void acp63_audio_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + + acp_disable_interrupts(adata); + acp_platform_unregister(dev); + pm_runtime_disable(&pdev->dev); +} + +static int __maybe_unused acp63_pcm_resume(struct device *dev) +{ + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_stream *stream; + struct snd_pcm_substream *substream; + snd_pcm_uframes_t buf_in_frames; + u64 buf_size; + + if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + acp63_i2s_master_clock_generate(adata); + + spin_lock(&adata->acp_lock); + list_for_each_entry(stream, &adata->stream_list, list) { + if (stream) { + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); + } + } + } + spin_unlock(&adata->acp_lock); + return 0; +} + +static const struct dev_pm_ops acp63_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume) +}; + +static struct platform_driver acp63_driver = { + .probe = acp63_audio_probe, + .remove_new = acp63_audio_remove, + .driver = { + .name = "acp_asoc_acp63", + .pm = &acp63_dma_pm_ops, + }, +}; + +module_platform_driver(acp63_driver); + +MODULE_DESCRIPTION("AMD ACP acp63 Driver"); +MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c new file mode 100644 index 000000000000..dd384c966ae9 --- /dev/null +++ b/sound/soc/amd/acp/acp70.c @@ -0,0 +1,254 @@ +// 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) 2023 Advanced Micro Devices, Inc. +// +// Authors: Syed Saba kareem <syed.sabakareem@amd.com> +/* + * Hardware interface for ACP7.0 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 <linux/pm_runtime.h> +#include <linux/pci.h> +#include "amd.h" +#include "acp-mach.h" + +#define DRV_NAME "acp_asoc_acp70" + +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_mach snd_soc_acpi_amd_acp70_acp_machines[] = { + { + .id = "AMDI0029", + .drv_name = "acp70-acp", + }, + {}, +}; + +static struct snd_soc_dai_driver acp70_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, +}, +{ + .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, +}, +{ + .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, +}, +{ + .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 acp_acp70_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 != ACP70_DEV) { + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); + return -ENODEV; + } + + 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 = acp70_dai; + adata->num_dai = ARRAY_SIZE(acp70_dai); + adata->rsrc = &rsrc; + adata->machines = snd_soc_acpi_amd_acp70_acp_machines; + adata->platform = ACP70; + adata->flag = chip->flag; + acp_machine_select(adata); + + dev_set_drvdata(dev, adata); + acp_enable_interrupts(adata); + acp_platform_register(dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; +} + +static void acp_acp70_audio_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + + acp_disable_interrupts(adata); + acp_platform_unregister(dev); + pm_runtime_disable(&pdev->dev); +} + +static int __maybe_unused acp70_pcm_resume(struct device *dev) +{ + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_stream *stream; + struct snd_pcm_substream *substream; + snd_pcm_uframes_t buf_in_frames; + u64 buf_size; + + spin_lock(&adata->acp_lock); + list_for_each_entry(stream, &adata->stream_list, list) { + if (stream) { + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); + } + } + } + spin_unlock(&adata->acp_lock); + return 0; +} + +static const struct dev_pm_ops acp70_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume) +}; + +static struct platform_driver acp70_driver = { + .probe = acp_acp70_audio_probe, + .remove_new = acp_acp70_audio_remove, + .driver = { + .name = "acp_asoc_acp70", + .pm = &acp70_dma_pm_ops, + }, +}; + +module_platform_driver(acp70_driver); + +MODULE_DESCRIPTION("AMD ACP ACP70 Driver"); +MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index d6cfae6ec5f7..5017e868f39b 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -20,6 +20,8 @@ #define ACP3X_DEV 3 #define ACP6X_DEV 6 +#define ACP63_DEV 0x63 +#define ACP70_DEV 0x70 #define DMIC_INSTANCE 0x00 #define I2S_SP_INSTANCE 0x01 @@ -95,9 +97,15 @@ #define ACP6X_PGFSM_CONTROL 0x1024 #define ACP6X_PGFSM_STATUS 0x1028 +#define ACP63_PGFSM_CONTROL ACP6X_PGFSM_CONTROL +#define ACP63_PGFSM_STATUS ACP6X_PGFSM_STATUS + +#define ACP70_PGFSM_CONTROL ACP6X_PGFSM_CONTROL +#define ACP70_PGFSM_STATUS ACP6X_PGFSM_STATUS + #define ACP_SOFT_RST_DONE_MASK 0x00010001 -#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0xffffffff #define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 #define ACP_PGFSM_STATUS_MASK 0x03 #define ACP_POWERED_ON 0x00 @@ -129,6 +137,7 @@ struct acp_chip_info { unsigned int acp_rev; /* ACP Revision id */ void __iomem *base; /* ACP memory PCI base */ struct platform_device *chip_pdev; + unsigned int flag; /* Distinguish b/w Legacy or Only PDM */ }; struct acp_stream { @@ -182,17 +191,27 @@ struct acp_dev_data { u32 tdm_rx_fmt[3]; u32 xfer_tx_resolution[3]; u32 xfer_rx_resolution[3]; + unsigned int flag; + unsigned int platform; }; -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; +enum acp_config { + ACP_CONFIG_0 = 0, + ACP_CONFIG_1, + ACP_CONFIG_2, + ACP_CONFIG_3, + ACP_CONFIG_4, + ACP_CONFIG_5, + ACP_CONFIG_6, + ACP_CONFIG_7, + ACP_CONFIG_8, + ACP_CONFIG_9, + ACP_CONFIG_10, + ACP_CONFIG_11, + ACP_CONFIG_12, + ACP_CONFIG_13, + ACP_CONFIG_14, + ACP_CONFIG_15, }; extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; @@ -207,7 +226,7 @@ int smn_read(struct pci_dev *dev, u32 smn_addr); int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data); int acp_init(struct acp_chip_info *chip); -int acp_deinit(void __iomem *base); +int acp_deinit(struct acp_chip_info *chip); void acp_enable_interrupts(struct acp_dev_data *adata); void acp_disable_interrupts(struct acp_dev_data *adata); /* Machine configuration */ @@ -221,6 +240,8 @@ void restore_acp_pdm_params(struct snd_pcm_substream *substream, int restore_acp_i2s_params(struct snd_pcm_substream *substream, struct acp_dev_data *adata, struct acp_stream *stream); +int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip); + static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction) { u64 byte_count = 0, low = 0, high = 0; @@ -272,32 +293,4 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int POINTER_RETURN_BYTES: 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 diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h index ce3948e0679c..cfd6c4d07594 100644 --- a/sound/soc/amd/acp/chip_offset_byte.h +++ b/sound/soc/amd/acp/chip_offset_byte.h @@ -19,6 +19,7 @@ #define ACP_PGFSM_STATUS 0x1420 #define ACP_SOFT_RESET 0x1000 #define ACP_CONTROL 0x1004 +#define ACP_PIN_CONFIG 0x1440 #define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \ (adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04)) |