diff options
author | Linus Torvalds | 2023-06-29 10:46:47 -0700 |
---|---|---|
committer | Linus Torvalds | 2023-06-29 10:46:47 -0700 |
commit | f8824e151fbfa0ac0a258015d606ea6f4a10251b (patch) | |
tree | 805b029bf3a550168d66ac9f1f5e9aa6f2a68b84 /sound/soc | |
parent | 86e203edf24bb327ce8fcd3c5c8c6bf530a846df (diff) | |
parent | d6048fdc870240e5020343f8af0c825829c232bd (diff) |
Merge tag 'sound-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Lots of changes as usual, but the only significant stuff in ALSA core
part is the MIDI 2.0 support, while ASoC core kept receiving the code
refactoring. The majority of changes are seen rather in device
drivers, and quite a few new drivers can be found there.
Here we go, some highlights:
ALSA and ASoC Core:
- Support of MIDI 2.0 devices: rawmidi and sequencer API have been
extended for the support of the new UMP (Universal MIDI Packet)
protocol, USB audio driver got the USB MIDI 2.0 interface support
- Continued refactoring around ASoC DAI links and the ordering of
trigger callbacks
- PCM ABI extension for better drain support
ASoC Drivers:
- Conversions of many drivers to use maple tree based caches
- Everlasting improvement works on ASoC Intel drivers
- Compressed audio support for Qualcomm
- Support for AMD SoundWire, Analog Devices SSM3515, Google
Chameleon, Ingenic X1000, Intel systems with various CODECs,
Loongson platforms, Maxim MAX98388, Mediatek MT8188, Nuvoton
NAU8825C, NXP platforms with NAU8822, Qualcomm WSA884x, StarFive
JH7110, Texas Instruments TAS2781
HD-audio:
- Quirks for HP and ASUS machines
- CS35L41 HD-audio codec fixes
- Loongson HD-audio support
Misc:
- A new virtual PCM test driver for kselftests
- Continued refactoring and improvements on the legacy emu10k1
driver"
* tag 'sound-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (556 commits)
ALSA: hda/realtek: Enable mute/micmute LEDs and limit mic boost on EliteBook
ASoC: hdmi-codec: fix channel info for compressed formats
ALSA: pcm: fix ELD constraints for (E)AC3, DTS(-HD) and MLP formats
ASoC: core: Always store of_node when getting DAI link component
ASoC: tas2781: Fix error code in tas2781_load_calibration()
ASoC: amd: update pm_runtime enable sequence
ALSA: ump: Export MIDI1 / UMP conversion helpers
ASoC: tas2781: fix Kconfig dependencies
ASoC: amd: acp: remove acp poweroff function
ASoC: amd: acp: clear pdm dma interrupt mask
ASoC: codecs: max98090: Allow dsp_a mode
ASoC: qcom: common: add default jack dapm pins
ASoC: loongson: fix address space confusion
ASoC: dt-bindings: microchip,sama7g5-pdmc: Simplify "microchip,mic-pos" constraints
ASoC: tegra: Remove stale comments in AHUB
ASoC: tegra: Use normal system sleep for ASRC
ALSA: hda/realtek: Add quirks for ROG ALLY CS35l41 audio
ASoC: fsl-asoc-card: Allow passing the number of slots in use
ASoC: codecs: wsa884x: Add WSA884x family of speakers
ASoC: dt-bindings: qcom,wsa8840: Add WSA884x family of speakers
...
Diffstat (limited to 'sound/soc')
438 files changed, 19770 insertions, 3461 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 848fbae26c3b..bfa9622e1ab1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -75,9 +75,11 @@ source "sound/soc/bcm/Kconfig" source "sound/soc/cirrus/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/google/Kconfig" source "sound/soc/hisilicon/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/kirkwood/Kconfig" +source "sound/soc/loongson/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" @@ -91,6 +93,7 @@ source "sound/soc/sh/Kconfig" source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" +source "sound/soc/starfive/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 507eaed1d6a1..8376fdb217ed 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -43,8 +43,10 @@ obj-$(CONFIG_SND_SOC) += bcm/ obj-$(CONFIG_SND_SOC) += cirrus/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ +obj-$(CONFIG_SND_SOC) += google/ obj-$(CONFIG_SND_SOC) += hisilicon/ obj-$(CONFIG_SND_SOC) += jz4740/ +obj-$(CONFIG_SND_SOC) += loongson/ obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ @@ -59,6 +61,7 @@ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sprd/ +obj-$(CONFIG_SND_SOC) += starfive/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 08e42082f5e9..1dd8579e8034 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -71,6 +71,7 @@ config SND_SOC_AMD_RENOIR_MACH config SND_SOC_AMD_ACP5x tristate "AMD Audio Coprocessor-v5.x I2S support" depends on X86 && PCI + select SND_AMD_ACP_CONFIG help This option enables ACP v5.x support on AMD platform @@ -81,6 +82,7 @@ config SND_SOC_AMD_VANGOGH_MACH tristate "AMD Vangogh support for NAU8821 CS35L41" select SND_SOC_NAU8821 select SND_SOC_CS35L41_SPI + select SND_AMD_ACP_CONFIG depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER help This option enables machine driver for Vangogh platform @@ -136,7 +138,8 @@ config SND_SOC_AMD_PS help This option enables Audio Coprocessor i.e ACP v6.3 support on AMD Pink sardine platform. By enabling this flag build will be - triggered for ACP PCI driver, ACP PDM DMA driver. + triggered for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire + DMA driver. Say m if you have such a device. If unsure select "N". diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 375417bd7d6e..7464ca2b596c 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -524,7 +524,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { | SND_SOC_DAIFMT_CBP_CFP, .init = cz_da7219_init, .dpcm_playback = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_da7219_play_ops, SND_SOC_DAILINK_REG(designware1, dlgs, platform), }, @@ -534,7 +534,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_da7219_cap_ops, SND_SOC_DAILINK_REG(designware2, dlgs, platform), }, @@ -544,7 +544,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_max_play_ops, SND_SOC_DAILINK_REG(designware3, mx, platform), }, @@ -555,7 +555,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_dmic0_cap_ops, SND_SOC_DAILINK_REG(designware3, adau, platform), }, @@ -566,7 +566,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_dmic1_cap_ops, SND_SOC_DAILINK_REG(designware2, adau, platform), }, @@ -580,7 +580,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { | SND_SOC_DAIFMT_CBP_CFP, .init = cz_rt5682_init, .dpcm_playback = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_rt5682_play_ops, SND_SOC_DAILINK_REG(designware1, rt5682, platform), }, @@ -590,7 +590,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_rt5682_cap_ops, SND_SOC_DAILINK_REG(designware2, rt5682, platform), }, @@ -600,7 +600,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_rt5682_max_play_ops, SND_SOC_DAILINK_REG(designware3, mx, platform), }, @@ -611,7 +611,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_rt5682_dmic0_cap_ops, SND_SOC_DAILINK_REG(designware3, adau, platform), }, @@ -622,7 +622,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .ops = &cz_rt5682_dmic1_cap_ops, SND_SOC_DAILINK_REG(designware2, adau, platform), }, diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c index 89499542c803..5e56d3a53be7 100644 --- a/sound/soc/amd/acp-es8336.c +++ b/sound/soc/amd/acp-es8336.c @@ -149,7 +149,7 @@ static struct snd_soc_dai_link st_dai_es8336[] = { .stream_name = "ES8336 HiFi Play", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, - .stop_dma_first = 1, + .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC, .dpcm_capture = 1, .dpcm_playback = 1, .init = st_es8336_init, diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index b4dcce4fbae9..6da17140beea 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -795,13 +795,6 @@ SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); /* Declare ACP CPU components */ -static struct snd_soc_dai_link_component dummy_codec[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - static struct snd_soc_dai_link_component platform_component[] = { { .name = "acp_asoc_renoir.0", @@ -912,8 +905,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->hs_codec_id == RT5682) { links[i].codecs = rt5682; @@ -943,8 +936,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->hs_codec_id == NAU8825) { links[i].codecs = nau8825; @@ -973,8 +966,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->amp_codec_id == RT1019) { links[i].codecs = rt1019; @@ -1005,8 +998,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->amp_codec_id == MAX98360A) { links[i].codecs = max98360a; @@ -1076,8 +1069,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->hs_codec_id == RT5682) { links[i].codecs = rt5682; @@ -1110,8 +1103,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->hs_codec_id == NAU8825) { links[i].codecs = nau8825; @@ -1138,8 +1131,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->amp_codec_id == RT1019) { links[i].codecs = rt1019; @@ -1173,8 +1166,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } if (drv_data->amp_codec_id == MAX98360A) { links[i].codecs = max98360a; @@ -1201,8 +1194,8 @@ 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 = dummy_codec; - links[i].num_codecs = ARRAY_SIZE(dummy_codec); + links[i].codecs = &asoc_dummy_dlc; + links[i].num_codecs = 1; } links[i].cpus = pdm_dmic; links[i].num_cpus = ARRAY_SIZE(pdm_dmic); diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index a0c84cd07fde..8154fbfd1229 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -15,7 +15,6 @@ #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/module.h> #include "amd.h" diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index 66ec6b6a5972..f8030b79ac17 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -176,7 +176,7 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream, /* Disable DMIC interrupts */ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK; writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); } diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index 447612a7a762..f220378ec20e 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -18,7 +18,6 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dai.h> -#include <linux/pm_runtime.h> #include <linux/dma-mapping.h> #include "amd.h" diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 5c455cc04113..1b997837c7d8 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -204,23 +204,6 @@ static int acp6x_power_on(void __iomem *base) 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; @@ -299,14 +282,6 @@ static int rmb_acp_deinit(void __iomem *base) } 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; } diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index b3cbc7f19ec5..f188365fe214 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -169,17 +169,6 @@ static int acp3x_power_on(void __iomem *base) return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); } -static int acp3x_power_off(void __iomem *base) -{ - u32 val; - - writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); - - return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, - (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF, - DELAY_US, ACP_TIMEOUT); -} - static int acp3x_reset(void __iomem *base) { u32 val; @@ -246,12 +235,6 @@ static int rn_acp_deinit(void __iomem *base) return ret; writel(0x00, base + ACP_CONTROL); - - /* power off */ - ret = acp3x_power_off(base); - if (ret) - return ret; - return 0; } static int renoir_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile index 383973a12f6a..f2a5eaf2fa4d 100644 --- a/sound/soc/amd/ps/Makefile +++ b/sound/soc/amd/ps/Makefile @@ -3,7 +3,9 @@ snd-pci-ps-objs := pci-ps.o snd-ps-pdm-dma-objs := ps-pdm-dma.o snd-soc-ps-mach-objs := ps-mach.o +snd-ps-sdw-dma-objs := ps-sdw-dma.o obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o +obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o obj-$(CONFIG_SND_SOC_AMD_PS_MACH) += snd-soc-ps-mach.o diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index dd36790b25ae..e96e6dc9d90f 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -2,7 +2,7 @@ /* * AMD ALSA SoC PDM Driver * - * Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved. */ #include <sound/acp63_chip_offset_byte.h> @@ -10,7 +10,7 @@ #define ACP_DEVICE_ID 0x15E2 #define ACP63_REG_START 0x1240000 #define ACP63_REG_END 0x1250200 -#define ACP63_DEVS 3 +#define ACP63_DEVS 5 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP_PGFSM_CNTL_POWER_ON_MASK 1 @@ -53,11 +53,99 @@ /* time in ms for runtime suspend delay */ #define ACP_SUSPEND_DELAY_MS 2000 -#define ACP63_DMIC_ADDR 2 -#define ACP63_PDM_MODE_DEVS 3 -#define ACP63_PDM_DEV_MASK 1 #define ACP_DMIC_DEV 2 +/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */ +#define ACP63_PDM_MODE_DEVS 3 + +/* + * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for + * SW0 SoundWire manager instance configuration + */ +#define ACP63_SDW0_MODE_DEVS 2 + +/* + * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager + * instances configuration + */ +#define ACP63_SDW0_SDW1_MODE_DEVS 3 + +/* + * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager + * instance + ACP PDM controller configuration + */ +#define ACP63_SDW0_PDM_MODE_DEVS 4 + +/* + * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for + * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration + */ +#define ACP63_SDW0_SDW1_PDM_MODE_DEVS 5 +#define ACP63_DMIC_ADDR 2 +#define ACP63_SDW_ADDR 5 +#define AMD_SDW_MAX_MANAGERS 2 + +/* time in ms for acp timeout */ +#define ACP_TIMEOUT 500 + +/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */ +#define ACP63_PDM_DEV_CONFIG BIT(0) + +/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */ +#define ACP63_SDW_DEV_CONFIG BIT(1) + +/* + * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire + * manager instance combination. + */ +#define ACP63_SDW_PDM_DEV_CONFIG GENMASK(1, 0) +#define ACP_SDW0_STAT BIT(21) +#define ACP_SDW1_STAT BIT(2) +#define ACP_ERROR_IRQ BIT(29) + +#define ACP_AUDIO0_TX_THRESHOLD 0x1c +#define ACP_AUDIO1_TX_THRESHOLD 0x1a +#define ACP_AUDIO2_TX_THRESHOLD 0x18 +#define ACP_AUDIO0_RX_THRESHOLD 0x1b +#define ACP_AUDIO1_RX_THRESHOLD 0x19 +#define ACP_AUDIO2_RX_THRESHOLD 0x17 +#define ACP_P1_AUDIO1_TX_THRESHOLD BIT(6) +#define ACP_P1_AUDIO1_RX_THRESHOLD BIT(5) +#define ACP_SDW_DMA_IRQ_MASK 0x1F800000 +#define ACP_P1_SDW_DMA_IRQ_MASK 0x60 +#define ACP63_SDW0_DMA_MAX_STREAMS 6 +#define ACP63_SDW1_DMA_MAX_STREAMS 2 +#define ACP_P1_AUDIO_TX_THRESHOLD 6 +#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i))) +#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * (i))) +#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO_TX_THRESHOLD - (i)) + +#define ACP_DELAY_US 5 +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024) +#define SDW0_MEM_WINDOW_START 0x4800000 +#define ACP_SDW_SRAM_PTE_OFFSET 0x03800400 +#define SDW0_PTE_OFFSET 0x400 +#define SDW_FIFO_SIZE 0x100 +#define SDW_DMA_SIZE 0x40 +#define ACP_SDW0_FIFO_OFFSET 0x100 +#define ACP_SDW_PTE_OFFSET 0x100 +#define SDW_FIFO_OFFSET 0x100 +#define SDW_PTE_OFFSET(i) (SDW0_PTE_OFFSET + ((i) * 0x600)) +#define ACP_SDW_FIFO_OFFSET(i) (ACP_SDW0_FIFO_OFFSET + ((i) * 0x500)) +#define SDW_MEM_WINDOW_START(i) (SDW0_MEM_WINDOW_START + ((i) * 0xC0000)) + +#define SDW_PLAYBACK_MIN_NUM_PERIODS 2 +#define SDW_PLAYBACK_MAX_NUM_PERIODS 8 +#define SDW_PLAYBACK_MAX_PERIOD_SIZE 8192 +#define SDW_PLAYBACK_MIN_PERIOD_SIZE 1024 +#define SDW_CAPTURE_MIN_NUM_PERIODS 2 +#define SDW_CAPTURE_MAX_NUM_PERIODS 8 +#define SDW_CAPTURE_MAX_PERIOD_SIZE 8192 +#define SDW_CAPTURE_MIN_PERIOD_SIZE 1024 + +#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS) +#define SDW_MIN_BUFFER SDW_MAX_BUFFER + enum acp_config { ACP_CONFIG_0 = 0, ACP_CONFIG_1, @@ -77,6 +165,20 @@ enum acp_config { ACP_CONFIG_15, }; +enum amd_sdw0_channel { + ACP_SDW0_AUDIO0_TX = 0, + ACP_SDW0_AUDIO1_TX, + ACP_SDW0_AUDIO2_TX, + ACP_SDW0_AUDIO0_RX, + ACP_SDW0_AUDIO1_RX, + ACP_SDW0_AUDIO2_RX, +}; + +enum amd_sdw1_channel { + ACP_SDW1_AUDIO1_TX, + ACP_SDW1_AUDIO1_RX, +}; + struct pdm_stream_instance { u16 num_pages; u16 channels; @@ -92,24 +194,77 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; }; -static inline u32 acp63_readl(void __iomem *base_addr) -{ - return readl(base_addr); -} +struct sdw_dma_dev_data { + void __iomem *acp_base; + struct mutex *acp_lock; /* used to protect acp common register access */ + struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS]; + struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS]; +}; -static inline void acp63_writel(u32 val, void __iomem *base_addr) -{ - writel(val, base_addr); -} +struct acp_sdw_dma_stream { + u16 num_pages; + u16 channels; + u32 stream_id; + u32 instance; + dma_addr_t dma_addr; + u64 bytescount; +}; + +union acp_sdw_dma_count { + struct { + u32 low; + u32 high; + } bcount; + u64 bytescount; +}; + +struct sdw_dma_ring_buf_reg { + u32 reg_dma_size; + u32 reg_fifo_addr; + u32 reg_fifo_size; + u32 reg_ring_buf_size; + u32 reg_ring_buf_addr; + u32 water_mark_size_reg; + u32 pos_low_reg; + u32 pos_high_reg; +}; + +/** + * struct acp63_dev_data - acp pci driver context + * @acp63_base: acp mmio base + * @res: resource + * @pdev: array of child platform device node structures + * @acp_lock: used to protect acp common registers + * @sdw_fw_node: SoundWire controller fw node handle + * @pdev_config: platform device configuration + * @pdev_count: platform devices count + * @pdm_dev_index: pdm platform device index + * @sdw_manager_count: SoundWire manager instance count + * @sdw0_dev_index: SoundWire Manager-0 platform device index + * @sdw1_dev_index: SoundWire Manager-1 platform device index + * @sdw_dma_dev_index: SoundWire DMA controller platform device index + * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance + * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance + * @acp_reset: flag set to true when bus reset is applied across all + * the active SoundWire manager instances + */ struct acp63_dev_data { void __iomem *acp63_base; struct resource *res; struct platform_device *pdev[ACP63_DEVS]; struct mutex acp_lock; /* protect shared registers */ - u16 pdev_mask; + struct fwnode_handle *sdw_fw_node; + u16 pdev_config; u16 pdev_count; u16 pdm_dev_index; + u8 sdw_manager_count; + u16 sdw0_dev_index; + u16 sdw1_dev_index; + u16 sdw_dma_dev_index; + u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; + u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; + bool acp_reset; }; int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index b1337b96ea8d..5b46dc8573f8 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -6,6 +6,7 @@ */ #include <linux/pci.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> @@ -14,66 +15,55 @@ #include <linux/interrupt.h> #include <sound/pcm_params.h> #include <linux/pm_runtime.h> +#include <linux/iopoll.h> +#include <linux/soundwire/sdw_amd.h> #include "acp63.h" static int acp63_power_on(void __iomem *acp_base) { u32 val; - int timeout; - val = acp63_readl(acp_base + ACP_PGFSM_STATUS); + val = readl(acp_base + ACP_PGFSM_STATUS); if (!val) return val; if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) - acp63_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); - timeout = 0; - while (++timeout < 500) { - val = acp63_readl(acp_base + ACP_PGFSM_STATUS); - if (!val) - return 0; - udelay(1); - } - return -ETIMEDOUT; + writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); + + return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); } static int acp63_reset(void __iomem *acp_base) { u32 val; - int timeout; - - acp63_writel(1, acp_base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = acp63_readl(acp_base + ACP_SOFT_RESET); - if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) - break; - cpu_relax(); - } - acp63_writel(0, acp_base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = acp63_readl(acp_base + ACP_SOFT_RESET); - if (!val) - return 0; - cpu_relax(); - } - return -ETIMEDOUT; + int ret; + + writel(1, acp_base + ACP_SOFT_RESET); + + ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, + val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK, + DELAY_US, ACP_TIMEOUT); + if (ret) + return ret; + + writel(0, acp_base + ACP_SOFT_RESET); + + return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); } static void acp63_enable_interrupts(void __iomem *acp_base) { - acp63_writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL); } static void acp63_disable_interrupts(void __iomem *acp_base) { - acp63_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + - ACP_EXTERNAL_INTR_STAT); - acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); - acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT); + writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); + writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); } static int acp63_init(void __iomem *acp_base, struct device *dev) @@ -85,7 +75,7 @@ static int acp63_init(void __iomem *acp_base, struct device *dev) dev_err(dev, "ACP power on failed\n"); return ret; } - acp63_writel(0x01, acp_base + ACP_CONTROL); + writel(0x01, acp_base + ACP_CONTROL); ret = acp63_reset(acp_base); if (ret) { dev_err(dev, "ACP reset failed\n"); @@ -105,64 +95,306 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev) dev_err(dev, "ACP reset failed\n"); return ret; } - acp63_writel(0, acp_base + ACP_CONTROL); + writel(0, acp_base + ACP_CONTROL); return 0; } +static irqreturn_t acp63_irq_thread(int irq, void *context) +{ + struct sdw_dma_dev_data *sdw_dma_data; + struct acp63_dev_data *adata = context; + u32 stream_index; + u16 pdev_index; + + pdev_index = adata->sdw_dma_dev_index; + sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + + for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) { + if (adata->sdw0_dma_intr_stat[stream_index]) { + if (sdw_dma_data->sdw0_dma_stream[stream_index]) + snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]); + adata->sdw0_dma_intr_stat[stream_index] = 0; + } + } + for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) { + if (adata->sdw1_dma_intr_stat[stream_index]) { + if (sdw_dma_data->sdw1_dma_stream[stream_index]) + snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]); + adata->sdw1_dma_intr_stat[stream_index] = 0; + } + } + return IRQ_HANDLED; +} + static irqreturn_t acp63_irq_handler(int irq, void *dev_id) { struct acp63_dev_data *adata; struct pdm_dev_data *ps_pdm_data; - u32 val; + struct amd_sdw_manager *amd_manager; + u32 ext_intr_stat, ext_intr_stat1; + u32 stream_id = 0; + u16 irq_flag = 0; + u16 sdw_dma_irq_flag = 0; u16 pdev_index; + u16 index; adata = dev_id; if (!adata) return IRQ_NONE; + /* ACP interrupts will be cleared by reading particular bit and writing + * same value to the status register. writing zero's doesn't have any + * effect. + * Bit by bit checking of IRQ field is implemented. + */ + ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + if (ext_intr_stat & ACP_SDW0_STAT) { + writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + pdev_index = adata->sdw0_dev_index; + amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + + ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + if (ext_intr_stat1 & ACP_SDW1_STAT) { + writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + pdev_index = adata->sdw1_dev_index; + amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + + if (ext_intr_stat & ACP_ERROR_IRQ) { + writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + /* TODO: Report SoundWire Manager instance errors */ + writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON); + writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON); + writel(0, adata->acp63_base + ACP_ERROR_STATUS); + irq_flag = 1; + } - val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); - if (val & BIT(PDM_DMA_STAT)) { + if (ext_intr_stat & BIT(PDM_DMA_STAT)) { pdev_index = adata->pdm_dev_index; ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); - acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ps_pdm_data->capture_stream) snd_pcm_period_elapsed(ps_pdm_data->capture_stream); + irq_flag = 1; + } + if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) { + for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) { + if (ext_intr_stat & BIT(index)) { + writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + switch (index) { + case ACP_AUDIO0_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO0_TX; + break; + case ACP_AUDIO1_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO1_TX; + break; + case ACP_AUDIO2_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO2_TX; + break; + case ACP_AUDIO0_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO0_RX; + break; + case ACP_AUDIO1_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO1_RX; + break; + case ACP_AUDIO2_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO2_RX; + break; + } + + adata->sdw0_dma_intr_stat[stream_id] = 1; + sdw_dma_irq_flag = 1; + } + } + } + + if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) { + writel(ACP_P1_AUDIO1_RX_THRESHOLD, + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1; + sdw_dma_irq_flag = 1; + } + + if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) { + writel(ACP_P1_AUDIO1_TX_THRESHOLD, + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1; + sdw_dma_irq_flag = 1; + } + + if (sdw_dma_irq_flag) + return IRQ_WAKE_THREAD; + + if (irq_flag) return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static int sdw_amd_scan_controller(struct device *dev) +{ + struct acp63_dev_data *acp_data; + struct fwnode_handle *link; + char name[32]; + u32 sdw_manager_bitmap; + u8 count = 0; + u32 acp_sdw_power_mode = 0; + int index; + int ret; + + acp_data = dev_get_drvdata(dev); + /* + * Current implementation is based on MIPI DisCo 2.0 spec. + * Found controller, find links supported. + */ + ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list", + &sdw_manager_bitmap, 1); + + if (ret) { + dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret); + return -EINVAL; + } + count = hweight32(sdw_manager_bitmap); + /* Check count is within bounds */ + if (count > AMD_SDW_MAX_MANAGERS) { + dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS); + return -EINVAL; + } + + if (!count) { + dev_dbg(dev, "No SoundWire Managers detected\n"); + return -EINVAL; + } + dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count); + acp_data->sdw_manager_count = count; + for (index = 0; index < count; index++) { + snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index); + link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name); + if (!link) { + dev_err(dev, "Manager node %s not found\n", name); + return -EIO; + } + + ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode); + if (ret) + return ret; + /* + * when SoundWire configuration is selected from acp pin config, + * based on manager instances count, acp init/de-init sequence should be + * executed as part of PM ops only when Bus reset is applied for the active + * SoundWire manager instances. + */ + if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) { + acp_data->acp_reset = false; + return 0; + } } - return IRQ_NONE; + return 0; } -static void get_acp63_device_config(u32 config, struct pci_dev *pci, - struct acp63_dev_data *acp_data) +static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data) { struct acpi_device *dmic_dev; + struct acpi_device *sdw_dev; const union acpi_object *obj; bool is_dmic_dev = false; + bool is_sdw_dev = false; + int ret; dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); if (dmic_dev) { + /* is_dmic_dev flag will be set when ACP PDM controller device exists */ if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type", ACPI_TYPE_INTEGER, &obj) && obj->integer.value == ACP_DMIC_DEV) is_dmic_dev = true; } + sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0); + if (sdw_dev) { + acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev); + ret = sdw_amd_scan_controller(&pci->dev); + /* is_sdw_dev flag will be set when SoundWire Manager device exists */ + if (!ret) + is_sdw_dev = true; + } + if (!is_dmic_dev && !is_sdw_dev) + return -ENODEV; + dev_dbg(&pci->dev, "Audio Mode %d\n", config); switch (config) { - case ACP_CONFIG_0: - case ACP_CONFIG_1: + case ACP_CONFIG_4: + case ACP_CONFIG_5: + case ACP_CONFIG_10: + case ACP_CONFIG_11: + if (is_dmic_dev) { + acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; + acp_data->pdev_count = ACP63_PDM_MODE_DEVS; + } + break; case ACP_CONFIG_2: case ACP_CONFIG_3: - case ACP_CONFIG_9: - case ACP_CONFIG_15: - dev_dbg(&pci->dev, "Audio Mode %d\n", config); + if (is_sdw_dev) { + switch (acp_data->sdw_manager_count) { + case 1: + acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; + break; + case 2: + acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; + break; + default: + return -EINVAL; + } + } break; - default: - if (is_dmic_dev) { - acp_data->pdev_mask = ACP63_PDM_DEV_MASK; + case ACP_CONFIG_6: + case ACP_CONFIG_7: + case ACP_CONFIG_12: + case ACP_CONFIG_8: + case ACP_CONFIG_13: + case ACP_CONFIG_14: + if (is_dmic_dev && is_sdw_dev) { + switch (acp_data->sdw_manager_count) { + case 1: + acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS; + break; + case 2: + acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS; + break; + default: + return -EINVAL; + } + } else if (is_dmic_dev) { + acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; acp_data->pdev_count = ACP63_PDM_MODE_DEVS; + } else if (is_sdw_dev) { + switch (acp_data->sdw_manager_count) { + case 1: + acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; + break; + case 2: + acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; + acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; + break; + default: + return -EINVAL; + } } break; + default: + break; } + return 0; } static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, @@ -186,6 +418,7 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr) { + struct acp_sdw_pdata *sdw_pdata; struct platform_device_info pdevinfo[ACP63_DEVS]; struct device *parent; int index; @@ -193,9 +426,9 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data parent = &pci->dev; dev_dbg(&pci->dev, - "%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask, + "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config, adata->pdev_count); - if (adata->pdev_mask) { + if (adata->pdev_config) { adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); if (!adata->res) { ret = -ENOMEM; @@ -207,8 +440,8 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data memset(&pdevinfo, 0, sizeof(pdevinfo)); } - switch (adata->pdev_mask) { - case ACP63_PDM_DEV_MASK: + switch (adata->pdev_config) { + case ACP63_PDM_DEV_CONFIG: adata->pdm_dev_index = 0; acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", 0, adata->res, 1, NULL, 0); @@ -217,8 +450,104 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach", 0, NULL, 0, NULL, 0); break; + case ACP63_SDW_DEV_CONFIG: + if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) { + sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), + GFP_KERNEL); + if (!sdw_pdata) { + ret = -ENOMEM; + goto de_init; + } + + sdw_pdata->instance = 0; + sdw_pdata->acp_sdw_lock = &adata->acp_lock; + adata->sdw0_dev_index = 0; + adata->sdw_dma_dev_index = 1; + acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, + "amd_sdw_manager", 0, adata->res, 1, + sdw_pdata, sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); + } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) { + sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, + GFP_KERNEL); + if (!sdw_pdata) { + ret = -ENOMEM; + goto de_init; + } + + sdw_pdata[0].instance = 0; + sdw_pdata[1].instance = 1; + sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; + sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; + sdw_pdata->acp_sdw_lock = &adata->acp_lock; + adata->sdw0_dev_index = 0; + adata->sdw1_dev_index = 1; + adata->sdw_dma_dev_index = 2; + acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, + "amd_sdw_manager", 0, adata->res, 1, + &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, + "amd_sdw_manager", 1, adata->res, 1, + &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); + } + break; + case ACP63_SDW_PDM_DEV_CONFIG: + if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) { + sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), + GFP_KERNEL); + if (!sdw_pdata) { + ret = -ENOMEM; + goto de_init; + } + + sdw_pdata->instance = 0; + sdw_pdata->acp_sdw_lock = &adata->acp_lock; + adata->pdm_dev_index = 0; + adata->sdw0_dev_index = 1; + adata->sdw_dma_dev_index = 2; + acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", + 0, adata->res, 1, NULL, 0); + acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, + "amd_sdw_manager", 0, adata->res, 1, + sdw_pdata, sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); + acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec", + 0, NULL, 0, NULL, 0); + } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) { + sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, + GFP_KERNEL); + if (!sdw_pdata) { + ret = -ENOMEM; + goto de_init; + } + sdw_pdata[0].instance = 0; + sdw_pdata[1].instance = 1; + sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; + sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; + adata->pdm_dev_index = 0; + adata->sdw0_dev_index = 1; + adata->sdw1_dev_index = 2; + adata->sdw_dma_dev_index = 3; + acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", + 0, adata->res, 1, NULL, 0); + acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, + "amd_sdw_manager", 0, adata->res, 1, + &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node, + "amd_sdw_manager", 1, adata->res, 1, + &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); + acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); + acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec", + 0, NULL, 0, NULL, 0); + } + break; default: - dev_dbg(&pci->dev, "No PDM devices found\n"); + dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); return 0; } @@ -289,25 +618,38 @@ static int snd_acp63_probe(struct pci_dev *pci, ret = -ENOMEM; goto release_regions; } + /* + * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init() + * will be invoked for all ACP configurations during suspend/resume callbacks. + * This flag should be set to false only when SoundWire manager power mode + * set to ClockStopMode. + */ + adata->acp_reset = true; pci_set_master(pci); pci_set_drvdata(pci, adata); mutex_init(&adata->acp_lock); ret = acp63_init(adata->acp63_base, &pci->dev); if (ret) goto release_regions; - ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler, - irqflags, "ACP_PCI_IRQ", adata); + ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler, + acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata); if (ret) { dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); goto de_init; } - val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG); - get_acp63_device_config(val, pci, adata); + val = readl(adata->acp63_base + ACP_PIN_CONFIG); + ret = get_acp63_device_config(val, pci, adata); + /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */ + if (ret) { + dev_err(&pci->dev, "get acp device config failed:%d\n", ret); + goto skip_pdev_creation; + } ret = create_acp63_platform_devs(pci, adata, addr); if (ret < 0) { dev_err(&pci->dev, "ACP platform devices creation failed\n"); goto de_init; } +skip_pdev_creation: pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pci->dev); pm_runtime_put_noidle(&pci->dev); @@ -327,24 +669,28 @@ disable_pci: static int __maybe_unused snd_acp63_suspend(struct device *dev) { struct acp63_dev_data *adata; - int ret; + int ret = 0; adata = dev_get_drvdata(dev); - ret = acp63_deinit(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP de-init failed\n"); + if (adata->acp_reset) { + ret = acp63_deinit(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + } return ret; } static int __maybe_unused snd_acp63_resume(struct device *dev) { struct acp63_dev_data *adata; - int ret; + int ret = 0; adata = dev_get_drvdata(dev); - ret = acp63_init(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP init failed\n"); + if (adata->acp_reset) { + ret = acp63_init(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP init failed\n"); + } return ret; } diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index 3a83dc178e7d..d48f7c5af289 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -45,10 +45,10 @@ static const struct snd_pcm_hardware acp63_pdm_hardware_capture = { static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size, u32 watermark_size, void __iomem *acp_base) { - acp63_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR); - acp63_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE); - acp63_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); - acp63_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL); + writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR); + writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE); + writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); + writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL); } static void acp63_enable_pdm_clock(void __iomem *acp_base) @@ -58,11 +58,11 @@ static void acp63_enable_pdm_clock(void __iomem *acp_base) pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK; pdm_ctrl = 0x00; - acp63_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL); - pdm_ctrl = acp63_readl(acp_base + ACP_WOV_MISC_CTRL); + writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL); + pdm_ctrl = readl(acp_base + ACP_WOV_MISC_CTRL); pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL; pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3)); - acp63_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL); + writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL); } static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata) @@ -70,9 +70,9 @@ static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata) u32 ext_int_ctrl; mutex_lock(adata->acp_lock); - ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); ext_int_ctrl |= PDM_DMA_INTR_MASK; - acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); mutex_unlock(adata->acp_lock); } @@ -81,9 +81,9 @@ static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata) u32 ext_int_ctrl; mutex_lock(adata->acp_lock); - ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); ext_int_ctrl &= ~PDM_DMA_INTR_MASK; - acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); mutex_unlock(adata->acp_lock); } @@ -93,8 +93,8 @@ static bool acp63_check_pdm_dma_status(void __iomem *acp_base) u32 pdm_enable, pdm_dma_enable; pdm_dma_status = false; - pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE); - pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); + pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE); + pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS)) pdm_dma_status = true; @@ -111,11 +111,11 @@ static int acp63_start_pdm_dma(void __iomem *acp_base) pdm_dma_enable = 0x01; acp63_enable_pdm_clock(acp_base); - acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE); - acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE); + writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE); + writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE); timeout = 0; while (++timeout < ACP_COUNTER) { - pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); + pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS) return 0; udelay(DELAY_US); @@ -131,14 +131,14 @@ static int acp63_stop_pdm_dma(void __iomem *acp_base) pdm_enable = 0x00; pdm_dma_enable = 0x00; - pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE); - pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); + pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE); + pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); if (pdm_dma_enable & 0x01) { pdm_dma_enable = 0x02; - acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE); + writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE); timeout = 0; while (++timeout < ACP_COUNTER) { - pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); + pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE); if ((pdm_dma_enable & 0x02) == 0x00) break; udelay(DELAY_US); @@ -148,9 +148,9 @@ static int acp63_stop_pdm_dma(void __iomem *acp_base) } if (pdm_enable == ACP_PDM_ENABLE) { pdm_enable = ACP_PDM_DISABLE; - acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE); + writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE); } - acp63_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH); + writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH); return 0; } @@ -164,18 +164,16 @@ static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction) val = PDM_PTE_OFFSET; /* Group Enable */ - acp63_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + - ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); - acp63_writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base + - ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); + writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); + writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { /* Load the low address of page int ACP SRAM through SRBM */ low = lower_32_bits(addr); high = upper_32_bits(addr); - acp63_writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val); + writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val); high |= BIT(31); - acp63_writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4); + writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4); val += 8; addr += PAGE_SIZE; } @@ -242,9 +240,9 @@ static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd, u32 high, low; u64 byte_count; - high = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH); + high = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH); byte_count = high; - low = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW); + low = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW); byte_count = (byte_count << 32) | low; return byte_count; } @@ -309,9 +307,8 @@ static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - acp63_writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS); - acp63_writel(PDM_DECIMATION_FACTOR, rtd->acp63_base + - ACP_WOV_PDM_DECIMATION_FACTOR); + writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DECIMATION_FACTOR, rtd->acp63_base + ACP_WOV_PDM_DECIMATION_FACTOR); rtd->bytescount = acp63_pdm_get_byte_count(rtd, substream->stream); pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base); if (!pdm_status) @@ -394,8 +391,9 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev) } 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); - pm_runtime_allow(&pdev->dev); return 0; } diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c new file mode 100644 index 000000000000..ade130a8062a --- /dev/null +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AMD ALSA SoC Pink Sardine SoundWire DMA Driver + * + * Copyright 2023 Advanced Micro Devices, Inc. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_amd.h> +#include "acp63.h" + +#define DRV_NAME "amd_ps_sdw_dma" + +static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { + {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, + ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE, + ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE, + ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE, + ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE, + ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE, + ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} +}; + +static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { + {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, + ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, + ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE, + ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR, + ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, +}; + +static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { + ACP_SW0_AUDIO0_TX_EN, + ACP_SW0_AUDIO1_TX_EN, + ACP_SW0_AUDIO2_TX_EN, + ACP_SW0_AUDIO0_RX_EN, + ACP_SW0_AUDIO1_RX_EN, + ACP_SW0_AUDIO2_RX_EN, +}; + +static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { + ACP_SW1_AUDIO1_TX_EN, + ACP_SW1_AUDIO1_RX_EN, +}; + +static const struct snd_pcm_hardware acp63_sdw_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS, + .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS, +}; + +static const struct snd_pcm_hardware acp63_sdw_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE, + .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS, + .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS, +}; + +static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable) +{ + u32 ext_intr_cntl, ext_intr_cntl1; + u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; + u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; + + if (enable) { + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl |= irq_mask; + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); + ext_intr_cntl1 |= irq_mask1; + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); + } else { + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl &= ~irq_mask; + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); + ext_intr_cntl1 &= ~irq_mask1; + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); + } +} + +static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base, + u32 stream_id) +{ + u16 page_idx; + u32 low, high, val; + u32 sdw_dma_pte_offset; + dma_addr_t addr; + + addr = stream->dma_addr; + sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance); + val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET); + + /* Group Enable */ + writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); + writel(PAGE_SIZE_4K_ENABLE, acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2); + for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { + /* Load the low address of page int ACP SRAM through SRBM */ + low = lower_32_bits(addr); + high = upper_32_bits(addr); + + writel(low, acp_base + ACP_SCRATCH_REG_0 + val); + high |= BIT(31); + writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4); + val += 8; + addr += PAGE_SIZE; + } + writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL); +} + +static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size, + u32 manager_instance) +{ + u32 reg_dma_size; + u32 reg_fifo_addr; + u32 reg_fifo_size; + u32 reg_ring_buf_size; + u32 reg_ring_buf_addr; + u32 sdw_fifo_addr; + u32 sdw_fifo_offset; + u32 sdw_ring_buf_addr; + u32 sdw_ring_buf_size; + u32 sdw_mem_window_offset; + + switch (manager_instance) { + case ACP_SDW0: + reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size; + reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr; + reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + break; + case ACP_SDW1: + reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size; + reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr; + reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + break; + default: + return -EINVAL; + } + sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance); + sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance); + sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET); + sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET); + sdw_ring_buf_size = size; + writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size); + writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr); + writel(sdw_fifo_addr, acp_base + reg_fifo_addr); + writel(SDW_DMA_SIZE, acp_base + reg_dma_size); + writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size); + return 0; +} + +static int acp63_sdw_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct acp_sdw_dma_stream *stream; + struct snd_soc_dai *cpu_dai; + struct amd_sdw_manager *amd_manager; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + int ret; + + runtime = substream->runtime; + cpu_dai = asoc_rtd_to_cpu(prtd, 0); + amd_manager = snd_soc_dai_get_drvdata(cpu_dai); + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = acp63_sdw_hardware_playback; + else + runtime->hw = acp63_sdw_hardware_capture; + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(component->dev, "set integer constraint failed\n"); + kfree(stream); + return ret; + } + + stream->stream_id = cpu_dai->id; + stream->instance = amd_manager->instance; + runtime->private_data = stream; + return ret; +} + +static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct acp_sdw_dma_stream *stream; + struct sdw_dma_dev_data *sdw_data; + u32 period_bytes; + u32 water_mark_size_reg; + u32 irq_mask, ext_intr_ctrl; + u64 size; + u32 stream_id; + u32 acp_ext_intr_cntl_reg; + int ret; + + sdw_data = dev_get_drvdata(component->dev); + stream = substream->runtime->private_data; + if (!stream) + return -EINVAL; + stream_id = stream->stream_id; + switch (stream->instance) { + case ACP_SDW0: + sdw_data->sdw0_dma_stream[stream_id] = substream; + water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id)); + else + irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id)); + break; + case ACP_SDW1: + sdw_data->sdw1_dma_stream[stream_id] = substream; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; + water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg; + irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id)); + break; + default: + return -EINVAL; + } + size = params_buffer_bytes(params); + period_bytes = params_period_bytes(params); + stream->dma_addr = substream->runtime->dma_addr; + stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + acp63_config_dma(stream, sdw_data->acp_base, stream_id); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size, + stream->instance); + if (ret) { + dev_err(component->dev, "Invalid DMA channel\n"); + return -EINVAL; + } + ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg); + ext_intr_ctrl |= irq_mask; + writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg); + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + return 0; +} + +static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base) +{ + union acp_sdw_dma_count byte_count; + u32 pos_low_reg, pos_high_reg; + + byte_count.bytescount = 0; + switch (stream->instance) { + case ACP_SDW0: + pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg; + pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg; + break; + case ACP_SDW1: + pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg; + pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg; + break; + default: + return -EINVAL; + } + if (pos_low_reg) { + byte_count.bcount.high = readl(acp_base + pos_high_reg); + byte_count.bcount.low = readl(acp_base + pos_low_reg); + } + return byte_count.bytescount; +} + +static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp, + struct snd_pcm_substream *substream) +{ + struct sdw_dma_dev_data *sdw_data; + struct acp_sdw_dma_stream *stream; + u32 pos, buffersize; + u64 bytescount; + + sdw_data = dev_get_drvdata(comp->dev); + stream = substream->runtime->private_data; + buffersize = frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base); + if (bytescount > stream->bytescount) + bytescount -= stream->bytescount; + pos = do_div(bytescount, buffersize); + return bytes_to_frames(substream->runtime, pos); +} + +static int acp63_sdw_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct device *parent = component->dev->parent; + + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER); + return 0; +} + +static int acp63_sdw_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct sdw_dma_dev_data *sdw_data; + struct acp_sdw_dma_stream *stream; + + sdw_data = dev_get_drvdata(component->dev); + stream = substream->runtime->private_data; + if (!stream) + return -EINVAL; + switch (stream->instance) { + case ACP_SDW0: + sdw_data->sdw0_dma_stream[stream->stream_id] = NULL; + break; + case ACP_SDW1: + sdw_data->sdw1_dma_stream[stream->stream_id] = NULL; + break; + default: + return -EINVAL; + } + kfree(stream); + return 0; +} + +static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, + void __iomem *acp_base, bool sdw_dma_enable) +{ + struct acp_sdw_dma_stream *stream; + u32 stream_id; + u32 sdw_dma_en_reg; + u32 sdw_dma_en_stat_reg; + u32 sdw_dma_stat; + u32 dma_enable; + + stream = substream->runtime->private_data; + stream_id = stream->stream_id; + switch (stream->instance) { + case ACP_SDW0: + sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id]; + break; + case ACP_SDW1: + sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id]; + break; + default: + return -EINVAL; + } + sdw_dma_en_stat_reg = sdw_dma_en_reg + 4; + dma_enable = sdw_dma_enable; + writel(dma_enable, acp_base + sdw_dma_en_reg); + return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat, + (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER); +} + +static int acp63_sdw_dma_trigger(struct snd_soc_component *comp, + struct snd_pcm_substream *substream, + int cmd) +{ + struct sdw_dma_dev_data *sdw_data; + int ret; + + sdw_data = dev_get_drvdata(comp->dev); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, true); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, false); + break; + default: + ret = -EINVAL; + } + if (ret) + dev_err(comp->dev, "trigger %d failed: %d", cmd, ret); + return ret; +} + +static const struct snd_soc_component_driver acp63_sdw_component = { + .name = DRV_NAME, + .open = acp63_sdw_dma_open, + .close = acp63_sdw_dma_close, + .hw_params = acp63_sdw_dma_hw_params, + .trigger = acp63_sdw_dma_trigger, + .pointer = acp63_sdw_dma_pointer, + .pcm_construct = acp63_sdw_dma_new, +}; + +static int acp63_sdw_platform_probe(struct platform_device *pdev) +{ + struct resource *res; + struct sdw_dma_dev_data *sdw_data; + struct acp63_dev_data *acp_data; + struct device *parent; + int status; + + parent = pdev->dev.parent; + acp_data = dev_get_drvdata(parent); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); + return -ENODEV; + } + + sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL); + if (!sdw_data) + return -ENOMEM; + + sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!sdw_data->acp_base) + return -ENOMEM; + + sdw_data->acp_lock = &acp_data->acp_lock; + dev_set_drvdata(&pdev->dev, sdw_data); + status = devm_snd_soc_register_component(&pdev->dev, + &acp63_sdw_component, + NULL, 0); + if (status) { + dev_err(&pdev->dev, "Fail to register sdw dma component\n"); + return status; + } + 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 int acp63_sdw_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) +{ + struct acp_sdw_dma_stream *stream; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + u32 period_bytes, buf_size, water_mark_size_reg; + u32 stream_count; + int index, instance, ret; + + for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { + if (instance == ACP_SDW0) + stream_count = ACP63_SDW0_DMA_MAX_STREAMS; + else + stream_count = ACP63_SDW1_DMA_MAX_STREAMS; + + for (index = 0; index < stream_count; index++) { + if (instance == ACP_SDW0) { + substream = sdw_data->sdw0_dma_stream[index]; + water_mark_size_reg = + sdw0_dma_ring_buf_reg[index].water_mark_size_reg; + } else { + substream = sdw_data->sdw1_dma_stream[index]; + water_mark_size_reg = + sdw1_dma_ring_buf_reg[index].water_mark_size_reg; + } + + if (substream && substream->runtime) { + runtime = substream->runtime; + stream = runtime->private_data; + period_bytes = frames_to_bytes(runtime, runtime->period_size); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + acp63_config_dma(stream, sdw_data->acp_base, index); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, + buf_size, instance); + if (ret) + return ret; + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + } + } + } + acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true); + return 0; +} + +static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev) +{ + struct sdw_dma_dev_data *sdw_data; + + sdw_data = dev_get_drvdata(dev); + return acp_restore_sdw_dma_config(sdw_data); +} + +static const struct dev_pm_ops acp63_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) +}; + +static struct platform_driver acp63_sdw_dma_driver = { + .probe = acp63_sdw_platform_probe, + .remove = acp63_sdw_platform_remove, + .driver = { + .name = "amd_ps_sdw_dma", + .pm = &acp63_pm_ops, + }, +}; + +module_platform_driver(acp63_sdw_dma_driver); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 7362dd15ad30..9538f3ffc5d9 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -416,8 +416,9 @@ static int acp3x_audio_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - pm_runtime_allow(&pdev->dev); return 0; } diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index 4e299f96521f..c3b47e9bd239 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -430,8 +430,9 @@ static int acp_pdm_audio_probe(struct platform_device *pdev) } 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); - pm_runtime_allow(&pdev->dev); return 0; } diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index 29901ee4bfe3..587dec5bb33d 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -409,9 +409,9 @@ static int acp5x_audio_probe(struct platform_device *pdev) } pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - pm_runtime_allow(&pdev->dev); - return 0; } diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h index bd9f1c5684d1..ac1936a8c43f 100644 --- a/sound/soc/amd/vangogh/acp5x.h +++ b/sound/soc/amd/vangogh/acp5x.h @@ -147,6 +147,8 @@ static inline void acp_writel(u32 val, void __iomem *base_addr) writel(val, base_addr - ACP5x_PHY_BASE_ADDRESS); } +int snd_amd_acp_find_config(struct pci_dev *pci); + static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction) { diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c index e0df17c88e8e..c4634a8a17cd 100644 --- a/sound/soc/amd/vangogh/pci-acp5x.c +++ b/sound/soc/amd/vangogh/pci-acp5x.c @@ -125,10 +125,15 @@ static int snd_acp5x_probe(struct pci_dev *pci, { struct acp5x_dev_data *adata; struct platform_device_info pdevinfo[ACP5x_DEVS]; - unsigned int irqflags; + unsigned int irqflags, flag; int ret, i; u32 addr, val; + /* Return if acp config flag is defined */ + flag = snd_amd_acp_find_config(pci); + if (flag) + return -ENODEV; + irqflags = IRQF_SHARED; if (pci->revision != 0x50) return -ENODEV; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 246299a178f9..a2fe3bd4f9a1 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -321,6 +321,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MECHREVO"), + DMI_MATCH(DMI_BOARD_NAME, "MRID6"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "System76"), DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"), } diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index d818eba48546..72c4591e451b 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -383,8 +383,9 @@ static int acp6x_pdm_audio_probe(struct platform_device *pdev) } 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); - pm_runtime_allow(&pdev->dev); return 0; } diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 007ab746973d..4c1985711218 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -473,21 +473,19 @@ static int atmel_classd_asoc_card_init(struct device *dev, if (!dai_link) return -ENOMEM; - comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL); + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) return -ENOMEM; - dai_link->cpus = &comp[0]; - dai_link->codecs = &comp[1]; + dai_link->cpus = comp; + dai_link->codecs = &asoc_dummy_dlc; dai_link->num_cpus = 1; dai_link->num_codecs = 1; dai_link->name = "CLASSD"; dai_link->stream_name = "CLASSD PCM"; - dai_link->codecs->dai_name = "snd-soc-dummy-dai"; dai_link->cpus->dai_name = dev_name(dev); - dai_link->codecs->name = "snd-soc-dummy"; card->dai_link = dai_link; card->num_links = 1; diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index 00c7b3a34ef5..efcbdd1a629f 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -496,21 +496,19 @@ static int atmel_pdmic_asoc_card_init(struct device *dev, if (!dai_link) return -ENOMEM; - comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL); + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) return -ENOMEM; - dai_link->cpus = &comp[0]; - dai_link->codecs = &comp[1]; + dai_link->cpus = comp; + dai_link->codecs = &asoc_dummy_dlc; dai_link->num_cpus = 1; dai_link->num_codecs = 1; dai_link->name = "PDMIC"; dai_link->stream_name = "PDMIC PCM"; - dai_link->codecs->dai_name = "snd-soc-dummy-dai"; dai_link->cpus->dai_name = dev_name(dev); - dai_link->codecs->name = "snd-soc-dummy"; card->dai_link = dai_link; card->num_links = 1; diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index da23855a0e40..c79c73e6791e 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -423,7 +423,7 @@ static const struct snd_soc_component_driver mchp_pdmc_dai_component = { .open = &mchp_pdmc_open, .close = &mchp_pdmc_close, .legacy_dai_naming = 1, - .start_dma_last = 1, + .trigger_start = SND_SOC_TRIGGER_ORDER_LDC, }; static const unsigned int mchp_pdmc_1mic[] = {1}; diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index baf38964b491..0405e9e49140 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -23,7 +23,6 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/i2c.h> #include <linux/of.h> #include <linux/atmel-ssc.h> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c4c5cbaa809..2a62dbd5339e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AW88395 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 + imply SND_SOC_CHV3_CODEC imply SND_SOC_CQ0093VC imply SND_SOC_CROS_EC_CODEC imply SND_SOC_CS35L32 @@ -136,6 +137,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MAX98363 imply SND_SOC_MAX98373_I2C imply SND_SOC_MAX98373_SDW + imply SND_SOC_MAX98388 imply SND_SOC_MAX98390 imply SND_SOC_MAX98396 imply SND_SOC_MAX9850 @@ -207,6 +209,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT712_SDCA_DMIC_SDW imply SND_SOC_RT715_SDW imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318_SDW @@ -234,6 +237,9 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 + imply SND_SOC_TAS2781_COMLIB + imply SND_SOC_TAS2781_FMWLIB + imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 imply SND_SOC_TAS571X imply SND_SOC_TAS5720 @@ -324,6 +330,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WM9713 imply SND_SOC_WSA881X imply SND_SOC_WSA883X + imply SND_SOC_WSA884X imply SND_SOC_ZL38060 help Normally ASoC codec drivers are only built if a machine driver which @@ -643,6 +650,13 @@ config SND_SOC_BD28623 config SND_SOC_BT_SCO tristate "Dummy BT SCO codec driver" +config SND_SOC_CHV3_CODEC + tristate "Google Chameleon v3 codec driver" + help + Enable support for the Google Chameleon v3 audio codec. + This codec does not have a control interface, it always outputs + 8 channel S32_LE audio. + config SND_SOC_CPCAP tristate "Motorola CPCAP codec" depends on MFD_CPCAP || COMPILE_TEST @@ -1166,6 +1180,15 @@ config SND_SOC_MAX98373_SDW interface for control data. Select this if MAX98373 is connected via soundwire. +config SND_SOC_MAX98388 + tristate "Analog Devices MAX98388 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98388 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + config SND_SOC_MAX98390 tristate "Maxim Integrated MAX98390 Speaker Amplifier" depends on I2C @@ -1539,6 +1562,12 @@ config SND_SOC_RT712_SDCA_DMIC_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT722_SDCA_SDW + tristate "Realtek RT722 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT715 tristate @@ -1652,6 +1681,12 @@ config SND_SOC_SSM2602_I2C select SND_SOC_SSM2602 select REGMAP_I2C +config SND_SOC_SSM3515 + tristate "Analog Devices SSM3515 amplifier driver" + select REGMAP_I2C + depends on I2C + depends on OF + config SND_SOC_SSM4567 tristate "Analog Devices ssm4567 amplifier driver support" depends on I2C @@ -1699,6 +1734,29 @@ config SND_SOC_TAS2780 Enable support for Texas Instruments TAS2780 high-efficiency digital input mono Class-D audio power amplifiers. +config SND_SOC_TAS2781_COMLIB + depends on I2C + select CRC8 + select REGMAP_I2C + tristate + +config SND_SOC_TAS2781_FMWLIB + depends on SND_SOC_TAS2781_COMLIB + tristate + default n + +config SND_SOC_TAS2781_I2C + tristate "Texas Instruments TAS2781 speaker amplifier based on I2C" + depends on I2C + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + help + Enable support for Texas Instruments TAS2781 Smart Amplifier + Digital input mono Class-D and DSP-inside audio power amplifiers. + Note the TAS2781 driver implements a flexible and configurable + algo coefficient setting, for one, two or even multiple TAS2781 + chips. + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C @@ -2162,6 +2220,15 @@ config SND_SOC_WSA883X This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. +config SND_SOC_WSA884X + tristate "WSA884X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5cdbae88e6e3..b48a9a323b84 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -51,6 +51,7 @@ snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o +snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o @@ -152,6 +153,7 @@ snd-soc-max98363-objs := max98363.o snd-soc-max98373-objs := max98373.o snd-soc-max98373-i2c-objs := max98373-i2c.o snd-soc-max98373-sdw-objs := max98373-sdw.o +snd-soc-max98388-objs := max98388.o snd-soc-max98390-objs := max98390.o snd-soc-max98396-objs := max98396.o snd-soc-max9850-objs := max9850.o @@ -237,6 +239,7 @@ snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt722-sdca-objs := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-objs := rt9120.o snd-soc-sdw-mockup-objs := sdw-mockup.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -256,6 +259,7 @@ snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o +snd-soc-ssm3515-objs := ssm3515.o snd-soc-ssm4567-objs := ssm4567.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta350-objs := sta350.o @@ -269,6 +273,9 @@ snd-soc-tas5805m-objs := tas5805m.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o +snd-soc-tas2781-comlib-objs := tas2781-comlib.o +snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o +snd-soc-tas2781-i2c-objs := tas2781-i2c.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tfa989x-objs := tfa989x.o snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o @@ -359,6 +366,7 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-wsa881x-objs := wsa881x.o snd-soc-wsa883x-objs := wsa883x.o +snd-soc-wsa884x-objs := wsa884x.o snd-soc-zl38060-objs := zl38060.o # Amp snd-soc-max9877-objs := max9877.o @@ -425,6 +433,7 @@ obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o +obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o @@ -521,6 +530,7 @@ obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o +obj-$(CONFIG_SND_SOC_MAX98388) += snd-soc-max98388.o obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o @@ -607,6 +617,7 @@ obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -623,6 +634,7 @@ obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o +obj-$(CONFIG_SND_SOC_SSM3515) += snd-soc-ssm3515.o obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o @@ -633,6 +645,9 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o +obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o +obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o +obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o @@ -733,6 +748,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o +obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o # Amp diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 4cb8d87f9011..15d74bb31c4c 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = { .driver = { .name = "ad193x", }, - .probe_new = ad193x_i2c_probe, + .probe = ad193x_i2c_probe, .id_table = ad193x_id, }; module_i2c_driver(ad193x_i2c_driver); diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c index 8ed0ffdedbc9..132b9e2cca59 100644 --- a/sound/soc/codecs/adau1372-i2c.c +++ b/sound/soc/codecs/adau1372-i2c.c @@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = { .driver = { .name = "adau1372", }, - .probe_new = adau1372_i2c_probe, + .probe = adau1372_i2c_probe, .id_table = adau1372_i2c_ids, }; module_i2c_driver(adau1372_i2c_driver); diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 37e178e8a1d0..c5b087b8fffc 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1505,7 +1505,7 @@ static struct i2c_driver adau1373_i2c_driver = { .driver = { .name = "adau1373", }, - .probe_new = adau1373_i2c_probe, + .probe = adau1373_i2c_probe, .id_table = adau1373_i2c_id, }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 135a7db7fcf9..8c8de3b3c901 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -876,7 +876,7 @@ static struct i2c_driver adau1701_i2c_driver = { .name = "adau1701", .of_match_table = of_match_ptr(adau1701_dt_ids), }, - .probe_new = adau1701_i2c_probe, + .probe = adau1701_i2c_probe, .id_table = adau1701_i2c_id, }; diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 0cefff49569c..a554255186ae 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver adau1761_i2c_driver = { .name = "adau1761", .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, - .probe_new = adau1761_i2c_probe, + .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, .id_table = adau1761_i2c_ids, }; diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 39021b8cfb62..3a170fd78ff3 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -56,7 +56,7 @@ static struct i2c_driver adau1781_i2c_driver = { .name = "adau1781", .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, - .probe_new = adau1781_i2c_probe, + .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, .id_table = adau1781_i2c_ids, }; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 634d4dbca5ec..f2932713b4de 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -1059,13 +1059,12 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, if (!adau) return -ENOMEM; - adau->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(adau->mclk)) { - if (PTR_ERR(adau->mclk) != -ENOENT) - return PTR_ERR(adau->mclk); - /* Clock is optional (for the driver) */ - adau->mclk = NULL; - } else if (adau->mclk) { + /* Clock is optional (for the driver) */ + adau->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(adau->mclk)) + return PTR_ERR(adau->mclk); + + if (adau->mclk) { adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO; /* diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 9f137a0634d5..24c7b9c84c19 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -42,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", }, - .probe_new = adau1977_i2c_probe, + .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, }; module_i2c_driver(adau1977_i2c_driver); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index afed48401b25..73f181f7757e 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -78,7 +78,7 @@ static struct i2c_driver adau7118_driver = { .name = "adau7118", .of_match_table = adau7118_of_match, }, - .probe_new = adau7118_probe_i2c, + .probe = adau7118_probe_i2c, .id_table = adau7118_id, }; module_i2c_driver(adau7118_driver); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index bf181bbaabed..78a317947df9 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -29,7 +29,7 @@ static struct i2c_driver adav803_driver = { .driver = { .name = "adav803", }, - .probe_new = adav803_probe, + .probe = adav803_probe, .id_table = adav803_id, }; module_i2c_driver(adav803_driver); diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index b6d9a10bdccd..e34e5533765c 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -264,8 +264,6 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) struct ak4118_priv *ak4118 = data; struct snd_soc_component *component = ak4118->component; struct snd_kcontrol_new *kctl_new; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_id *id; unsigned int i; if (!component) @@ -273,13 +271,8 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { kctl_new = &ak4118_iec958_controls[i]; - kctl = snd_soc_card_get_kcontrol(component->card, - kctl_new->name); - if (!kctl) - continue; - id = &kctl->id; - snd_ctl_notify(component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, id); + + snd_soc_component_notify_control(component, kctl_new->name); } return IRQ_HANDLED; @@ -414,7 +407,7 @@ static struct i2c_driver ak4118_i2c_driver = { .of_match_table = of_match_ptr(ak4118_of_match), }, .id_table = ak4118_id_table, - .probe_new = ak4118_i2c_probe, + .probe = ak4118_i2c_probe, }; module_i2c_driver(ak4118_i2c_driver); diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 573389e402f8..f287acb98646 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -597,7 +597,7 @@ static struct i2c_driver ak4375_i2c_driver = { .pm = &ak4375_pm, .of_match_table = ak4375_of_match, }, - .probe_new = ak4375_i2c_probe, + .probe = ak4375_i2c_probe, .remove = ak4375_i2c_remove, }; module_i2c_driver(ak4375_i2c_driver); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 8c69c8cf9b67..77678f85ad94 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -818,7 +818,7 @@ static struct i2c_driver ak4458_i2c_driver = { .pm = &ak4458_pm, .of_match_table = ak4458_of_match, }, - .probe_new = ak4458_i2c_probe, + .probe = ak4458_i2c_probe, .remove = ak4458_i2c_remove, }; diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 8c8c93eea704..904bf91090aa 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -439,7 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = { .driver = { .name = "ak4535", }, - .probe_new = ak4535_i2c_probe, + .probe = ak4535_i2c_probe, .id_table = ak4535_i2c_id, }; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index f75c19ef3551..ad56caec9dac 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -925,7 +925,7 @@ static struct i2c_driver ak4613_i2c_driver = { .name = "ak4613-codec", .of_match_table = ak4613_of_match, }, - .probe_new = ak4613_i2c_probe, + .probe = ak4613_i2c_probe, .id_table = ak4613_i2c_id, }; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 0d3ee195b3cc..5b7df2f0dd6a 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -628,7 +628,7 @@ static struct i2c_driver ak4641_i2c_driver = { .driver = { .name = "ak4641", }, - .probe_new = ak4641_i2c_probe, + .probe = ak4641_i2c_probe, .remove = ak4641_i2c_remove, .id_table = ak4641_i2c_id, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 914d5b1969f8..2a8984c1fa9c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -698,7 +698,7 @@ static struct i2c_driver ak4642_i2c_driver = { .name = "ak4642-codec", .of_match_table = ak4642_of_match, }, - .probe_new = ak4642_i2c_probe, + .probe = ak4642_i2c_probe, .id_table = ak4642_i2c_id, }; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index cd76765f8cc0..5b849b390c2a 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -655,7 +655,7 @@ static struct i2c_driver ak4671_i2c_driver = { .driver = { .name = "ak4671-codec", }, - .probe_new = ak4671_i2c_probe, + .probe = ak4671_i2c_probe, .id_table = ak4671_i2c_id, }; diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 60abcffe6a0c..442e2cb42df4 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -497,7 +497,7 @@ static struct i2c_driver ak5558_i2c_driver = { .of_match_table = of_match_ptr(ak5558_i2c_dt_ids), .pm = &ak5558_pm, }, - .probe_new = ak5558_i2c_probe, + .probe = ak5558_i2c_probe, .remove = ak5558_i2c_remove, }; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9ef01b1dd294..b24c32206884 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -1083,7 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = { .name = "alc562x-codec", .of_match_table = of_match_ptr(alc5623_of_match), }, - .probe_new = alc5623_i2c_probe, + .probe = alc5623_i2c_probe, .id_table = alc5623_i2c_table, }; diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index a770704a4e17..d5021f266930 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1182,7 +1182,7 @@ static struct i2c_driver alc5632_i2c_driver = { .name = "alc5632", .of_match_table = of_match_ptr(alc5632_of_match), }, - .probe_new = alc5632_i2c_probe, + .probe = alc5632_i2c_probe, .id_table = alc5632_i2c_table, }; diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index afdce6b7fa26..9dcd75dd799a 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -570,7 +570,7 @@ static struct i2c_driver aw88395_i2c_driver = { .driver = { .name = AW88395_I2C_NAME, }, - .probe_new = aw88395_i2c_probe, + .probe = aw88395_i2c_probe, .id_table = aw88395_i2c_id, }; module_i2c_driver(aw88395_i2c_driver); diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c new file mode 100644 index 000000000000..ab99effa6874 --- /dev/null +++ b/sound/soc/codecs/chv3-codec.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <sound/soc.h> + +static struct snd_soc_dai_driver chv3_codec_dai = { + .name = "chv3-codec-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 8, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_chv3_codec = { +}; + +static int chv3_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_chv3_codec, &chv3_codec_dai, 1); +} + +static const struct of_device_id chv3_codec_of_match[] = { + { .compatible = "google,chv3-codec", }, + { } +}; + +static struct platform_driver chv3_codec_platform_driver = { + .driver = { + .name = "chv3-codec", + .of_match_table = chv3_codec_of_match, + }, + .probe = chv3_codec_probe, +}; +module_platform_driver(chv3_codec_platform_driver); + +MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver"); +MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index dc7a58d68076..6e658bb16fb0 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -260,7 +260,7 @@ static const struct regmap_config cs35l32_regmap = { .volatile_reg = cs35l32_volatile_register, .readable_reg = cs35l32_readable_register, .precious_reg = cs35l32_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -572,7 +572,7 @@ static struct i2c_driver cs35l32_i2c_driver = { .of_match_table = cs35l32_of_match, }, .id_table = cs35l32_id, - .probe_new = cs35l32_i2c_probe, + .probe = cs35l32_i2c_probe, .remove = cs35l32_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 15e79168d256..9968c2e189e6 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -852,7 +852,7 @@ static const struct regmap_config cs35l33_regmap = { .volatile_reg = cs35l33_volatile_register, .readable_reg = cs35l33_readable_register, .writeable_reg = cs35l33_writeable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1282,7 +1282,7 @@ static struct i2c_driver cs35l33_i2c_driver = { }, .id_table = cs35l33_id, - .probe_new = cs35l33_i2c_probe, + .probe = cs35l33_i2c_probe, .remove = cs35l33_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index b3f98023e6a7..6974dd461410 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -799,7 +799,7 @@ static struct regmap_config cs35l34_regmap = { .volatile_reg = cs35l34_volatile_register, .readable_reg = cs35l34_readable_register, .precious_reg = cs35l34_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1213,7 +1213,7 @@ static struct i2c_driver cs35l34_i2c_driver = { }, .id_table = cs35l34_id, - .probe_new = cs35l34_i2c_probe, + .probe = cs35l34_i2c_probe, .remove = cs35l34_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 947a440a3a47..0a4b5aa78185 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1099,7 +1099,7 @@ static struct regmap_config cs35l35_regmap = { .volatile_reg = cs35l35_volatile_register, .readable_reg = cs35l35_readable_register, .precious_reg = cs35l35_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1654,7 +1654,7 @@ static struct i2c_driver cs35l35_i2c_driver = { .of_match_table = cs35l35_of_match, }, .id_table = cs35l35_id, - .probe_new = cs35l35_i2c_probe, + .probe = cs35l35_i2c_probe, .remove = cs35l35_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index a078dd422ea1..04ba7f25012e 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1944,7 +1944,7 @@ static struct i2c_driver cs35l36_i2c_driver = { .of_match_table = cs35l36_of_match, }, .id_table = cs35l36_id, - .probe_new = cs35l36_i2c_probe, + .probe = cs35l36_i2c_probe, .remove = cs35l36_i2c_remove, }; module_i2c_driver(cs35l36_i2c_driver); diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index 3676b596f60b..7ea890d7d387 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -88,7 +88,7 @@ static struct i2c_driver cs35l41_i2c_driver = { .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, .id_table = cs35l41_id_i2c, - .probe_new = cs35l41_i2c_probe, + .probe = cs35l41_i2c_probe, .remove = cs35l41_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c index 562f73df7afa..77e0f8750f37 100644 --- a/sound/soc/codecs/cs35l45-i2c.c +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-i2c.c -- CS35L45 I2C driver // @@ -65,12 +65,12 @@ static struct i2c_driver cs35l45_i2c_driver = { .pm = &cs35l45_pm_ops, }, .id_table = cs35l45_id_i2c, - .probe_new = cs35l45_i2c_probe, + .probe = cs35l45_i2c_probe, .remove = cs35l45_i2c_remove, }; module_i2c_driver(cs35l45_i2c_driver); MODULE_DESCRIPTION("I2C CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c index a00b23b4180c..5efb77530cc3 100644 --- a/sound/soc/codecs/cs35l45-spi.c +++ b/sound/soc/codecs/cs35l45-spi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-spi.c -- CS35L45 SPI driver // @@ -74,5 +74,5 @@ module_spi_driver(cs35l45_spi_driver); MODULE_DESCRIPTION("SPI CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 46610e64e818..066f83c0c7ac 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-tables.c -- CS35L45 ALSA SoC audio driver // diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index c31597f6bfae..d1edb9876c10 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45.c - CS35L45 ALSA SoC audio driver // @@ -1296,4 +1296,4 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45); MODULE_DESCRIPTION("ASoC CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h index 0da28439f628..61135a316df3 100644 --- a/sound/soc/codecs/cs35l45.h +++ b/sound/soc/codecs/cs35l45.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * cs35l45.h - CS35L45 ALSA SoC audio driver * diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 295caad26224..ed2a41943d97 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -68,7 +68,7 @@ static struct i2c_driver cs35l56_i2c_driver = { .pm = &cs35l56_pm_ops_i2c_spi, }, .id_table = cs35l56_id_i2c, - .probe_new = cs35l56_i2c_probe, + .probe = cs35l56_i2c_probe, .remove = cs35l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index e0d2b9bb2326..c03f9d3c9a13 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -822,25 +822,23 @@ static void cs35l56_system_reset(struct cs35l56_private *cs35l56) regcache_cache_only(cs35l56->regmap, false); } -static void cs35l56_dsp_work(struct work_struct *work) +static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) { - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - dsp_work); - unsigned int reg; - unsigned int val; - int ret = 0; - - if (!cs35l56->init_done) - return; - - cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", - cs35l56->secured ? "s" : "", cs35l56->rev); + int ret; - if (!cs35l56->dsp.part) - return; + /* Use wm_adsp to load and apply the firmware patch and coefficient files */ + ret = wm_adsp_power_up(&cs35l56->dsp); + if (ret) + dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + else + cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT); +} - pm_runtime_get_sync(cs35l56->dev); +static void cs35l56_patch(struct cs35l56_private *cs35l56) +{ + unsigned int reg; + unsigned int val; + int ret; /* * Disable SoundWire interrupts to prevent race with IRQ work. @@ -907,9 +905,6 @@ static void cs35l56_dsp_work(struct work_struct *work) err_unlock: mutex_unlock(&cs35l56->irq_lock); err: - pm_runtime_mark_last_busy(cs35l56->dev); - pm_runtime_put_autosuspend(cs35l56->dev); - /* Re-enable SoundWire interrupts */ if (cs35l56->sdw_peripheral) { cs35l56->sdw_irq_no_unmask = false; @@ -918,6 +913,32 @@ err: } } +static void cs35l56_dsp_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + dsp_work); + + if (!cs35l56->init_done) + return; + + pm_runtime_get_sync(cs35l56->dev); + + /* + * When the device is running in secure mode the firmware files can + * only contain insecure tunings and therefore we do not need to + * shutdown the firmware to apply them and can use the lower cost + * reinit sequence instead. + */ + if (cs35l56->secured) + cs35l56_secure_patch(cs35l56); + else + cs35l56_patch(cs35l56); + + pm_runtime_mark_last_busy(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->dev); +} + static int cs35l56_component_probe(struct snd_soc_component *component) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1505,6 +1526,12 @@ int cs35l56_init(struct cs35l56_private *cs35l56) dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", cs35l56->secured ? "s" : "", cs35l56->rev, otpid); + /* Populate the DSP information with the revision and security state */ + cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->secured ? "s" : "", cs35l56->rev); + if (!cs35l56->dsp.part) + return -ENOMEM; + /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index dee1a6662c2e..69287ba7e955 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -675,7 +675,7 @@ static const struct regmap_config cs4234_regmap = { .writeable_reg = cs4234_writeable_register, .reg_defaults = cs4234_default_reg, .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -906,7 +906,7 @@ static struct i2c_driver cs4234_i2c_driver = { .pm = &cs4234_pm, .of_match_table = cs4234_of_match, }, - .probe_new = cs4234_i2c_probe, + .probe = cs4234_i2c_probe, .remove = cs4234_i2c_remove, }; module_i2c_driver(cs4234_i2c_driver); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 3573363b7e31..0cfc5ab36a13 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -649,7 +649,7 @@ static struct i2c_driver cs4265_i2c_driver = { .of_match_table = cs4265_of_match, }, .id_table = cs4265_id, - .probe_new = cs4265_i2c_probe, + .probe = cs4265_i2c_probe, .remove = cs4265_i2c_remove, }; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 1b640d8232ba..ab32f15e3b44 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -751,7 +751,7 @@ static struct i2c_driver cs4270_i2c_driver = { .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, - .probe_new = cs4270_i2c_probe, + .probe = cs4270_i2c_probe, .remove = cs4270_i2c_remove, }; diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index 4033be1c3bc1..89fe7d1665df 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -33,7 +33,7 @@ static struct i2c_driver cs4271_i2c_driver = { .name = "cs4271", .of_match_table = of_match_ptr(cs4271_dt_ids), }, - .probe_new = cs4271_i2c_probe, + .probe = cs4271_i2c_probe, .id_table = cs4271_i2c_id, }; module_i2c_driver(cs4271_i2c_driver); diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c index 67b253287daf..2552a1e6b82f 100644 --- a/sound/soc/codecs/cs42l42-i2c.c +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -92,7 +92,7 @@ static struct i2c_driver cs42l42_i2c_driver = { .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, .id_table = cs42l42_id, - .probe_new = cs42l42_i2c_probe, + .probe = cs42l42_i2c_probe, .remove = cs42l42_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index e3edaa1a2761..a0de0329406a 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -393,7 +393,7 @@ const struct regmap_config cs42l42_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l42_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -646,12 +646,19 @@ static const struct cs42l42_pll_params pll_ratio_table[] = { { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 85238339fbca..b2106ff6a7cb 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -43,7 +43,7 @@ static struct i2c_driver cs42l51_i2c_driver = { .of_match_table = cs42l51_of_match, .pm = &cs42l51_pm_ops, }, - .probe_new = cs42l51_i2c_probe, + .probe = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index e88d9ff95cdf..a67cd3ee84e0 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -724,12 +724,9 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); cs42l51->regmap = regmap; - cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); - if (IS_ERR(cs42l51->mclk_handle)) { - if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) - return PTR_ERR(cs42l51->mclk_handle); - cs42l51->mclk_handle = NULL; - } + cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) + return PTR_ERR(cs42l51->mclk_handle); for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) cs42l51->supplies[i].supply = cs42l51_supply_names[i]; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 90bf535fc5a5..1f1ded0ff0ac 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1226,7 +1226,7 @@ static struct i2c_driver cs42l52_i2c_driver = { .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, - .probe_new = cs42l52_i2c_probe, + .probe = cs42l52_i2c_probe, }; module_i2c_driver(cs42l52_i2c_driver); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 3b0e715549c9..4c646e8d72aa 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1341,7 +1341,7 @@ static struct i2c_driver cs42l56_i2c_driver = { .of_match_table = cs42l56_of_match, }, .id_table = cs42l56_id, - .probe_new = cs42l56_i2c_probe, + .probe = cs42l56_i2c_probe, .remove = cs42l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 0a146319755a..6ab67d196d10 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1267,7 +1267,7 @@ static const struct regmap_config cs42l73_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults), .volatile_reg = cs42l73_volatile_register, .readable_reg = cs42l73_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1384,7 +1384,7 @@ static struct i2c_driver cs42l73_i2c_driver = { .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, - .probe_new = cs42l73_i2c_probe, + .probe = cs42l73_i2c_probe, }; diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c index 37629ebd90e0..f482b6a4f5c3 100644 --- a/sound/soc/codecs/cs42l83-i2c.c +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -158,7 +158,7 @@ static const struct regmap_config cs42l83_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l83_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -228,7 +228,7 @@ static struct i2c_driver cs42l83_i2c_driver = { .pm = &cs42l83_i2c_pm_ops, .of_match_table = of_match_ptr(cs42l83_of_match), }, - .probe_new = cs42l83_i2c_probe, + .probe = cs42l83_i2c_probe, .remove = cs42l83_i2c_remove, }; diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 052ffb7dcfc6..a422472820fb 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -70,7 +70,7 @@ static struct i2c_driver cs42xx8_i2c_driver = { .pm = &cs42xx8_pm, .of_match_table = cs42xx8_of_match, }, - .probe_new = cs42xx8_i2c_probe, + .probe = cs42xx8_i2c_probe, .remove = cs42xx8_i2c_remove, .id_table = cs42xx8_i2c_id, }; diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index db39abb2a31b..3292405024bc 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -2357,7 +2357,7 @@ static const struct regmap_config cs43130_regmap = { .readable_reg = cs43130_readable_register, .precious_reg = cs43130_precious_register, .volatile_reg = cs43130_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, /* needed for regcache_sync */ .use_single_read = true, .use_single_write = true, @@ -2697,7 +2697,7 @@ static struct i2c_driver cs43130_i2c_driver = { .pm = &cs43130_runtime_pm, }, .id_table = cs43130_i2c_id, - .probe_new = cs43130_i2c_probe, + .probe = cs43130_i2c_probe, .remove = cs43130_i2c_remove, }; diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index ac1696034846..2ceca5d0e5bf 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -258,7 +258,7 @@ static struct i2c_driver cs4341_i2c_driver = { .name = "cs4341-i2c", .of_match_table = of_match_ptr(cs4341_dt_ids), }, - .probe_new = cs4341_i2c_probe, + .probe = cs4341_i2c_probe, .id_table = cs4341_i2c_id, }; #endif diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index ba94ffd0a7e4..8365dd0ebe2a 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -375,7 +375,7 @@ static struct i2c_driver cs4349_i2c_driver = { .pm = &cs4349_runtime_pm, }, .id_table = cs4349_i2c_id, - .probe_new = cs4349_i2c_probe, + .probe = cs4349_i2c_probe, .remove = cs4349_i2c_remove, }; diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 69db0013d243..f4065555c36e 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -911,7 +911,7 @@ static struct regmap_config cs53l30_regmap = { .volatile_reg = cs53l30_volatile_register, .writeable_reg = cs53l30_writeable_register, .readable_reg = cs53l30_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -990,14 +990,10 @@ static int cs53l30_i2c_probe(struct i2c_client *client) } /* Check if MCLK provided */ - cs53l30->mclk = devm_clk_get(dev, "mclk"); + cs53l30->mclk = devm_clk_get_optional(dev, "mclk"); if (IS_ERR(cs53l30->mclk)) { - if (PTR_ERR(cs53l30->mclk) != -ENOENT) { - ret = PTR_ERR(cs53l30->mclk); - goto error; - } - /* Otherwise mark the mclk pointer to NULL */ - cs53l30->mclk = NULL; + ret = PTR_ERR(cs53l30->mclk); + goto error; } /* Fetch the MUTE control */ @@ -1121,7 +1117,7 @@ static struct i2c_driver cs53l30_i2c_driver = { .pm = &cs53l30_runtime_pm, }, .id_table = cs53l30_id, - .probe_new = cs53l30_i2c_probe, + .probe = cs53l30_i2c_probe, .remove = cs53l30_i2c_remove, }; diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 5deceaa89282..082231088a26 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1706,7 +1706,7 @@ static struct i2c_driver cx2072x_i2c_driver = { .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), .pm = &cx2072x_runtime_pm, }, - .probe_new = cx2072x_i2c_probe, + .probe = cx2072x_i2c_probe, .remove = cx2072x_i2c_remove, .id_table = cx2072x_i2c_id, }; diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index f838466bfebf..1e232d01809c 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1248,7 +1248,7 @@ static struct i2c_driver da7210_i2c_driver = { .driver = { .name = "da7210", }, - .probe_new = da7210_i2c_probe, + .probe = da7210_i2c_probe, .id_table = da7210_i2c_id, }; #endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 1c1f211a8e2e..3a6449c44b23 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2069,7 +2069,7 @@ static struct i2c_driver da7213_i2c_driver = { .acpi_match_table = ACPI_PTR(da7213_acpi_match), .pm = &da7213_pm, }, - .probe_new = da7213_i2c_probe, + .probe = da7213_i2c_probe, .remove = da7213_i2c_remove, .id_table = da7213_i2c_id, }; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index d9c28e701613..3f456b08b809 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3317,7 +3317,7 @@ static struct i2c_driver da7218_i2c_driver = { .name = "da7218", .of_match_table = da7218_of_match, }, - .probe_new = da7218_i2c_probe, + .probe = da7218_i2c_probe, .id_table = da7218_i2c_id, }; diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 993a0d00bc48..c65256bd526d 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -571,16 +571,29 @@ static enum da7219_aad_jack_ins_deb } } +static enum da7219_aad_jack_ins_det_pty + da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str) +{ + if (!strcmp(str, "low")) { + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } else if (!strcmp(str, "high")) { + return DA7219_AAD_JACK_INS_DET_PTY_HIGH; + } else { + dev_warn(dev, "Invalid jack insertion detection polarity"); + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } +} + static enum da7219_aad_jack_det_rate da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) { - if (!strcmp(str, "32ms_64ms")) { + if (!strcmp(str, "32_64")) { return DA7219_AAD_JACK_DET_RATE_32_64MS; - } else if (!strcmp(str, "64ms_128ms")) { + } else if (!strcmp(str, "64_128")) { return DA7219_AAD_JACK_DET_RATE_64_128MS; - } else if (!strcmp(str, "128ms_256ms")) { + } else if (!strcmp(str, "128_256")) { return DA7219_AAD_JACK_DET_RATE_128_256MS; - } else if (!strcmp(str, "256ms_512ms")) { + } else if (!strcmp(str, "256_512")) { return DA7219_AAD_JACK_DET_RATE_256_512MS; } else { dev_warn(dev, "Invalid jack detect rate"); @@ -688,6 +701,12 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) else aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + if (!fwnode_property_read_string(aad_np, "dlg,jack-ins-det-pty", &fw_str)) + aad_pdata->jack_ins_det_pty = + da7219_aad_fw_jack_ins_det_pty(dev, fw_str); + else + aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW; + if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str)) aad_pdata->jack_det_rate = da7219_aad_fw_jack_det_rate(dev, fw_str); @@ -849,6 +868,21 @@ static void da7219_aad_handle_pdata(struct snd_soc_component *component) mask |= DA7219_ADC_1_BIT_REPEAT_MASK; } snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg); + + switch (aad_pdata->jack_ins_det_pty) { + case DA7219_AAD_JACK_INS_DET_PTY_LOW: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x80); + snd_soc_component_write(component, 0xF0, 0x00); + break; + case DA7219_AAD_JACK_INS_DET_PTY_HIGH: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x00); + snd_soc_component_write(component, 0xF0, 0x00); + break; + default: + break; + } } } diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 7468ee4af2ea..600c2db58756 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2714,7 +2714,7 @@ static struct i2c_driver da7219_i2c_driver = { .of_match_table = of_match_ptr(da7219_of_match), .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, - .probe_new = da7219_i2c_probe, + .probe = da7219_i2c_probe, .id_table = da7219_i2c_id, }; diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 2c5b0b74201c..f8ca1afa8af5 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1555,7 +1555,7 @@ static struct i2c_driver da732x_i2c_driver = { .driver = { .name = "da7320", }, - .probe_new = da732x_i2c_probe, + .probe = da732x_i2c_probe, .id_table = da732x_i2c_id, }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 28043b4530df..ae20086777b5 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1531,7 +1531,7 @@ static struct i2c_driver da9055_i2c_driver = { .name = "da9055-codec", .of_match_table = of_match_ptr(da9055_of_match), }, - .probe_new = da9055_i2c_probe, + .probe = da9055_i2c_probe, .id_table = da9055_i2c_id, }; diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index a27d80956459..34cf60769b62 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -52,7 +52,12 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), + 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), +); + static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), @@ -115,7 +120,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { alc_max_gain_tlv), SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, alc_min_gain_tlv), - SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0, + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0, alc_target_tlv), SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), @@ -364,13 +369,11 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, int count = 0; es8316->sysclk = freq; + es8316->sysclk_constraints.list = NULL; + es8316->sysclk_constraints.count = 0; - if (freq == 0) { - es8316->sysclk_constraints.list = NULL; - es8316->sysclk_constraints.count = 0; - + if (freq == 0) return 0; - } ret = clk_set_rate(es8316->mclk, freq); if (ret) @@ -386,8 +389,10 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, es8316->allowed_rates[count++] = freq / ratio; } - es8316->sysclk_constraints.list = es8316->allowed_rates; - es8316->sysclk_constraints.count = count; + if (count) { + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + } return 0; } @@ -820,7 +825,7 @@ static const struct regmap_config es8316_regmap = { .use_single_write = true, .max_register = 0x53, .volatile_reg = es8316_volatile_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int es8316_i2c_probe(struct i2c_client *i2c_client) @@ -887,7 +892,7 @@ static struct i2c_driver es8316_i2c_driver = { .acpi_match_table = ACPI_PTR(es8316_acpi_match), .of_match_table = of_match_ptr(es8316_of_match), }, - .probe_new = es8316_i2c_probe, + .probe = es8316_i2c_probe, .id_table = es8316_i2c_id, }; module_i2c_driver(es8316_i2c_driver); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 28a0565c2a95..7cb5b57ae655 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -896,7 +896,7 @@ static struct i2c_driver es8326_i2c_driver = { .acpi_match_table = ACPI_PTR(es8326_acpi_match), .of_match_table = of_match_ptr(es8326_of_match), }, - .probe_new = es8326_i2c_probe, + .probe = es8326_i2c_probe, .id_table = es8326_i2c_id, }; module_i2c_driver(es8326_i2c_driver); diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 68072e99fcc7..3c4aaa0032a0 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -40,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = { .name = "es8328", .of_match_table = es8328_of_match, }, - .probe_new = es8328_i2c_probe, + .probe = es8328_i2c_probe, .id_table = es8328_id, }; diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 160adc706cc6..0bd9ba5a11b4 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -822,7 +822,7 @@ const struct regmap_config es8328_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = ES8328_REG_MAX, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 6d980fbc4207..d21f69f05342 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -495,31 +495,43 @@ static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, struct hdmi_codec_params *hp) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - int idx; - - /* Select a channel allocation that matches with ELD and pcm channels */ - idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); - if (idx < 0) { - dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", - idx); - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - return idx; + int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + u8 ca_id = 0; + bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO); + + if (pcm_audio) { + /* Select a channel allocation that matches with ELD and pcm channels */ + idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); + + if (idx < 0) { + dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", + idx); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + return idx; + } + + ca_id = hdmi_codec_channel_alloc[idx].ca_id; } memset(hp, 0, sizeof(*hp)); hdmi_audio_infoframe_init(&hp->cea); - hp->cea.channels = channels; + + if (pcm_audio) + hp->cea.channels = channels; + else + hp->cea.channels = 0; + hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; - hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; + hp->cea.channel_allocation = ca_id; hp->sample_width = sample_width; hp->sample_rate = sample_rate; hp->channels = channels; - hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; + hcp->chmap_idx = idx; return 0; } diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 50105d72b2b7..f9456133a89a 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1142,7 +1142,7 @@ static struct i2c_driver isabelle_i2c_driver = { .driver = { .name = "isabelle", }, - .probe_new = isabelle_i2c_probe, + .probe = isabelle_i2c_probe, .id_table = isabelle_i2c_id, }; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index dba161305de8..e7542f71323d 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -137,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", }, - .probe_new = lm4857_i2c_probe, + .probe = lm4857_i2c_probe, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index a2e782cc4276..a4094689b3dd 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1451,7 +1451,7 @@ static struct i2c_driver lm49453_i2c_driver = { .driver = { .name = "lm49453", }, - .probe_new = lm49453_i2c_probe, + .probe = lm49453_i2c_probe, .id_table = lm49453_i2c_id, }; diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index d711eb1da0a8..d22b4ba51ed8 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -214,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = { .driver = { .name = "max9768", }, - .probe_new = max9768_i2c_probe, + .probe = max9768_i2c_probe, .id_table = max9768_i2c_id, }; module_i2c_driver(max9768_i2c_driver); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 405ec16be2b6..8b56ee550c09 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -310,24 +310,24 @@ static const struct regmap_config max98088_regmap = { static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai, unsigned int band, u16 *coefs) { - unsigned int eq_reg; - unsigned int i; + unsigned int eq_reg; + unsigned int i; if (WARN_ON(band > 4) || WARN_ON(dai > 1)) return; - /* Load the base register address */ - eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; + /* Load the base register address */ + eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; - /* Add the band address offset, note adjustment for word address */ - eq_reg += band * (M98088_COEFS_PER_BAND << 1); + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98088_COEFS_PER_BAND << 1); - /* Step through the registers and coefs */ - for (i = 0; i < M98088_COEFS_PER_BAND; i++) { - snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); - snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); - } + /* Step through the registers and coefs */ + for (i = 0; i < M98088_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); + snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); + } } /* @@ -1789,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = { .name = "max98088", .of_match_table = of_match_ptr(max98088_of_match), }, - .probe_new = max98088_i2c_probe, + .probe = max98088_i2c_probe, .id_table = max98088_i2c_id, }; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index b419c49e1e08..2adf744c6526 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/acpi.h> @@ -1582,7 +1581,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); struct max98090_cdata *cdata; - u8 regval; + u8 regval, tdm_regval; max98090->dai_fmt = fmt; cdata = &max98090->dai[0]; @@ -1591,6 +1590,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, cdata->fmt = fmt; regval = 0; + tdm_regval = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: /* Set to consumer mode PLL - MAS mode off */ @@ -1636,7 +1636,8 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, regval |= M98090_RJ_MASK; break; case SND_SOC_DAIFMT_DSP_A: - /* Not supported mode */ + tdm_regval |= M98090_TDM_MASK; + break; default: dev_err(component->dev, "DAI format unsupported"); return -EINVAL; @@ -1665,11 +1666,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, * seen for the case of TDM mode. The remaining cases have * normal logic. */ - if (max98090->tdm_slots > 1) + if (tdm_regval) regval ^= M98090_BCI_MASK; snd_soc_component_write(component, M98090_REG_INTERFACE_FORMAT, regval); + + regval = 0; + if (tdm_regval) + regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT | + max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT; + + snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval); + snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval); } return 0; @@ -1680,33 +1690,22 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - struct max98090_cdata *cdata; - cdata = &max98090->dai[0]; if (slots < 0 || slots > 4) return -EINVAL; - max98090->tdm_slots = slots; - max98090->tdm_width = slot_width; - - if (max98090->tdm_slots > 1) { - /* SLOTL SLOTR SLOTDLY */ - snd_soc_component_write(component, M98090_REG_TDM_FORMAT, - 0 << M98090_TDM_SLOTL_SHIFT | - 1 << M98090_TDM_SLOTR_SHIFT | - 0 << M98090_TDM_SLOTDLY_SHIFT); - - /* FSW TDM */ - snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL, - M98090_TDM_MASK, - M98090_TDM_MASK); - } + if (slot_width != 16) + return -EINVAL; - /* - * Normally advisable to set TDM first, but this permits either order - */ - cdata->fmt = 0; - max98090_dai_set_fmt(codec_dai, max98090->dai_fmt); + if (rx_mask != tx_mask) + return -EINVAL; + + if (!rx_mask) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_lslot = ffs(rx_mask) - 1; + max98090->tdm_rslot = fls(rx_mask) - 1; return 0; } @@ -2409,6 +2408,9 @@ static int max98090_probe(struct snd_soc_component *component) max98090->pa1en = 0; max98090->pa2en = 0; + max98090->tdm_lslot = 0; + max98090->tdm_rslot = 1; + ret = snd_soc_component_read(component, M98090_REG_REVISION_ID); if (ret < 0) { dev_err(component->dev, "Failed to read device revision: %d\n", @@ -2691,7 +2693,7 @@ static struct i2c_driver max98090_i2c_driver = { .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, - .probe_new = max98090_i2c_probe, + .probe = max98090_i2c_probe, .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a197114b0dad..6ce8dd176e48 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1533,7 +1533,8 @@ struct max98090_priv { struct snd_soc_jack *jack; unsigned int dai_fmt; int tdm_slots; - int tdm_width; + int tdm_lslot; + int tdm_rslot; u8 lin_state; unsigned int pa1en; unsigned int pa2en; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 44aa58fcc23f..7e525d49328d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2155,7 +2155,7 @@ static struct i2c_driver max98095_i2c_driver = { .name = "max98095", .of_match_table = of_match_ptr(max98095_of_match), }, - .probe_new = max98095_i2c_probe, + .probe = max98095_i2c_probe, .id_table = max98095_i2c_id, }; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index e6b84e222b50..b5c69bba0e48 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -15,11 +15,6 @@ #include "max98363.h" static struct reg_default max98363_reg[] = { - {MAX98363_R2001_INTR_RAW, 0x0}, - {MAX98363_R2003_INTR_STATE, 0x0}, - {MAX98363_R2005_INTR_FALG, 0x0}, - {MAX98363_R2007_INTR_EN, 0x0}, - {MAX98363_R2009_INTR_CLR, 0x0}, {MAX98363_R2021_ERR_MON_CTRL, 0x0}, {MAX98363_R2022_SPK_MON_THRESH, 0x0}, {MAX98363_R2023_SPK_MON_DURATION, 0x0}, @@ -28,7 +23,6 @@ static struct reg_default max98363_reg[] = { {MAX98363_R2040_AMP_VOL, 0x0}, {MAX98363_R2041_AMP_GAIN, 0x5}, {MAX98363_R2042_DSP_CFG, 0x0}, - {MAX98363_R21FF_REV_ID, 0x0}, }; static bool max98363_readable_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index bac9d1bcf60a..f0e49179c38f 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -419,7 +419,7 @@ static struct i2c_driver max98371_i2c_driver = { .name = "max98371", .of_match_table = of_match_ptr(max98371_of_match), }, - .probe_new = max98371_i2c_probe, + .probe = max98371_i2c_probe, .id_table = max98371_i2c_id, }; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index ec0905df65d1..0fa5ceca62a2 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -9,7 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/pm_runtime.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/cdev.h> @@ -624,7 +624,7 @@ static struct i2c_driver max98373_i2c_driver = { .acpi_match_table = ACPI_PTR(max98373_acpi_match), .pm = &max98373_pm, }, - .probe_new = max98373_i2c_probe, + .probe = max98373_i2c_probe, .id_table = max98373_i2c_id, }; diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c new file mode 100644 index 000000000000..cde5e85946cb --- /dev/null +++ b/sound/soc/codecs/max98388.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98388.h" + +static struct reg_default max98388_reg[] = { + {MAX98388_R2000_SW_RESET, 0x00}, + {MAX98388_R2001_INT_RAW1, 0x00}, + {MAX98388_R2002_INT_RAW2, 0x00}, + {MAX98388_R2004_INT_STATE1, 0x00}, + {MAX98388_R2005_INT_STATE2, 0x00}, + {MAX98388_R2020_THERM_WARN_THRESH, 0x0A}, + {MAX98388_R2031_SPK_MON_THRESH, 0x58}, + {MAX98388_R2032_SPK_MON_LD_SEL, 0x08}, + {MAX98388_R2033_SPK_MON_DURATION, 0x02}, + {MAX98388_R2037_ERR_MON_CTRL, 0x01}, + {MAX98388_R2040_PCM_MODE_CFG, 0xC0}, + {MAX98388_R2041_PCM_CLK_SETUP, 0x04}, + {MAX98388_R2042_PCM_SR_SETUP, 0x88}, + {MAX98388_R2044_PCM_TX_CTRL1, 0x00}, + {MAX98388_R2045_PCM_TX_CTRL2, 0x00}, + {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF}, + {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF}, + {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF}, + {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF}, + {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF}, + {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF}, + {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF}, + {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF}, + {MAX98388_R2058_PCM_RX_SRC1, 0x00}, + {MAX98388_R2059_PCM_RX_SRC2, 0x01}, + {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00}, + {MAX98388_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98388_R205E_PCM_RX_EN, 0x00}, + {MAX98388_R205F_PCM_TX_EN, 0x00}, + {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00}, + {MAX98388_R2091_SPK_CH_CFG, 0x02}, + {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03}, + {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01}, + {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00}, + {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00}, + {MAX98388_R209F_SPK_CH_AMP_EN, 0x00}, + {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10}, + {MAX98388_R20A7_IV_DATA_EN, 0x00}, + {MAX98388_R20E0_BP_ALC_THRESH, 0x04}, + {MAX98388_R20E1_BP_ALC_RATES, 0x20}, + {MAX98388_R20E2_BP_ALC_ATTEN, 0x06}, + {MAX98388_R20E3_BP_ALC_REL, 0x02}, + {MAX98388_R20E4_BP_ALC_MUTE, 0x33}, + {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00}, + {MAX98388_R20EF_BP_ALC_EN, 0x00}, + {MAX98388_R210E_AUTO_RESTART, 0x00}, + {MAX98388_R210F_GLOBAL_EN, 0x00}, + {MAX98388_R22FF_REV_ID, 0x00}, +}; + +static int max98388_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + max98388->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98388_monomix_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1, + MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98388_monomix_switch_text); + +static const struct snd_kcontrol_new max98388_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98388_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98388_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 1, 0), + SND_SOC_DAPM_ADC("ADC Voltage", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 0, 0), + SND_SOC_DAPM_ADC("ADC Current", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98388_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1); +static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0); + +static const char * const max98388_alc_max_atten_text[] = { + "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS", + "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS", + "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum, + MAX98388_R20E2_BP_ALC_ATTEN, + MAX98388_ALC_MAX_ATTEN_SHIFT, + max98388_alc_max_atten_text); + +static const char * const max98388_thermal_warn_text[] = { + "95C", "105C", "115C", "125C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_WARN_THRESH_SHIFT, + max98388_thermal_warn_text); + +static const char * const max98388_thermal_shutdown_text[] = { + "135C", "145C", "155C", "165C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_SHDN_THRESH_SHIFT, + max98388_thermal_shutdown_text); + +static const char * const max98388_alc_thresh_single_text[] = { + "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V", + "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V", + "2.725V", "2.650V", "2.575V", "2.500V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum, + MAX98388_R20E0_BP_ALC_THRESH, + MAX98388_ALC_THRESH_SHIFT, + max98388_alc_thresh_single_text); + +static const char * const max98388_alc_attack_rate_text[] = { + "0", "10us", "20us", "40us", "80us", "160us", + "320us", "640us", "1.28ms", "2.56ms", "5.12ms", "10.24ms", + "20.48ms", "40.96ms", "81.92ms", "163.84ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_attack_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_ATTACK_RATE_SHIFT, + max98388_alc_attack_rate_text); + +static const char * const max98388_alc_release_rate_text[] = { + "20us", "40us", "80us", "160us", "320us", "640us", + "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms", + "81.92ms", "163.84ms", "327.68ms", "655.36ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_RELEASE_RATE_SHIFT, + max98388_alc_release_rate_text); + +static const char * const max98388_alc_debounce_text[] = { + "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum, + MAX98388_R20E3_BP_ALC_REL, + MAX98388_ALC_DEBOUNCE_TIME_SHIFT, + max98388_alc_debounce_text); + +static const char * const max98388_alc_mute_delay_text[] = { + "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum, + MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_DELAY_SHIFT, + max98388_alc_mute_delay_text); + +static const char * const max98388_spkmon_duration_text[] = { + "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms", + "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum, + MAX98388_R2033_SPK_MON_DURATION, + MAX98388_SPKMON_DURATION_SHIFT, + max98388_spkmon_duration_text); + +static const char * const max98388_spkmon_thresh_text[] = { + "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V", + "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V", + "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V", + "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V", + "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V", + "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V", + "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V", + "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V", + "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V", + "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V", + "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V", + "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V", + "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V", + "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V", + "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V", + "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum, + MAX98388_R2031_SPK_MON_THRESH, + MAX98388_SPKMON_THRESH_SHIFT, + max98388_spkmon_thresh_text); + +static const char * const max98388_spkmon_load_text[] = { + "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm", + "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm", + "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm", + "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm", + "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm", + "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm", + "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm", + "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm", + "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm", + "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm", + "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm", + "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm", + "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm", + "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm", + "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm", + "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm", + "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm", + "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm", + "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm", + "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm", + "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm", + "33.50ohm", "33.75ohm" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum, + MAX98388_R2032_SPK_MON_LD_SEL, + MAX98388_SPKMON_LOAD_SHIFT, + max98388_spkmon_load_text); + +static const char * const max98388_edge_rate_text[] = { + "Normal", "Reduced", "Maximum", "Increased", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_FALL_SHIFT, + max98388_edge_rate_text); + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_RISE_SHIFT, + max98388_edge_rate_text); + +static const char * const max98388_ssm_mod_text[] = { + "1.5%", "3.0%", "4.5%", "6.0%", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum, + MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_MOD_SHIFT, + max98388_ssm_mod_text); + +static const struct snd_kcontrol_new max98388_snd_controls[] = { + SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0), + /* Two Cell Mode Enable */ + SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG, + MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0), + /* Speaker Amplifier Overcurrent Automatic Restart Enable */ + SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_OVC_AUTORESTART_SHIFT, 1, 0), + /* Thermal Shutdown Automatic Restart Enable */ + SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_THERM_AUTORESTART_SHIFT, 1, 0), + /* PVDD UVLO Auto Restart */ + SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0), + /* Clock Monitor Automatic Restart Enable */ + SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_CMON_AUTORESTART_SHIFT, 1, 0), + SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_CLOCK_MON_SHIFT, 1, 0), + /* Pinknoise Generator Enable */ + SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN, + MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0), + /* Digital Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL, + 0, 0x7F, 1, max98388_digital_tlv), + /* Speaker Volume */ + SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG, + 0, 5, 0, max98388_amp_gain_tlv), + SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum), + SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum), + /* Brownout Protection Automatic Level Control */ + SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0), + SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum), + SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum), + SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum), + SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum), + SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum), + SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum), + /* Speaker Monitor */ + SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_SPK_MON_SHIFT, 1, 0), + SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum), + SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum), + SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum), + /* General Parameters */ + SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum), + SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum), + SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0), + SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum), +}; + +static const struct snd_soc_dapm_route max98388_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "ADC Voltage", NULL, "VMON"}, + { "ADC Current", NULL, "IMON"}, + { "VI Sense", "Switch", "ADC Voltage"}, + { "VI Sense", "Switch", "ADC Current"}, + { "Voltage Sense", NULL, "VI Sense"}, + { "Current Sense", NULL, "VI Sense"}, +}; + +static void max98388_reset(struct max98388_priv *max98388, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98388->regmap, + MAX98388_R2000_SW_RESET, + MAX98388_SOFT_RESET, + MAX98388_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98388_probe(struct snd_soc_component *component) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98388_reset(max98388, component->dev); + + /* General channel source configuration */ + regmap_write(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + 0x10); + + /* Enable DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R2091_SPK_CH_CFG, + 0x1); + /* Enable IMON VMON DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R20A0_IV_DATA_DSP_CTRL, + 0x3); + /* TX slot configuration */ + regmap_write(max98388->regmap, + MAX98388_R2044_PCM_TX_CTRL1, + max98388->v_slot); + + regmap_write(max98388->regmap, + MAX98388_R2045_PCM_TX_CTRL2, + max98388->i_slot); + /* Enable Auto-restart behavior by default */ + regmap_write(max98388->regmap, + MAX98388_R210E_AUTO_RESTART, 0xF); + /* Set interleave mode */ + if (max98388->interleave_mode) + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker Amplifier Channel Enable */ + regmap_update_bits(max98388->regmap, + MAX98388_R209F_SPK_CH_AMP_EN, + MAX98388_SPK_EN_MASK, 1); + + return 0; +} + +static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98388_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98388_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98388_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98388_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98388_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98388_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98388_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98388->ch_size; + int value; + + if (!max98388->tdm_mode) { + /* BCLK configuration */ + value = max98388_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98388_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg; + int status = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98388->ch_size = snd_pcm_format_width(params_format(params)); + + ret = regmap_read(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + + /* GLOBAL_EN OFF prior to the channel size re-configure */ + if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) { + ret = regmap_read(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + } + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + } + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98388_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98388_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98388_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98388_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98388_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98388_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98388_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98388_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98388_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98388_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98388_PCM_SR_96000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_MASK, + sampling_rate); + + /* set sampling rate of IV */ + if (max98388->interleave_mode && + sampling_rate > MAX98388_PCM_SR_16000) + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + sampling_rate << MAX98388_PCM_SR_IV_SHIFT); + + ret = max98388_set_clock(component, params); + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + } + + return ret; + +err: + return -EINVAL; +} + +#define MAX_NUM_SLOTS 16 +#define MAX_NUM_CH 2 + +static int max98388_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int cnt, slot_found; + int addr, bits; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98388->tdm_mode = false; + else + max98388->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98388_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH0_SHIFT, + cnt); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH1_SHIFT, + cnt); + slot_found++; + if (slot_found >= MAX_NUM_CH) + break; + } + } + + /* speaker feedback slot configuration */ + slot_found = 0; + mask = tx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8); + bits = cnt % 8; + regmap_update_bits(max98388->regmap, addr, bits, bits); + if (slot_found >= MAX_NUM_CH) + break; + } + } + + return 0; +} + +#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98388_dai_ops = { + .set_fmt = max98388_dai_set_fmt, + .hw_params = max98388_dai_hw_params, + .set_tdm_slot = max98388_dai_tdm_slot, +}; + +static bool max98388_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2: + case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2: + case MAX98388_R2020_THERM_WARN_THRESH: + case MAX98388_R2031_SPK_MON_THRESH + ... MAX98388_R2033_SPK_MON_DURATION: + case MAX98388_R2037_ERR_MON_CTRL: + case MAX98388_R2040_PCM_MODE_CFG + ... MAX98388_R2042_PCM_SR_SETUP: + case MAX98388_R2044_PCM_TX_CTRL1 + ... MAX98388_R2045_PCM_TX_CTRL2: + case MAX98388_R2050_PCM_TX_HIZ_CTRL1 + ... MAX98388_R2059_PCM_RX_SRC2: + case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH + ... MAX98388_R205F_PCM_TX_EN: + case MAX98388_R2090_SPK_CH_VOL_CTRL + ... MAX98388_R2094_SPK_AMP_ER_CTRL: + case MAX98388_R209E_SPK_CH_PINK_NOISE_EN + ... MAX98388_R209F_SPK_CH_AMP_EN: + case MAX98388_R20A0_IV_DATA_DSP_CTRL: + case MAX98388_R20A7_IV_DATA_EN: + case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE: + case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN: + case MAX98388_R210E_AUTO_RESTART: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98388_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +} + +static struct snd_soc_dai_driver max98388_dai[] = { + { + .name = "max98388-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .ops = &max98388_dai_ops, + } +}; + +static int max98388_suspend(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + + return 0; +} + +static int max98388_resume(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); + regcache_sync(max98388->regmap); + + return 0; +} + +static const struct dev_pm_ops max98388_pm = { + SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume) +}; + +static const struct regmap_config max98388_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98388_R22FF_REV_ID, + .reg_defaults = max98388_reg, + .num_reg_defaults = ARRAY_SIZE(max98388_reg), + .readable_reg = max98388_readable_register, + .volatile_reg = max98388_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98388 = { + .probe = max98388_probe, + .controls = max98388_snd_controls, + .num_controls = ARRAY_SIZE(max98388_snd_controls), + .dapm_widgets = max98388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets), + .dapm_routes = max98388_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98388_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void max98388_read_deveice_property(struct device *dev, + struct max98388_priv *max98388) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98388->v_slot = value & 0xF; + else + max98388->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98388->i_slot = value & 0xF; + else + max98388->i_slot = 1; + + if (device_property_read_bool(dev, "adi,interleave-mode")) + max98388->interleave_mode = true; + else + max98388->interleave_mode = false; +} + +static int max98388_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + + struct max98388_priv *max98388 = NULL; + + max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL); + if (!max98388) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98388); + + /* regmap initialization */ + max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap); + if (IS_ERR(max98388->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap), + "Failed to allocate register map.\n"); + + /* voltage/current slot & gpio configuration */ + max98388_read_deveice_property(&i2c->dev, max98388); + + /* Device Reset */ + max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98388->reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio), + "Unable to request GPIO\n"); + + if (max98388->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98388->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + /* Read Revision ID */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, + "Failed to read the revision ID\n"); + + dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98388, + max98388_dai, + ARRAY_SIZE(max98388_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98388_i2c_id[] = { + { "max98388", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98388_i2c_id); + +static const struct of_device_id max98388_of_match[] = { + { .compatible = "adi,max98388", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98388_of_match); + +static const struct acpi_device_id max98388_acpi_match[] = { + { "ADS8388", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98388_acpi_match); + +static struct i2c_driver max98388_i2c_driver = { + .driver = { + .name = "max98388", + .of_match_table = max98388_of_match, + .acpi_match_table = max98388_acpi_match, + .pm = pm_sleep_ptr(&max98388_pm), + }, + .probe = max98388_i2c_probe, + .id_table = max98388_i2c_id, +}; + +module_i2c_driver(max98388_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98388 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h new file mode 100644 index 000000000000..77833d181913 --- /dev/null +++ b/sound/soc/codecs/max98388.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98388.h -- MAX98388 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98388_H +#define _MAX98388_H + +/* Device Status Registers */ +#define MAX98388_R2000_SW_RESET 0x2000 +#define MAX98388_R2001_INT_RAW1 0x2001 +#define MAX98388_R2002_INT_RAW2 0x2002 +#define MAX98388_R2004_INT_STATE1 0x2004 +#define MAX98388_R2005_INT_STATE2 0x2005 +/* Thermal Protection Registers */ +#define MAX98388_R2020_THERM_WARN_THRESH 0x2020 +/* Error Monitor */ +#define MAX98388_R2031_SPK_MON_THRESH 0x2031 +#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032 +#define MAX98388_R2033_SPK_MON_DURATION 0x2033 +#define MAX98388_R2037_ERR_MON_CTRL 0x2037 +/* PCM Registers */ +#define MAX98388_R2040_PCM_MODE_CFG 0x2040 +#define MAX98388_R2041_PCM_CLK_SETUP 0x2041 +#define MAX98388_R2042_PCM_SR_SETUP 0x2042 +#define MAX98388_R2044_PCM_TX_CTRL1 0x2044 +#define MAX98388_R2045_PCM_TX_CTRL2 0x2045 +#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050 +#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051 +#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052 +#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053 +#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054 +#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055 +#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056 +#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057 +#define MAX98388_R2058_PCM_RX_SRC1 0x2058 +#define MAX98388_R2059_PCM_RX_SRC2 0x2059 +#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C +#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98388_R205E_PCM_RX_EN 0x205E +#define MAX98388_R205F_PCM_TX_EN 0x205F +/* Speaker Channel Control */ +#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090 +#define MAX98388_R2091_SPK_CH_CFG 0x2091 +#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092 +#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093 +#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094 +#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E +#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F +#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0 +#define MAX98388_R20A7_IV_DATA_EN 0x20A7 +#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0 +#define MAX98388_R20E1_BP_ALC_RATES 0x20E1 +#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2 +#define MAX98388_R20E3_BP_ALC_REL 0x20E3 +#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4 +#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE +#define MAX98388_R20EF_BP_ALC_EN 0x20EF +#define MAX98388_R210E_AUTO_RESTART 0x210E +#define MAX98388_R210F_GLOBAL_EN 0x210F +#define MAX98388_R22FF_REV_ID 0x22FF + +/* MAX98388_R2000_SW_RESET */ +#define MAX98388_SOFT_RESET (0x1 << 0) + +/* MAX98388_R2020_THERM_WARN_THRESH */ +#define MAX98388_THERM_SHDN_THRESH_SHIFT (0) +#define MAX98388_THERM_WARN_THRESH_SHIFT (2) + +/* MAX98388_R2022_PCM_TX_SRC_1 */ +#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98388_R2024_PCM_DATA_FMT_CFG */ +#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98388_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98388_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98388_R2031_SPK_MON_THRESH */ +#define MAX98388_SPKMON_THRESH_SHIFT (0) + +/* MAX98388_R2032_SPK_MON_LD_SEL */ +#define MAX98388_SPKMON_LOAD_SHIFT (0) + +/* MAX98388_R2033_SPK_MON_DURATION */ +#define MAX98388_SPKMON_DURATION_SHIFT (0) + +/* MAX98388_R2037_ERR_MON_CTRL */ +#define MAX98388_CLOCK_MON_SHIFT (0) +#define MAX98388_SPK_MON_SHIFT (1) + +/* MAX98388_R203E_AMP_PATH_GAIN */ +#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98388_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98388_FS_GAIN_MAX_SHIFT (0) + +/* MAX98388_R2041_PCM_CLK_SETUP */ +#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98388_R2042_PCM_SR_SETUP */ +#define MAX98388_PCM_SR_MASK (0xF << 0) +#define MAX98388_PCM_SR_IV_MASK (0xF << 4) +#define MAX98388_PCM_SR_IV_SHIFT (4) +#define MAX98388_PCM_SR_8000 (0x0 << 0) +#define MAX98388_PCM_SR_11025 (0x1 << 0) +#define MAX98388_PCM_SR_12000 (0x2 << 0) +#define MAX98388_PCM_SR_16000 (0x3 << 0) +#define MAX98388_PCM_SR_22050 (0x4 << 0) +#define MAX98388_PCM_SR_24000 (0x5 << 0) +#define MAX98388_PCM_SR_32000 (0x6 << 0) +#define MAX98388_PCM_SR_44100 (0x7 << 0) +#define MAX98388_PCM_SR_48000 (0x8 << 0) +#define MAX98388_PCM_SR_88200 (0x9 << 0) +#define MAX98388_PCM_SR_96000 (0xA << 0) + +/* MAX98388_R2043_AMP_EN */ +#define MAX98388_SPK_EN_MASK (0x1 << 0) +#define MAX98388_SPKFB_EN_MASK (0x1 << 1) +#define MAX98388_SPKFB_EN_SHIFT (1) + +/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98388_FLT_EN_SHIFT (4) + +/* MAX98388_R2058_PCM_RX_SRC1 */ +#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0) + +/* MAX98388_R2059_PCM_RX_SRC2 */ +#define MAX98388_RX_SRC_CH0_SHIFT (0) +#define MAX98388_RX_SRC_CH1_SHIFT (4) + +/* MAX98388_R2091_SPK_CH_CFG */ +#define MAX98388_SPK_CFG_DCBLK_SHIFT (0) +#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1) +#define MAX98388_SPK_CFG_INV_SHIFT (2) +#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3) +#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4) + +/* MAX98388_R2092_SPK_AMP_OUT_CFG */ +#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0) +#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3) + +/* MAX98388_R2093_SPK_AMP_SSM_CFG */ +#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0) +#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1) + +/* MAX98388_R2094_SPK_AMP_ER_CTRL */ +#define MAX98388_EDGE_RATE_RISE_SHIFT (0) +#define MAX98388_EDGE_RATE_FALL_SHIFT (2) + +/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */ +#define MAX98388_PINK_NOISE_GEN_SHIFT (0) + +/* MAX98388_R20A0_IV_DATA_DSP_CTRL */ +#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0) +#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1) +#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2) +#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3) +#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4) + +/* MAX98388_R20B2_BDE_L4_CFG_2 */ +#define MAX98388_LVL4_HOLD_EN_SHIFT (6) +#define MAX98388_LVL4_MUTE_EN_SHIFT (7) + +/* MAX98388_R20B5_BDE_EN */ +#define MAX98388_BDE_EN_SHIFT (0) + +/* MAX98388_R20D1_DHT_CFG */ +#define MAX98388_DHT_ROT_PNT_SHIFT (0) +#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4) + +/* MAX98388_R20D2_DHT_ATTACK_CFG */ +#define MAX98388_DHT_ATTACK_RATE_SHIFT (0) +#define MAX98388_DHT_ATTACK_STEP_SHIFT (3) + +/* MAX98388_R20D3_DHT_RELEASE_CFG */ +#define MAX98388_DHT_RELEASE_RATE_SHIFT (0) +#define MAX98388_DHT_RELEASE_STEP_SHIFT (3) + +/* MAX98388_R20D4_DHT_EN */ +#define MAX98388_DHT_EN_SHIFT (0) + +/* MAX98388_R20E0_BP_ALC_THRESH */ +#define MAX98388_ALC_THRESH_SHIFT (0) + +/* MAX98388_R20E1_BP_ALC_RATES */ +#define MAX98388_ALC_RELEASE_RATE_SHIFT (0) +#define MAX98388_ALC_ATTACK_RATE_SHIFT (4) + +/* MAX98388_R20E2_BP_ALC_ATTEN */ +#define MAX98388_ALC_MAX_ATTEN_SHIFT (0) + +/* MAX98388_R20E3_BP_ALC_REL */ +#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0) + +/* MAX98388_R20E4_BP_ALC_MUTE */ +#define MAX98388_ALC_MUTE_EN_SHIFT (0) +#define MAX98388_ALC_MUTE_DELAY_SHIFT (1) +#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4) +#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5) + +/* MAX98388_R210E_AUTO_RESTART */ +#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0) +#define MAX98388_THERM_AUTORESTART_SHIFT (1) +#define MAX98388_OVC_AUTORESTART_SHIFT (2) +#define MAX98388_CMON_AUTORESTART_SHIFT (3) + +/* MAX98388_R210F_GLOBAL_EN */ +#define MAX98388_GLOBAL_EN_MASK (0x1 << 0) + +struct max98388_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; +}; + +#endif diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 7a5260ff8d6b..5b8e78e51630 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1133,7 +1133,7 @@ static struct i2c_driver max98390_i2c_driver = { .acpi_match_table = ACPI_PTR(max98390_acpi_match), .pm = &max98390_pm, }, - .probe_new = max98390_i2c_probe, + .probe = max98390_i2c_probe, .id_table = max98390_i2c_id, }; diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index db3de41f10e0..3a1d8c211f3c 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1907,7 +1907,7 @@ static struct i2c_driver max98396_i2c_driver = { .acpi_match_table = ACPI_PTR(max98396_acpi_match), .pm = &max98396_pm, }, - .probe_new = max98396_i2c_probe, + .probe = max98396_i2c_probe, .id_table = max98396_i2c_id, }; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index a6733396b0ca..8b012a85360a 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -329,7 +329,7 @@ static struct i2c_driver max9850_i2c_driver = { .driver = { .name = "max9850", }, - .probe_new = max9850_i2c_probe, + .probe = max9850_i2c_probe, .id_table = max9850_i2c_id, }; diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 0daa2a3dd621..93412b966b33 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = { .name = "max98504", .of_match_table = of_match_ptr(max98504_of_match), }, - .probe_new = max98504_i2c_probe, + .probe = max98504_i2c_probe, .id_table = max98504_i2c_id, }; module_i2c_driver(max98504_i2c_driver); diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 5edd6f90f8a7..8637fff307ad 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -756,7 +756,7 @@ static struct i2c_driver max98520_i2c_driver = { .of_match_table = of_match_ptr(max98520_of_match), .pm = &max98520_pm, }, - .probe_new = max98520_i2c_probe, + .probe = max98520_i2c_probe, .id_table = max98520_i2c_id, }; diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 9611ab1e79e5..4015ed2c47ec 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -723,7 +723,7 @@ static const struct of_device_id max9860_of_match[] = { MODULE_DEVICE_TABLE(of, max9860_of_match); static struct i2c_driver max9860_i2c_driver = { - .probe_new = max9860_probe, + .probe = max9860_probe, .remove = max9860_remove, .id_table = max9860_i2c_id, .driver = { diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index ae552d72beec..b616ad39858c 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -702,7 +702,7 @@ static struct i2c_driver max9867_i2c_driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), }, - .probe_new = max9867_i2c_probe, + .probe = max9867_i2c_probe, .id_table = max9867_i2c_id, }; diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 3bf86328d5cd..2ae64fcf29c7 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -160,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = { .driver = { .name = "max9877", }, - .probe_new = max9877_i2c_probe, + .probe = max9877_i2c_probe, .id_table = max9877_i2c_id, }; diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index c24d9f2c8874..a9c1d85cd0d5 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -635,7 +635,7 @@ static struct i2c_driver max98925_i2c_driver = { .name = "max98925", .of_match_table = of_match_ptr(max98925_of_match), }, - .probe_new = max98925_i2c_probe, + .probe = max98925_i2c_probe, .id_table = max98925_i2c_id, }; diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index bffd56e240e9..bdc508e23e59 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -582,7 +582,7 @@ static struct i2c_driver max98926_i2c_driver = { .name = "max98926", .of_match_table = of_match_ptr(max98926_of_match), }, - .probe_new = max98926_i2c_probe, + .probe = max98926_i2c_probe, .id_table = max98926_i2c_id, }; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 331d3e1d735c..0aaf2e6ae78d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -973,7 +973,7 @@ static struct i2c_driver max98927_i2c_driver = { .acpi_match_table = ACPI_PTR(max98927_acpi_match), .pm = &max98927_pm, }, - .probe_new = max98927_i2c_probe, + .probe = max98927_i2c_probe, .remove = max98927_i2c_remove, .id_table = max98927_i2c_id, }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 3c6ac77379cb..a45ef9d65703 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -581,7 +581,7 @@ static struct i2c_driver ml26124_i2c_driver = { .driver = { .name = "ml26124", }, - .probe_new = ml26124_i2c_probe, + .probe = ml26124_i2c_probe, .id_table = ml26124_i2c_id, }; diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index cb487e63615c..30690479ec17 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -18,6 +18,20 @@ #include "mt6359.h" +static void mt6359_set_gpio_smt(struct mt6359_priv *priv) +{ + /* set gpio SMT mode */ + regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0); +} + +static void mt6359_set_gpio_driving(struct mt6359_priv *priv) +{ + /* 8:4mA(default), a:8mA, c:12mA, e:16mA */ + regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88); +} + static void mt6359_set_playback_gpio(struct mt6359_priv *priv) { /* set gpio mosi mode, clk / data mosi */ @@ -360,8 +374,34 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = 0; int index = ucontrol->value.integer.value[0]; + int orig_gain[2], new_gain[2]; int ret; + switch (mc->reg) { + case MT6359_ZCD_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + case MT6359_AUDENC_ANA_CON0: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + break; + case MT6359_AUDENC_ANA_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + break; + case MT6359_AUDENC_ANA_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + break; + default: + return -EINVAL; + } + ret = snd_soc_put_volsw(kcontrol, ucontrol); if (ret < 0) return ret; @@ -373,6 +413,8 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; break; case MT6359_ZCD_CON1: regmap_read(priv->regmap, MT6359_ZCD_CON1, ®); @@ -380,35 +422,82 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; break; case MT6359_ZCD_CON3: regmap_read(priv->regmap, MT6359_ZCD_CON3, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; break; case MT6359_AUDENC_ANA_CON0: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; break; case MT6359_AUDENC_ANA_CON1: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; break; case MT6359_AUDENC_ANA_CON2: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] = (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; break; } + ret = 0; + if (orig_gain[0] != new_gain[0]) { + ret = 1; + } else if (snd_soc_volsw_is_stereo(mc)) { + if (orig_gain[1] != new_gain[1]) + ret = 1; + } + dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n", __func__, kcontrol->id.name, mc->reg, reg, index); return ret; } +static int mt6359_get_playback_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + default: + return -EINVAL; + } + + return 0; +} + /* MUX */ /* LOL MUX */ @@ -1070,9 +1159,10 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", - __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + __func__, event, mux); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1110,14 +1200,29 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, /* Enable AUD_CLK */ mt6359_set_decoder_clk(priv, true); - /* Enable Audio DAC (3rd DAC) */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); - /* Enable low-noise mode of DAC */ - if (priv->dev_counter[DEVICE_HP] == 0) - regmap_write(priv->regmap, - MT6359_AUDDEC_ANA_CON9, 0x0001); - /* Switch LOL MUX to audio 3rd DAC */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + /* Switch LOL MUX to audio DAC */ + if (mux == LO_MUX_L_DAC) { + if (priv->dev_counter[DEVICE_HP] > 0) { + dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n", + __func__, priv->dev_counter[DEVICE_HP]); + break; + } + /* Enable DACL and switch HP MUX to open*/ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009); + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + usleep_range(100, 120); + /* Switch LOL MUX to DACL */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117); + } else if (mux == LO_MUX_3RD_DAC) { + /* Enable Audio DAC (3rd DAC) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); + /* Enable low-noise mode of DAC */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch LOL MUX to audio 3rd DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + } break; case SND_SOC_DAPM_PRE_PMD: /* Switch LOL MUX to open */ @@ -1129,6 +1234,15 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x000f, 0x0000); + if (mux == LO_MUX_L_DAC) { + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + } + /* Disable AUD_CLK */ mt6359_set_decoder_clk(priv, false); @@ -2358,6 +2472,10 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"}, {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + {"MISO0_MUX", NULL, "UL_SRC"}, + {"MISO1_MUX", NULL, "UL_SRC"}, + {"MISO2_MUX", NULL, "UL_SRC_34"}, + {"UL_SRC_MUX", "AMIC", "ADC_L"}, {"UL_SRC_MUX", "AMIC", "ADC_R"}, {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"}, @@ -2497,6 +2615,7 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { /* Lineout Path */ {"LOL Mux", "Playback", "DAC_3RD"}, + {"LOL Mux", "Playback_L_DAC", "DACL"}, {"LINEOUT L", NULL, "LOL Mux"}, /* Headphone Path */ @@ -2666,6 +2785,8 @@ static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt) 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT); /* set gpio */ + mt6359_set_gpio_smt(priv); + mt6359_set_gpio_driving(priv); mt6359_reset_playback_gpio(priv); mt6359_reset_capture_gpio(priv); @@ -2697,22 +2818,23 @@ static void mt6359_codec_remove(struct snd_soc_component *cmpnt) cmpnt->regmap = NULL; } -static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0); static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0); static const struct snd_kcontrol_new mt6359_snd_controls[] = { /* dl pga gain */ SOC_DOUBLE_EXT_TLV("Headset Volume", - MT6359_ZCD_CON2, 0, 7, 0x1E, 0, - snd_soc_get_volsw, mt6359_put_volsw, - hp_playback_tlv), + MT6359_ZCD_CON2, 0, 7, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_DOUBLE_EXT_TLV("Lineout Volume", MT6359_ZCD_CON1, 0, 7, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_SINGLE_EXT_TLV("Handset Volume", MT6359_ZCD_CON3, 0, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), /* ul pga gain */ SOC_SINGLE_EXT_TLV("PGA1 Volume", diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index cc2df5f7ea19..5c50c7de26cd 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -570,7 +570,7 @@ static struct i2c_driver mt6660_i2c_driver = { .of_match_table = of_match_ptr(mt6660_of_id), .pm = &mt6660_dev_pm_ops, }, - .probe_new = mt6660_i2c_probe, + .probe = mt6660_i2c_probe, .remove = mt6660_i2c_remove, .id_table = mt6660_i2c_id, }; diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 0626d5694c22..2174a89772fc 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -890,7 +890,7 @@ static struct i2c_driver nau8540_i2c_driver = { .name = "nau8540", .of_match_table = of_match_ptr(nau8540_of_ids), }, - .probe_new = nau8540_i2c_probe, + .probe = nau8540_i2c_probe, .id_table = nau8540_i2c_ids, }; module_i2c_driver(nau8540_i2c_driver); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index ccb512c21d74..47f000cd4d99 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -914,7 +914,7 @@ static struct i2c_driver nau8810_i2c_driver = { .name = "nau8810", .of_match_table = of_match_ptr(nau8810_of_match), }, - .probe_new = nau8810_i2c_probe, + .probe = nau8810_i2c_probe, .id_table = nau8810_i2c_id, }; diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index fee970427a24..96d75882b33a 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1859,7 +1859,7 @@ static struct i2c_driver nau8821_driver = { .of_match_table = of_match_ptr(nau8821_of_ids), .acpi_match_table = ACPI_PTR(nau8821_acpi_match), }, - .probe_new = nau8821_i2c_probe, + .probe = nau8821_i2c_probe, .id_table = nau8821_i2c_ids, }; module_i2c_driver(nau8821_driver); diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index d5006d8de639..ff3024899f45 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1166,7 +1166,7 @@ static struct i2c_driver nau8822_i2c_driver = { .name = "nau8822", .of_match_table = of_match_ptr(nau8822_of_match), }, - .probe_new = nau8822_i2c_probe, + .probe = nau8822_i2c_probe, .id_table = nau8822_i2c_id, }; module_i2c_driver(nau8822_i2c_driver); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 5a4db8944d06..704af1cf8cbf 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -2030,7 +2030,7 @@ static struct i2c_driver nau8824_i2c_driver = { .of_match_table = of_match_ptr(nau8824_of_ids), .acpi_match_table = ACPI_PTR(nau8824_acpi_match), }, - .probe_new = nau8824_i2c_probe, + .probe = nau8824_i2c_probe, .id_table = nau8824_i2c_ids, }; module_i2c_driver(nau8824_i2c_driver); diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f4eb999761a4..9e0e4ddf128e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -53,6 +53,7 @@ struct nau8825_fll { int mclk_src; int ratio; int fll_frac; + int fll_frac_num; int fll_int; int clk_ref_div; }; @@ -178,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_CLASSG_CTRL, 0x0 }, { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_FLL2_LOWER, 0x0 }, + { NAU8825_REG_FLL2_UPPER, 0x0 }, { NAU8825_REG_BIAS_ADJ, 0x0 }, { NAU8825_REG_TRIM_SETTINGS, 0x0 }, { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, @@ -200,6 +203,23 @@ static struct reg_default nau8825_xtalk_baktab[] = { { NAU8825_REG_DACR_CTRL, 0x02cf }, }; +/* The regmap patch for Rev C */ +static const struct reg_sequence nau8825_regmap_patch[] = { + { NAU8825_REG_FLL2, 0x0000 }, + { NAU8825_REG_FLL4, 0x8010 }, + { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0800 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 }, + { NAU8825_REG_FLL2_LOWER, 0x26e9 }, + { NAU8825_REG_FLL2_UPPER, 0x0031 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0220 }, + { NAU8825_REG_MIC_BIAS, 0x0046 }, +}; + + static const unsigned short logtable[256] = { 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, @@ -608,8 +628,13 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825) regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0); /* Power up left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); } static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) @@ -622,9 +647,14 @@ static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); /* Power down left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + /* Enable the TESTDAC and disable L/R HP impedance */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP | @@ -855,7 +885,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: - case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -881,6 +911,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -911,6 +942,32 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) } } +static int nau8825_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, + NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, 0); + break; + default: + break; + } + + return 0; +} + static int nau8825_adc_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -970,10 +1027,25 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, /* Disables the TESTDAC to let DAC signal pass through. */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); break; case SND_SOC_DAPM_POST_PMD: regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + break; default: return -EINVAL; @@ -1127,8 +1199,8 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC"), SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), - SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, - NULL, 0), + SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, nau8825_adc_event, SND_SOC_DAPM_POST_PMU | @@ -1181,12 +1253,13 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), SND_SOC_DAPM_PGA_S("Output DACL", 7, - NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_S("Output DACR", 7, - NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), @@ -1678,6 +1751,10 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825) { struct regmap *regmap = nau8825->regmap; + /* Enable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE); + /* Enable headset jack type detection complete interruption and * jack ejection interruption. */ @@ -1929,6 +2006,9 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_MCLK_SRC_MASK, 0xf); + /* Disable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0); + /* Leaving HPOL/R grounded after jack insert by default. They will be * ungrounded as part of the widget power up sequence at the beginning * of playback to reduce pop. @@ -2173,9 +2253,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); /* Disable DACR/L power */ - regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input * signal to avoid any glitches due to power up transients in both * the analog and digital DAC circuit. @@ -2307,9 +2388,12 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional * input based on FDCO, FREF and FLL ratio. */ - fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); - fll_param->fll_int = (fvco >> 16) & 0x3FF; - fll_param->fll_frac = fvco & 0xFFFF; + fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF; + if (fll_param->fll_frac_num == 16) + fll_param->fll_frac = fvco & 0xFFFF; + else + fll_param->fll_frac = fvco & 0xFFFFFF; return 0; } @@ -2323,8 +2407,16 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); - /* FLL 16-bit fractional input */ - regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 16/24 bit fractional input */ + if (fll_param->fll_frac_num == 16) + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, + fll_param->fll_frac); + else { + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER, + fll_param->fll_frac & 0xffff); + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER, + (fll_param->fll_frac >> 16) & 0xff); + } /* FLL 10-bit integer input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); @@ -2366,6 +2458,11 @@ static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int struct nau8825_fll fll_param; int ret, fs; + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + fll_param.fll_frac_num = 16; + else + fll_param.fll_frac_num = 24; + fs = freq_out / 256; ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); if (ret < 0) { @@ -2897,8 +2994,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c) ret); return ret; } - if ((value & NAU8825_SOFTWARE_ID_MASK) != - NAU8825_SOFTWARE_ID_NAU8825) { + nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK; + switch (nau8825->sw_id) { + case NAU8825_SOFTWARE_ID_NAU8825: + break; + case NAU8825_SOFTWARE_ID_NAU8825C: + ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch, + ARRAY_SIZE(nau8825_regmap_patch)); + if (ret) { + dev_err(dev, "Failed to register Rev C patch: %d\n", ret); + return ret; + } + break; + default: dev_err(dev, "Not a NAU8825 chip\n"); return -ENODEV; } @@ -2944,7 +3052,7 @@ static struct i2c_driver nau8825_driver = { .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, - .probe_new = nau8825_i2c_probe, + .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, .id_table = nau8825_i2c_ids, }; diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 44b62bc3880f..2abfbb5184da 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -75,6 +75,8 @@ #define NAU8825_REG_MISC_CTRL 0x55 #define NAU8825_REG_I2C_DEVICE_ID 0x58 #define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_FLL2_LOWER 0x5a +#define NAU8825_REG_FLL2_UPPER 0x5b #define NAU8825_REG_BIAS_ADJ 0x66 #define NAU8825_REG_TRIM_SETTINGS 0x68 #define NAU8825_REG_ANALOG_CONTROL_1 0x69 @@ -386,6 +388,7 @@ #define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_NAU8825 0x0 +#define NAU8825_SOFTWARE_ID_NAU8825C 0x1 /* BIAS_ADJ (0x66) */ #define NAU8825_BIAS_HPR_IMP (1 << 15) @@ -442,10 +445,17 @@ /* BOOST (0x76) */ #define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_DISCHRG_EN (1 << 11) #define NAU8825_HP_BOOST_DIS (1 << 9) #define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) +/* FEPGA (0x77) */ +#define NAU8825_ACDC_CTRL_SFT 14 +#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT) + /* POWER_UP_CONTROL (0x7f) */ #define NAU8825_POWERUP_INTEGR_R (1 << 5) #define NAU8825_POWERUP_INTEGR_L (1 << 4) @@ -490,6 +500,7 @@ struct nau8825 { struct clk *mclk; struct work_struct xtalk_work; struct semaphore xtalk_sem; + int sw_id; int irq; int mclk_freq; /* 0 - mclk is disabled */ int button_pressed; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 3591f6f53901..735e1942b530 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -327,7 +327,7 @@ static struct i2c_driver pcm1681_i2c_driver = { .of_match_table = of_match_ptr(pcm1681_dt_ids), }, .id_table = pcm1681_i2c_id, - .probe_new = pcm1681_i2c_probe, + .probe = pcm1681_i2c_probe, }; module_i2c_driver(pcm1681_i2c_driver); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index fafe0dcbe4ea..f2d0b4d21e41 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -52,7 +52,7 @@ static struct i2c_driver pcm1789_i2c_driver = { .of_match_table = of_match_ptr(pcm1789_of_match), }, .id_table = pcm1789_i2c_ids, - .probe_new = pcm1789_i2c_probe, + .probe = pcm1789_i2c_probe, .remove = pcm1789_i2c_remove, }; diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index e20fe3a85af8..10579681f44b 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = { .of_match_table = of_match_ptr(pcm179x_of_match), }, .id_table = pcm179x_i2c_ids, - .probe_new = pcm179x_i2c_probe, + .probe = pcm179x_i2c_probe, }; module_i2c_driver(pcm179x_i2c_driver); diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index 932c8d41c3ea..a514ebd1b68a 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -46,7 +46,7 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c) } static struct i2c_driver pcm186x_i2c_driver = { - .probe_new = pcm186x_i2c_probe, + .probe = pcm186x_i2c_probe, .id_table = pcm186x_i2c_id, .driver = { .name = "pcm186x", diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index dd21803ba13c..451a8fd8fac5 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -12,7 +12,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c index 2388098eeca3..5330cf46b127 100644 --- a/sound/soc/codecs/pcm3060-i2c.c +++ b/sound/soc/codecs/pcm3060-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = { #endif /* CONFIG_OF */ }, .id_table = pcm3060_i2c_id, - .probe_new = pcm3060_i2c_probe, + .probe = pcm3060_i2c_probe, }; module_i2c_driver(pcm3060_i2c_driver); diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index a0eec82e9872..7052cc0c97d1 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -44,7 +44,7 @@ static const struct of_device_id pcm3168a_of_match[] = { MODULE_DEVICE_TABLE(of, pcm3168a_of_match); static struct i2c_driver pcm3168a_i2c_driver = { - .probe_new = pcm3168a_i2c_probe, + .probe = pcm3168a_i2c_probe, .remove = pcm3168a_i2c_remove, .id_table = pcm3168a_i2c_id, .driver = { diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 9dfbbe8f4a0b..5cd2b64b9337 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); #endif static struct i2c_driver pcm512x_i2c_driver = { - .probe_new = pcm512x_i2c_probe, + .probe = pcm512x_i2c_probe, .remove = pcm512x_i2c_remove, .id_table = pcm512x_i2c_id, .driver = { diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 1d523bfd9d84..9697aefc6e03 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <sound/dmaengine_pcm.h> diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index c1568216126e..42bac8150f62 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -2184,7 +2184,7 @@ static const struct regmap_config rt1011_regmap = { .max_register = RT1011_MAX_REG + 1, .volatile_reg = rt1011_volatile_register, .readable_reg = rt1011_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1011_reg, .num_reg_defaults = ARRAY_SIZE(rt1011_reg), .use_single_read = true, @@ -2483,7 +2483,7 @@ static struct i2c_driver rt1011_i2c_driver = { .of_match_table = of_match_ptr(rt1011_of_match), .acpi_match_table = ACPI_PTR(rt1011_acpi_match) }, - .probe_new = rt1011_i2c_probe, + .probe = rt1011_i2c_probe, .shutdown = rt1011_i2c_shutdown, .id_table = rt1011_i2c_id, }; diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 57d0f1c69e46..38d9f69b08d6 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1015_i2c_driver = { .of_match_table = of_match_ptr(rt1015_of_match), .acpi_match_table = ACPI_PTR(rt1015_acpi_match), }, - .probe_new = rt1015_i2c_probe, + .probe = rt1015_i2c_probe, .shutdown = rt1015_i2c_shutdown, .id_table = rt1015_i2c_id, }; diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 37eeec650f03..b1e69fa290b2 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -683,7 +683,7 @@ static struct i2c_driver rt1016_i2c_driver = { .of_match_table = of_match_ptr(rt1016_of_match), .acpi_match_table = ACPI_PTR(rt1016_acpi_match), }, - .probe_new = rt1016_i2c_probe, + .probe = rt1016_i2c_probe, .shutdown = rt1016_i2c_shutdown, .id_table = rt1016_i2c_id, }; diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index dff2596c81eb..fd55049920c1 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -535,7 +535,7 @@ static const struct regmap_config rt1019_regmap = { .max_register = RT1019_BEEP_2, .volatile_reg = rt1019_volatile_register, .readable_reg = rt1019_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1019_reg, .num_reg_defaults = ARRAY_SIZE(rt1019_reg), }; @@ -600,7 +600,7 @@ static struct i2c_driver rt1019_i2c_driver = { .of_match_table = of_match_ptr(rt1019_of_match), .acpi_match_table = ACPI_PTR(rt1019_acpi_match), }, - .probe_new = rt1019_i2c_probe, + .probe = rt1019_i2c_probe, .id_table = rt1019_i2c_id, }; module_i2c_driver(rt1019_i2c_driver); diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 5b39a440b6dc..59895131e6e0 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -955,7 +955,7 @@ static const struct regmap_config rt1305_regmap = { RT1305_PR_SPACING), .volatile_reg = rt1305_volatile_register, .readable_reg = rt1305_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1305_reg, .num_reg_defaults = ARRAY_SIZE(rt1305_reg), .ranges = rt1305_ranges, @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1305_i2c_driver = { .acpi_match_table = ACPI_PTR(rt1305_acpi_match) #endif }, - .probe_new = rt1305_i2c_probe, + .probe = rt1305_i2c_probe, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 1797af824f60..f43520ca3187 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -68,7 +68,7 @@ static const struct regmap_config rt1308_sdw_regmap = { .max_register = 0xcfff, .reg_defaults = rt1308_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -304,9 +304,6 @@ static int rt1308_update_status(struct sdw_slave *slave, { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1308->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1308->hw_init = false; @@ -314,7 +311,7 @@ static int rt1308_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index 04ff18fa18e2..f816c73e247e 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -159,7 +159,6 @@ struct rt1308_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index d2a8e9fe3e23..9d07756cda40 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -773,7 +773,7 @@ static const struct regmap_config rt1308_regmap = { .max_register = RT1308_MAX_REG, .volatile_reg = rt1308_volatile_register, .readable_reg = rt1308_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1308_reg, .num_reg_defaults = ARRAY_SIZE(rt1308_reg), .use_single_read = true, @@ -862,7 +862,7 @@ static struct i2c_driver rt1308_i2c_driver = { .of_match_table = of_match_ptr(rt1308_of_match), .acpi_match_table = ACPI_PTR(rt1308_acpi_match), }, - .probe_new = rt1308_i2c_probe, + .probe = rt1308_i2c_probe, .shutdown = rt1308_i2c_shutdown, .id_table = rt1308_i2c_id, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 2ee5e763e345..721821d9e9af 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -188,7 +188,7 @@ static const struct regmap_config rt1316_sdw_regmap = { .max_register = 0x4108ffff, .reg_defaults = rt1316_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -323,9 +323,6 @@ static int rt1316_update_status(struct sdw_slave *slave, { struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1316->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1316->hw_init = false; @@ -333,7 +330,7 @@ static int rt1316_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED) + if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h index e37121655bc1..dc1bfe40edd3 100644 --- a/sound/soc/codecs/rt1316-sdw.h +++ b/sound/soc/codecs/rt1316-sdw.h @@ -42,7 +42,6 @@ struct rt1316_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 795accedc22c..16d750102c8c 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -337,7 +337,7 @@ static const struct regmap_config rt1318_sdw_regmap = { .max_register = 0x41081488, .reg_defaults = rt1318_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -456,9 +456,6 @@ static int rt1318_update_status(struct sdw_slave *slave, { struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1318->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1318->hw_init = false; @@ -466,7 +463,7 @@ static int rt1318_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1318->hw_init || rt1318->status != SDW_SLAVE_ATTACHED) + if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h index 85918c184f16..86e83d63a017 100644 --- a/sound/soc/codecs/rt1318-sdw.h +++ b/sound/soc/codecs/rt1318-sdw.h @@ -88,7 +88,6 @@ struct rt1318_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 4667bf7561b1..9a33e3776b55 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1221,7 +1221,7 @@ static struct i2c_driver rt274_i2c_driver = { .of_match_table = of_match_ptr(rt274_of_match), #endif }, - .probe_new = rt274_i2c_probe, + .probe = rt274_i2c_probe, .remove = rt274_i2c_remove, .id_table = rt274_i2c_id, }; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ceb56647e369..981155b046fd 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1263,7 +1263,7 @@ static struct i2c_driver rt286_i2c_driver = { .name = "rt286", .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, - .probe_new = rt286_i2c_probe, + .probe = rt286_i2c_probe, .remove = rt286_i2c_remove, .id_table = rt286_i2c_id, }; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index cea26f3a02b6..8fbd25ad9b47 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1311,7 +1311,7 @@ static struct i2c_driver rt298_i2c_driver = { .name = "rt298", .acpi_match_table = ACPI_PTR(rt298_acpi_match), }, - .probe_new = rt298_i2c_probe, + .probe = rt298_i2c_probe, .remove = rt298_i2c_remove, .id_table = rt298_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index b9bcf04d4dc9..b3aac2373357 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1195,7 +1195,7 @@ static const struct regmap_config rt5514_regmap = { .reg_read = rt5514_i2c_read, .reg_write = rt5514_i2c_write, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5514_reg, .num_reg_defaults = ARRAY_SIZE(rt5514_reg), .use_single_read = true, @@ -1328,7 +1328,7 @@ static struct i2c_driver rt5514_i2c_driver = { .of_match_table = of_match_ptr(rt5514_of_match), .pm = &rt5514_i2_pm_ops, }, - .probe_new = rt5514_i2c_probe, + .probe = rt5514_i2c_probe, .id_table = rt5514_i2c_id, }; module_i2c_driver(rt5514_i2c_driver); diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 948abde10463..c13108b51eaf 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1315,7 +1315,7 @@ static const struct regmap_config rt5616_regmap = { RT5616_PR_SPACING), .volatile_reg = rt5616_volatile_register, .readable_reg = rt5616_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5616_reg, .num_reg_defaults = ARRAY_SIZE(rt5616_reg), .ranges = rt5616_ranges, @@ -1404,7 +1404,7 @@ static struct i2c_driver rt5616_i2c_driver = { .name = "rt5616", .of_match_table = of_match_ptr(rt5616_of_match), }, - .probe_new = rt5616_i2c_probe, + .probe = rt5616_i2c_probe, .remove = rt5616_i2c_remove, .shutdown = rt5616_i2c_shutdown, .id_table = rt5616_i2c_id, diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 55c232413e2b..a64e66c2d3c4 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1693,7 +1693,7 @@ static const struct regmap_config rt5631_regmap_config = { .max_register = RT5631_VENDOR_ID2, .reg_defaults = rt5631_reg, .num_reg_defaults = ARRAY_SIZE(rt5631_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1728,7 +1728,7 @@ static struct i2c_driver rt5631_i2c_driver = { .name = "rt5631", .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, - .probe_new = rt5631_i2c_probe, + .probe = rt5631_i2c_probe, .remove = rt5631_i2c_remove, .id_table = rt5631_i2c_id, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 139257055507..0ed4fa261abf 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2949,7 +2949,7 @@ static const struct regmap_config rt5640_regmap = { .volatile_reg = rt5640_volatile_register, .readable_reg = rt5640_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5640_reg, .num_reg_defaults = ARRAY_SIZE(rt5640_reg), .ranges = rt5640_ranges, @@ -3079,7 +3079,7 @@ static struct i2c_driver rt5640_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5640_acpi_match), .of_match_table = of_match_ptr(rt5640_of_match), }, - .probe_new = rt5640_i2c_probe, + .probe = rt5640_i2c_probe, .id_table = rt5640_i2c_id, }; module_i2c_driver(rt5640_i2c_driver); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 7c7cbb6362ea..acc7fb1581b2 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3546,7 +3546,7 @@ static const struct regmap_config rt5645_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5645_reg, .num_reg_defaults = ARRAY_SIZE(rt5645_reg), .ranges = rt5645_ranges, @@ -3563,7 +3563,7 @@ static const struct regmap_config rt5650_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5650_reg, .num_reg_defaults = ARRAY_SIZE(rt5650_reg), .ranges = rt5645_ranges, @@ -4184,7 +4184,7 @@ static struct i2c_driver rt5645_i2c_driver = { .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), }, - .probe_new = rt5645_i2c_probe, + .probe = rt5645_i2c_probe, .remove = rt5645_i2c_remove, .shutdown = rt5645_i2c_shutdown, .id_table = rt5645_i2c_id, diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index df90af906563..0cee4fd1c84b 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2172,7 +2172,7 @@ static const struct regmap_config rt5651_regmap = { .volatile_reg = rt5651_volatile_register, .readable_reg = rt5651_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5651_reg, .num_reg_defaults = ARRAY_SIZE(rt5651_reg), .ranges = rt5651_ranges, @@ -2279,7 +2279,7 @@ static struct i2c_driver rt5651_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5651_acpi_match), .of_match_table = of_match_ptr(rt5651_of_match), }, - .probe_new = rt5651_i2c_probe, + .probe = rt5651_i2c_probe, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 5e21e3c37ab5..df6f0d769bbd 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4141,13 +4141,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) regmap_write(rt5659->regmap, RT5659_RESET, 0); /* Check if MCLK provided */ - rt5659->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (IS_ERR(rt5659->mclk)) { - if (PTR_ERR(rt5659->mclk) != -ENOENT) - return PTR_ERR(rt5659->mclk); - /* Otherwise mark the mclk pointer to NULL */ - rt5659->mclk = NULL; - } + rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5659->mclk)) + return PTR_ERR(rt5659->mclk); rt5659_calibrate(rt5659); @@ -4340,7 +4336,7 @@ static struct i2c_driver rt5659_i2c_driver = { .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, - .probe_new = rt5659_i2c_probe, + .probe = rt5659_i2c_probe, .shutdown = rt5659_i2c_shutdown, .id_table = rt5659_i2c_id, }; diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 341baa29fdb1..eade087b2d8b 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1221,7 +1221,7 @@ static const struct regmap_config rt5660_regmap = { .volatile_reg = rt5660_volatile_register, .readable_reg = rt5660_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5660_reg, .num_reg_defaults = ARRAY_SIZE(rt5660_reg), .ranges = rt5660_ranges, @@ -1341,7 +1341,7 @@ static struct i2c_driver rt5660_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5660_acpi_match), .of_match_table = of_match_ptr(rt5660_of_match), }, - .probe_new = rt5660_i2c_probe, + .probe = rt5660_i2c_probe, .id_table = rt5660_i2c_id, }; module_i2c_driver(rt5660_i2c_driver); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index f73751dbde30..77246f84de29 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3268,7 +3268,7 @@ static const struct regmap_config rt5663_v2_regmap = { .max_register = 0x07fa, .volatile_reg = rt5663_v2_volatile_register, .readable_reg = rt5663_v2_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_v2_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg), }; @@ -3281,7 +3281,7 @@ static const struct regmap_config rt5663_regmap = { .max_register = 0x03f3, .volatile_reg = rt5663_volatile_register, .readable_reg = rt5663_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_reg), }; @@ -3733,7 +3733,7 @@ static struct i2c_driver rt5663_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5663_acpi_match), .of_match_table = of_match_ptr(rt5663_of_match), }, - .probe_new = rt5663_i2c_probe, + .probe = rt5663_i2c_probe, .remove = rt5663_i2c_remove, .shutdown = rt5663_i2c_shutdown, .id_table = rt5663_i2c_id, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 17afaef85c77..83c367af91da 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4626,7 +4626,7 @@ static const struct regmap_config rt5665_regmap = { .max_register = 0x0400, .volatile_reg = rt5665_volatile_register, .readable_reg = rt5665_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5665_reg, .num_reg_defaults = ARRAY_SIZE(rt5665_reg), .use_single_read = true, @@ -4968,7 +4968,7 @@ static struct i2c_driver rt5665_i2c_driver = { .of_match_table = of_match_ptr(rt5665_of_match), .acpi_match_table = ACPI_PTR(rt5665_acpi_match), }, - .probe_new = rt5665_i2c_probe, + .probe = rt5665_i2c_probe, .shutdown = rt5665_i2c_shutdown, .id_table = rt5665_i2c_id, }; diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index ecf3b0527dbe..f04c810fd710 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2370,7 +2370,7 @@ static const struct regmap_config rt5668_regmap = { .max_register = RT5668_I2C_MODE, .volatile_reg = rt5668_volatile_register, .readable_reg = rt5668_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5668_reg, .num_reg_defaults = ARRAY_SIZE(rt5668_reg), .use_single_read = true, @@ -2618,7 +2618,7 @@ static struct i2c_driver rt5668_i2c_driver = { .of_match_table = of_match_ptr(rt5668_of_match), .acpi_match_table = ACPI_PTR(rt5668_acpi_match), }, - .probe_new = rt5668_i2c_probe, + .probe = rt5668_i2c_probe, .shutdown = rt5668_i2c_shutdown, .id_table = rt5668_i2c_id, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index a230f441559a..0e34293f3395 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2863,7 +2863,7 @@ static const struct regmap_config rt5670_regmap = { RT5670_PR_SPACING), .volatile_reg = rt5670_volatile_register, .readable_reg = rt5670_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5670_reg, .num_reg_defaults = ARRAY_SIZE(rt5670_reg), .ranges = rt5670_ranges, @@ -3328,7 +3328,7 @@ static struct i2c_driver rt5670_i2c_driver = { .name = "rt5670", .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, - .probe_new = rt5670_i2c_probe, + .probe = rt5670_i2c_probe, .remove = rt5670_i2c_remove, .id_table = rt5670_i2c_id, }; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 3bf019b3f700..ad14d18860fc 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5704,7 +5704,7 @@ static struct i2c_driver rt5677_i2c_driver = { .of_match_table = rt5677_of_match, .acpi_match_table = ACPI_PTR(rt5677_acpi_match), }, - .probe_new = rt5677_i2c_probe, + .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, }; module_i2c_driver(rt5677_i2c_driver); @@ -5712,3 +5712,5 @@ module_i2c_driver(rt5677_i2c_driver); MODULE_DESCRIPTION("ASoC RT5677 driver"); MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE("rt5677_elf_vad"); diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 5bc46b041786..fb8ffb5b2ff6 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -47,7 +46,7 @@ static const struct regmap_config rt5682_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -334,7 +333,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = rt5682_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682_i2c_probe, + .probe = rt5682_i2c_probe, .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 23f17f70d7e9..67404f45389f 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -79,7 +79,7 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -500,9 +500,6 @@ static int rt5682_update_status(struct sdw_slave *slave, { struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt5682->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt5682->hw_init = false; @@ -510,7 +507,7 @@ static int rt5682_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED) + if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index e8efd8a84a6c..1a43d595f341 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1440,7 +1440,6 @@ struct rt5682_priv { bool disable_irq; struct mutex calibrate_mutex; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 9c34dca58f54..c77c675bd5f5 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -2848,14 +2847,9 @@ static int rt5682s_dai_probe_clks(struct snd_soc_component *component) int ret; /* Check if MCLK provided */ - rt5682s->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(rt5682s->mclk)) { - if (PTR_ERR(rt5682s->mclk) != -ENOENT) { - ret = PTR_ERR(rt5682s->mclk); - return ret; - } - rt5682s->mclk = NULL; - } + rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5682s->mclk)) + return PTR_ERR(rt5682s->mclk); /* Register CCF DAI clock control */ ret = rt5682s_register_dai_clks(component); @@ -3046,7 +3040,7 @@ static const struct regmap_config rt5682s_regmap = { .max_register = RT5682S_MAX_REG, .volatile_reg = rt5682s_volatile_register, .readable_reg = rt5682s_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682s_reg, .num_reg_defaults = ARRAY_SIZE(rt5682s_reg), .use_single_read = true, @@ -3316,7 +3310,7 @@ static struct i2c_driver rt5682s_i2c_driver = { .acpi_match_table = rt5682s_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682s_i2c_probe, + .probe = rt5682s_i2c_probe, .remove = rt5682s_i2c_remove, .shutdown = rt5682s_i2c_shutdown, .id_table = rt5682s_i2c_id, diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 96fc5f36d0d0..8b28e47775cc 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -292,7 +292,7 @@ static const struct regmap_config rt700_regmap = { .max_register = 0x755800, .reg_defaults = rt700_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt700_sdw_read, @@ -315,9 +315,6 @@ static int rt700_update_status(struct sdw_slave *slave, { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt700->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt700->hw_init = false; @@ -325,7 +322,7 @@ static int rt700_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED) + if (rt700->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 93c44005d38c..491774d207de 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -15,7 +15,6 @@ struct rt700_priv { struct regmap *regmap; struct regmap *sdw_regmap; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 51f3335343e0..119e1f9605d7 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -119,7 +119,7 @@ static const struct regmap_config rt711_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt711_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -133,7 +133,7 @@ static const struct regmap_config rt711_sdca_mbq_regmap = { .max_register = 0x40800f12, .reg_defaults = rt711_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -143,9 +143,6 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, { struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -168,7 +165,7 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 22076f268577..11d421e8ab2b 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -19,7 +19,6 @@ struct rt711_sdca_priv { struct regmap *regmap, *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 4fe68bcf2a7c..87dafcb4545d 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -296,7 +296,7 @@ static const struct regmap_config rt711_regmap = { .max_register = 0x755800, .reg_defaults = rt711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt711_sdw_read, @@ -319,9 +319,6 @@ static int rt711_update_status(struct sdw_slave *slave, { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -329,7 +326,7 @@ static int rt711_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h index b31351f11df9..491e357191f9 100644 --- a/sound/soc/codecs/rt711.h +++ b/sound/soc/codecs/rt711.h @@ -15,7 +15,6 @@ struct rt711_priv { struct regmap *sdw_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 09807b6d6353..869cc7bfd178 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -110,7 +110,7 @@ static const struct regmap_config rt712_sdca_dmic_regmap = { .max_register = 0x40981300, .reg_defaults = rt712_sdca_dmic_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -124,7 +124,7 @@ static const struct regmap_config rt712_sdca_dmic_mbq_regmap = { .max_register = 0x40800f14, .reg_defaults = rt712_sdca_dmic_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -803,9 +803,6 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, { struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -813,7 +810,7 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 74c29677c251..110154e74efe 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -16,7 +16,6 @@ struct rt712_sdca_dmic_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 3f319459dfec..ad06267b0ea0 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -116,7 +116,7 @@ static const struct regmap_config rt712_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt712_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -130,7 +130,7 @@ static const struct regmap_config rt712_sdca_mbq_regmap = { .max_register = 0x41000312, .reg_defaults = rt712_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -140,9 +140,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, { struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -165,7 +162,7 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index c6a94a23f46e..ff79e03118ce 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -20,7 +20,6 @@ struct rt712_sdca_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 38a82e4e2f95..df10916bab46 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -97,7 +97,7 @@ static const struct regmap_config rt715_sdca_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -111,7 +111,7 @@ static const struct regmap_config rt715_sdca_mbq_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_mbq_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -121,14 +121,11 @@ static int rt715_sdca_update_status(struct sdw_slave *slave, { struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; - /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h index 7577f3151934..e5d6928ecaba 100644 --- a/sound/soc/codecs/rt715-sdca.h +++ b/sound/soc/codecs/rt715-sdca.h @@ -24,7 +24,6 @@ struct rt715_sdca_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 4e61e16470ed..6db87442b783 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -354,7 +354,7 @@ static const struct regmap_config rt715_regmap = { .max_register = 0x752039, /* Maximum number of register */ .reg_defaults = rt715_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt715_sdw_read, @@ -417,13 +417,11 @@ static int rt715_update_status(struct sdw_slave *slave, { struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 17a8d041c1c3..12a0ae656d09 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -18,7 +18,6 @@ struct rt715_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c new file mode 100644 index 000000000000..cc57e4e27805 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> + +#include "rt722-sdca.h" +#include "rt722-sdca-sdw.h" + +static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01: + case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x2000024: + case 0x2000029 ... 0x200004a: + case 0x2000051 ... 0x2000052: + case 0x200005a ... 0x200005b: + case 0x2000061 ... 0x2000069: + case 0x200006b: + case 0x2000070: + case 0x200007f: + case 0x2000082 ... 0x200008e: + case 0x2000090 ... 0x2000094: + case 0x5300000 ... 0x5300002: + case 0x5400002: + case 0x5600000 ... 0x5600007: + case 0x5700000 ... 0x5700004: + case 0x5800000 ... 0x5800004: + case 0x5b00003: + case 0x5c00011: + case 0x5d00006: + case 0x5f00000 ... 0x5f0000d: + case 0x5f00030: + case 0x6100000 ... 0x6100051: + case 0x6100055 ... 0x6100057: + case 0x6100062: + case 0x6100064 ... 0x6100065: + case 0x6100067: + case 0x6100070 ... 0x610007c: + case 0x6100080: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R): + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200000d: + case 0x2000019: + case 0x2000020: + case 0x2000030: + case 0x2000046: + case 0x2000067: + case 0x2000084: + case 0x2000086: + return true; + default: + return false; + } +} + +static const struct regmap_config rt722_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt722_sdca_readable_register, + .volatile_reg = rt722_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt722_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt722_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt722_sdca_mbq_readable_register, + .volatile_reg = rt722_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt722_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt722_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt722->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt722->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt722_sdca_jack_init set. + */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt722->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt722_sdca_io_init(&slave->dev, slave); +} + +static int rt722_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* + * port = 1 for headphone playback + * port = 2 for headset-mic capture + * port = 3 for speaker playback + * port = 6 for digital-mic capture + */ + prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt722_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + if (cancel_delayed_work_sync(&rt722->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt722->scp_sdca_stat2) + scp_sdca_stat2 = rt722->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt722->disable_irq_lock); + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt722->scp_sdca_stat2 |= scp_sdca_stat2; + do { + /* clear flag */ + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0); + if (ret < 0) + goto io_error; + } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + if (status->sdca_cascade && !rt722->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt722->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt722->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt722_sdca_slave_ops = { + .read_prop = rt722_sdca_read_prop, + .interrupt_callback = rt722_sdca_interrupt_callback, + .update_status = rt722_sdca_update_status, +}; + +static int rt722_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt722_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (rt722->hw_init) { + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + } + + if (rt722->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt722->calibrate_mutex); + mutex_destroy(&rt722->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt722_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt722_sdca_id); + +static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + if (!rt722->hw_init) + return 0; + + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + + regcache_cache_only(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt722_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt722_sdca->disable_irq_lock); + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt722_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt722_sdca_dev_suspend(dev); +} + +#define RT722_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt722->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT722_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt722->regmap, false); + regcache_sync(rt722->regmap); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_sync(rt722->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt722_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt722_sdca_sdw_driver = { + .driver = { + .name = "rt722-sdca", + .owner = THIS_MODULE, + .pm = &rt722_sdca_pm, + }, + .probe = rt722_sdca_sdw_probe, + .remove = rt722_sdca_sdw_remove, + .ops = &rt722_sdca_slave_ops, + .id_table = rt722_sdca_id, +}; +module_sdw_driver(rt722_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h new file mode 100644 index 000000000000..5b43e86f75d1 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_SDW_H__ +#define __RT722_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt722_sdca_reg_defaults[] = { + { 0x202d, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f35, 0x00 }, + { 0x2f36, 0x00 }, + { 0x2f50, 0xf0 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x2f5a, 0x07 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x27 }, + { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), + 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt722_sdca_mbq_defaults[] = { + { 0x200003c, 0xc214 }, + { 0x2000046, 0x8004 }, + { 0x6100006, 0x0005 }, + { 0x6100010, 0x2630 }, + { 0x6100011, 0x152f }, + { 0x6100013, 0x0102 }, + { 0x6100015, 0x2200 }, + { 0x6100017, 0x0102 }, + { 0x6100025, 0x2a29 }, + { 0x6100026, 0x2a00 }, + { 0x6100028, 0x2a2a }, + { 0x6100029, 0x4141 }, + { 0x6100055, 0x0000 }, + { 0x5810000, 0x702d }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R), + 0x0000 }, +}; + +#endif /* __RT722_SDW_H__ */ diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c new file mode 100644 index 000000000000..9c0d34366c9e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.c @@ -0,0 +1,1555 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <sound/pcm.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/slab.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "rt722-sdca.h" + +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt722_sdca_index_read(rt722, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt722_sdca_index_write(rt722, nid, reg, tmp); +} + +static int rt722_sdca_btn_type(unsigned char *buffer) +{ + if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) || + (*(buffer + 1) == 0x10)) + return SND_JACK_BTN_2; + else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) || + (*(buffer + 1) == 0x20)) + return SND_JACK_BTN_3; + else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) || + (*(buffer + 1) == 0x40)) + return SND_JACK_BTN_0; + else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) || + (*(buffer + 1) == 0x80)) + return SND_JACK_BTN_1; + + return 0; +} + +static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt722->jack_type = 0; + break; + case 0x03: + rt722->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt722->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt722->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt722_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt722->hs_jack) + return; + + if (!rt722->component->card || !rt722->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ + if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 || + rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt722_sdca_headset_detect(rt722); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt722_sdca_button_detect(rt722); + + if (rt722->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt722->slave->dev, + "in %s, jack_type=%d\n", __func__, rt722->jack_type); + dev_dbg(&rt722->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt722->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt722_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + } else + rt722->jack_type = 0; + + dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) +{ + mutex_lock(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_UNSOL_CTL, 0x016E); + /* set XU(et03h) & XU(et0Dh) to Not bypassed */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + /* trigger GE interrupt */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } + mutex_unlock(&rt722->calibrate_mutex); +} + +static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722->hs_jack = hs_jack; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt722_sdca_jack_init(rt722); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + /* Lch*/ + regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return changed; + + return -EIO; +} + +static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else { + ctl_r = ctl_l; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) { + ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i]; + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt722_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt722_sdca_set_fu1e_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt722_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt722->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute; + return 0; +} + +static int rt722_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt722->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt722_sdca_set_fu0f_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / boost_step; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * boost_step; + else { /* ADC gain */ + gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt722->slave->dev, "%#08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt722_sdca_fu_info, \ + .get = rt722_sdca_fu1e_capture_get, \ + .put = rt722_sdca_fu1e_capture_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt722_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt722_sdca_controls[] = { + /* Headphone playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* Headset mic capture settings */ + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv), + /* AMP playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* DMIC capture settings */ + RT722_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4), + RT722_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_VOLUME, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv), + RT722_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, + RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 3, boost_vol_tlv), +}; + +static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val2); + val2 = (0x7 << mask_sft) & val2; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc22_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", +}; + +static const char * const adc07_10_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static const struct snd_kcontrol_new rt722_sdca_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu1e_dapm_mute = false; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu1e_dapm_mute = true; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu0f_dapm_mute = false; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu0f_dapm_mute = true; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC3_4"), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu42_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc22_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc25_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = { + {"FU 42", NULL, "DP1RX"}, + {"FU 21", NULL, "DP3RX"}, + + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 24 Mux", "DMIC2", "DMIC3_4"}, + {"ADC 25 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 25 Mux", "DMIC2", "DMIC3_4"}, + {"FU 36", NULL, "PDE 12"}, + {"FU 36", NULL, "ADC 22 Mux"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "ADC 24 Mux"}, + {"FU 113", NULL, "ADC 25 Mux"}, + {"DP2TX", NULL, "FU 36"}, + {"DP6TX", NULL, "FU 113"}, + + {"HP", NULL, "PDE 47"}, + {"HP", NULL, "FU 42"}, + {"SPK", NULL, "PDE 23"}, + {"SPK", NULL, "FU 21"}, +}; + +static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src); + + return 0; +} + +static int rt722_sdca_probe(struct snd_soc_component *component) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722_sdca_parse_dt(rt722, &rt722->slave->dev); + rt722->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt722 = { + .probe = rt722_sdca_probe, + .controls = rt722_sdca_controls, + .num_controls = ARRAY_SIZE(rt722_sdca_controls), + .dapm_widgets = rt722_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets), + .dapm_routes = rt722_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map), + .set_jack = rt722_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt722_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt722_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt722->slave) + return -EINVAL; + + /* + * RT722_AIF1 with port = 1 for headphone playback + * RT722_AIF1 with port = 2 for headset-mic capture + * RT722_AIF2 with port = 3 for speaker playback + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) + port = 6; + else + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT722_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT722_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT722_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT722_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + if (dai->id == RT722_AIF1) { + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + } + + if (dai->id == RT722_AIF2) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + if (dai->id == RT722_AIF3) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + return 0; +} + +static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt722->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt722->slave, sdw_stream); + return 0; +} + +#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt722_sdca_ops = { + .hw_params = rt722_sdca_pcm_hw_params, + .hw_free = rt722_sdca_pcm_hw_free, + .set_stream = rt722_sdca_set_sdw_stream, + .shutdown = rt722_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt722_sdca_dai[] = { + { + .name = "rt722-sdca-aif1", + .id = RT722_AIF1, + .playback = { + .stream_name = "DP1 Headphone Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .capture = { + .stream_name = "DP2 Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif2", + .id = RT722_AIF2, + .playback = { + .stream_name = "DP3 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif3", + .id = RT722_AIF3, + .capture = { + .stream_name = "DP6 DMic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + } +}; + +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722; + + rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL); + if (!rt722) + return -ENOMEM; + + dev_set_drvdata(dev, rt722); + rt722->slave = slave; + rt722->regmap = regmap; + rt722->mbq_regmap = mbq_regmap; + + mutex_init(&rt722->calibrate_mutex); + mutex_init(&rt722->disable_irq_lock); + + INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt722->hw_init = false; + rt722->first_hw_init = false; + rt722->fu1e_dapm_mute = true; + rt722->fu0f_dapm_mute = true; + rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true; + rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] = + rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true; + + return devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); +} + +static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) +{ + /* Set AD07 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); + /* Set AD10 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); + /* Set DMIC2 IT entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_ENT_FLOAT_CTL, 0x2626); + /* Set AD10 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_ENT_FLOAT_CTL, 0x1e00); + /* Set DMIC2 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + /* Set AD10 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); + /* Set DMIC2 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Fine tune PDE2A latency */ + regmap_write(rt722->regmap, 0x2f5c, 0x25); +} + +static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) +{ + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* Reset dc_cal_top */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0x702c); + /* W1C Trigger Calibration */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0xf02d); + /* Set DAC02/ClassD power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, + 0x2323); + /* Set EAPD high */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, + 0x0002); + /* Enable vf707_r14 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); +} + +static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) +{ + int loop_check, chk_cnt = 100, ret; + unsigned int calib_status = 0; + + /* Read eFuse */ + rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, + 0x4808); + /* Button A, B, C, D bypass mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, + 0xcf00); + /* HID1 slot enable */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, + 0x000f); + /* Report ID for HID1 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, + 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, + 0x0c12); + /* Set JD de-bounce clock control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, + 0x7002); + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* FSM switch to calibration manual mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, + 0x4100); + /* W1C Trigger DC calibration (HP) */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, + 0x008d); + /* check HP calibration FSM status */ + for (loop_check = 0; loop_check < chk_cnt; loop_check++) { + ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, + RT722_DAC_DC_CALI_CTL3, &calib_status); + if (ret < 0 || loop_check == chk_cnt) + dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); + if ((calib_status & 0x0040) == 0x0) + break; + } + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); + /* Set ADC09 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, + 0x2a12); + /* Set MIC2 and LINE1 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, + 0x3429); + /* Set ET41h and LINE2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, + 0x4112); + /* Set DAC03 and HP power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, + 0x4040); + /* Fine tune PDE40 latency */ + regmap_write(rt722->regmap, 0x2f58, 0x07); +} + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + rt722->disable_irq = false; + + if (rt722->hw_init) + return 0; + + if (rt722->first_hw_init) { + regcache_cache_only(rt722->regmap, false); + regcache_cache_bypass(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_cache_bypass(rt722->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt722_sdca_dmic_preset(rt722); + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); + regcache_cache_bypass(rt722->mbq_regmap, false); + regcache_mark_dirty(rt722->mbq_regmap); + } else + rt722->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt722->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h new file mode 100644 index 000000000000..44af8901352e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_H__ +#define __RT722_H__ + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt722_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct mutex calibrate_mutex; + struct mutex disable_irq_lock; + bool disable_irq; + /* For Headset jack & Headphone */ + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + int jd_src; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; + /* For DMIC */ + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt722_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* NID */ +#define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_CALI 0x58 +#define RT722_VENDOR_SPK_EFUSE 0x5c +#define RT722_VENDOR_IMS_DRE 0x5b +#define RT722_VENDOR_ANALOG_CTL 0x5f +#define RT722_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT722_JD_PRODUCT_NUM 0x00 +#define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_JD_CTRL1 0x09 +#define RT722_LDO2_3_CTL1 0x0e +#define RT722_LDO1_CTL 0x1a +#define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL6 0x3c +#define RT722_COMBO_JACK_AUTO_CTL1 0x45 +#define RT722_COMBO_JACK_AUTO_CTL2 0x46 +#define RT722_COMBO_JACK_AUTO_CTL3 0x47 +#define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_FSM_CTL 0x67 +#define RT722_SDCA_INTR_REC 0x82 +#define RT722_SW_CONFIG1 0x8a +#define RT722_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT722_DAC_DC_CALI_CTL0 0x00 +#define RT722_DAC_DC_CALI_CTL1 0x01 +#define RT722_DAC_DC_CALI_CTL2 0x02 +#define RT722_DAC_DC_CALI_CTL3 0x03 + +/* Index (NID:59h) */ +#define RT722_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT722_IMS_DIGITAL_CTL1 0x00 +#define RT722_IMS_DIGITAL_CTL5 0x05 +#define RT722_HP_DETECT_RLDET_CTL1 0x29 +#define RT722_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT722_MISC_POWER_CTL0 0x00 +#define RT722_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT722_HDA_LEGACY_MUX_CTL0 0x00 +#define RT722_HDA_LEGACY_UNSOL_CTL 0x03 +#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT722_HDA_LEGACY_RESET_CTL 0x08 +#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT722_DMIC_ENT_FLOAT_CTL 0x10 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT722_ADC_ENT_FLOAT_CTL 0x15 +#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17 +#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18 +#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24 +#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25 +#define RT722_ADC10_PDE_FLOAT_CTL 0x26 +#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28 +#define RT722_AMP_PDE_FLOAT_CTL 0x29 +#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT722_GE_RELATED_CTL1 0x45 +#define RT722_GE_RELATED_CTL2 0x46 +#define RT722_MIXER_CTL0 0x52 +#define RT722_MIXER_CTL1 0x53 +#define RT722_EAPD_CTL 0x55 +#define RT722_UMP_HID_CTL0 0x60 +#define RT722_UMP_HID_CTL1 0x61 +#define RT722_UMP_HID_CTL2 0x62 +#define RT722_UMP_HID_CTL3 0x63 +#define RT722_UMP_HID_CTL4 0x64 +#define RT722_UMP_HID_CTL5 0x65 +#define RT722_UMP_HID_CTL6 0x66 +#define RT722_UMP_HID_CTL7 0x67 +#define RT722_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC calibration control (0x00)(NID:58h) */ +#define RT722_DC_CALIB_CTRL (0x1 << 16) +/* DAC DC offset calibration control-1 (0x01)(NID:58h) */ +#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15) + +#define RT722_EAPD_HIGH 0x2 +#define RT722_EAPD_LOW 0x0 + +/* Buffer address for HID */ +#define RT722_BUF_ADDR_HID1 0x44030000 +#define RT722_BUF_ADDR_HID2 0x44030020 + +/* RT722 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT722 SDCA entity */ +#define RT722_SDCA_ENT_HID01 0x01 +#define RT722_SDCA_ENT_GE49 0x49 +#define RT722_SDCA_ENT_USER_FU05 0x05 +#define RT722_SDCA_ENT_USER_FU06 0x06 +#define RT722_SDCA_ENT_USER_FU0F 0x0f +#define RT722_SDCA_ENT_USER_FU10 0x19 +#define RT722_SDCA_ENT_USER_FU1E 0x1e +#define RT722_SDCA_ENT_FU15 0x15 +#define RT722_SDCA_ENT_PDE23 0x23 +#define RT722_SDCA_ENT_PDE40 0x40 +#define RT722_SDCA_ENT_PDE11 0x11 +#define RT722_SDCA_ENT_PDE12 0x12 +#define RT722_SDCA_ENT_PDE2A 0x2a +#define RT722_SDCA_ENT_CS01 0x01 +#define RT722_SDCA_ENT_CS11 0x11 +#define RT722_SDCA_ENT_CS1F 0x1f +#define RT722_SDCA_ENT_CS1C 0x1c +#define RT722_SDCA_ENT_CS31 0x31 +#define RT722_SDCA_ENT_OT23 0x42 +#define RT722_SDCA_ENT_IT26 0x26 +#define RT722_SDCA_ENT_IT09 0x09 +#define RT722_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT722_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT722_SDCA_ENT_XU03 0x03 +#define RT722_SDCA_ENT_XU0D 0x0d + +/* RT722 SDCA control */ +#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT722_SDCA_CTL_FU_MUTE 0x01 +#define RT722_SDCA_CTL_FU_VOLUME 0x02 +#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT722_SDCA_CTL_SELECTED_MODE 0x01 +#define RT722_SDCA_CTL_DETECTED_MODE 0x02 +#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT722_SDCA_CTL_VENDOR_DEF 0x30 +#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT722 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_08 0x08 + +/* sample frequency index */ +#define RT722_SDCA_RATE_16000HZ 0x04 +#define RT722_SDCA_RATE_32000HZ 0x07 +#define RT722_SDCA_RATE_44100HZ 0x08 +#define RT722_SDCA_RATE_48000HZ 0x09 +#define RT722_SDCA_RATE_96000HZ 0x0b +#define RT722_SDCA_RATE_192000HZ 0x0d + +enum { + RT722_AIF1, /* For headset mic and headphone */ + RT722_AIF2, /* For speaker */ + RT722_AIF3, /* For dmic */ + RT722_AIFS, +}; + +enum rt722_sdca_jd_src { + RT722_JD_NULL, + RT722_JD1, +}; + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value); +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value); + +int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic); +#endif /* __RT722_H__ */ diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index fcf4fbaed3c7..733a7d130a95 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -633,7 +633,7 @@ static struct i2c_driver rt9120_driver = { .of_match_table = rt9120_device_table, .pm = &rt9120_pm_ops, }, - .probe_new = rt9120_probe, + .probe = rt9120_probe, .remove = rt9120_remove, }; module_i2c_driver(rt9120_driver); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index a916f4619ea3..b22ba95bd0c0 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1826,7 +1826,7 @@ static struct i2c_driver sgtl5000_i2c_driver = { .name = "sgtl5000", .of_match_table = sgtl5000_dt_ids, }, - .probe_new = sgtl5000_i2c_probe, + .probe = sgtl5000_i2c_probe, .remove = sgtl5000_i2c_remove, .shutdown = sgtl5000_i2c_shutdown, .id_table = sgtl5000_id, diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index b6c132edf3bd..7b9abbc1bd94 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1807,7 +1807,7 @@ static struct i2c_driver sma1303_i2c_driver = { .name = "sma1303", .of_match_table = sma1303_of_match, }, - .probe_new = sma1303_i2c_probe, + .probe = sma1303_i2c_probe, .remove = sma1303_i2c_remove, .id_table = sma1303_i2c_id, }; diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c index a40fd20df984..93af8e209b05 100644 --- a/sound/soc/codecs/src4xxx-i2c.c +++ b/sound/soc/codecs/src4xxx-i2c.c @@ -36,7 +36,7 @@ static struct i2c_driver src4xxx_i2c_driver = { .name = "src4xxx", .of_match_table = of_match_ptr(src4xxx_of_match), }, - .probe_new = src4xxx_i2c_probe, + .probe = src4xxx_i2c_probe, .id_table = src4xxx_i2c_ids, }; module_i2c_driver(src4xxx_i2c_driver); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 22cb3b7c8283..d20d897407eb 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -803,7 +803,7 @@ static struct i2c_driver ssm2518_driver = { .name = "ssm2518", .of_match_table = of_match_ptr(ssm2518_dt_ids), }, - .probe_new = ssm2518_i2c_probe, + .probe = ssm2518_i2c_probe, .id_table = ssm2518_i2c_ids, }; module_i2c_driver(ssm2518_driver); diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 3c85772901f5..596096466cd4 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = { .name = "ssm2602", .of_match_table = ssm2602_of_match, }, - .probe_new = ssm2602_i2c_probe, + .probe = ssm2602_i2c_probe, .id_table = ssm2602_i2c_id, }; module_i2c_driver(ssm2602_i2c_driver); diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c new file mode 100644 index 000000000000..008cb3eb5758 --- /dev/null +++ b/sound/soc/codecs/ssm3515.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +// +// Analog Devices' SSM3515 audio amp driver +// +// Copyright (C) The Asahi Linux Contributors + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + + +#define SSM3515_PWR 0x00 +#define SSM3515_PWR_APWDN_EN BIT(7) +#define SSM3515_PWR_BSNS_PWDN BIT(6) +#define SSM3515_PWR_S_RST BIT(1) +#define SSM3515_PWR_SPWDN BIT(0) + +#define SSM3515_GEC 0x01 +#define SSM3515_GEC_EDGE BIT(4) +#define SSM3515_GEC_EDGE_SHIFT 4 +#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0) + +#define SSM3515_DAC 0x02 +#define SSM3515_DAC_HV BIT(7) +#define SSM3515_DAC_MUTE BIT(6) +#define SSM3515_DAC_HPF BIT(5) +#define SSM3515_DAC_LPM BIT(4) +#define SSM3515_DAC_FS GENMASK(2, 0) + +#define SSM3515_DAC_VOL 0x03 + +#define SSM3515_SAI1 0x04 +#define SSM3515_SAI1_DAC_POL BIT(7) +#define SSM3515_SAI1_BCLK_POL BIT(6) +#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3) +#define SSM3515_SAI1_FSYNC_MODE BIT(2) +#define SSM3515_SAI1_SDATA_FMT BIT(1) +#define SSM3515_SAI1_SAI_MODE BIT(0) + +#define SSM3515_SAI2 0x05 +#define SSM3515_SAI2_DATA_WIDTH BIT(7) +#define SSM3515_SAI2_AUTO_SLOT BIT(4) +#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0) + +#define SSM3515_VBAT_OUT 0x06 + +#define SSM3515_STATUS 0x0a +#define SSM3515_STATUS_UVLO_REG BIT(6) +#define SSM3515_STATUS_LIM_EG BIT(5) +#define SSM3515_STATUS_CLIP BIT(4) +#define SSM3515_STATUS_AMP_OC BIT(3) +#define SSM3515_STATUS_OTF BIT(2) +#define SSM3515_STATUS_OTW BIT(1) +#define SSM3515_STATUS_BAT_WARN BIT(0) + +static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM3515_STATUS: + case SSM3515_VBAT_OUT: + return true; + + default: + return false; + } +} + +static const struct reg_default ssm3515_reg_defaults[] = { + { SSM3515_PWR, 0x81 }, + { SSM3515_GEC, 0x01 }, + { SSM3515_DAC, 0x32 }, + { SSM3515_DAC_VOL, 0x40 }, + { SSM3515_SAI1, 0x11 }, + { SSM3515_SAI2, 0x00 }, +}; + +static const struct regmap_config ssm3515_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = ssm3515_volatile_reg, + .max_register = 0xb, + .reg_defaults = ssm3515_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +struct ssm3515_data { + struct device *dev; + struct regmap *regmap; +}; + +// The specced range is -71.25...24.00 dB with step size of 0.375 dB, +// and a mute item below that. This is represented by -71.62...24.00 dB +// with the mute item mapped onto the low end. +static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400); + +static const char * const ssm3515_ana_gain_text[] = { + "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span", +}; + +static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC, + __bf_shf(SSM3515_GEC_ANA_GAIN), + ssm3515_ana_gain_text); + +static const struct snd_kcontrol_new ssm3515_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL, + 0, 255, 1, ssm3515_dac_volume), + SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC, + __bf_shf(SSM3515_GEC_EDGE), 1, 0), + SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HV), 1, 1), + SOC_SINGLE("HPF Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HPF), 1, 0), + SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1, + __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0), + SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum), +}; + +static void ssm3515_read_faults(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_read(component, SSM3515_STATUS); + if (ret <= 0) { + /* + * If the read was erroneous, ASoC core has printed a message, + * and that's all that's appropriate in handling the error here. + */ + return; + } + + dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n", + FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "", + FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "", + FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "", + FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "", + FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "", + FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "", + FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : ""); +} + +static int ssm3515_probe(struct snd_soc_component *component) +{ + int ret; + + /* Start out muted */ + ret = snd_soc_component_update_bits(component, SSM3515_DAC, + SSM3515_DAC_MUTE, SSM3515_DAC_MUTE); + if (ret < 0) + return ret; + + /* Disable the 'master power-down' */ + ret = snd_soc_component_update_bits(component, SSM3515_PWR, + SSM3515_PWR_SPWDN, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + int ret; + + ret = snd_soc_component_update_bits(dai->component, + SSM3515_DAC, + SSM3515_DAC_MUTE, + FIELD_PREP(SSM3515_DAC_MUTE, mute)); + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret, rateval; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + case SNDRV_PCM_FORMAT_S24: + ret = snd_soc_component_update_bits(component, + SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH, + FIELD_PREP(SSM3515_SAI2_DATA_WIDTH, + params_width(params) == 16)); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000 ... 12000: + rateval = 0; + break; + case 16000 ... 24000: + rateval = 1; + break; + case 32000 ... 48000: + rateval = 2; + break; + case 64000 ... 96000: + rateval = 3; + break; + case 128000 ... 192000: + rateval = 4; + break; + case 48001 ... 63999: /* this is ...72000 but overlaps */ + rateval = 5; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, + SSM3515_DAC, SSM3515_DAC_FS, + FIELD_PREP(SSM3515_DAC_FS, rateval)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */ + int ret; + u8 sai1 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + sai1 |= SSM3515_SAI1_BCLK_POL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + fpol_inv = 1; + sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */ + break; + case SND_SOC_DAIFMT_LEFT_J: + fpol_inv = 0; + sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + fpol_inv ^= 1; + break; + } + + /* Set the serial input to 'TDM mode' */ + sai1 |= SSM3515_SAI1_SAI_MODE; + + if (fpol_inv) { + /* + * We configure the codec in a 'TDM mode', in which the + * FSYNC_MODE bit of SAI1 is supposed to select between + * what the datasheet calls 'Pulsed FSYNC mode' and '50% + * FSYNC mode'. + * + * Experiments suggest that this bit in fact simply selects + * the FSYNC polarity, so go with that. + */ + sai1 |= SSM3515_SAI1_FSYNC_MODE; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT | + SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1); + + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + int slot, tdm_bclks_val, ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + slot = __ffs(tx_mask); + + if (tx_mask & ~BIT(slot)) + return -EINVAL; + + switch (slot_width) { + case 16: + tdm_bclks_val = 0; + break; + case 24: + tdm_bclks_val = 1; + break; + case 32: + tdm_bclks_val = 2; + break; + case 48: + tdm_bclks_val = 3; + break; + case 64: + tdm_bclks_val = 4; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_TDM_BCLKS, + FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, SSM3515_SAI2, + SSM3515_SAI2_TDM_SLOT, + FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * We don't get live notification of faults, so at least at + * this time, when playback is over, check if we have tripped + * over anything and if so, log it. + */ + ssm3515_read_faults(dai->component); + return 0; +} + +static const struct snd_soc_dai_ops ssm3515_dai_ops = { + .mute_stream = ssm3515_mute, + .hw_params = ssm3515_hw_params, + .set_fmt = ssm3515_set_fmt, + .set_tdm_slot = ssm3515_set_tdm_slot, + .hw_free = ssm3515_hw_free, +}; + +static struct snd_soc_dai_driver ssm3515_dai_driver = { + .name = "SSM3515 SAI", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ssm3515_dai_ops, +}; + +static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = { + {"OUT", NULL, "DAC"}, + {"DAC", NULL, "Playback"}, +}; + +static const struct snd_soc_component_driver ssm3515_asoc_component = { + .probe = ssm3515_probe, + .controls = ssm3515_snd_controls, + .num_controls = ARRAY_SIZE(ssm3515_snd_controls), + .dapm_widgets = ssm3515_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets), + .dapm_routes = ssm3515_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes), + .endianness = 1, +}; + +static int ssm3515_i2c_probe(struct i2c_client *client) +{ + struct ssm3515_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &client->dev; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap), + "initializing register map\n"); + + /* Perform a reset */ + ret = regmap_update_bits(data->regmap, SSM3515_PWR, + SSM3515_PWR_S_RST, SSM3515_PWR_S_RST); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "performing software reset\n"); + regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap); + + return devm_snd_soc_register_component(data->dev, + &ssm3515_asoc_component, + &ssm3515_dai_driver, 1); +} + +static const struct of_device_id ssm3515_of_match[] = { + { .compatible = "adi,ssm3515" }, + {} +}; +MODULE_DEVICE_TABLE(of, ssm3515_of_match); + +static struct i2c_driver ssm3515_i2c_driver = { + .driver = { + .name = "ssm3515", + .of_match_table = of_match_ptr(ssm3515_of_match), + }, + .probe = ssm3515_i2c_probe, +}; +module_i2c_driver(ssm3515_i2c_driver); + +MODULE_AUTHOR("Martin PoviÅ¡er <povik+lin@cutebit.org>"); +MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 4b0265617c7b..0a6f04d8f636 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -500,7 +500,7 @@ static struct i2c_driver ssm4567_driver = { .of_match_table = of_match_ptr(ssm4567_of_match), .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), }, - .probe_new = ssm4567_i2c_probe, + .probe = ssm4567_i2c_probe, .id_table = ssm4567_i2c_ids, }; module_i2c_driver(ssm4567_driver); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 29af9595dac1..4a694d0bfd68 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1167,7 +1167,7 @@ static struct i2c_driver sta32x_i2c_driver = { .name = "sta32x", .of_match_table = of_match_ptr(st32x_dt_ids), }, - .probe_new = sta32x_i2c_probe, + .probe = sta32x_i2c_probe, .id_table = sta32x_i2c_id, }; diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index b033a5fcd6c0..d05f3fd57661 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1249,7 +1249,7 @@ static struct i2c_driver sta350_i2c_driver = { .name = "sta350", .of_match_table = of_match_ptr(st350_dt_ids), }, - .probe_new = sta350_i2c_probe, + .probe = sta350_i2c_probe, .remove = sta350_i2c_remove, .id_table = sta350_i2c_id, }; diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 313957099145..0ac08478ddac 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -379,7 +379,7 @@ static struct i2c_driver sta529_i2c_driver = { .name = "sta529", .of_match_table = sta529_of_match, }, - .probe_new = sta529_i2c_probe, + .probe = sta529_i2c_probe, .id_table = sta529_i2c_id, }; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 59a4ea5f6e30..8c9dc318b0e8 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -761,7 +761,7 @@ static struct i2c_driver tas2552_i2c_driver = { .of_match_table = of_match_ptr(tas2552_of_match), .pm = &tas2552_pm, }, - .probe_new = tas2552_probe, + .probe = tas2552_probe, .remove = tas2552_i2c_remove, .id_table = tas2552_id, }; diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index b486d0bd86c9..962c2cdfa017 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -8,7 +8,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/gpio/consumer.h> @@ -784,7 +783,7 @@ static struct i2c_driver tas2562_i2c_driver = { .name = "tas2562", .of_match_table = of_match_ptr(tas2562_of_match), }, - .probe_new = tas2562_probe, + .probe = tas2562_probe, .id_table = tas2562_id, }; diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 2e0ed3e68fa5..a9838e0738cc 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -756,7 +756,7 @@ static struct i2c_driver tas2764_i2c_driver = { .name = "tas2764", .of_match_table = of_match_ptr(tas2764_of_match), }, - .probe_new = tas2764_i2c_probe, + .probe = tas2764_i2c_probe, .id_table = tas2764_i2c_id, }; module_i2c_driver(tas2764_i2c_driver); diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 8557759acb1f..99bf402eb566 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -720,7 +720,7 @@ static struct i2c_driver tas2770_i2c_driver = { .name = "tas2770", .of_match_table = of_match_ptr(tas2770_of_match), }, - .probe_new = tas2770_i2c_probe, + .probe = tas2770_i2c_probe, .id_table = tas2770_i2c_id, }; module_i2c_driver(tas2770_i2c_driver); diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 09e7ada1bca4..86bd6c18a944 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -645,7 +645,7 @@ static struct i2c_driver tas2780_i2c_driver = { .name = "tas2780", .of_match_table = of_match_ptr(tas2780_of_match), }, - .probe_new = tas2780_i2c_probe, + .probe = tas2780_i2c_probe, .id_table = tas2780_i2c_id, }; module_i2c_driver(tas2780_i2c_driver); diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c new file mode 100644 index 000000000000..a88c6c28a394 --- /dev/null +++ b/sound/soc/codecs/tas2781-comlib.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = 256 * 128, +}; + +static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, + unsigned short chn, int book) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + int ret = 0; + + if (chn < tas_priv->ndev) { + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + if (tasdev->cur_book == book) { + ret = regmap_write(map, + TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + } + goto out; + } + + if (tasdev->cur_book != book) { + ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + tasdev->cur_book = book; + } + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} + +int tasdevice_dev_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int *val) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_read); + +int tasdevice_dev_write(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_write(map, TASDEVICE_PGRG(reg), + value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_write); + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), + data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); + +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), + mask, value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + ret = -EINVAL; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); + +struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) +{ + struct tasdevice_priv *tas_priv; + + tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return NULL; + tas_priv->dev = &i2c->dev; + tas_priv->client = (void *)i2c; + + return tas_priv; +} +EXPORT_SYMBOL_GPL(tasdevice_kzalloc); + +void tas2781_reset(struct tasdevice_priv *tas_dev) +{ + int ret, i; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, + "dev %d swreset fail, %d\n", + i, ret); + } + } + usleep_range(1000, 1050); +} +EXPORT_SYMBOL_GPL(tas2781_reset); + +int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret = 0; + + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + mutex_lock(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tascodec_init); + +int tasdevice_init(struct tasdevice_priv *tas_priv) +{ + int ret = 0; + int i; + + tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, + &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + dev_set_drvdata(tas_priv->dev, tas_priv); + + mutex_init(&tas_priv->codec_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_init); + +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &(prog->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &(cfg->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +void tasdevice_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_dev->fmw; + + if (!tas_dev->fmw) + return; + + if (tas_fmw->programs) + tasdev_dsp_prog_remove(tas_fmw->programs, + tas_fmw->nr_programs); + if (tas_fmw->configs) + tasdev_dsp_cfg_remove(tas_fmw->configs, + tas_fmw->nr_configurations); + kfree(tas_fmw); + tas_dev->fmw = NULL; +} +EXPORT_SYMBOL_GPL(tasdevice_dsp_remove); + +void tasdevice_remove(struct tasdevice_priv *tas_priv) +{ + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) + gpio_free(tas_priv->irq_info.irq_gpio); + kfree(tas_priv->acpi_subsystem_id); + mutex_destroy(&tas_priv->codec_lock); +} +EXPORT_SYMBOL_GPL(tasdevice_remove); + +static int tasdevice_clamp(int val, int max, unsigned int invert) +{ + if (val > max) + val = max; + if (invert) + val = max - val; + if (val < 0) + val = 0; + return val; +} + +int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int err_cnt = 0; + int val, i, ret; + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_priv, i, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); + +int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret = 0; + int val; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); + +int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int err_cnt = 0; + int ret; + int val, i; + + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_write(tas_priv, i, mc->reg, + (unsigned int)val); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, + "set digital vol err in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); + +int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol error\n", + __func__); + goto out; + } + + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); + +MODULE_DESCRIPTION("TAS2781 common library"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c new file mode 100644 index 000000000000..eb55abae0d7b --- /dev/null +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -0,0 +1,2428 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tasdevice-fmw.c -- TASDEVICE firmware support +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/tas2781.h> + + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/*should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * dsp firmware. + */ + cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + goto out; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto out; + } + config_offset += 64; + } + + if (config_offset + 4 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + /* Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(struct tasdev_blk_data *), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = + (1 << tas_priv->ndev) - 1; + else + cfg_info->active_dev |= 1 << + (bk_da[i]->dev_idx - 1); + + } + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + goto out; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + +out: + return cfg_info; +} + +int tasdevice_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + unsigned char *buf; + int offset = 0; + int ret = 0; + int i; + + rca = &(tas_priv->rcabin); + fw_hdr = &(rca->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n", + fw_hdr->ndev, tas_priv->ndev); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file error!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + ret = -ENOMEM; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + offset += (int)fw_hdr->config_size[i]; + } +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB); + +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + + block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += block->blk_size; + +out: + return offset; +} + +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + break; + } + } + +out: + return offset; +} + +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + /*skip 72 unused byts*/ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_configuration_data_kernel( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + /*skip extra 16 bytes*/ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_variable_header_kernel( + struct tasdevice_priv *tas_priv, const struct firmware *fmw, + int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_prog *program; + struct tasdevice_config *config; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + goto out; + } + + tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > + TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + offset = -EINVAL; + goto out; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(struct tasdevice_prog), GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + /* The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = (fw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + offset = -EINVAL; + goto out; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + offset = -EINVAL; + goto out; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &(tas_fmw->configs[i]); + config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + +out: + return offset; +} + +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + int subblk_offset, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + int blktyp = dev_idx & 0xC0; + int idx = dev_idx & 0x3F; + bool is_err = false; + + if (idx) { + chn = idx - 1; + chnend = idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + + for (; chn < chnend; chn++) { + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + + is_err = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i; + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, + "process_block: Out of boundary\n"); + is_err = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, + "%s: BST Out of boundary\n", + __func__); + is_err = true; + break; + } + if (len % 4) { + dev_err(tas_priv->dev, + "%s:Bst-len(%u)not div by 4\n", + __func__, len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: bulk_write error = %d\n", + __func__, rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned int sleep_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, + "%s: delay Out of boundary\n", + __func__); + is_err = true; + break; + } + sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(sleep_time, sleep_time + 50); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, + "%s: bit write Out of boundary\n", + __func__); + is_err = true; + break; + } + rc = tasdevice_dev_update_bits(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: update_bits error = %d\n", + __func__, rc); + } + subblk_offset += 6; + break; + default: + break; + } + if (is_err == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->tasdevice[chn].cur_prog = -1; + tas_priv->tasdevice[chn].cur_conf = -1; + } else + tas_priv->tasdevice[chn].cur_conf = -1; + } + } + + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + for (; chn < chnend; chn++) + tas_priv->tasdevice[chn].is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned int blk_size = block->blk_size; + unsigned int i, length; + unsigned char *data = block->data; + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + case PRE_SOFTWARE_RESET_DEVICE_A: + case POST_SOFTWARE_RESET_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + case PRE_SOFTWARE_RESET_DEVICE_B: + case POST_SOFTWARE_RESET_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + case PRE_SOFTWARE_RESET_DEVICE_C: + case POST_SOFTWARE_RESET_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + case PRE_SOFTWARE_RESET_DEVICE_D: + case POST_SOFTWARE_RESET_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else if (fw_fixed_hdr->ppcver >= + PPC3_VERSION) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else { + switch (block->type) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + break; + } + } + + return 0; +} + +static int fw_parse_variable_hdr(struct tasdevice_priv + *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + offset += len; + + fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + +out: + return offset; +} + +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + offset = -EINVAL; + goto out; + } + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + offset = -EINVAL; + goto out; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += n; + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i; + int n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += n; + img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + goto out; + } + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /*Not error in calibration Data file, return directly*/ + dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + goto out; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog), + GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &(tas_fmw->programs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i; + int n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /*Not error for calibration Data file, return directly*/ + goto out; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) { + in = check_inpage_yram_bk1(cd, page, reg, len); + goto end; + } + if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + +end: + return in; +} + +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (in) + goto end; + in = check_inblock_yram(cd, book, page, reg, len); + +end: + return in; +} + +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned short chn, unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0; + int i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_dev_bulk_read(tasdevice, chn, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG) + + 4))) + /*DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned short chl, unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (reg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + goto end; + ret = tasdevice_dev_read(tasdevice, chl, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + goto end; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK; + ret = -EAGAIN; + goto end; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + +end: + return ret; +} + +static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) + || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) + || (type == MAIN_DEVICE_D)) + dev->cur_prog = -1; + else + dev->cur_conf = -1; +} + +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, chn, book, page, reg, + val); + + if (ret > 0) { + *crc_chksum += (unsigned char)ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + +end: + return ret; +} + +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + +end: + return ret; +} + +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + goto end; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__, + chn); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, + &tas_priv->tasdevice[chn]); + } else + tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK; + +end: + return ret; +} + +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int sleep_time; + unsigned int len; + unsigned int nr_cmds; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + unsigned char offset; + unsigned char book; + unsigned char page; + unsigned char val; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_I2CChecksum, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /*Single byte write*/ + if (offset <= 0x7F) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + goto end; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, chn, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /*sleep command*/ + if (offset == 0x81) { + /*book -- data[0] page -- data[1]*/ + sleep_time = ((book << 8) + page)*1000; + usleep_range(sleep_time, sleep_time + 50); + continue; + } + /*Multiple bytes write*/ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, chn, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block, chn); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->tasdevice[chn].err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /*skip current blk*/ + break; + } + +end: + return ret; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int chnend = 0; + int ret = 0; + int chn = 0; + int rc = 0; + + switch (block->type) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_priv->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n", + block->type); + break; + } + + for (; chn < chnend; chn++) { + block->nr_retry = 6; + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + ret = tasdev_load_blk(tas_priv, block, chn); + if (ret < 0) + dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n", + chn, block->type); + rc |= ret; + } + + return rc; +} + +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else { + switch (ppcver) { + case 0x00: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + dev_err(tas_priv->dev, + "%s: PPCVer must be 0x0 or 0x%02x", + __func__, PPC3_VERSION); + dev_err(tas_priv->dev, " Current:0x%02x\n", + ppcver); + rc = -EINVAL; + break; + } + } + } else { + dev_err(tas_priv->dev, + "DrvVer must be 0x0, 0x230 or above 0x230 "); + dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +static int load_calib_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 72; + + out: + return offset; +} + +static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != 1) { + dev_err(tas_priv->dev, + "%s: calbin must be 1, but currently ndev(%u)\n", + __func__, fw_hdr->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +/* When calibrated data parsing error occurs, DSP can still work with default + * calibrated data, memory resource related to calibrated data will be + * released in the tasdevice_codec_remove. + */ +static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_calibration *calibration; + unsigned char *data = (unsigned char *)fmw->data; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_calibrations != 1) { + dev_err(tas_priv->dev, + "%s: only supports one calibration (%d)!\n", + __func__, tas_fmw->nr_calibrations); + goto out; + } + + tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations, + sizeof(struct tasdevice_calibration), GFP_KERNEL); + if (!tas_fmw->calibrations) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "Calibrations error\n"); + offset = -EINVAL; + goto out; + } + calibration = &(tas_fmw->calibrations[i]); + offset += 64; + + n = strlen((char *)&data[offset]); + /* skip '\0' and 2 unused bytes */ + n += 3; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + offset += n; + + offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +int tas2781_load_calibration(void *context, char *file_name, + unsigned short i) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); + const struct firmware *fw_entry; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; + int ret; + + ret = request_firmware(&fw_entry, file_name, tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); + goto out; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); + ret = -EINVAL; + goto out; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw), + GFP_KERNEL); + if (!tasdev->cali_data_fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + ret = offset; + goto out; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); + ret = offset; + goto out; + } + +out: + if (fw_entry) + release_firmware(fw_entry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_dspfw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0; + int ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + ret = -EINVAL; + goto out; + } + + tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); + if (!tas_priv->fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) { + ret = -EINVAL; + goto out; + } + fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr); + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + goto out; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + +out: + return ret; +} + +int tasdevice_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + goto out; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB); + +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + struct tasdevice_data *im; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &(tas_fmw->calibrations[i]); + if (!calibration) + continue; + + im = &(calibration->dev_data); + + if (!im->dev_blks) + continue; + + for (blks = 0; blks < im->nr_blk; blks++) { + block = &(im->dev_blks[blks]); + if (!block) + continue; + kfree(block->data); + } + kfree(im->dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +void tasdevice_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev; + int i; + + if (!tas_priv) + return; + + for (i = 0; i < tas_priv->ndev; i++) { + tasdev = &(tas_priv->tasdevice[i]); + if (!tasdev->cali_data_fmw) + continue; + tas2781_clear_calfirmware(tasdev->cali_data_fmw); + tasdev->cali_data_fmw = NULL; + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB); + +void tasdevice_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **ci = rca->cfg_info; + int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < (int)ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +int tasdevice_select_tuningprm_cfg(void *context, int prm_no, + int cfg_no, int rca_conf_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int prog_status = 0; + int status, i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (cfg_no >= tas_fmw->nr_configurations) { + dev_err(tas_priv->dev, + "%s: cfg(%d) is not in range of conf %u\n", + __func__, cfg_no, tas_fmw->nr_configurations); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 || + !cfg_info) { + dev_err(tas_priv->dev, + "conf_no:%d should be in range from 0 to %u\n", + rca_conf_no, rca->ncfgs-1); + goto out; + } + + conf = &(tas_fmw->configs[cfg_no]); + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (cfg_info[rca_conf_no]->active_dev & (1 << i)) { + if (tas_priv->tasdevice[i].cur_prog != prm_no + || tas_priv->force_fwload_status) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } else + tas_priv->tasdevice[i].is_loading = false; + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration + *cal = cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + + for (i = 0, status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_conf != cfg_no + && (cfg_info[rca_conf_no]->active_dev & (1 << i)) + && (tas_priv->tasdevice[i].is_loaderr == false)) { + status++; + tas_priv->tasdevice[i].is_loading = true; + } else + tas_priv->tasdevice[i].is_loading = false; + } + + if (status) { + status = 0; + tasdevice_load_data(tas_priv, &(conf->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_conf = cfg_no; + } + } else + dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n", + __func__, cfg_no); + + status |= cfg_info[rca_conf_no]->active_dev; + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, + SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_calibdata_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration *cal = + cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load, + SND_SOC_TAS2781_FMWLIB); + +void tasdevice_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) { + if (tas_priv->cur_prog < tas_fmw->nr_programs) { + /*dsp mode or tuning mode*/ + profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); + } + + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + } else + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, + SND_SOC_TAS2781_FMWLIB); + +MODULE_DESCRIPTION("Texas Firmware Support"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c new file mode 100644 index 000000000000..4c59429a42b7 --- /dev/null +++ b/sound/soc/codecs/tas2781-i2c.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// TAS2781 chips. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> +// Author: Kevin Lu <kevin-lu@ti.com> +// + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +static const struct i2c_device_id tasdevice_id[] = { + { "tas2781", TAS2781 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tasdevice_id); + +#ifdef CONFIG_OF +static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,tas2781" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tasdevice_of_match); +#endif + +/** + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) + change = false; + else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->rcabin.profile_cfg_id != + ucontrol->value.integer.value[0]) { + tas_priv->rcabin.profile_cfg_id = + ucontrol->value.integer.value[0]; + ret = 1; + } + + return ret; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_programs; + + return 0; +} + +static int tasdevice_info_configurations( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_create_control(struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *prof_ctrls; + int nr_controls = 1; + int mix_index = 0; + int ret; + char *name; + + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id"); + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_program = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_prog != nr_program) { + tas_priv->cur_prog = nr_program; + ret = 1; + } + + return ret; +} + +static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_configuration_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_configuration = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_conf != nr_configuration) { + tas_priv->cur_conf = nr_configuration; + ret = 1; + } + + return ret; +} + +static int tasdevice_dsp_create_ctrls( + struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *dsp_ctrls; + char *prog_name, *conf_name; + int nr_controls = 2; + int mix_index = 0; + int ret; + + /* Alloc kcontrol via devm_kzalloc, which don't manually + * free the kcontrol + */ + dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(dsp_ctrls[0]), GFP_KERNEL); + if (!dsp_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + prog_name = devm_kzalloc(tas_priv->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!prog_name || !conf_name) { + ret = -ENOMEM; + goto out; + } + + scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Program Id"); + dsp_ctrls[mix_index].name = prog_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_programs; + dsp_ctrls[mix_index].get = tasdevice_program_get; + dsp_ctrls[mix_index].put = tasdevice_program_put; + mix_index++; + + scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Config Id"); + dsp_ctrls[mix_index].name = conf_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_configurations; + dsp_ctrls[mix_index].get = tasdevice_configuration_get; + dsp_ctrls[mix_index].put = tasdevice_configuration_put; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static void tasdevice_fw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = context; + int ret = 0; + int i; + + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + tasdevice_create_control(tas_priv); + + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); + ret = tasdevice_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + tasdevice_dsp_create_ctrls(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + for (i = 0; i < tas_priv->ndev; i++) { + scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + ret = tas2781_load_calibration(tas_priv, + tas_priv->cal_binaryname[i], i); + if (ret != 0) + dev_err(tas_priv->dev, + "%s: load %s error, default will effect\n", + __func__, tas_priv->cal_binaryname[i]); + } + + tasdevice_prmg_calibdata_load(tas_priv, 0); + tas_priv->cur_prog = 0; +out: + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + /*If DSP FW fail, kcontrol won't be created */ + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + } + mutex_unlock(&tas_priv->codec_lock); + if (fmw) + release_firmware(fmw); +} + +static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + /* Codec Lock Hold */ + mutex_lock(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state); + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, + 0, 0, tasdevice_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("DMIC") +}; + +static const struct snd_soc_dapm_route tasdevice_audio_map[] = { + {"SPK", NULL, "ASI"}, + {"OUT", NULL, "SPK"}, + {"ASI OUT", NULL, "DMIC"} +}; + +static int tasdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + + return ret; +} + +static int tasdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai); + unsigned int slot_width; + unsigned int fsrate; + int bclk_rate; + int rc = 0; + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + case 44100: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + +out: + return rc; +} + +static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai); + + tas_priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops tasdevice_dai_ops = { + .startup = tasdevice_startup, + .hw_params = tasdevice_hw_params, + .set_sysclk = tasdevice_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver tasdevice_dai_driver[] = { + { + .name = "tas2781_codec", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .ops = &tasdevice_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tasdevice_codec_probe(struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + return tascodec_init(tas_priv, codec, tasdevice_fw_ready); +} + +static void tasdevice_deinit(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static void tasdevice_codec_remove( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + tasdevice_deinit(tas_priv); +} + +static const struct snd_soc_component_driver + soc_codec_driver_tasdevice = { + .probe = tasdevice_codec_probe, + .remove = tasdevice_codec_remove, + .controls = tas2781_snd_controls, + .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .dapm_widgets = tasdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), + .dapm_routes = tasdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS]; + int rc, i, ndev = 0; + + if (tas_priv->isacpi) { + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", NULL, 0); + if (ndev <= 0) { + ndev = 1; + dev_addrs[0] = client->addr; + } else { + ndev = (ndev < ARRAY_SIZE(dev_addrs)) + ? ndev : ARRAY_SIZE(dev_addrs); + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", dev_addrs, ndev); + } + + tas_priv->irq_info.irq_gpio = + acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); + } else { + struct device_node *np = tas_priv->dev->of_node; +#ifdef CONFIG_OF + const __be32 *reg, *reg_end; + int len, sw, aw; + + aw = of_n_addr_cells(np); + sw = of_n_size_cells(np); + if (sw == 0) { + reg = (const __be32 *)of_get_property(np, + "reg", &len); + reg_end = reg + len/sizeof(*reg); + ndev = 0; + do { + dev_addrs[ndev] = of_read_number(reg, aw); + reg += aw; + ndev++; + } while (reg < reg_end); + } else { + ndev = 1; + dev_addrs[0] = client->addr; + } +#else + ndev = 1; + dev_addrs[0] = client->addr; +#endif + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } + tas_priv->ndev = ndev; + for (i = 0; i < ndev; i++) + tas_priv->tasdevice[i].dev_addr = dev_addrs[i]; + + tas_priv->reset = devm_gpiod_get_optional(&client->dev, + "reset-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(tas_priv->reset)) + dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", + __func__); + + strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name); + + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) { + rc = gpio_request(tas_priv->irq_info.irq_gpio, + "AUDEV-IRQ"); + if (!rc) { + gpio_direction_input( + tas_priv->irq_info.irq_gpio); + + tas_priv->irq_info.irq = + gpio_to_irq(tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, "%s: GPIO %d request error\n", + __func__, tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, + "Looking up irq-gpio property failed %d\n", + tas_priv->irq_info.irq_gpio); +} + +static int tasdevice_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); + const struct acpi_device_id *acpi_id; + struct tasdevice_priv *tas_priv; + int ret; + + tas_priv = tasdevice_kzalloc(i2c); + if (!tas_priv) + return -ENOMEM; + + if (ACPI_HANDLE(&i2c->dev)) { + acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, + &i2c->dev); + if (!acpi_id) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + tas_priv->chip_id = acpi_id->driver_data; + tas_priv->isacpi = true; + } else { + tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->isacpi = false; + } + + tasdevice_parse_dt(tas_priv); + + ret = tasdevice_init(tas_priv); + if (ret) + goto err; + + ret = devm_snd_soc_register_component(tas_priv->dev, + &soc_codec_driver_tasdevice, + tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); + if (ret) { + dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n", + __func__, ret); + goto err; + } +err: + if (ret < 0) + tasdevice_remove(tas_priv); + return ret; +} + +static void tasdevice_i2c_remove(struct i2c_client *client) +{ + struct tasdevice_priv *tas_priv = i2c_get_clientdata(client); + + tasdevice_remove(tas_priv); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id tasdevice_acpi_match[] = { + { "TAS2781", TAS2781 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); +#endif + +static struct i2c_driver tasdevice_i2c_driver = { + .driver = { + .name = "tas2781-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tasdevice_of_match), +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), +#endif + }, + .probe = tasdevice_i2c_probe, + .remove = tasdevice_i2c_remove, + .id_table = tasdevice_id, +}; + +module_i2c_driver(tasdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>"); +MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>"); +MODULE_DESCRIPTION("ASoC TAS2781 Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index f9e7122894bd..60e59e993ba6 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -990,7 +990,7 @@ static struct i2c_driver tas5086_i2c_driver = { .of_match_table = of_match_ptr(tas5086_dt_ids), }, .id_table = tas5086_i2c_id, - .probe_new = tas5086_i2c_probe, + .probe = tas5086_i2c_probe, .remove = tas5086_i2c_remove, }; diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index f39c3273b2fd..1756edb35961 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -975,7 +975,7 @@ static struct i2c_driver tas571x_i2c_driver = { .name = "tas571x", .of_match_table = of_match_ptr(tas571x_of_match), }, - .probe_new = tas571x_i2c_probe, + .probe = tas571x_i2c_probe, .remove = tas571x_i2c_remove, .id_table = tas571x_i2c_id, }; diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 4d27b60bd804..6dd6c0896eff 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -821,7 +820,7 @@ static struct i2c_driver tas5720_i2c_driver = { .name = "tas5720", .of_match_table = of_match_ptr(tas5720_of_match), }, - .probe_new = tas5720_probe, + .probe = tas5720_probe, .id_table = tas5720_id, }; diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index 4e38eb7acea1..aca3756ffab6 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -597,7 +597,7 @@ MODULE_DEVICE_TABLE(of, tas5805m_of_match); #endif static struct i2c_driver tas5805m_i2c_driver = { - .probe_new = tas5805m_i2c_probe, + .probe = tas5805m_i2c_probe, .remove = tas5805m_i2c_remove, .id_table = tas5805m_i2c_id, .driver = { diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index f8ff69fa2549..da89e8c681dd 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -803,7 +802,7 @@ static struct i2c_driver tas6424_i2c_driver = { .name = "tas6424", .of_match_table = of_match_ptr(tas6424_of_ids), }, - .probe_new = tas6424_i2c_probe, + .probe = tas6424_i2c_probe, .remove = tas6424_i2c_remove, .id_table = tas6424_i2c_ids, }; diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index d964e5207569..e187d74a1737 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -629,7 +629,7 @@ static struct i2c_driver tda7419_driver = { .name = "tda7419", .of_match_table = tda7419_of_match, }, - .probe_new = tda7419_probe, + .probe = tda7419_probe, .id_table = tda7419_i2c_id, }; diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 9f7902ec40db..8cca2ceadd9e 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -312,7 +312,7 @@ static struct i2c_driver tfa9879_i2c_driver = { .name = "tfa9879", .of_match_table = tfa9879_of_match, }, - .probe_new = tfa9879_i2c_probe, + .probe = tfa9879_i2c_probe, .id_table = tfa9879_i2c_id, }; diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index b853507e65a8..79847a90ac46 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -416,7 +416,7 @@ static struct i2c_driver tfa989x_i2c_driver = { .name = "tfa989x", .of_match_table = tfa989x_of_match, }, - .probe_new = tfa989x_i2c_probe, + .probe = tfa989x_i2c_probe, }; module_i2c_driver(tfa989x_i2c_driver); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 52bb55724724..b976c1946286 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1451,7 +1451,7 @@ static struct i2c_driver adc3xxx_i2c_driver = { .name = "tlv320adc3xxx-codec", .of_match_table = tlv320adc3xxx_of_match, }, - .probe_new = adc3xxx_i2c_probe, + .probe = adc3xxx_i2c_probe, .remove = __exit_p(adc3xxx_i2c_remove), .id_table = adc3xxx_i2c_id, }; diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 530f321d08e9..41342b340680 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1208,7 +1208,7 @@ static struct i2c_driver adcx140_i2c_driver = { .name = "tlv320adcx140-codec", .of_match_table = of_match_ptr(tlv320adcx140_of_match), }, - .probe_new = adcx140_i2c_probe, + .probe = adcx140_i2c_probe, .id_table = adcx140_i2c_id, }; module_i2c_driver(adcx140_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index 1f97673a1cc0..9692ae007c91 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -47,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .name = "tlv320aic23-codec", .of_match_table = of_match_ptr(tlv320aic23_of_match), }, - .probe_new = tlv320aic23_i2c_probe, + .probe = tlv320aic23_i2c_probe, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 0847302121f6..9611aa8acb0d 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1746,7 +1746,7 @@ static struct i2c_driver aic31xx_i2c_driver = { .of_match_table = of_match_ptr(tlv320aic31xx_of_match), .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), }, - .probe_new = aic31xx_i2c_probe, + .probe = aic31xx_i2c_probe, .id_table = aic31xx_i2c_id, }; module_i2c_driver(aic31xx_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index d1e543ca3521..49b33a256cd7 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -71,7 +71,7 @@ static struct i2c_driver aic32x4_i2c_driver = { .name = "tlv320aic32x4", .of_match_table = aic32x4_of_id, }, - .probe_new = aic32x4_i2c_probe, + .probe = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, .id_table = aic32x4_i2c_id, }; diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index d7e94d564dbf..bb33fd3dfb4f 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -61,7 +61,7 @@ static struct i2c_driver aic3x_i2c_driver = { .name = "tlv320aic3x", .of_match_table = aic3x_of_id, }, - .probe_new = aic3x_i2c_probe, + .probe = aic3x_i2c_probe, .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 16ce3ef1134b..fa46f51d4341 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1560,7 +1560,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { .name = "tlv320dac33-codec", }, - .probe_new = dac33_i2c_probe, + .probe = dac33_i2c_probe, .remove = dac33_i2c_remove, .id_table = tlv320dac33_i2c_id, }; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 8c00db32996b..5bc486283fde 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -319,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = { .name = "tpa6130a2", .of_match_table = of_match_ptr(tpa6130a2_of_match), }, - .probe_new = tpa6130a2_probe, + .probe = tpa6130a2_probe, .id_table = tpa6130a2_id, }; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 5282112c7d8d..6d9166d9116a 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -455,7 +455,7 @@ static struct i2c_driver ts3a227e_driver = { .of_match_table = of_match_ptr(ts3a227e_of_match), .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, - .probe_new = ts3a227e_i2c_probe, + .probe = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, }; module_i2c_driver(ts3a227e_driver); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index fa0c525189c2..1eefc1fe6ea8 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1503,7 +1503,7 @@ static struct i2c_driver tscs42xx_i2c_driver = { .name = "tscs42xx", .of_match_table = tscs42xx_of_match, }, - .probe_new = tscs42xx_i2c_probe, + .probe = tscs42xx_i2c_probe, .id_table = tscs42xx_i2c_id, }; diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 38622bc247fc..744aef32a21f 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -3473,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = { .name = "tscs454", .of_match_table = tscs454_of_match, }, - .probe_new = tscs454_i2c_probe, + .probe = tscs454_i2c_probe, .id_table = tscs454_i2c_id, }; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index fdaaee845176..5c5751dc14e5 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -798,7 +798,7 @@ static struct i2c_driver uda1380_i2c_driver = { .name = "uda1380-codec", .of_match_table = uda1380_of_match, }, - .probe_new = uda1380_i2c_probe, + .probe = uda1380_i2c_probe, .id_table = uda1380_i2c_id, }; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 9c10200ff34b..bd0e9fbc12eb 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1270,7 +1270,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, /* Start in cache-only until device is enumerated */ regcache_cache_only(wcd->regmap, true); - }; + } pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 034a4e858c7e..1d4259433f47 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -994,3 +994,6 @@ module_spi_driver(wm0010_spi_driver); MODULE_DESCRIPTION("ASoC WM0010 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("wm0010.dfw"); +MODULE_FIRMWARE("wm0010_stage2.bin"); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 0064a607ec68..d7eeb41ba60f 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -243,7 +243,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .driver = { .name = "wm1250-ev1", }, - .probe_new = wm1250_ev1_probe, + .probe = wm1250_ev1_probe, .remove = wm1250_ev1_remove, .id_table = wm1250_ev1_i2c_id, }; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 14b4fd97488c..9571ea53cb3f 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -938,7 +938,7 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", }, - .probe_new = wm2000_i2c_probe, + .probe = wm2000_i2c_probe, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 0a65afa44a59..277b8c468c78 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2485,7 +2485,7 @@ static struct i2c_driver wm2200_i2c_driver = { .name = "wm2200", .pm = &wm2200_pm, }, - .probe_new = wm2200_i2c_probe, + .probe = wm2200_i2c_probe, .remove = wm2200_i2c_remove, .id_table = wm2200_i2c_id, }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 3b09d4a1684f..a86eacb2a9bb 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2709,7 +2709,7 @@ static struct i2c_driver wm5100_i2c_driver = { .name = "wm5100", .pm = &wm5100_pm, }, - .probe_new = wm5100_i2c_probe, + .probe = wm5100_i2c_probe, .remove = wm5100_i2c_remove, .id_table = wm5100_i2c_id, }; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index e13f9780a111..c0ed76d5b65f 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -678,7 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = { .name = "wm8510", .of_match_table = wm8510_of_match, }, - .probe_new = wm8510_i2c_probe, + .probe = wm8510_i2c_probe, .id_table = wm8510_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 66f6371d8acf..55c72c5ac845 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -527,7 +527,7 @@ static struct i2c_driver wm8523_i2c_driver = { .name = "wm8523", .of_match_table = wm8523_of_match, }, - .probe_new = wm8523_i2c_probe, + .probe = wm8523_i2c_probe, .id_table = wm8523_i2c_id, }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index ca796aa0aeb7..34ae7fe05398 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1049,7 +1049,7 @@ static struct i2c_driver wm8580_i2c_driver = { .name = "wm8580", .of_match_table = wm8580_of_match, }, - .probe_new = wm8580_i2c_probe, + .probe = wm8580_i2c_probe, .id_table = wm8580_i2c_id, }; diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 383c6796e8a3..903a0147d584 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -464,7 +464,7 @@ static struct i2c_driver wm8711_i2c_driver = { .name = "wm8711", .of_match_table = wm8711_of_match, }, - .probe_new = wm8711_i2c_probe, + .probe = wm8711_i2c_probe, .id_table = wm8711_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index a3dbdbf40723..5ea6d8fd10f6 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -305,7 +305,7 @@ static struct i2c_driver wm8728_i2c_driver = { .name = "wm8728", .of_match_table = wm8728_of_match, }, - .probe_new = wm8728_i2c_probe, + .probe = wm8728_i2c_probe, .id_table = wm8728_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c index fdf03bf91606..c39e637d813d 100644 --- a/sound/soc/codecs/wm8731-i2c.c +++ b/sound/soc/codecs/wm8731-i2c.c @@ -57,7 +57,7 @@ static struct i2c_driver wm8731_i2c_driver = { .name = "wm8731", .of_match_table = wm8731_of_match, }, - .probe_new = wm8731_i2c_probe, + .probe = wm8731_i2c_probe, .id_table = wm8731_i2c_id, }; diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 90b54343370c..9f4e372e90ea 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -649,7 +649,7 @@ static struct i2c_driver wm8737_i2c_driver = { .name = "wm8737", .of_match_table = wm8737_of_match, }, - .probe_new = wm8737_i2c_probe, + .probe = wm8737_i2c_probe, .id_table = wm8737_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index c7afa4f2795d..787156b980a1 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -616,7 +616,7 @@ static struct i2c_driver wm8741_i2c_driver = { .name = "wm8741", .of_match_table = wm8741_of_match, }, - .probe_new = wm8741_i2c_probe, + .probe = wm8741_i2c_probe, .id_table = wm8741_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 2f6ee8d6639f..20dc9ff9fea9 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -813,7 +813,7 @@ static struct i2c_driver wm8750_i2c_driver = { .name = "wm8750", .of_match_table = wm8750_of_match, }, - .probe_new = wm8750_i2c_probe, + .probe = wm8750_i2c_probe, .id_table = wm8750_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index bb18f58dc670..5e8a8eb41b2b 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1590,7 +1590,7 @@ static struct i2c_driver wm8753_i2c_driver = { .name = "wm8753", .of_match_table = wm8753_of_match, }, - .probe_new = wm8753_i2c_probe, + .probe = wm8753_i2c_probe, .id_table = wm8753_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 936ea24621b0..212224a68006 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -523,7 +523,7 @@ static struct i2c_driver wm8776_i2c_driver = { .name = "wm8776", .of_match_table = wm8776_of_match, }, - .probe_new = wm8776_i2c_probe, + .probe = wm8776_i2c_probe, .id_table = wm8776_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index 3ce1a39d76eb..7062a8b2f8b5 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver wm8804_i2c_driver = { .of_match_table = of_match_ptr(wm8804_of_match), .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, - .probe_new = wm8804_i2c_probe, + .probe = wm8804_i2c_probe, .remove = wm8804_i2c_remove, .id_table = wm8804_i2c_id }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 03bbd85ebdf4..320ccd92f318 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1295,7 +1295,7 @@ static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", }, - .probe_new = wm8900_i2c_probe, + .probe = wm8900_i2c_probe, .remove = wm8900_i2c_remove, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 1dc8e20bdace..901b65ef8de5 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2209,7 +2209,7 @@ static struct i2c_driver wm8903_i2c_driver = { .name = "wm8903", .of_match_table = wm8903_of_match, }, - .probe_new = wm8903_i2c_probe, + .probe = wm8903_i2c_probe, .remove = wm8903_i2c_remove, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 791d8738d1c0..068e610b1b4c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2337,7 +2337,7 @@ static struct i2c_driver wm8904_i2c_driver = { .name = "wm8904", .of_match_table = of_match_ptr(wm8904_of_match), }, - .probe_new = wm8904_i2c_probe, + .probe = wm8904_i2c_probe, .id_table = wm8904_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8eb4782c9232..53c27986d216 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -860,7 +860,7 @@ static struct i2c_driver wm8940_i2c_driver = { .name = "wm8940", .of_match_table = wm8940_of_match, }, - .probe_new = wm8940_i2c_probe, + .probe = wm8940_i2c_probe, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 05ef45672ebc..78044f580a67 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -1003,7 +1003,7 @@ static struct i2c_driver wm8955_i2c_driver = { .driver = { .name = "wm8955", }, - .probe_new = wm8955_i2c_probe, + .probe = wm8955_i2c_probe, .id_table = wm8955_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0d167238a369..366f5d769d6d 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1518,7 +1518,7 @@ static struct i2c_driver wm8960_i2c_driver = { .of_match_table = of_match_ptr(wm8960_of_match), .acpi_match_table = ACPI_PTR(wm8960_acpi_match), }, - .probe_new = wm8960_i2c_probe, + .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index a4857024711d..c076f78d04ce 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -982,7 +982,7 @@ static struct i2c_driver wm8961_i2c_driver = { .name = "wm8961", .of_match_table = of_match_ptr(wm8961_of_match), }, - .probe_new = wm8961_i2c_probe, + .probe = wm8961_i2c_probe, .id_table = wm8961_i2c_id, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b901e4c65e8a..68ea15be7330 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3946,7 +3946,7 @@ static struct i2c_driver wm8962_i2c_driver = { .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, - .probe_new = wm8962_i2c_probe, + .probe = wm8962_i2c_probe, .remove = wm8962_i2c_remove, .id_table = wm8962_i2c_id, }; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 4db9248de54b..b22d8f0b59be 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -700,7 +700,7 @@ static struct i2c_driver wm8971_i2c_driver = { .driver = { .name = "wm8971", }, - .probe_new = wm8971_i2c_probe, + .probe = wm8971_i2c_probe, .id_table = wm8971_i2c_id, }; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 010a394c705c..044b6f604c09 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -723,7 +723,7 @@ static struct i2c_driver wm8974_i2c_driver = { .name = "wm8974", .of_match_table = wm8974_of_match, }, - .probe_new = wm8974_i2c_probe, + .probe = wm8974_i2c_probe, .id_table = wm8974_i2c_id, }; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index aa2f55401a88..5c829301cf4c 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1072,7 +1072,7 @@ static struct i2c_driver wm8978_i2c_driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, - .probe_new = wm8978_i2c_probe, + .probe = wm8978_i2c_probe, .id_table = wm8978_i2c_id, }; diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 50e6ac6ccbe0..2bd26e2478d9 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -1068,7 +1068,7 @@ static struct i2c_driver wm8983_i2c_driver = { .driver = { .name = "wm8983", }, - .probe_new = wm8983_i2c_probe, + .probe = wm8983_i2c_probe, .id_table = wm8983_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 751aa6730833..c0816bcfa294 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1206,7 +1206,7 @@ static struct i2c_driver wm8985_i2c_driver = { .driver = { .name = "wm8985", }, - .probe_new = wm8985_i2c_probe, + .probe = wm8985_i2c_probe, .id_table = wm8985_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 5dbdf647cd97..b440719cca7d 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -905,7 +905,7 @@ static struct i2c_driver wm8988_i2c_driver = { .driver = { .name = "wm8988", }, - .probe_new = wm8988_i2c_probe, + .probe = wm8988_i2c_probe, .id_table = wm8988_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 589af286f133..5a8e765090af 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1247,7 +1247,7 @@ static struct i2c_driver wm8990_i2c_driver = { .driver = { .name = "wm8990", }, - .probe_new = wm8990_i2c_probe, + .probe = wm8990_i2c_probe, .id_table = wm8990_i2c_id, }; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 30121993b7b4..8cb2ae829699 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1323,7 +1323,7 @@ static struct i2c_driver wm8991_i2c_driver = { .driver = { .name = "wm8991", }, - .probe_new = wm8991_i2c_probe, + .probe = wm8991_i2c_probe, .id_table = wm8991_i2c_id, }; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 22a47acbc6d1..feb997c698e2 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1741,7 +1741,7 @@ static struct i2c_driver wm8993_i2c_driver = { .driver = { .name = "wm8993", }, - .probe_new = wm8993_i2c_probe, + .probe = wm8993_i2c_probe, .remove = wm8993_i2c_remove, .id_table = wm8993_i2c_id, }; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index eed48bf339f2..90588614edcc 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2268,7 +2268,7 @@ static struct i2c_driver wm8995_i2c_driver = { .driver = { .name = "wm8995", }, - .probe_new = wm8995_i2c_probe, + .probe = wm8995_i2c_probe, .id_table = wm8995_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index b52ed89d631a..5d0eb0ae0475 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -3086,7 +3086,7 @@ static struct i2c_driver wm8996_i2c_driver = { .driver = { .name = "wm8996", }, - .probe_new = wm8996_i2c_probe, + .probe = wm8996_i2c_probe, .remove = wm8996_i2c_remove, .id_table = wm8996_i2c_id, }; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 513ec0ba81bb..34a07db7342a 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1369,7 +1369,7 @@ static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", }, - .probe_new = wm9081_i2c_probe, + .probe = wm9081_i2c_probe, .remove = wm9081_i2c_remove, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index ef3524c3f07f..432729c753dd 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -616,7 +616,7 @@ static struct i2c_driver wm9090_i2c_driver = { .driver = { .name = "wm9090", }, - .probe_new = wm9090_i2c_probe, + .probe = wm9090_i2c_probe, .id_table = wm9090_id, }; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 216120b68b64..5a89abfe8784 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,7 +15,6 @@ #include <linux/firmware.h> #include <linux/list.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -686,8 +685,6 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, { struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); struct wm_coeff_ctl *ctl; - struct snd_kcontrol *kcontrol; - char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); @@ -699,23 +696,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, ctl = cs_ctl->priv; - if (dsp->component->name_prefix) - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", - dsp->component->name_prefix, ctl->name); - else - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", - ctl->name); - - kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); - if (!kcontrol) { - adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); - return -EINVAL; - } - - snd_ctl_notify(dsp->component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); - - return 0; + return snd_soc_component_notify_control(dsp->component, ctl->name); } EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index e80b53143569..e40d583a1ce6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1333,7 +1333,8 @@ static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) WSA883X_DRE_GAIN_EN_MASK, WSA883X_DRE_GAIN_FROM_CSR); snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, - WSA883X_GLOBAL_PA_EN_MASK, 1); + WSA883X_GLOBAL_PA_EN_MASK, + WSA883X_GLOBAL_PA_ENABLE); } diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c new file mode 100644 index 000000000000..993d76b18b53 --- /dev/null +++ b/sound/soc/codecs/wsa884x.c @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define WSA884X_BASE 0x3000 +#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001) +#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00) +#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01) +#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02) +#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03) +#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0 +#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4 +#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f +#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0 +#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04) +#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05) +#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06) +#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07) +#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08) +#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09) +#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a) +#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03 +#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0 +#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b) +#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c) +#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d) +#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e) +#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f) +#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10) +#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11) +#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12) +#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13) +#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14) +#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15) +#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16) +#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17) +#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18) +#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19) +#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a) +#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b) +#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c) +#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d) + +#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020) +#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00) +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0 +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5 +#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01) +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0 +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5 + +#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02) +#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03) +#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04) +#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05) +#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06) +#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07) +#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08) +#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09) +#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a) +#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b) +#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c) +#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d) +#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e) +#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f) +#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10) +#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11) +#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12) +#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13) +#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14) +#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15) +#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16) +#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17) +#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18) + +#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040) +#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00) +#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01 +#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01) +#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02) +#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03) +#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04) +#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05) +#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06) +#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07) +#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08) +#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09) +#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a) +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80 +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0 +#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b) +#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c) +#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d) +#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e) +#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f) +#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10) +#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11) +#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12) +#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13) +#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14) +#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15) +#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16) +#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17) +#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18) +#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19) +#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a) +#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b) +#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c) +#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d) +#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e) +#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f) +#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20) +#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21) +#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22) +#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23) +#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24) +#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25) +#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26) +#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27) +#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28) +#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29) +#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a) +#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b) +#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c) +#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d) +#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e) +#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f) +#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30) +#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31) +#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32) +#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33) +#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34) +#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35) +#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36) +#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37) +#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38) +#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39) +#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a) +#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b) +#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c) +#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d) +#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e) + +#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090) +#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00) +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8 +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3 +#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07 +#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0 +#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01) +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2 +#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0 +#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02) +#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03) +#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04) +#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05) +#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06) +#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07) +#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08) +#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09) +#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a) +#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b) +#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c) +#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d) +#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e) +#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f) +#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10) +#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80 +#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0 +#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11) +#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12) +#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13) +#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14) +#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15) +#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16) +#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17) +#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18) +#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19) +#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a) +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80 +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00 +#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b) +#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c) +#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d) +#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e) +#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f) + +#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0) +#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00) +#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01) +#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02) +#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03) +#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04) +#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05) +#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06) +#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07) +#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08) +#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09) +#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a) +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20 +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5 +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0 +#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b) +#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c) +#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d) +#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e) +#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f) +#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10) +#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11) +#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12) +#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13) +#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14) +#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15) +#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16) + +#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400) +#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00) +#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01) +#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02) +#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03) +#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04) +#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05) +#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10) +#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14) +#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18) +#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19) +#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a) +#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02 +#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0 +#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c) +#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30) +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01 +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0 +#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31) +#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32) +#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38 +#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33) +#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34) +#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35) +#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36) +#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37) +#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38) +#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39) +#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a) +#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b) +#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c) +#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d) +#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e) +#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50) +#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51) +#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52) +#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53) +#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54) +#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55) +#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56) +#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57) +#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58) +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0 +#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59) +#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e +#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01 +#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a) +#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b) +#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c) +#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d) +#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e) +#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f) +#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60) +#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61) +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0 +#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64) +#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68) +#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70) +#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71) +#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72) +#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73) +#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74) +#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75) +#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76) +#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77) +#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78) +#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79) +#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a) +#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b) +#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c) +#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d) +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e) +#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f) +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80) +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81) +#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82) +#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83) +#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84) +#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85) +#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86) +#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87) +#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88) +#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89) +#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a) +#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b) +#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04 +#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0 +#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90) +#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91) +#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92) +#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93) +#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0) +#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0 +#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4 +#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07 +#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0 +#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1) +#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e +#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0 +#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2) +#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8) +#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9) +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0 +#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0) +#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1) +#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2) +#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8) +#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9) +#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca) +#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb) +#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc) +#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0) +#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7 +#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70 +#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4 +#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3 +#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2 +#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1 +#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0 +#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1) +#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2) +#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3) +#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4) +#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5) +#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6) +#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7) +#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8) +#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0) +#define WSA884X_PBR_MAX_VOLTAGE 20 +#define WSA884X_PBR_MAX_CODE 255 +#define WSA884X_VTH_TO_REG(vth) \ + ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0) +#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1) +#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2) +#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3) +#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4) +#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5) +#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6) +#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7) +#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8) +#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9) +#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea) +#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb) +#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec) +#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed) +#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee) +#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef) + +#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500) +#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00) +#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01) +#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04) +#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0 +#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa +#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c +#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01 +#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05) +#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10) +#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11) +#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12) +#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13) +#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14) +#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15) +#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16) +#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17) +#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18) +#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19) +#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a) +#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30) +#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31) +#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32) +#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33) +#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34) +#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35) +#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36) +#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37) +#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38) +#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39) +#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a) +#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b) +#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c) +#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40) +#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41) +#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43) +#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44) +#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45) +#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46) +#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47) +#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48) +#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49) +#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a) +#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b) +#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c) +#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d) +#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e) +#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f) +#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50) +#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51) +#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52) +#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53) +#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54) +#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55) +#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56) +#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57) +#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58) +#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59) +#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a) +#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b) +#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c) +#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d) +#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80) +#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81) +#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82) +#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83) +#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84) +#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85) +#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86) +#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87) +#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88) +#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89) +#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a) +#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b) +#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c) +#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0) +#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1) +#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2) +#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3) +#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4) +#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5) +#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9) +#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca) +#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb) +#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc) +#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd) +#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce) +#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf) +#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0) +#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1) +#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2) +#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0) +#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1) +#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2) +#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3) +#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff) + +#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800) +#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00) +#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80) +#define WSA884X_OTP_ID_WSA8840 0x0 +#define WSA884X_OTP_ID_WSA8845 0x5 +#define WSA884X_OTP_ID_WSA8845H 0xc +#define WSA884X_OTP_REG_0_ID_MASK 0x0f +#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81) +#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82) +#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83) +#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84) +#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85) +#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86) +#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87) +#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88) +#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89) +#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a) +#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b) +#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c) +#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d) +#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e) +#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f) +#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90) +#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91) +#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92) +#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93) +#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94) +#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95) +#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96) +#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97) +#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98) +#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99) +#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a) +#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b) +#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c) +#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d) +#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e) +#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f) +#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0) +#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1) +#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2) +#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3) +#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4) +#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5) +#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6) +#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0 +#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0 +#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7) +#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8) +#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0 +#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6 +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0 +#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9) +#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf) + +#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0) +#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00) +#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01) +#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02) +#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03) +#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04) +#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05) +#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06) +#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07) +#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08) +#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09) +#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a) +#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b) +#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c) +#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d) +#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e) +#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f) +#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10) +#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11) +#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12) +#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13) +#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14) +#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15) +#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16) +#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17) +#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18) +#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19) +#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a) +#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b) +#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c) +#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d) +#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e) +#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f) +#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20) +#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21) +#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22) +#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23) +#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24) +#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25) +#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26) +#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27) +#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28) +#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29) +#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a) +#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b) +#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c) +#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d) +#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e) +#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f) +#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30) +#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31) +#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32) +#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33) +#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34) +#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35) +#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36) +#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37) +#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38) +#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39) +#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a) +#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b) +#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c) +#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d) +#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e) +#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f) + +#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1) +#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1) + +#define WSA884X_SUPPLIES_NUM 2 +#define WSA884X_MAX_SWR_PORTS 6 +#define WSA884X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct wsa884x_priv { + struct regmap *regmap; + struct device *dev; + struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM]; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + bool port_prepared[WSA884X_MAX_SWR_PORTS]; + bool port_enable[WSA884X_MAX_SWR_PORTS]; + unsigned int variant; + int active_ports; + int dev_mode; + bool hw_init; +}; + +enum { + COMP_OFFSET0, + COMP_OFFSET1, + COMP_OFFSET2, + COMP_OFFSET3, + COMP_OFFSET4, +}; + +enum wsa884x_gain { + G_21_DB = 0, + G_19P5_DB, + G_18_DB, + G_16P5_DB, + G_15_DB, + G_13P5_DB, + G_12_DB, + G_10P5_DB, + G_9_DB, + G_7P5_DB, + G_6_DB, + G_4P5_DB, + G_3_DB, + G_1P5_DB, + G_0_DB, + G_M1P5_DB, + G_M3_DB, + G_M4P5_DB, + G_M6_DB, + G_MAX_DB, +}; + +enum wsa884x_isense { + ISENSE_6_DB = 0, + ISENSE_12_DB, + ISENSE_15_DB, + ISENSE_18_DB, +}; + +enum wsa884x_vsense { + VSENSE_M12_DB = 0, + VSENSE_M15_DB, + VSENSE_M18_DB, + VSENSE_M21_DB, + VSENSE_M24_DB, +}; + +enum wsa884x_port_ids { + WSA884X_PORT_DAC, + WSA884X_PORT_COMP, + WSA884X_PORT_BOOST, + WSA884X_PORT_PBR, + WSA884X_PORT_VISENSE, + WSA884X_PORT_CPS, +}; + +static const char * const wsa884x_supply_name[] = { + "vdd-io", + "vdd-1p8", +}; + +static const char * const wsa884x_dev_mode_text[] = { + "Speaker", "Receiver" +}; + +enum wsa884x_mode { + WSA884X_SPEAKER, + WSA884X_RECEIVER, +}; + +static const struct soc_enum wsa884x_dev_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text); + +static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_COMP + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_BOOST + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_PBR + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_CPS + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_COMP + 1, + .ch_mask = 0xf, + }, { + .num = WSA884X_PORT_BOOST + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_PBR + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_CPS + 1, + .ch_mask = 0x3, + }, +}; + +static struct reg_default wsa884x_defaults[] = { + { WSA884X_BG_CTRL, 0xa5 }, + { WSA884X_ADC_CTRL, 0x00 }, + { WSA884X_BOP1_PROG, 0x22 }, + { WSA884X_BOP2_PROG, 0x44 }, + { WSA884X_UVLO_PROG, 0x99 }, + { WSA884X_UVLO_PROG1, 0x70 }, + { WSA884X_SPARE_CTRL_0, 0x00 }, + { WSA884X_SPARE_CTRL_1, 0x00 }, + { WSA884X_SPARE_CTRL_2, 0x00 }, + { WSA884X_SPARE_CTRL_3, 0x00 }, + { WSA884X_REF_CTRL, 0xd2 }, + { WSA884X_BG_TEST_CTL, 0x06 }, + { WSA884X_BG_BIAS, 0xd7 }, + { WSA884X_ADC_PROG, 0x08 }, + { WSA884X_ADC_IREF_CTL, 0x57 }, + { WSA884X_ADC_ISENS_CTL, 0x47 }, + { WSA884X_ADC_CLK_CTL, 0x87 }, + { WSA884X_ADC_TEST_CTL, 0x00 }, + { WSA884X_ADC_BIAS, 0x51 }, + { WSA884X_VBAT_SNS, 0xa0 }, + { WSA884X_BOP_ATEST_SEL, 0x00 }, + { WSA884X_MISC0, 0x04 }, + { WSA884X_MISC1, 0x75 }, + { WSA884X_MISC2, 0x00 }, + { WSA884X_MISC3, 0x10 }, + { WSA884X_SPARE_TSBG_0, 0x00 }, + { WSA884X_SPARE_TUNE_0, 0x00 }, + { WSA884X_SPARE_TUNE_1, 0x00 }, + { WSA884X_VSENSE1, 0xe7 }, + { WSA884X_ISENSE2, 0x27 }, + { WSA884X_SPARE_CTL_1, 0x00 }, + { WSA884X_SPARE_CTL_2, 0x00 }, + { WSA884X_SPARE_CTL_3, 0x00 }, + { WSA884X_SPARE_CTL_4, 0x00 }, + { WSA884X_EN, 0x10 }, + { WSA884X_OVERRIDE1, 0x00 }, + { WSA884X_OVERRIDE2, 0x08 }, + { WSA884X_ISENSE1, 0xd4 }, + { WSA884X_ISENSE_CAL, 0x00 }, + { WSA884X_MISC, 0x00 }, + { WSA884X_ADC_0, 0x00 }, + { WSA884X_ADC_1, 0x00 }, + { WSA884X_ADC_2, 0x40 }, + { WSA884X_ADC_3, 0x80 }, + { WSA884X_ADC_4, 0x25 }, + { WSA884X_ADC_5, 0x24 }, + { WSA884X_ADC_6, 0x0a }, + { WSA884X_ADC_7, 0x81 }, + { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 }, + { WSA884X_SPARE_TUNE_2, 0x00 }, + { WSA884X_SPARE_TUNE_3, 0x00 }, + { WSA884X_SPARE_TUNE_4, 0x00 }, + { WSA884X_TOP_CTRL1, 0xd3 }, + { WSA884X_CLIP_DET_CTRL1, 0x7e }, + { WSA884X_CLIP_DET_CTRL2, 0x4c }, + { WSA884X_DAC_CTRL1, 0xa4 }, + { WSA884X_DAC_VCM_CTRL_REG1, 0x02 }, + { WSA884X_DAC_VCM_CTRL_REG2, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG3, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG4, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG5, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG6, 0x00 }, + { WSA884X_PWM_CLK_CTL, 0x20 }, + { WSA884X_DRV_LF_LDO_SEL, 0xaa }, + { WSA884X_OCP_CTL, 0xc6 }, + { WSA884X_PDRV_HS_CTL, 0x52 }, + { WSA884X_PDRV_LS_CTL, 0x4a }, + { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 }, + { WSA884X_SPARE_CTL_5, 0x00 }, + { WSA884X_DAC_EN_DEBUG_REG, 0x00 }, + { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 }, + { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 }, + { WSA884X_DAC_TUNE1, 0x02 }, + { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 }, + { WSA884X_ATEST1_REG, 0x00 }, + { WSA884X_ATEST2_REG, 0x00 }, + { WSA884X_TOP_BIAS_REG1, 0x6a }, + { WSA884X_TOP_BIAS_REG2, 0x65 }, + { WSA884X_TOP_BIAS_REG3, 0x55 }, + { WSA884X_TOP_BIAS_REG4, 0xa9 }, + { WSA884X_PWRSTG_DBG2, 0x21 }, + { WSA884X_DRV_LF_BLK_EN, 0x0f }, + { WSA884X_DRV_LF_EN, 0x0a }, + { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 }, + { WSA884X_DRV_LF_MISC_CTL1, 0x30 }, + { WSA884X_DRV_LF_REG_GAIN, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL1, 0x90 }, + { WSA884X_PWRSTG_DBG, 0x08 }, + { WSA884X_BBM_CTL, 0x92 }, + { WSA884X_TOP_MISC1, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG7, 0x00 }, + { WSA884X_TOP_BIAS_REG5, 0x15 }, + { WSA884X_DRV_LF_MISC_CTL2, 0x00 }, + { WSA884X_STB_CTRL1, 0x42 }, + { WSA884X_CURRENT_LIMIT, 0x54 }, + { WSA884X_BYP_CTRL1, 0x01 }, + { WSA884X_SPARE_CTL_0, 0x00 }, + { WSA884X_BOOST_SPARE_CTL_1, 0x00 }, + { WSA884X_IBIAS1, 0x00 }, + { WSA884X_IBIAS2, 0x00 }, + { WSA884X_IBIAS3, 0x00 }, + { WSA884X_EN_CTRL, 0x42 }, + { WSA884X_STB_CTRL2, 0x03 }, + { WSA884X_STB_CTRL3, 0x3c }, + { WSA884X_STB_CTRL4, 0x30 }, + { WSA884X_BYP_CTRL2, 0x97 }, + { WSA884X_BYP_CTRL3, 0x11 }, + { WSA884X_ZX_CTRL1, 0xf0 }, + { WSA884X_ZX_CTRL2, 0x04 }, + { WSA884X_BLEEDER_CTRL, 0x04 }, + { WSA884X_BOOST_MISC, 0x62 }, + { WSA884X_PWRSTAGE_CTRL1, 0x00 }, + { WSA884X_PWRSTAGE_CTRL2, 0x31 }, + { WSA884X_PWRSTAGE_CTRL3, 0x81 }, + { WSA884X_PWRSTAGE_CTRL4, 0x5f }, + { WSA884X_MAXD_REG1, 0x00 }, + { WSA884X_MAXD_REG2, 0x5b }, + { WSA884X_ILIM_CTRL1, 0xe2 }, + { WSA884X_ILIM_CTRL2, 0x90 }, + { WSA884X_TEST_CTRL1, 0x00 }, + { WSA884X_TEST_CTRL2, 0x00 }, + { WSA884X_SPARE1, 0x00 }, + { WSA884X_BOOT_CAP_CHECK, 0x01 }, + { WSA884X_PON_CTL_0, 0x12 }, + { WSA884X_PWRSAV_CTL, 0xaa }, + { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 }, + { WSA884X_PON_CLT_1, 0xe1 }, + { WSA884X_PON_CTL_2, 0x00 }, + { WSA884X_PON_CTL_3, 0x70 }, + { WSA884X_CKWD_CTL_0, 0x14 }, + { WSA884X_CKWD_CTL_1, 0x3b }, + { WSA884X_CKWD_CTL_2, 0x00 }, + { WSA884X_CKSK_CTL_0, 0x00 }, + { WSA884X_PADSW_CTL_0, 0x00 }, + { WSA884X_TEST_0, 0x00 }, + { WSA884X_TEST_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 }, + { WSA884X_DIG_CTRL0_PAGE, 0x00 }, + { WSA884X_CDC_RST_CTL, 0x01 }, + { WSA884X_SWR_RESET_EN, 0x00 }, + { WSA884X_TOP_CLK_CFG, 0x00 }, + { WSA884X_SWR_CLK_RATE, 0x00 }, + { WSA884X_CDC_PATH_MODE, 0x00 }, + { WSA884X_CDC_CLK_CTL, 0x1f }, + { WSA884X_PA_FSM_EN, 0x00 }, + { WSA884X_PA_FSM_CTL0, 0x00 }, + { WSA884X_PA_FSM_CTL1, 0xfe }, + { WSA884X_PA_FSM_TIMER0, 0x80 }, + { WSA884X_PA_FSM_TIMER1, 0x80 }, + { WSA884X_PA_FSM_ERR_CTL, 0x00 }, + { WSA884X_PA_FSM_MSK0, 0x00 }, + { WSA884X_PA_FSM_MSK1, 0x00 }, + { WSA884X_PA_FSM_BYP_CTL, 0x00 }, + { WSA884X_PA_FSM_BYP0, 0x00 }, + { WSA884X_PA_FSM_BYP1, 0x00 }, + { WSA884X_TADC_VALUE_CTL, 0x03 }, + { WSA884X_TEMP_DETECT_CTL, 0x01 }, + { WSA884X_TEMP_CONFIG0, 0x00 }, + { WSA884X_TEMP_CONFIG1, 0x00 }, + { WSA884X_VBAT_THRM_FLT_CTL, 0x7f }, + { WSA884X_VBAT_CAL_CTL, 0x01 }, + { WSA884X_UVLO_DEGLITCH_CTL, 0x05 }, + { WSA884X_BOP_DEGLITCH_CTL, 0x05 }, + { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 }, + { WSA884X_CPS_CTL, 0x00 }, + { WSA884X_CDC_RX_CTL, 0xfe }, + { WSA884X_CDC_SPK_DSM_A1_0, 0x00 }, + { WSA884X_CDC_SPK_DSM_A1_1, 0x01 }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x96 }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x09 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xab }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x05 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x1c }, + { WSA884X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x17 }, + { WSA884X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A6_0, 0xaa }, + { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 }, + { WSA884X_CDC_SPK_DSM_C_0, 0x69 }, + { WSA884X_CDC_SPK_DSM_C_1, 0x54 }, + { WSA884X_CDC_SPK_DSM_C_2, 0x02 }, + { WSA884X_CDC_SPK_DSM_C_3, 0x15 }, + { WSA884X_CDC_SPK_DSM_R1, 0xa4 }, + { WSA884X_CDC_SPK_DSM_R2, 0xb5 }, + { WSA884X_CDC_SPK_DSM_R3, 0x86 }, + { WSA884X_CDC_SPK_DSM_R4, 0x85 }, + { WSA884X_CDC_SPK_DSM_R5, 0xaa }, + { WSA884X_CDC_SPK_DSM_R6, 0xe2 }, + { WSA884X_CDC_SPK_DSM_R7, 0x62 }, + { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 }, + { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc }, + { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 }, + { WSA884X_PDM_WD_CTL, 0x00 }, + { WSA884X_DEM_BYPASS_DATA0, 0x00 }, + { WSA884X_DEM_BYPASS_DATA1, 0x00 }, + { WSA884X_DEM_BYPASS_DATA2, 0x00 }, + { WSA884X_DEM_BYPASS_DATA3, 0x00 }, + { WSA884X_DRE_CTL_0, 0x70 }, + { WSA884X_DRE_CTL_1, 0x04 }, + { WSA884X_DRE_IDLE_DET_CTL, 0x2f }, + { WSA884X_GAIN_RAMPING_CTL, 0x50 }, + { WSA884X_GAIN_RAMPING_MIN, 0x12 }, + { WSA884X_TAGC_CTL, 0x15 }, + { WSA884X_TAGC_TIME, 0xbc }, + { WSA884X_TAGC_FORCE_VAL, 0x00 }, + { WSA884X_VAGC_CTL, 0x01 }, + { WSA884X_VAGC_TIME, 0x0f }, + { WSA884X_VAGC_ATTN_LVL_1, 0x03 }, + { WSA884X_VAGC_ATTN_LVL_2, 0x06 }, + { WSA884X_VAGC_ATTN_LVL_3, 0x09 }, + { WSA884X_CLSH_CTL_0, 0x37 }, + { WSA884X_CLSH_CTL_1, 0x81 }, + { WSA884X_CLSH_V_HD_PA, 0x0c }, + { WSA884X_CLSH_V_PA_MIN, 0x00 }, + { WSA884X_CLSH_OVRD_VAL, 0x00 }, + { WSA884X_CLSH_HARD_MAX, 0xff }, + { WSA884X_CLSH_SOFT_MAX, 0xf5 }, + { WSA884X_CLSH_SIG_DP, 0x00 }, + { WSA884X_PBR_DELAY_CTL, 0x07 }, + { WSA884X_CLSH_SRL_MAX_PBR, 0x02 }, + { WSA884X_CLSH_VTH1, 0x00 }, + { WSA884X_CLSH_VTH2, 0x00 }, + { WSA884X_CLSH_VTH3, 0x00 }, + { WSA884X_CLSH_VTH4, 0x00 }, + { WSA884X_CLSH_VTH5, 0x00 }, + { WSA884X_CLSH_VTH6, 0x00 }, + { WSA884X_CLSH_VTH7, 0x00 }, + { WSA884X_CLSH_VTH8, 0x00 }, + { WSA884X_CLSH_VTH9, 0x00 }, + { WSA884X_CLSH_VTH10, 0x00 }, + { WSA884X_CLSH_VTH11, 0x00 }, + { WSA884X_CLSH_VTH12, 0x00 }, + { WSA884X_CLSH_VTH13, 0x00 }, + { WSA884X_CLSH_VTH14, 0x00 }, + { WSA884X_CLSH_VTH15, 0x00 }, + { WSA884X_DIG_CTRL1_PAGE, 0x00 }, + { WSA884X_PIN_CTL, 0x04 }, + { WSA884X_PIN_CTL_OE, 0x00 }, + { WSA884X_PIN_WDATA_IOPAD, 0x00 }, + { WSA884X_I2C_SLAVE_CTL, 0x00 }, + { WSA884X_SPMI_PAD_CTL0, 0x2f }, + { WSA884X_SPMI_PAD_CTL1, 0x2f }, + { WSA884X_SPMI_PAD_CTL2, 0x2f }, + { WSA884X_MEM_CTL, 0x00 }, + { WSA884X_SWR_HM_TEST0, 0x08 }, + { WSA884X_OTP_CTRL0, 0x00 }, + { WSA884X_OTP_CTRL2, 0x00 }, + { WSA884X_OTP_PRG_TCSP0, 0x77 }, + { WSA884X_OTP_PRG_TCSP1, 0x00 }, + { WSA884X_OTP_PRG_TPPS, 0x47 }, + { WSA884X_OTP_PRG_TVPS, 0x3b }, + { WSA884X_OTP_PRG_TVPH, 0x47 }, + { WSA884X_OTP_PRG_TPPR0, 0x47 }, + { WSA884X_OTP_PRG_TPPR1, 0x00 }, + { WSA884X_OTP_PRG_TPPH, 0x47 }, + { WSA884X_OTP_PRG_END, 0x47 }, + { WSA884X_WAVG_PLAY, 0x00 }, + { WSA884X_WAVG_CTL, 0x06 }, + { WSA884X_WAVG_LRA_PER_0, 0xd1 }, + { WSA884X_WAVG_LRA_PER_1, 0x00 }, + { WSA884X_WAVG_DELTA_THETA_0, 0xe6 }, + { WSA884X_WAVG_DELTA_THETA_1, 0x04 }, + { WSA884X_WAVG_DIRECT_AMP_0, 0x50 }, + { WSA884X_WAVG_DIRECT_AMP_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP0_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP0_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP1_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP1_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP2_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP2_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP3_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP3_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP4_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP4_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP5_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP5_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP6_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP6_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP7_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP7_1, 0x00 }, + { WSA884X_WAVG_PER_0_1, 0x88 }, + { WSA884X_WAVG_PER_2_3, 0x88 }, + { WSA884X_WAVG_PER_4_5, 0x88 }, + { WSA884X_WAVG_PER_6_7, 0x88 }, + { WSA884X_INTR_MODE, 0x00 }, + { WSA884X_INTR_MASK0, 0x90 }, + { WSA884X_INTR_MASK1, 0x00 }, + { WSA884X_INTR_CLEAR0, 0x00 }, + { WSA884X_INTR_CLEAR1, 0x00 }, + { WSA884X_INTR_LEVEL0, 0x04 }, + { WSA884X_INTR_LEVEL1, 0x00 }, + { WSA884X_INTR_SET0, 0x00 }, + { WSA884X_INTR_SET1, 0x00 }, + { WSA884X_INTR_TEST0, 0x00 }, + { WSA884X_INTR_TEST1, 0x00 }, + { WSA884X_PDM_TEST_MODE, 0x00 }, + { WSA884X_PA_FSM_DBG, 0x00 }, + { WSA884X_DIG_DEBUG_MODE, 0x00 }, + { WSA884X_DIG_DEBUG_SEL, 0x00 }, + { WSA884X_DIG_DEBUG_EN, 0x00 }, + { WSA884X_TADC_DETECT_DBG_CTL, 0x00 }, + { WSA884X_TADC_DEBUG_MSB, 0x00 }, + { WSA884X_TADC_DEBUG_LSB, 0x00 }, + { WSA884X_SAMPLE_EDGE_SEL, 0x7f }, + { WSA884X_SWR_EDGE_SEL, 0x00 }, + { WSA884X_TEST_MODE_CTL, 0x05 }, + { WSA884X_IOPAD_CTL, 0x00 }, + { WSA884X_ANA_CSR_DBG_ADD, 0x00 }, + { WSA884X_ANA_CSR_DBG_CTL, 0x12 }, + { WSA884X_CLK_DBG_CTL, 0x00 }, + { WSA884X_SPARE_0, 0x00 }, + { WSA884X_SPARE_1, 0x00 }, + { WSA884X_SPARE_2, 0x00 }, + { WSA884X_SCODE, 0x00 }, + { WSA884X_DIG_TRIM_PAGE, 0x00 }, + { WSA884X_EMEM_0, 0x00 }, + { WSA884X_EMEM_1, 0x00 }, + { WSA884X_EMEM_2, 0x00 }, + { WSA884X_EMEM_3, 0x00 }, + { WSA884X_EMEM_4, 0x00 }, + { WSA884X_EMEM_5, 0x00 }, + { WSA884X_EMEM_6, 0x00 }, + { WSA884X_EMEM_7, 0x00 }, + { WSA884X_EMEM_8, 0x00 }, + { WSA884X_EMEM_9, 0x00 }, + { WSA884X_EMEM_10, 0x00 }, + { WSA884X_EMEM_11, 0x00 }, + { WSA884X_EMEM_12, 0x00 }, + { WSA884X_EMEM_13, 0x00 }, + { WSA884X_EMEM_14, 0x00 }, + { WSA884X_EMEM_15, 0x00 }, + { WSA884X_EMEM_16, 0x00 }, + { WSA884X_EMEM_17, 0x00 }, + { WSA884X_EMEM_18, 0x00 }, + { WSA884X_EMEM_19, 0x00 }, + { WSA884X_EMEM_20, 0x00 }, + { WSA884X_EMEM_21, 0x00 }, + { WSA884X_EMEM_22, 0x00 }, + { WSA884X_EMEM_23, 0x00 }, + { WSA884X_EMEM_24, 0x00 }, + { WSA884X_EMEM_25, 0x00 }, + { WSA884X_EMEM_26, 0x00 }, + { WSA884X_EMEM_27, 0x00 }, + { WSA884X_EMEM_28, 0x00 }, + { WSA884X_EMEM_29, 0x00 }, + { WSA884X_EMEM_30, 0x00 }, + { WSA884X_EMEM_31, 0x00 }, + { WSA884X_EMEM_32, 0x00 }, + { WSA884X_EMEM_33, 0x00 }, + { WSA884X_EMEM_34, 0x00 }, + { WSA884X_EMEM_35, 0x00 }, + { WSA884X_EMEM_36, 0x00 }, + { WSA884X_EMEM_37, 0x00 }, + { WSA884X_EMEM_38, 0x00 }, + { WSA884X_EMEM_39, 0x00 }, + { WSA884X_EMEM_40, 0x00 }, + { WSA884X_EMEM_41, 0x00 }, + { WSA884X_EMEM_42, 0x00 }, + { WSA884X_EMEM_43, 0x00 }, + { WSA884X_EMEM_44, 0x00 }, + { WSA884X_EMEM_45, 0x00 }, + { WSA884X_EMEM_46, 0x00 }, + { WSA884X_EMEM_47, 0x00 }, + { WSA884X_EMEM_48, 0x00 }, + { WSA884X_EMEM_49, 0x00 }, + { WSA884X_EMEM_50, 0x00 }, + { WSA884X_EMEM_51, 0x00 }, + { WSA884X_EMEM_52, 0x00 }, + { WSA884X_EMEM_53, 0x00 }, + { WSA884X_EMEM_54, 0x00 }, + { WSA884X_EMEM_55, 0x00 }, + { WSA884X_EMEM_56, 0x00 }, + { WSA884X_EMEM_57, 0x00 }, + { WSA884X_EMEM_58, 0x00 }, + { WSA884X_EMEM_59, 0x00 }, + { WSA884X_EMEM_60, 0x00 }, + { WSA884X_EMEM_61, 0x00 }, + { WSA884X_EMEM_62, 0x00 }, + { WSA884X_EMEM_63, 0x00 }, +}; + +static bool wsa884x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_DOUT_MSB: + case WSA884X_DOUT_LSB: + case WSA884X_STATUS: + case WSA884X_SPK_TOP_SPARE_TUNE_2: + case WSA884X_SPK_TOP_SPARE_TUNE_3: + case WSA884X_SPK_TOP_SPARE_TUNE_4: + case WSA884X_SPARE_TUNE_5: + case WSA884X_SPARE_TUNE_6: + case WSA884X_SPARE_TUNE_7: + case WSA884X_SPARE_TUNE_8: + case WSA884X_SPARE_TUNE_9: + case WSA884X_SPARE_TUNE_10: + case WSA884X_PA_STATUS0: + case WSA884X_PA_STATUS1: + case WSA884X_PA_STATUS2: + case WSA884X_PA_STATUS3: + case WSA884X_PA_STATUS4: + case WSA884X_PA_STATUS5: + case WSA884X_SPARE_RO_1: + case WSA884X_SPARE_RO_2: + case WSA884X_SPARE_RO_3: + case WSA884X_SPARE_RO_0: + case WSA884X_BOOST_SPARE_RO_1: + case WSA884X_STATUS_0: + case WSA884X_STATUS_1: + case WSA884X_CHIP_ID0: + case WSA884X_CHIP_ID1: + case WSA884X_CHIP_ID2: + case WSA884X_CHIP_ID3: + case WSA884X_BUS_ID: + case WSA884X_PA_FSM_STA0: + case WSA884X_PA_FSM_STA1: + case WSA884X_PA_FSM_ERR_COND0: + case WSA884X_PA_FSM_ERR_COND1: + case WSA884X_TEMP_DIN_MSB: + case WSA884X_TEMP_DIN_LSB: + case WSA884X_TEMP_DOUT_MSB: + case WSA884X_TEMP_DOUT_LSB: + case WSA884X_VBAT_DIN_MSB: + case WSA884X_VBAT_DIN_LSB: + case WSA884X_VBAT_DOUT_MSB: + case WSA884X_VBAT_DOUT_LSB: + case WSA884X_VBAT_CAL_MSB: + case WSA884X_VBAT_CAL_LSB: + case WSA884X_VPHX_SYS_EN_STATUS: + case WSA884X_PIN_STATUS: + case WSA884X_SWR_HM_TEST1: + case WSA884X_OTP_CTRL1: + case WSA884X_OTP_STAT: + case WSA884X_WAVG_STA: + case WSA884X_INTR_STATUS0: + case WSA884X_INTR_STATUS1: + case WSA884X_ATE_TEST_MODE: + case WSA884X_SPARE_R: + return true; + } + return false; +} + +static bool wsa884x_writeable_register(struct device *dev, unsigned int reg) +{ + return !wsa884x_readonly_register(dev, reg); +} + +static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_ANA_WO_CTL_0: + case WSA884X_ANA_WO_CTL_1: + return true; + } + return wsa884x_readonly_register(dev, reg); +} + +static struct regmap_config wsa884x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa884x_defaults, + .max_register = WSA884X_MAX_REGISTER, + .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults), + .volatile_reg = wsa884x_volatile_register, + .writeable_reg = wsa884x_writeable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .use_single_read = true, +}; + +static const struct reg_sequence wsa884x_reg_init[] = { + { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) }, + { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) }, + /* + * Downstream suggests for batteries different than 1-Stacked (1S): + * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK }, + */ + { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) | + FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) }, + { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) | + FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) }, + { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) }, + { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) | + FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) }, + { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) | + FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) }, + { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) | + FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */ + { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) }, + { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) | + FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) }, + { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) | + FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x0a }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x08 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x07 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x79 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x0b }, + { WSA884X_CDC_SPK_DSM_A6_0, 0x8a }, + { WSA884X_CDC_SPK_DSM_A7_0, 0x9b }, + { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) }, + { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) }, + { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) }, + { WSA884X_CDC_SPK_DSM_R1, 0x83 }, + { WSA884X_CDC_SPK_DSM_R2, 0x7f }, + { WSA884X_CDC_SPK_DSM_R3, 0x9d }, + { WSA884X_CDC_SPK_DSM_R4, 0x82 }, + { WSA884X_CDC_SPK_DSM_R5, 0x8b }, + { WSA884X_CDC_SPK_DSM_R6, 0x9b }, + { WSA884X_CDC_SPK_DSM_R7, 0x3f }, + /* Speaker mode by default */ + { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) }, + { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) | + FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) }, + /* + * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain, + * battery 1S and rload 8 Ohms. + */ + { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), }, + { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), }, + { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), }, + { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), }, + { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), }, + { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), }, + { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), }, + { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), }, + { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), }, + { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), }, + { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), }, + { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), }, + { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), }, + { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), }, + { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), }, + { WSA884X_ANA_WO_CTL_1, 0x00 }, + { WSA884X_OTP_REG_38, 0x00 }, + { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) }, +}; + +static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) +{ + struct regmap *regmap = wsa884x->regmap; + unsigned int min_gain, igain, vgain, comp_offset; + + /* + * Downstream sets gain parameters customized per boards per use-case. + * Choose here some sane values matching knowon users, like QRD8550 + * board:. + * + * Values match here downstream: + * For WSA884X_RECEIVER - G_7P5_DB system gain + * For WSA884X_SPEAKER - G_21_DB system gain + */ + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + comp_offset = COMP_OFFSET4; + min_gain = G_M6_DB; + igain = ISENSE_18_DB; + vgain = VSENSE_M12_DB; + } else { + /* WSA884X_SPEAKER */ + comp_offset = COMP_OFFSET0; + min_gain = G_0_DB; + igain = ISENSE_12_DB; + vgain = VSENSE_M24_DB; + } + + regmap_update_bits(regmap, WSA884X_ISENSE2, + WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, + FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain)); + regmap_update_bits(regmap, WSA884X_VSENSE1, + WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, + FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain)); + regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN, + WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, + FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain)); + + if (wsa884x->port_enable[WSA884X_PORT_COMP]) { + regmap_update_bits(regmap, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_OFFSET_MASK, + FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset)); + + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0)); + } else { + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1)); + } +} + +static void wsa884x_init(struct wsa884x_priv *wsa884x) +{ + unsigned int wo_ctl_0; + unsigned int variant = 0; + + if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant)) + wsa884x->variant = variant & WSA884X_OTP_REG_0_ID_MASK; + + regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init, + ARRAY_SIZE(wsa884x_reg_init)); + + wo_ctl_0 = 0xc; + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK, + WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER); + /* Assume that compander is enabled by default unless it is haptics sku */ + if (wsa884x->variant == WSA884X_OTP_ID_WSA8845H) + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB); + else + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB); + regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0); + + wsa884x_set_gain_parameters(wsa884x); + + wsa884x->hw_init = false; +} + +static int wsa884x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + int ret; + + if (status == SDW_SLAVE_UNATTACHED) { + wsa884x->hw_init = false; + regcache_cache_only(wsa884x->regmap, true); + regcache_mark_dirty(wsa884x->regmap); + return 0; + } + + if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + regcache_cache_only(wsa884x->regmap, false); + ret = regcache_sync(wsa884x->regmap); + if (ret < 0) { + dev_err(&slave->dev, "Cannot sync regmap cache\n"); + return ret; + } + + wsa884x_init(wsa884x); + + return 0; +} + +static int wsa884x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa884x->port_prepared[prepare_ch->num - 1] = true; + else + wsa884x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static const struct sdw_slave_ops wsa884x_slave_ops = { + .update_status = wsa884x_update_status, + .port_prep = wsa884x_port_prep, +}; + +static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wsa884x->dev_mode; + + return 0; +} + +static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wsa884x->dev_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx]; + + return 0; +} + +static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = true; + } else { + if (!wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = false; + } + + return 1; +} + +static int wsa884x_codec_probe(struct snd_soc_component *comp) +{ + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa884x->regmap); + + return 0; +} + +static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, + struct wsa884x_priv *wsa884x) +{ + unsigned int curr_limit, curr_ovrd_en; + + wsa884x_set_gain_parameters(wsa884x); + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3); + snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE, + WSA884X_CDC_PATH_MODE_RXD_MODE_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL, + WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK, + 0x1); + } else { + /* WSA884X_SPEAKER */ + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf); + } + + if (wsa884x->port_enable[WSA884X_PORT_PBR]) { + curr_ovrd_en = 0x0; + curr_limit = 0x15; + } else { + curr_ovrd_en = 0x1; + if (wsa884x->dev_mode == WSA884X_RECEIVER) + curr_limit = 0x9; + else + curr_limit = 0x15; + } + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK, + curr_ovrd_en); + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, + curr_limit); +} + +static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wsa884x_spkr_post_pmu(component, wsa884x); + + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event), +}; + +static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900); + +static const struct snd_kcontrol_new wsa884x_snd_controls[] = { + SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT, + 0x0, 0x1f, 1, pa_gain), + SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum, + wsa884x_dev_mode_get, wsa884x_dev_mode_put), + SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), +}; + +static const struct snd_soc_dapm_route wsa884x_audio_map[] = { + {"SPKR", NULL, "IN"}, +}; + +static const struct snd_soc_component_driver wsa884x_component_drv = { + .name = "WSA884x", + .probe = wsa884x_codec_probe, + .controls = wsa884x_snd_controls, + .num_controls = ARRAY_SIZE(wsa884x_snd_controls), + .dapm_widgets = wsa884x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets), + .dapm_routes = wsa884x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map), +}; + +static int wsa884x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + int i; + + wsa884x->active_ports = 0; + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { + if (!wsa884x->port_enable[i]) + continue; + + wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i]; + wsa884x->active_ports++; + } + + wsa884x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, + wsa884x->port_config, wsa884x->active_ports, + wsa884x->sruntime); +} + +static int wsa884x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime); + + return 0; +} + +static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + + } else { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + } + + return 0; +} + +static int wsa884x_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + wsa884x->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wsa884x_dai_ops = { + .hw_params = wsa884x_hw_params, + .hw_free = wsa884x_hw_free, + .mute_stream = wsa884x_mute_stream, + .set_stream = wsa884x_set_stream, +}; + +static struct snd_soc_dai_driver wsa884x_dais[] = { + { + .name = "SPKR", + .playback = { + .stream_name = "SPKR Playback", + .rates = WSA884X_RATES | WSA884X_FRAC_RATES, + .formats = WSA884X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, +}; + +static void wsa884x_gpio_powerdown(void *data) +{ + gpiod_direction_output(data, 1); +} + +static void wsa884x_regulator_disable(void *data) +{ + regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); +} + +static int wsa884x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wsa884x_priv *wsa884x; + unsigned int i; + int ret; + + wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL); + if (!wsa884x) + return -ENOMEM; + + for (i = 0; i < WSA884X_SUPPLIES_NUM; i++) + wsa884x->supplies[i].supply = wsa884x_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM, + wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable, + wsa884x->supplies); + if (ret) + return ret; + + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + dev_set_drvdata(dev, wsa884x); + wsa884x->slave = pdev; + wsa884x->dev = dev; + wsa884x->dev_mode = WSA884X_SPEAKER; + wsa884x->sconfig.ch_count = 1; + wsa884x->sconfig.bps = 1; + wsa884x->sconfig.direction = SDW_DATA_DIR_RX; + wsa884x->sconfig.type = SDW_STREAM_PDM; + + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); + pdev->prop.simple_clk_stop_capable = true; + pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* Bring out of reset */ + gpiod_direction_output(wsa884x->sd_n, 0); + ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + if (ret) + return ret; + + wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config); + if (IS_ERR(wsa884x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa884x->regmap), + "regmap_init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wsa884x->regmap, true); + wsa884x->hw_init = true; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return devm_snd_soc_register_component(dev, + &wsa884x_component_drv, + wsa884x_dais, + ARRAY_SIZE(wsa884x_dais)); +} + +static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa884x_runtime_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa884x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa884x_swr_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x204, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id); + +static struct sdw_driver wsa884x_codec_driver = { + .driver = { + .name = "wsa884x-codec", + .pm = &wsa884x_pm_ops, + }, + .probe = wsa884x_probe, + .ops = &wsa884x_slave_ops, + .id_table = wsa884x_swr_id, +}; +module_sdw_driver(wsa884x_codec_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); +MODULE_DESCRIPTION("WSA884x codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 399a489f24f2..97d652f0e84d 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -17,6 +17,7 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <sound/designware_i2s.h> @@ -149,19 +150,51 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static void i2s_enable_dma(struct dw_i2s_dev *dev, u32 stream) +{ + u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR); + + /* Enable DMA handshake for stream */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_reg |= I2S_DMAEN_TXBLOCK; + else + dma_reg |= I2S_DMAEN_RXBLOCK; + + i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg); +} + +static void i2s_disable_dma(struct dw_i2s_dev *dev, u32 stream) +{ + u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR); + + /* Disable DMA handshake for stream */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_reg &= ~I2S_DMAEN_TXBLOCK; + i2s_write_reg(dev->i2s_base, I2S_RTXDMA, 1); + } else { + dma_reg &= ~I2S_DMAEN_RXBLOCK; + i2s_write_reg(dev->i2s_base, I2S_RRXDMA, 1); + } + i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg); +} + static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { struct i2s_clk_config_data *config = &dev->config; i2s_write_reg(dev->i2s_base, IER, 1); - i2s_enable_irqs(dev, substream->stream, config->chan_nr); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, ITER, 1); else i2s_write_reg(dev->i2s_base, IRER, 1); + if (dev->use_pio) + i2s_enable_irqs(dev, substream->stream, config->chan_nr); + else + i2s_enable_dma(dev, substream->stream); + i2s_write_reg(dev->i2s_base, CER, 1); } @@ -175,7 +208,10 @@ static void i2s_stop(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 0); - i2s_disable_irqs(dev, substream->stream, 8); + if (dev->use_pio) + i2s_disable_irqs(dev, substream->stream, 8); + else + i2s_disable_dma(dev, substream->stream); if (!dev->active) { i2s_write_reg(dev->i2s_base, CER, 0); @@ -448,9 +484,9 @@ static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { static const u32 formats[COMP_MAX_WORDSIZE] = { SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_FMTBIT_S16_LE, - SNDRV_PCM_FMTBIT_S24_LE, - SNDRV_PCM_FMTBIT_S24_LE, - SNDRV_PCM_FMTBIT_S32_LE, + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, 0, 0, 0 @@ -557,13 +593,9 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); - u32 idx = COMP1_APB_DATA_WIDTH(comp1); u32 idx2; int ret; - if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) - return -EINVAL; - ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); if (ret < 0) return ret; @@ -573,7 +605,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, dev->capability |= DWC_I2S_PLAY; dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; - dev->play_dma_data.dt.addr_width = bus_widths[idx]; dev->play_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2]) >> 8; dev->play_dma_data.dt.maxburst = 16; @@ -583,7 +614,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, dev->capability |= DWC_I2S_RECORD; dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; - dev->capture_dma_data.dt.addr_width = bus_widths[idx]; dev->capture_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2] >> 8); dev->capture_dma_data.dt.maxburst = 16; @@ -625,6 +655,14 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); + dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(dev->reset)) + return PTR_ERR(dev->reset); + + ret = reset_control_deassert(dev->reset); + if (ret) + return ret; + dev->dev = &pdev->dev; irq = platform_get_irq_optional(pdev, 0); @@ -633,7 +671,7 @@ static int dw_i2s_probe(struct platform_device *pdev) pdev->name, dev); if (ret < 0) { dev_err(&pdev->dev, "failed to request irq\n"); - return ret; + goto err_assert_reset; } } @@ -653,24 +691,27 @@ static int dw_i2s_probe(struct platform_device *pdev) ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); } if (ret < 0) - return ret; + goto err_assert_reset; if (dev->capability & DW_I2S_MASTER) { if (pdata) { dev->i2s_clk_cfg = pdata->i2s_clk_cfg; if (!dev->i2s_clk_cfg) { dev_err(&pdev->dev, "no clock configure method\n"); - return -ENODEV; + ret = -ENODEV; + goto err_assert_reset; } } dev->clk = devm_clk_get(&pdev->dev, clk_id); - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); + if (IS_ERR(dev->clk)) { + ret = PTR_ERR(dev->clk); + goto err_assert_reset; + } ret = clk_prepare_enable(dev->clk); if (ret < 0) - return ret; + goto err_assert_reset; } dev_set_drvdata(&pdev->dev, dev); @@ -704,6 +745,8 @@ static int dw_i2s_probe(struct platform_device *pdev) err_clk_disable: if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); +err_assert_reset: + reset_control_assert(dev->reset); return ret; } @@ -714,6 +757,7 @@ static void dw_i2s_remove(struct platform_device *pdev) if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); + reset_control_assert(dev->reset); pm_runtime_disable(&pdev->dev); } diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index 1c361eb6127e..ba4e397099be 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -53,6 +53,12 @@ #define I2S_COMP_VERSION 0x01F8 #define I2S_COMP_TYPE 0x01FC +#define I2S_RRXDMA 0x01C4 +#define I2S_RTXDMA 0x01CC +#define I2S_DMACR 0x0200 +#define I2S_DMAEN_RXBLOCK (1 << 16) +#define I2S_DMAEN_TXBLOCK (1 << 17) + /* * Component parameter register fields - define the I2S block's * configuration. @@ -89,6 +95,7 @@ union dw_i2s_snd_dma_data { struct dw_i2s_dev { void __iomem *i2s_base; struct clk *clk; + struct reset_control *reset; int active; unsigned int capability; unsigned int quirks; diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 40870668ee24..76b5bfc288fd 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -27,6 +27,7 @@ #include "../codecs/wm8960.h" #include "../codecs/wm8994.h" #include "../codecs/tlv320aic31xx.h" +#include "../codecs/nau8822.h" #define DRIVER_NAME "fsl-asoc-card" @@ -47,6 +48,7 @@ * @pll_id: PLL id for set_pll() */ struct codec_priv { + struct clk *mclk; unsigned long mclk_freq; unsigned long free_freq; u32 mclk_id; @@ -60,6 +62,7 @@ struct codec_priv { * @sysclk_dir: SYSCLK directions for set_sysclk() * @sysclk_id: SYSCLK ids for set_sysclk() * @slot_width: Slot width of each frame + * @slot_num: Number of slots of each frame * * Note: [1] for tx and [0] for rx */ @@ -68,6 +71,7 @@ struct cpu_priv { u32 sysclk_dir[2]; u32 sysclk_id[2]; u32 slot_width; + u32 slot_num; }; /** @@ -189,7 +193,11 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, } if (cpu_priv->slot_width) { - ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, + if (!cpu_priv->slot_num) + cpu_priv->slot_num = 2; + + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, + cpu_priv->slot_num, cpu_priv->slot_width); if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set TDM slot for cpu dai\n"); @@ -524,6 +532,9 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) return ret; } + if (!IS_ERR_OR_NULL(codec_priv->mclk)) + clk_prepare_enable(codec_priv->mclk); + return 0; } @@ -686,6 +697,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.free_freq = priv->codec_priv.mclk_freq; priv->card.dapm_routes = NULL; priv->card.num_dapm_routes = 0; + } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { + codec_dai_name = "nau8822-hifi"; + priv->codec_priv.mclk_id = NAU8822_CLK_MCLK; + priv->codec_priv.fll_id = NAU8822_CLK_PLL; + priv->codec_priv.pll_id = NAU8822_CLK_PLL; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + if (codec_dev) + priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL); } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; @@ -911,6 +930,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8524", }, { .compatible = "fsl,imx-audio-si476x", }, { .compatible = "fsl,imx-audio-wm8958", }, + { .compatible = "fsl,imx-audio-nau8822", }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index e3105d48fb65..5e09f634c61b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1407,7 +1407,9 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->cpu_dai_drv.symmetric_sample_bits = 0; } - if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") && + sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output"); + + if (sai->mclk_direction_output && of_device_is_compatible(np, "fsl,imx6ul-sai")) { gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); if (IS_ERR(gpr)) { @@ -1450,7 +1452,7 @@ static int fsl_sai_probe(struct platform_device *pdev) dev_warn(dev, "Error reading SAI version: %d\n", ret); /* Select MCLK direction */ - if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") && + if (sai->mclk_direction_output && sai->soc_data->max_register >= FSL_SAI_MCTL) { regmap_update_bits(sai->regmap, FSL_SAI_MCTL, FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); @@ -1569,6 +1571,17 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = { .max_register = FSL_SAI_MCTL, }; +static const struct fsl_sai_soc_data fsl_sai_imx8mn_data = { + .use_imx_pcm = true, + .use_edma = false, + .fifo_depth = 128, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 8, + .flags = 0, + .max_register = FSL_SAI_MDIV, +}; + static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = { .use_imx_pcm = true, .use_edma = false, @@ -1578,6 +1591,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = { .pins = 8, .flags = 0, .max_register = FSL_SAI_MDIV, + .mclk_with_tere = true, }; static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = { @@ -1613,7 +1627,7 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data }, { .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data }, { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data }, - { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mp_data }, + { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data }, { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data }, { /* sentinel */ } }; @@ -1678,6 +1692,10 @@ static int fsl_sai_runtime_resume(struct device *dev) if (ret) goto disable_rx_clk; + if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output) + regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), + FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); + return 0; disable_rx_clk: diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index a53c4f0e25fa..8254c3547b87 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -231,6 +231,7 @@ struct fsl_sai_soc_data { bool use_imx_pcm; bool use_edma; bool mclk0_is_mclk1; + bool mclk_with_tere; unsigned int fifo_depth; unsigned int pins; unsigned int reg_offset; @@ -288,6 +289,7 @@ struct fsl_sai { bool synchronous[2]; struct fsl_sai_dl_cfg *dl_cfg; unsigned int dl_cfg_cnt; + bool mclk_direction_output; unsigned int mclk_id[2]; unsigned int mclk_streams; diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index b2c5aca92c6b..0b58df56f4da 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -15,7 +15,6 @@ #include <linux/clk.h> #include <sound/soc.h> #include <sound/soc-dapm.h> -#include <linux/pm_runtime.h> #include "fsl_sai.h" #include "fsl_audmix.h" @@ -207,8 +206,8 @@ static int imx_audmix_probe(struct platform_device *pdev) for (i = 0; i < num_dai; i++) { struct snd_soc_dai_link_component *dlc; - /* for CPU/Codec x 2 */ - dlc = devm_kcalloc(&pdev->dev, 4, sizeof(*dlc), GFP_KERNEL); + /* for CPU x 2 */ + dlc = devm_kcalloc(&pdev->dev, 2, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; @@ -228,6 +227,8 @@ static int imx_audmix_probe(struct platform_device *pdev) dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s", fe_name_pref, args.np->full_name + 1); + if (!dai_name) + return -ENOMEM; dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name); @@ -236,6 +237,8 @@ static int imx_audmix_probe(struct platform_device *pdev) capture_dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", dai_name, "CPU-Capture"); + if (!capture_dai_name) + return -ENOMEM; } /* @@ -244,7 +247,7 @@ static int imx_audmix_probe(struct platform_device *pdev) */ priv->dai[i].cpus = priv->dai[i].platforms = &dlc[0]; - priv->dai[i].codecs = &dlc[1]; + priv->dai[i].codecs = &asoc_dummy_dlc; priv->dai[i].num_cpus = 1; priv->dai[i].num_codecs = 1; @@ -252,8 +255,6 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[i].name = dai_name; priv->dai[i].stream_name = "HiFi-AUDMIX-FE"; - priv->dai[i].codecs->dai_name = "snd-soc-dummy-dai"; - priv->dai[i].codecs->name = "snd-soc-dummy"; priv->dai[i].cpus->of_node = args.np; priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev); priv->dai[i].dynamic = 1; @@ -269,16 +270,16 @@ static int imx_audmix_probe(struct platform_device *pdev) "AUDMIX-Playback-%d", i); be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL, "AUDMIX-Capture-%d", i); + if (!be_name || !be_pb || !be_cp) + return -ENOMEM; - priv->dai[num_dai + i].cpus = &dlc[2]; - priv->dai[num_dai + i].codecs = &dlc[3]; + priv->dai[num_dai + i].cpus = &dlc[1]; + priv->dai[num_dai + i].codecs = &asoc_dummy_dlc; priv->dai[num_dai + i].num_cpus = 1; priv->dai[num_dai + i].num_codecs = 1; priv->dai[num_dai + i].name = be_name; - priv->dai[num_dai + i].codecs->dai_name = "snd-soc-dummy-dai"; - priv->dai[num_dai + i].codecs->name = "snd-soc-dummy"; priv->dai[num_dai + i].cpus->of_node = audmix_np; priv->dai[num_dai + i].cpus->dai_name = be_name; priv->dai[num_dai + i].no_pcm = 1; @@ -293,6 +294,9 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dapm_routes[i].source = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", dai_name, "CPU-Playback"); + if (!priv->dapm_routes[i].source) + return -ENOMEM; + priv->dapm_routes[i].sink = be_pb; priv->dapm_routes[num_dai + i].source = be_pb; priv->dapm_routes[num_dai + i].sink = be_cp; diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 64a4d7e9db60..356a0bc3b126 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -551,10 +551,10 @@ static int imx_card_parse_of(struct imx_card_data *data) goto err; } - ret = of_parse_phandle_with_args(cpu, "sound-dai", - "#sound-dai-cells", 0, &args); + ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { - dev_err(card->dev, "%s: error getting cpu phandle\n", link->name); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai info\n", link->name); goto err; } @@ -582,17 +582,9 @@ static int imx_card_parse_of(struct imx_card_data *data) } } - link->cpus->of_node = args.np; link->platforms->of_node = link->cpus->of_node; link->id = args.args[0]; - ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); - if (ret) { - dev_err_probe(card->dev, ret, - "%s: error getting cpu dai name\n", link->name); - goto err; - } - codec = of_get_child_by_name(np, "codec"); if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); @@ -615,17 +607,8 @@ static int imx_card_parse_of(struct imx_card_data *data) plat_data->type = CODEC_AK5552; } else { - dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); - if (!dlc) { - ret = -ENOMEM; - goto err; - } - - link->codecs = dlc; + link->codecs = &asoc_dummy_dlc; link->num_codecs = 1; - - link->codecs->dai_name = "snd-soc-dummy-dai"; - link->codecs->name = "snd-soc-dummy"; } if (!strncmp(link->name, "HiFi-ASRC-FE", 12)) { diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 89178106fe2c..3c7b95db2eac 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -92,13 +92,11 @@ static int imx_rpmsg_probe(struct platform_device *pdev) /* Optional codec node */ ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args); if (ret) { - data->dai.codecs->dai_name = "snd-soc-dummy-dai"; - data->dai.codecs->name = "snd-soc-dummy"; + *data->dai.codecs = asoc_dummy_dlc; } else { struct clk *clk; - data->dai.codecs->of_node = args.np; - ret = snd_soc_get_dai_name(&args, &data->dai.codecs->dai_name); + ret = snd_soc_get_dlc(&args, data->dai.codecs); if (ret) { dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); goto fail; diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index ab978431ac98..44463f92e522 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -26,7 +26,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - comp = devm_kzalloc(&pdev->dev, 2 * sizeof(*comp), GFP_KERNEL); + comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL); if (!data || !comp) { ret = -ENOMEM; goto end; @@ -37,8 +37,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) * platform is using soc-generic-dmaengine-pcm */ data->dai.cpus = - data->dai.platforms = &comp[0]; - data->dai.codecs = &comp[1]; + data->dai.platforms = comp; + data->dai.codecs = &asoc_dummy_dlc; data->dai.num_cpus = 1; data->dai.num_codecs = 1; @@ -46,8 +46,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) data->dai.name = "S/PDIF PCM"; data->dai.stream_name = "S/PDIF PCM"; - data->dai.codecs->dai_name = "snd-soc-dummy-dai"; - data->dai.codecs->name = "snd-soc-dummy"; data->dai.cpus->of_node = spdif_np; data->dai.playback_only = true; data->dai.capture_only = true; diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 4e85536a1b26..c6e0f9132193 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -55,60 +55,6 @@ static const struct snd_soc_ops graph_ops = { .hw_params = asoc_simple_hw_params, }; -static int graph_get_dai_id(struct device_node *ep) -{ - struct device_node *node; - struct device_node *endpoint; - struct of_endpoint info; - int i, id; - const u32 *reg; - int ret; - - /* use driver specified DAI ID if exist */ - ret = snd_soc_get_dai_id(ep); - if (ret != -ENOTSUPP) - return ret; - - /* use endpoint/port reg if exist */ - ret = of_graph_parse_endpoint(ep, &info); - if (ret == 0) { - /* - * Because it will count port/endpoint if it doesn't have "reg". - * But, we can't judge whether it has "no reg", or "reg = <0>" - * only of_graph_parse_endpoint(). - * We need to check "reg" property - */ - if (of_property_present(ep, "reg")) - return info.id; - - node = of_get_parent(ep); - reg = of_get_property(node, "reg", NULL); - of_node_put(node); - if (reg) - return info.port; - } - node = of_graph_get_port_parent(ep); - - /* - * Non HDMI sound case, counting port/endpoint on its DT - * is enough. Let's count it. - */ - i = 0; - id = -1; - for_each_endpoint_of_node(node, endpoint) { - if (endpoint == ep) - id = i; - i++; - } - - of_node_put(node); - - if (id < 0) - return -ENODEV; - - return id; -} - static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) { struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); @@ -120,57 +66,6 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) return false; } -static int asoc_simple_parse_dai(struct device_node *ep, - struct snd_soc_dai_link_component *dlc, - int *is_single_link) -{ - struct device_node *node; - struct of_phandle_args args; - int ret; - - if (!ep) - return 0; - - node = of_graph_get_port_parent(ep); - - /* Get dai->name */ - args.np = node; - args.args[0] = graph_get_dai_id(ep); - args.args_count = (of_graph_get_endpoint_count(node) > 1); - - /* - * FIXME - * - * Here, dlc->dai_name is pointer to CPU/Codec DAI name. - * If user unbinded CPU or Codec driver, but not for Sound Card, - * dlc->dai_name is keeping unbinded CPU or Codec - * driver's pointer. - * - * If user re-bind CPU or Codec driver again, ALSA SoC will try - * to rebind Card via snd_soc_try_rebind_card(), but because of - * above reason, it might can't bind Sound Card. - * Because Sound Card is pointing to released dai_name pointer. - * - * To avoid this rebind Card issue, - * 1) It needs to alloc memory to keep dai_name eventhough - * CPU or Codec driver was unbinded, or - * 2) user need to rebind Sound Card everytime - * if he unbinded CPU or Codec. - */ - ret = snd_soc_get_dai_name(&args, &dlc->dai_name); - if (ret < 0) { - of_node_put(node); - return ret; - } - - dlc->of_node = node; - - if (is_single_link) - *is_single_link = of_graph_get_endpoint_count(node) == 1; - - return 0; -} - static void graph_parse_convert(struct device *dev, struct device_node *ep, struct asoc_simple_data *adata) @@ -231,7 +126,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv, graph_parse_mclk_fs(top, ep, dai_props); - ret = asoc_simple_parse_dai(ep, dlc, cpu); + ret = asoc_graph_parse_dai(ep, dlc, cpu); if (ret < 0) return ret; diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 994db61a26b3..2ac0de3c21da 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -42,6 +42,15 @@ * [Normal] * cpu0 <-@-----------------> codec0 * + * [Semi-Multi] + * + * CPU:Codec = 1:N + * + * +-+ + * cpu7 <-@------->| |-> codec12 + * | |-> codec13 + * +-+ + * * [Multi-CPU/Codec] * +-+ +-+ * cpu1 <--| |<-@--------->| |-> codec1 @@ -128,6 +137,9 @@ */ &cpu0 + /* [Semi-Multi] */ + &sm0 + /* * [Multi-CPU/Codec]: cpu side only * cpu1/cpu2/codec1/codec2 @@ -160,76 +172,127 @@ >; multi { + #address-cells = <1>; + #size-cells = <0>; + ports@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; /* [Multi-CPU] */ - mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; - port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; - port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; + mcpu0: port@0 { reg = <0>; mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; + port@1 { reg = <1>; mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; + port@2 { reg = <2>; mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; }; /* [Multi-Codec] */ ports@1 { - port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; - port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; - port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; + port@1 { reg = <1>; mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; + port@2 { reg = <2>; mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; /* [DPCM-Multi]::BE */ ports@2 { - port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; }; - port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; }; - port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; }; + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; }; + port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; }; + port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; }; }; /* [Codec2Codec-Multi]::CPU */ ports@3 { - port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; }; - port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; }; - port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; }; + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; }; + port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; }; + port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; }; }; /* [Codec2Codec-Multi]::Codec */ ports@4 { - port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; }; - port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; }; - port@2 { mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; }; + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; }; + port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; }; + port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; }; + }; + + /* [Semi-Multi] */ + ports@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; }; }; + port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; }; }; + port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; }; }; }; }; dpcm { + #address-cells = <1>; + #size-cells = <0>; + ports@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; /* [DPCM]::FE */ - fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; }; - fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; }; + fe00: port@0 { reg = <0>; fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; }; + fe01: port@1 { reg = <1>; fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; }; /* [DPCM-Multi]::FE */ - fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; }; - fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; }; + fe10: port@2 { reg = <2>; fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; }; + fe11: port@3 { reg = <3>; fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; }; }; ports@1 { + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; /* [DPCM]::BE */ - be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; + be0: port@0 { reg = <0>; be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; /* [DPCM-Multi]::BE */ - be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; + be1: port@1 { reg = <1>; be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; }; }; codec2codec { + #address-cells = <1>; + #size-cells = <0>; /* [Codec2Codec] */ ports@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + /* use default settings */ - c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; }; - port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; + c2c: port@0 { reg = <0>; c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; }; + port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; }; /* [Codec2Codec-Multi] */ ports@1 { + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + /* use original settings */ rate = <48000>; - c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; }; - port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; }; + c2c_m: port@0 { reg = <0>; c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; }; + port@1 { reg = <1>; c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; }; }; }; }; @@ -245,22 +308,28 @@ */ compatible = "test-cpu"; ports { + #address-cells = <1>; + #size-cells = <0>; + bitclock-master; frame-master; /* [Normal] */ - cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; + cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; /* [Multi-CPU] */ - port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; - port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; + port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; + port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; /* [DPCM]::FE */ - port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; - port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; }; + port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; + port@4 { reg = <4>; cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; }; /* [DPCM-Multi]::FE */ - port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; }; - port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; }; + port@5 { reg = <5>; cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; }; + port@6 { reg = <6>; cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; }; + + /* [Semi-Multi] */ + sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; }; }; }; @@ -275,6 +344,9 @@ */ compatible = "test-codec"; ports { + #address-cells = <1>; + #size-cells = <0>; + /* * prefix can be added to *component*, * see audio-graph-card2::routing @@ -282,35 +354,40 @@ prefix = "TC"; /* [Normal] */ - port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; + port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; /* [Multi-Codec] */ - port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; - port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; + port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; + port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; /* [DPCM]::BE */ port@3 { convert-rate = <44100>; - codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; + reg = <3>; codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; }; /* [DPCM-Multi]::BE */ - port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; }; - port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; }; + port@4 { reg = <4>; codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; }; + port@5 { reg = <5>; codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; }; /* [Codec2Codec] */ port@6 { bitclock-master; frame-master; - codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; - port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; + reg = <6>; codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; + port@7 { reg = <7>; codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; /* [Codec2Codec-Multi] */ port@8 { bitclock-master; frame-master; - codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; }; - port@9 { codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; }; - port@10 { codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; }; - port@11 { codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; }; + reg = <8>; codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; }; + port@9 { reg = <9>; codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; }; + port@a { reg = <10>; codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; }; + port@b { reg = <11>; codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; }; + + /* [Semi-Multi] */ + port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; }; + port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; }; + }; }; }; diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 25aa79dd55b3..542c4a114940 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -353,111 +353,6 @@ static const struct snd_soc_ops graph_ops = { .hw_params = asoc_simple_hw_params, }; -static int graph_get_dai_id(struct device_node *ep) -{ - struct device_node *node; - struct device_node *endpoint; - struct of_endpoint info; - int i, id; - const u32 *reg; - int ret; - - /* use driver specified DAI ID if exist */ - ret = snd_soc_get_dai_id(ep); - if (ret != -ENOTSUPP) - return ret; - - /* use endpoint/port reg if exist */ - ret = of_graph_parse_endpoint(ep, &info); - if (ret == 0) { - /* - * Because it will count port/endpoint if it doesn't have "reg". - * But, we can't judge whether it has "no reg", or "reg = <0>" - * only of_graph_parse_endpoint(). - * We need to check "reg" property - */ - if (of_property_present(ep, "reg")) - return info.id; - - node = of_get_parent(ep); - reg = of_get_property(node, "reg", NULL); - of_node_put(node); - if (reg) - return info.port; - } - node = of_graph_get_port_parent(ep); - - /* - * Non HDMI sound case, counting port/endpoint on its DT - * is enough. Let's count it. - */ - i = 0; - id = -1; - for_each_endpoint_of_node(node, endpoint) { - if (endpoint == ep) - id = i; - i++; - } - - of_node_put(node); - - if (id < 0) - return -ENODEV; - - return id; -} - -static int asoc_simple_parse_dai(struct device_node *ep, - struct snd_soc_dai_link_component *dlc, - int *is_single_link) -{ - struct device_node *node; - struct of_phandle_args args; - int ret; - - if (!ep) - return 0; - - node = of_graph_get_port_parent(ep); - - /* Get dai->name */ - args.np = node; - args.args[0] = graph_get_dai_id(ep); - args.args_count = (of_graph_get_endpoint_count(node) > 1); - - /* - * FIXME - * - * Here, dlc->dai_name is pointer to CPU/Codec DAI name. - * If user unbinded CPU or Codec driver, but not for Sound Card, - * dlc->dai_name is keeping unbinded CPU or Codec - * driver's pointer. - * - * If user re-bind CPU or Codec driver again, ALSA SoC will try - * to rebind Card via snd_soc_try_rebind_card(), but because of - * above reason, it might can't bind Sound Card. - * Because Sound Card is pointing to released dai_name pointer. - * - * To avoid this rebind Card issue, - * 1) It needs to alloc memory to keep dai_name eventhough - * CPU or Codec driver was unbinded, or - * 2) user need to rebind Sound Card everytime - * if he unbinded CPU or Codec. - */ - ret = snd_soc_get_dai_name(&args, &dlc->dai_name); - if (ret < 0) { - of_node_put(node); - return ret; - } - - dlc->of_node = node; - - if (is_single_link) - *is_single_link = of_graph_get_endpoint_count(node) == 1; - - return 0; -} - static void graph_parse_convert(struct device_node *ep, struct simple_dai_props *props) { @@ -512,7 +407,7 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, graph_parse_mclk_fs(ep, dai_props); - ret = asoc_simple_parse_dai(ep, dlc, &is_single_links); + ret = asoc_graph_parse_dai(ep, dlc, &is_single_links); if (ret < 0) return ret; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index e5ff61c1e9d1..3019626b0592 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -889,11 +889,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dev_dbg(dev, "link %d, dais %d, ccnf %d\n", li->link, dai_num, cnf_num); - /* dummy CPU/Codec */ - priv->dummy.of_node = NULL; - priv->dummy.dai_name = "snd-soc-dummy-dai"; - priv->dummy.name = "snd-soc-dummy"; - priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; @@ -908,7 +903,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, for (i = 0; i < li->link; i++) { if (li->num[i].cpus) { /* Normal CPU */ - dai_props[i].cpus = dai_link[i].cpus = dlcs; dai_props[i].num.cpus = dai_link[i].num_cpus = li->num[i].cpus; @@ -918,15 +912,13 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dais += li->num[i].cpus; } else { /* DPCM Be's CPU = dummy */ - dai_props[i].cpus = - dai_link[i].cpus = &priv->dummy; + dai_link[i].cpus = &asoc_dummy_dlc; dai_props[i].num.cpus = dai_link[i].num_cpus = 1; } if (li->num[i].codecs) { /* Normal Codec */ - dai_props[i].codecs = dai_link[i].codecs = dlcs; dai_props[i].num.codecs = dai_link[i].num_codecs = li->num[i].codecs; @@ -942,15 +934,13 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, } } else { /* DPCM Fe's Codec = dummy */ - dai_props[i].codecs = - dai_link[i].codecs = &priv->dummy; + dai_link[i].codecs = &asoc_dummy_dlc; dai_props[i].num.codecs = dai_link[i].num_codecs = 1; } if (li->num[i].platforms) { /* Have Platform */ - dai_props[i].platforms = dai_link[i].platforms = dlcs; dai_props[i].num.platforms = dai_link[i].num_platforms = li->num[i].platforms; @@ -958,7 +948,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dlcs += li->num[i].platforms; } else { /* Doesn't have Platform */ - dai_props[i].platforms = dai_link[i].platforms = NULL; dai_props[i].num.platforms = dai_link[i].num_platforms = 0; @@ -1024,6 +1013,109 @@ int asoc_graph_is_ports0(struct device_node *np) } EXPORT_SYMBOL_GPL(asoc_graph_is_ports0); +static int graph_get_dai_id(struct device_node *ep) +{ + struct device_node *node; + struct device_node *endpoint; + struct of_endpoint info; + int i, id; + int ret; + + /* use driver specified DAI ID if exist */ + ret = snd_soc_get_dai_id(ep); + if (ret != -ENOTSUPP) + return ret; + + /* use endpoint/port reg if exist */ + ret = of_graph_parse_endpoint(ep, &info); + if (ret == 0) { + /* + * Because it will count port/endpoint if it doesn't have "reg". + * But, we can't judge whether it has "no reg", or "reg = <0>" + * only of_graph_parse_endpoint(). + * We need to check "reg" property + */ + if (of_property_present(ep, "reg")) + return info.id; + + node = of_get_parent(ep); + ret = of_property_present(node, "reg"); + of_node_put(node); + if (ret) + return info.port; + } + node = of_graph_get_port_parent(ep); + + /* + * Non HDMI sound case, counting port/endpoint on its DT + * is enough. Let's count it. + */ + i = 0; + id = -1; + for_each_endpoint_of_node(node, endpoint) { + if (endpoint == ep) + id = i; + i++; + } + + of_node_put(node); + + if (id < 0) + return -ENODEV; + + return id; +} + +int asoc_graph_parse_dai(struct device_node *ep, + struct snd_soc_dai_link_component *dlc, + int *is_single_link) +{ + struct device_node *node; + struct of_phandle_args args = {}; + int ret; + + if (!ep) + return 0; + + node = of_graph_get_port_parent(ep); + + /* Get dai->name */ + args.np = node; + args.args[0] = graph_get_dai_id(ep); + args.args_count = (of_graph_get_endpoint_count(node) > 1); + + /* + * FIXME + * + * Here, dlc->dai_name is pointer to CPU/Codec DAI name. + * If user unbinded CPU or Codec driver, but not for Sound Card, + * dlc->dai_name is keeping unbinded CPU or Codec + * driver's pointer. + * + * If user re-bind CPU or Codec driver again, ALSA SoC will try + * to rebind Card via snd_soc_try_rebind_card(), but because of + * above reason, it might can't bind Sound Card. + * Because Sound Card is pointing to released dai_name pointer. + * + * To avoid this rebind Card issue, + * 1) It needs to alloc memory to keep dai_name eventhough + * CPU or Codec driver was unbinded, or + * 2) user need to rebind Sound Card everytime + * if he unbinded CPU or Codec. + */ + ret = snd_soc_get_dlc(&args, dlc); + if (ret < 0) { + of_node_put(node); + return ret; + } + + if (is_single_link) + *is_single_link = of_graph_get_endpoint_count(node) == 1; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_graph_parse_dai); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 5a5e4ecd0f61..0745bf6a09aa 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -89,12 +89,10 @@ static int asoc_simple_parse_dai(struct device_node *node, * 2) user need to rebind Sound Card everytime * if he unbinded CPU or Codec. */ - ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); + ret = snd_soc_get_dlc(&args, dlc); if (ret < 0) return ret; - dlc->of_node = args.np; - if (is_single_link) *is_single_link = !args.args_count; diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig new file mode 100644 index 000000000000..7603782fb060 --- /dev/null +++ b/sound/soc/google/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_SOC_CHV3_I2S + tristate "Google Chameleon v3 I2S device" + help + Enable support for the Google Chameleon v3 I2S device. diff --git a/sound/soc/google/Makefile b/sound/soc/google/Makefile new file mode 100644 index 000000000000..862496af1ae1 --- /dev/null +++ b/sound/soc/google/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SND_SOC_CHV3_I2S) += chv3-i2s.o diff --git a/sound/soc/google/chv3-i2s.c b/sound/soc/google/chv3-i2s.c new file mode 100644 index 000000000000..0f6513444906 --- /dev/null +++ b/sound/soc/google/chv3-i2s.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +/* + * The I2S interface consists of two ring buffers - one for RX and one for + * TX. A ring buffer has a producer index and a consumer index. Depending + * on which way the data is flowing, either the software or the hardware + * writes data and updates the producer index, and the other end reads data + * and updates the consumer index. + * + * The pointer managed by software is updated using the .ack callback + * (see chv3_dma_ack). This seems to be the only way to reliably obtain + * the appl_ptr from within the driver and pass it to hardware. + * + * Because of the two pointer design, the ring buffer can never be full. With + * capture this isn't a problem, because the hardware being the producer + * will wait for the consumer index to move out of the way. With playback, + * however, this is problematic, because ALSA wants to fill up the buffer + * completely when waiting for hardware. In the .ack callback, the driver + * would have to wait for the consumer index to move out of the way by + * busy-waiting, which would keep stalling the kernel for quite a long time. + * + * The workaround to this problem is to "lie" to ALSA that the hw_pointer + * is one frame behind what it actually is (see chv3_dma_pointer). This + * way, ALSA will not try to fill up the entire buffer, and all callbacks + * are wait-free. + */ + +#define I2S_TX_ENABLE 0x00 +#define I2S_TX_BASE_ADDR 0x04 +#define I2S_TX_BUFFER_SIZE 0x08 +#define I2S_TX_PRODUCER_IDX 0x0c +#define I2S_TX_CONSUMER_IDX 0x10 +#define I2S_RX_ENABLE 0x14 +#define I2S_RX_BASE_ADDR 0x18 +#define I2S_RX_BUFFER_SIZE 0x1c +#define I2S_RX_PRODUCER_IDX 0x20 +#define I2S_RX_CONSUMER_IDX 0x24 + +#define I2S_SOFT_RESET 0x2c +#define I2S_SOFT_RESET_RX_BIT 0x1 +#define I2S_SOFT_RESET_TX_BIT 0x2 + +#define I2S_RX_IRQ 0x4c +#define I2S_RX_IRQ_CONST 0x50 +#define I2S_TX_IRQ 0x54 +#define I2S_TX_IRQ_CONST 0x58 + +#define I2S_IRQ_MASK 0x8 +#define I2S_IRQ_CLR 0xc +#define I2S_IRQ_RX_BIT 0x1 +#define I2S_IRQ_TX_BIT 0x2 + +#define I2S_MAX_BUFFER_SIZE 0x200000 + +struct chv3_i2s_dev { + struct device *dev; + void __iomem *iobase; + void __iomem *iobase_irq; + struct snd_pcm_substream *rx_substream; + struct snd_pcm_substream *tx_substream; + int tx_bytes_to_fetch; +}; + +static struct snd_soc_dai_driver chv3_i2s_dai = { + .name = "chv3-i2s", + .capture = { + .channels_min = 1, + .channels_max = 128, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 96000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .playback = { + .channels_min = 1, + .channels_max = 128, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 96000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static const struct snd_pcm_hardware chv3_dma_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .buffer_bytes_max = I2S_MAX_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 4, + .periods_max = 256, +}; + +static inline void chv3_i2s_wr(struct chv3_i2s_dev *i2s, int offset, u32 val) +{ + writel(val, i2s->iobase + offset); +} + +static inline u32 chv3_i2s_rd(struct chv3_i2s_dev *i2s, int offset) +{ + return readl(i2s->iobase + offset); +} + +static irqreturn_t chv3_i2s_isr(int irq, void *data) +{ + struct chv3_i2s_dev *i2s = data; + u32 reg; + + reg = readl(i2s->iobase_irq + I2S_IRQ_CLR); + if (!reg) + return IRQ_NONE; + + if (reg & I2S_IRQ_RX_BIT) + snd_pcm_period_elapsed(i2s->rx_substream); + + if (reg & I2S_IRQ_TX_BIT) + snd_pcm_period_elapsed(i2s->tx_substream); + + writel(reg, i2s->iobase_irq + I2S_IRQ_CLR); + + return IRQ_HANDLED; +} + +static int chv3_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + int res; + + snd_soc_set_runtime_hwparams(substream, &chv3_dma_hw); + + res = snd_pcm_hw_constraint_pow2(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES); + if (res) + return res; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + i2s->rx_substream = substream; + else + i2s->tx_substream = substream; + + return 0; +} +static int chv3_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + chv3_i2s_wr(i2s, I2S_RX_ENABLE, 0); + else + chv3_i2s_wr(i2s, I2S_TX_ENABLE, 0); + + return 0; +} + +static int chv3_dma_pcm_construct(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct snd_pcm_substream *substream; + int res; + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) { + res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev, + I2S_MAX_BUFFER_SIZE, &substream->dma_buffer); + if (res) + return res; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev, + I2S_MAX_BUFFER_SIZE, &substream->dma_buffer); + if (res) + return res; + } + + return 0; +} + +static int chv3_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static int chv3_dma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + unsigned int buffer_bytes, period_bytes, period_size; + + buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + period_size = substream->runtime->period_size; + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_RX_BIT); + chv3_i2s_wr(i2s, I2S_RX_BASE_ADDR, substream->dma_buffer.addr); + chv3_i2s_wr(i2s, I2S_RX_BUFFER_SIZE, buffer_bytes); + chv3_i2s_wr(i2s, I2S_RX_IRQ, (period_size << 8) | 1); + chv3_i2s_wr(i2s, I2S_RX_ENABLE, 1); + } else { + chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_TX_BIT); + chv3_i2s_wr(i2s, I2S_TX_BASE_ADDR, substream->dma_buffer.addr); + chv3_i2s_wr(i2s, I2S_TX_BUFFER_SIZE, buffer_bytes); + chv3_i2s_wr(i2s, I2S_TX_IRQ, ((period_bytes / i2s->tx_bytes_to_fetch) << 8) | 1); + chv3_i2s_wr(i2s, I2S_TX_ENABLE, 1); + } + writel(I2S_IRQ_RX_BIT | I2S_IRQ_TX_BIT, i2s->iobase_irq + I2S_IRQ_MASK); + + return 0; +} + +static snd_pcm_uframes_t chv3_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + u32 frame_bytes, buffer_bytes; + u32 idx_bytes; + + frame_bytes = substream->runtime->frame_bits * 8; + buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + idx_bytes = chv3_i2s_rd(i2s, I2S_RX_PRODUCER_IDX); + } else { + idx_bytes = chv3_i2s_rd(i2s, I2S_TX_CONSUMER_IDX); + /* lag the pointer by one frame */ + idx_bytes = (idx_bytes - frame_bytes) & (buffer_bytes - 1); + } + + return bytes_to_frames(substream->runtime, idx_bytes); +} + +static int chv3_dma_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + unsigned int bytes, idx; + + bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); + idx = bytes & (snd_pcm_lib_buffer_bytes(substream) - 1); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + chv3_i2s_wr(i2s, I2S_RX_CONSUMER_IDX, idx); + else + chv3_i2s_wr(i2s, I2S_TX_PRODUCER_IDX, idx); + + return 0; +} + +static const struct snd_soc_component_driver chv3_i2s_comp = { + .name = "chv3-i2s-comp", + .open = chv3_dma_open, + .close = chv3_dma_close, + .pcm_construct = chv3_dma_pcm_construct, + .hw_params = chv3_dma_hw_params, + .prepare = chv3_dma_prepare, + .pointer = chv3_dma_pointer, + .ack = chv3_dma_ack, +}; + +static int chv3_i2s_probe(struct platform_device *pdev) +{ + struct chv3_i2s_dev *i2s; + int res; + int irq; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + i2s->iobase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2s->iobase)) + return PTR_ERR(i2s->iobase); + + i2s->iobase_irq = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(i2s->iobase_irq)) + return PTR_ERR(i2s->iobase_irq); + + i2s->tx_bytes_to_fetch = (chv3_i2s_rd(i2s, I2S_TX_IRQ_CONST) >> 8) & 0xffff; + + i2s->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, i2s); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENXIO; + res = devm_request_irq(i2s->dev, irq, chv3_i2s_isr, 0, "chv3-i2s", i2s); + if (res) + return res; + + res = devm_snd_soc_register_component(&pdev->dev, &chv3_i2s_comp, + &chv3_i2s_dai, 1); + if (res) { + dev_err(&pdev->dev, "couldn't register component: %d\n", res); + return res; + } + + return 0; +} + +static const struct of_device_id chv3_i2s_of_match[] = { + { .compatible = "google,chv3-i2s" }, + {}, +}; + +static struct platform_driver chv3_i2s_driver = { + .probe = chv3_i2s_probe, + .driver = { + .name = "chv3-i2s", + .of_match_table = chv3_i2s_of_match, + }, +}; + +module_platform_driver(chv3_i2s_driver); + +MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>"); +MODULE_DESCRIPTION("Chameleon v3 I2S interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index d3973936426a..29d44c989e5f 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -15,7 +15,6 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/pm_runtime.h> #include <linux/pm_qos.h> #include <linux/dmi.h> #include <linux/acpi.h> diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 4e039c7173d8..3fc2c9a6c44d 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -15,7 +15,6 @@ #include <linux/firmware.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index eea889001c24..bf4ba6bcc429 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -20,7 +20,6 @@ #include <linux/sched.h> #include <linux/firmware.h> #include <linux/dmaengine.h> -#include <linux/pm_runtime.h> #include <linux/pm_qos.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 5862fe968083..4058b4f80a0c 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -15,7 +15,6 @@ #include <linux/pci.h> #include <linux/fs.h> #include <linux/firmware.h> -#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/soc.h> #include <asm/platform_sst_audio.h> diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index ea1ef8a61fa6..862a19ae5429 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -15,7 +15,6 @@ #include <linux/firmware.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 1a1d572cc1d0..964a763732ab 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -181,38 +181,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME); @@ -230,14 +198,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_da7219_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -249,12 +216,6 @@ static int avs_da7219_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -271,8 +232,8 @@ static int avs_da7219_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index 90a921638572..c270646faf86 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -44,8 +44,6 @@ static const struct snd_soc_dapm_widget card_widgets[] = { static const struct snd_soc_dapm_route card_routes[] = { {"DMic", NULL, "SoC DMIC"}, - {"DMIC Rx", NULL, "Capture"}, - {"DMIC WoV Rx", NULL, "Capture"}, }; static int avs_dmic_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index a542a67e21d0..cb00bc86ac94 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -64,56 +64,6 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int return 0; } -static int avs_create_dapm_routes(struct device *dev, struct hda_codec *codec, int pcm_count, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - struct hda_pcm *pcm; - const char *cname = dev_name(&codec->core.dev); - int i, n = 0; - - /* at max twice the number of pcms */ - dr = devm_kcalloc(dev, pcm_count * 2, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); - - for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { - struct hda_pcm_stream *stream; - int dir; - - dir = SNDRV_PCM_STREAM_PLAYBACK; - stream = &pcm->stream[dir]; - if (!stream->substreams) - goto capture_routes; - - dr[n].sink = devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name, - snd_pcm_direction_name(dir)); - dr[n].source = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d Tx", cname, i); - if (!dr[n].sink || !dr[n].source) - return -ENOMEM; - n++; - -capture_routes: - dir = SNDRV_PCM_STREAM_CAPTURE; - stream = &pcm->stream[dir]; - if (!stream->substreams) - continue; - - dr[n].sink = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d Rx", cname, i); - dr[n].source = devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name, - snd_pcm_direction_name(dir)); - if (!dr[n].sink || !dr[n].source) - return -ENOMEM; - n++; - } - - *routes = dr; - *num_routes = n; - return 0; -} - /* Should be aligned with SectionPCM's name from topology */ #define FEDAI_NAME_PREFIX "HDMI" @@ -172,13 +122,12 @@ static int avs_card_late_probe(struct snd_soc_card *card) static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) { - struct snd_soc_dapm_route *routes; struct snd_soc_acpi_mach *mach; struct snd_soc_dai_link *links = NULL; struct snd_soc_card *card = rtm->card; struct hda_codec *codec; struct hda_pcm *pcm; - int ret, n, pcm_count = 0; + int ret, pcm_count = 0; mach = dev_get_platdata(card->dev); codec = mach->pdata; @@ -200,18 +149,6 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) return ret; } - ret = avs_create_dapm_routes(card->dev, codec, pcm_count, &routes, &n); - if (ret < 0) { - dev_err(card->dev, "create routes failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_add_routes(&card->dapm, routes, n); - if (ret < 0) { - dev_err(card->dev, "add routes failed: %d\n", ret); - return ret; - } - return 0; } diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 8f0fd87bc866..bc3065c6ceda 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -28,13 +28,11 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); - if (!dl->name || !dl->cpus || !dl->codecs) + if (!dl->name || !dl->cpus) return -ENOMEM; dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port); - dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "snd-soc-dummy"); - dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "snd-soc-dummy-dai"); + dl->codecs = &asoc_dummy_dlc; if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index 183123d08c5a..b9b20562c691 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -86,41 +86,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 1; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "HiFi Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_max98357a_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -132,12 +105,6 @@ static int avs_max98357a_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -151,8 +118,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 8e221ecd34b0..3833251ade26 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -141,47 +141,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left HiFi Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right HiFi Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_max98373_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -193,12 +160,6 @@ static int avs_max98373_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -214,8 +175,8 @@ static int avs_max98373_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index 7cccce99f92e..09b231bf4e6d 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -138,47 +138,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left HiFi Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right HiFi Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_max98927_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -190,12 +157,6 @@ static int avs_max98927_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -211,8 +172,8 @@ static int avs_max98927_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index b69fc5567135..38c5087d98e9 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -215,38 +215,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); @@ -274,14 +242,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_nau8825_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -293,12 +260,6 @@ static int avs_nau8825_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -315,8 +276,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index 6a1e121f082f..ebfee54814ce 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -188,38 +188,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI); @@ -237,14 +205,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_rt274_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -256,12 +223,6 @@ static int avs_rt274_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -278,8 +239,8 @@ static int avs_rt274_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 3551a05bd599..84cf9a0c8dfe 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -158,38 +158,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI); @@ -207,14 +175,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_rt286_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -226,12 +193,6 @@ static int avs_rt286_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -248,8 +209,8 @@ static int avs_rt286_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 2923f3805bbe..3b0e2b1a3251 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -178,38 +178,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI); @@ -227,14 +195,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_rt298_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -246,12 +213,6 @@ static int avs_rt298_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -268,8 +229,8 @@ static int avs_rt298_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index b2c2ba93dcb5..7142a67900bf 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -234,38 +234,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 2; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME); @@ -283,14 +251,13 @@ static int avs_card_resume_post(struct snd_soc_card *card) static int avs_rt5682_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct snd_soc_jack *jack; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; if (pdev->id_entry && pdev->id_entry->driver_data) avs_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data; @@ -308,12 +275,6 @@ static int avs_rt5682_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!jack || !card) @@ -330,8 +291,8 @@ static int avs_rt5682_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; snd_soc_card_set_drvdata(card, jack); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index 2b7f5ad92aca..7324869d6132 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -129,59 +129,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in return 0; } -static int avs_create_dapm_routes(struct device *dev, int ssp_port, - struct snd_soc_dapm_route **routes, int *num_routes) -{ - struct snd_soc_dapm_route *dr; - const int num_base = ARRAY_SIZE(card_base_routes); - const int num_dr = num_base + 4; - int idx; - - dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - memcpy(dr, card_base_routes, num_base * sizeof(*dr)); - - idx = num_base; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right Playback"); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Left Capture Sense"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - idx++; - dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); - dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Right Capture Sense"); - if (!dr[idx].sink || !dr[idx].source) - return -ENOMEM; - - *routes = dr; - *num_routes = num_dr; - - return 0; -} - static int avs_ssm4567_probe(struct platform_device *pdev) { - struct snd_soc_dapm_route *routes; struct snd_soc_dai_link *dai_link; struct snd_soc_acpi_mach *mach; struct snd_soc_card *card; struct device *dev = &pdev->dev; const char *pname; - int num_routes, ssp_port, ret; + int ssp_port, ret; mach = dev_get_platdata(dev); pname = mach->mach_params.platform; @@ -193,12 +148,6 @@ static int avs_ssm4567_probe(struct platform_device *pdev) return ret; } - ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); - if (ret) { - dev_err(dev, "Failed to create dapm routes: %d", ret); - return ret; - } - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -214,8 +163,8 @@ static int avs_ssm4567_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(card_controls); card->dapm_widgets = card_widgets; card->num_dapm_widgets = ARRAY_SIZE(card_widgets); - card->dapm_routes = routes; - card->num_dapm_routes = num_routes; + card->dapm_routes = card_base_routes; + card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; card->disable_route_checks = true; diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 99308ed85277..f472f603ab75 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -662,11 +662,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST depends on SOUNDWIRE + select SND_SOC_MAX98363 select SND_SOC_MAX98373_I2C select SND_SOC_MAX98373_SDW select SND_SOC_RT700_SDW select SND_SOC_RT711_SDW select SND_SOC_RT711_SDCA_SDW + select SND_SOC_RT712_SDCA_SDW + select SND_SOC_RT712_SDCA_DMIC_SDW select SND_SOC_RT1308_SDW select SND_SOC_RT1308 select SND_SOC_RT1316_SDW @@ -674,6 +677,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_RT715_SDW select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT5682_SDW + select SND_SOC_CS42L42_SDW select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_MAXIM_COMMON diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index d1fd7a2b32db..931415d9cf6f 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -37,11 +37,13 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o snd-soc-ehl-rt5660-objs := ehl_rt5660.o snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o snd-soc-sof-sdw-objs += sof_sdw.o \ - sof_sdw_max98373.o sof_sdw_rt_amp.o \ + sof_sdw_maxim.o sof_sdw_rt_amp.o \ sof_sdw_rt5682.o sof_sdw_rt700.o \ - sof_sdw_rt711.o sof_sdw_rt711_sdca.o \ - sof_sdw_rt715.o sof_sdw_rt715_sdca.o \ - sof_sdw_dmic.o sof_sdw_hdmi.o + sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \ + sof_sdw_rt712_sdca.o sof_sdw_rt715.o \ + sof_sdw_rt715_sdca.o sof_sdw_dmic.o \ + sof_sdw_cs42l42.o \ + sof_sdw_hdmi.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c index d5235c294c4c..fee80638cba2 100644 --- a/sound/soc/intel/boards/ehl_rt5660.c +++ b/sound/soc/intel/boards/ehl_rt5660.c @@ -254,7 +254,6 @@ static void hdmi_link_init(struct snd_soc_card *card, struct sof_card_private *ctx, struct snd_soc_acpi_mach *mach) { - struct snd_soc_dai_link *link; int i; if (mach->mach_params.common_hdmi_codec_drv && @@ -267,11 +266,8 @@ static void hdmi_link_init(struct snd_soc_card *card, * if HDMI is not enabled in kernel config, or * hdmi codec is not supported */ - for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++) { - link = &card->dai_link[i]; - link->codecs[0].name = "snd-soc-dummy"; - link->codecs[0].dai_name = "snd-soc-dummy-dai"; - } + for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++) + card->dai_link[i].codecs[0] = asoc_dummy_dlc; } static int snd_ehl_rt5660_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 879ebba52832..a06e05154ae1 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -61,9 +61,6 @@ static const struct snd_soc_dapm_route skl_hda_map[] = { { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, }; -SND_SOC_DAILINK_DEF(dummy_codec, - DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai"))); - static int skl_hda_card_late_probe(struct snd_soc_card *card) { return skl_hda_hdmi_jack_init(card); @@ -158,9 +155,8 @@ static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); if (!ctx->idisp_codec) { for (i = 0; i < IDISP_DAI_COUNT; i++) { - skl_hda_be_dai_links[i].codecs = dummy_codec; - skl_hda_be_dai_links[i].num_codecs = - ARRAY_SIZE(dummy_codec); + skl_hda_be_dai_links[i].codecs = &asoc_dummy_dlc; + skl_hda_be_dai_links[i].num_codecs = 1; } } } diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c index e9d190cb13b0..e6695e77d594 100644 --- a/sound/soc/intel/boards/sof_cs42l42.c +++ b/sound/soc/intel/boards/sof_cs42l42.c @@ -296,13 +296,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - static int create_spk_amp_dai_links(struct device *dev, struct snd_soc_dai_link *links, struct snd_soc_dai_link_component *cpus, @@ -510,8 +503,8 @@ static int create_bt_offload_dai_links(struct device *dev, goto devm_err; links[*id].id = *id; - links[*id].codecs = dummy_component; - links[*id].num_codecs = ARRAY_SIZE(dummy_component); + links[*id].codecs = &asoc_dummy_dlc; + links[*id].num_codecs = 1; links[*id].platforms = platform_component; links[*id].num_platforms = ARRAY_SIZE(platform_component); diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index adf5852b2c9a..d6c38d8ea2ff 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -393,13 +393,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - static int sof_es8336_late_probe(struct snd_soc_card *card) { struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card); @@ -572,8 +565,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (!links[id].name) return NULL; links[id].id = id + hdmi_id_offset; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].dpcm_capture = 1; diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index 6794a0249a9a..4fc6e1c6aef3 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -346,13 +346,6 @@ static struct snd_soc_dai_link_component nau8318_components[] = { } }; -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, int ssp_codec, int ssp_amp, @@ -532,8 +525,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); if (!links[id].name) goto devm_err; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].dpcm_playback = 1; @@ -681,6 +674,16 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .name = "rpl_max98373_8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c index 5192e02b3cee..9f673ccf81b5 100644 --- a/sound/soc/intel/boards/sof_pcm512x.c +++ b/sound/soc/intel/boards/sof_pcm512x.c @@ -331,8 +331,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, devm_kasprintf(dev, GFP_KERNEL, "intel-hdmi-hifi%d", i); } else { - idisp_components[i - 1].name = "snd-soc-dummy"; - idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + idisp_components[i - 1] = asoc_dummy_dlc; } if (!idisp_components[i - 1].dai_name) goto devm_err; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 791a59c5f00d..7c034d671cf3 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -243,6 +243,20 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .callback = sof_rt5682_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-ALC1019_ALC5682I_I2S"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(2) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(0) | + SOF_RT5682_NUM_HDMIDEV(3) + ), + }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), }, .driver_data = (void *)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(2) | @@ -607,13 +621,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - #define IDISP_CODEC_MASK 0x4 static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, @@ -745,8 +752,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (!idisp_components[i - 1].dai_name) goto devm_err; } else { - idisp_components[i - 1].name = "snd-soc-dummy"; - idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + idisp_components[i - 1] = asoc_dummy_dlc; } links[id].codecs = &idisp_components[i - 1]; @@ -841,8 +847,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); if (!links[id].name) goto devm_err; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].dpcm_playback = 1; @@ -989,14 +995,13 @@ static const struct platform_device_id board_ids[] = { .name = "sof_rt5682", }, { - .name = "tgl_mx98357_rt5682", + .name = "cml_rt1015_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_MCLK_24MHZ | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), + SOF_RT1015_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1)), }, { .name = "jsl_rt5682_rt1015", @@ -1008,33 +1013,38 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1)), }, { - .name = "tgl_mx98373_rt5682", + .name = "jsl_rt5682_mx98360", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_MCLK_24MHZ | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | - SOF_MAX98373_SPEAKER_AMP_PRESENT | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), + SOF_MAX98360A_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1)), }, { - .name = "jsl_rt5682_mx98360", + .name = "jsl_rt5682_rt1015p", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_MCLK_24MHZ | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | - SOF_MAX98360A_SPEAKER_AMP_PRESENT | + SOF_RT1015P_SPEAKER_AMP_PRESENT | SOF_RT5682_SSP_AMP(1)), }, { - .name = "cml_rt1015_rt5682", + .name = "jsl_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_MCLK_24MHZ | + SOF_RT5682_SSP_CODEC(0)), + }, + { + .name = "tgl_mx98357_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | - SOF_RT1015_SPEAKER_AMP_PRESENT | - SOF_RT5682_SSP_AMP(1)), + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), }, { .name = "tgl_rt1011_rt5682", @@ -1048,13 +1058,15 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "jsl_rt5682_rt1015p", + .name = "tgl_mx98373_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_MCLK_24MHZ | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | - SOF_RT1015P_SPEAKER_AMP_PRESENT | - SOF_RT5682_SSP_AMP(1)), + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), }, { .name = "adl_mx98373_rt5682", @@ -1128,6 +1140,17 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { + .name = "rpl_rt1019_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, + { .name = "mtl_mx98357_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | @@ -1147,10 +1170,13 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(4)), }, { - .name = "jsl_rt5682", + .name = "mtl_rt1019_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_MCLK_24MHZ | - SOF_RT5682_SSP_CODEC(0)), + SOF_RT5682_SSP_CODEC(2) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(0) | + SOF_RT5682_NUM_HDMIDEV(3)), }, { } }; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 144f082c63fd..dbee8c98ff01 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -22,11 +22,16 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override"); #define INC_ID(BE, CPU, LINK) do { (BE)++; (CPU)++; (LINK)++; } while (0) +#define SDW_MAX_LINKS 4 + +/* To store SDW Pin index for each SoundWire link */ +static unsigned int sdw_pin_index[SDW_MAX_LINKS]; + static void log_quirks(struct device *dev) { - if (SOF_RT711_JDSRC(sof_sdw_quirk)) + if (SOF_JACK_JDSRC(sof_sdw_quirk)) dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", - SOF_RT711_JDSRC(sof_sdw_quirk)); + SOF_JACK_JDSRC(sof_sdw_quirk)); if (sof_sdw_quirk & SOF_SDW_FOUR_SPK) dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n"); if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) @@ -192,6 +197,20 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { RT711_JD1), }, { + /* + * this entry covers HP Spectre x360 where the DMI information + * changed somehow + */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8709"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + SOF_SDW_PCH_DMIC | + RT711_JD1), + }, + { /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ .callback = sof_sdw_quirk_cb, .matches = { @@ -360,6 +379,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { { .callback = sof_sdw_quirk_cb, .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"), + }, + /* No Jack */ + .driver_data = (void *)SOF_SDW_TGL_HDMI, + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HP"), DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16-k0xxx"), }, @@ -371,6 +399,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"), }, /* No Jack */ @@ -413,7 +451,32 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"), }, - .driver_data = (void *)(RT711_JD1 | SOF_SDW_TGL_HDMI), + .driver_data = (void *)(RT711_JD1), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"), + }, + .driver_data = (void *)(RT711_JD2_100K), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Rex"), + }, + .driver_data = (void *)(SOF_SDW_PCH_DMIC), + }, + /* LunarLake devices */ + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"), + }, + .driver_data = (void *)(RT711_JD2_100K), }, {} }; @@ -497,6 +560,55 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } +int sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ch = params_channels(params); + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; + unsigned int ch_mask; + int num_codecs; + int step; + int i; + int j; + + if (!rtd->dai_link->codec_ch_maps) + return 0; + + /* Identical data will be sent to all codecs in playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ch_mask = GENMASK(ch - 1, 0); + step = 0; + } else { + num_codecs = rtd->dai_link->num_codecs; + + if (ch < num_codecs || ch % num_codecs != 0) { + dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n", + ch, num_codecs); + return -EINVAL; + } + + ch_mask = GENMASK(ch / num_codecs - 1, 0); + step = hweight_long(ch_mask); + + } + + /* + * The captured data will be combined from each cpu DAI if the dai + * link has more than one codec DAIs. Set codec channel mask and + * ASoC will set the corresponding channel numbers for each cpu dai. + */ + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + for_each_rtd_codec_dais(rtd, j, codec_dai) { + if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i) + continue; + rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step); + } + } + return 0; +} + int sdw_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -525,6 +637,7 @@ static const struct snd_soc_ops sdw_ops = { .startup = sdw_startup, .prepare = sdw_prepare, .trigger = sdw_trigger, + .hw_params = sdw_hw_params, .hw_free = sdw_hw_free, .shutdown = sdw_shutdown, }; @@ -532,134 +645,323 @@ static const struct snd_soc_ops sdw_ops = { static struct sof_sdw_codec_info codec_info_list[] = { { .part_id = 0x700, - .direction = {true, true}, - .dai_name = "rt700-aif1", - .init = sof_sdw_rt700_init, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt700-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt700_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x711, .version_id = 3, - .direction = {true, true}, - .dai_name = "rt711-sdca-aif1", - .init = sof_sdw_rt711_sdca_init, - .exit = sof_sdw_rt711_sdca_exit, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt711-sdca-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt_sdca_jack_init, + .exit = sof_sdw_rt_sdca_jack_exit, + }, + }, + .dai_num = 1, }, { .part_id = 0x711, .version_id = 2, - .direction = {true, true}, - .dai_name = "rt711-aif1", - .init = sof_sdw_rt711_init, - .exit = sof_sdw_rt711_exit, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt711-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt711_init, + .exit = sof_sdw_rt711_exit, + }, + }, + .dai_num = 1, + }, + { + .part_id = 0x712, + .version_id = 3, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt712-sdca-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt_sdca_jack_init, + .exit = sof_sdw_rt_sdca_jack_exit, + }, + { + .direction = {true, false}, + .dai_name = "rt712-sdca-aif2", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .init = sof_sdw_rt712_spk_init, + }, + }, + .dai_num = 2, + }, + { + .part_id = 0x1712, + .version_id = 3, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt712-sdca-dmic-aif1", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt712_sdca_dmic_init, + }, + }, + .dai_num = 1, + }, + { + .part_id = 0x713, + .version_id = 3, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt712-sdca-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt_sdca_jack_init, + .exit = sof_sdw_rt_sdca_jack_exit, + }, + }, + .dai_num = 1, + }, + { + .part_id = 0x1713, + .version_id = 3, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt712-sdca-dmic-aif1", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt712_sdca_dmic_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x1308, .acpi_id = "10EC1308", - .direction = {true, false}, - .dai_name = "rt1308-aif", + .dais = { + { + .direction = {true, false}, + .dai_name = "rt1308-aif", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, + }, + }, + .dai_num = 1, .ops = &sof_sdw_rt1308_i2s_ops, - .init = sof_sdw_rt_amp_init, - .exit = sof_sdw_rt_amp_exit, - .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, - .direction = {true, true}, - .dai_name = "rt1316-aif", - .init = sof_sdw_rt_amp_init, - .exit = sof_sdw_rt_amp_exit, - .codec_type = SOF_SDW_CODEC_TYPE_AMP, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt1316-aif", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, + }, + }, + .dai_num = 1, }, { .part_id = 0x1318, - .direction = {true, true}, - .dai_name = "rt1318-aif", - .init = sof_sdw_rt_amp_init, - .codec_type = SOF_SDW_CODEC_TYPE_AMP, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt1318-aif", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, + }, + }, + .dai_num = 1, }, { .part_id = 0x714, .version_id = 3, - .direction = {false, true}, .ignore_pch_dmic = true, - .dai_name = "rt715-aif2", - .init = sof_sdw_rt715_sdca_init, - .codec_type = SOF_SDW_CODEC_TYPE_MIC, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt715-aif2", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt715_sdca_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x715, .version_id = 3, - .direction = {false, true}, .ignore_pch_dmic = true, - .dai_name = "rt715-aif2", - .init = sof_sdw_rt715_sdca_init, - .codec_type = SOF_SDW_CODEC_TYPE_MIC, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt715-aif2", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt715_sdca_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x714, .version_id = 2, - .direction = {false, true}, .ignore_pch_dmic = true, - .dai_name = "rt715-aif2", - .init = sof_sdw_rt715_init, - .codec_type = SOF_SDW_CODEC_TYPE_MIC, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt715-aif2", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt715_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x715, .version_id = 2, - .direction = {false, true}, .ignore_pch_dmic = true, - .dai_name = "rt715-aif2", - .init = sof_sdw_rt715_init, - .codec_type = SOF_SDW_CODEC_TYPE_MIC, + .dais = { + { + .direction = {false, true}, + .dai_name = "rt715-aif2", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt715_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x8373, - .direction = {true, true}, - .dai_name = "max98373-aif1", - .init = sof_sdw_mx8373_init, - .codec_type = SOF_SDW_CODEC_TYPE_AMP, + .dais = { + { + .direction = {true, true}, + .dai_name = "max98373-aif1", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, + .init = sof_sdw_maxim_init, + }, + }, + .dai_num = 1, + }, + { + .part_id = 0x8363, + .dais = { + { + .direction = {true, false}, + .dai_name = "max98363-aif1", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .init = sof_sdw_maxim_init, + }, + }, + .dai_num = 1, }, { .part_id = 0x5682, - .direction = {true, true}, - .dai_name = "rt5682-sdw", - .init = sof_sdw_rt5682_init, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt5682-sdw", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_rt5682_init, + }, + }, + .dai_num = 1, + }, + { + .part_id = 0x4242, + .dais = { + { + .direction = {true, true}, + .dai_name = "cs42l42-sdw", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = sof_sdw_cs42l42_init, + }, + }, + .dai_num = 1, }, { .part_id = 0xaaaa, /* generic codec mockup */ .version_id = 0, - .direction = {true, true}, - .dai_name = "sdw-mockup-aif1", - .init = NULL, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "sdw-mockup-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = NULL, + }, + }, + .dai_num = 1, }, { .part_id = 0xaa55, /* headset codec mockup */ .version_id = 0, - .direction = {true, true}, - .dai_name = "sdw-mockup-aif1", - .init = NULL, - .codec_type = SOF_SDW_CODEC_TYPE_JACK, + .dais = { + { + .direction = {true, true}, + .dai_name = "sdw-mockup-aif1", + .dai_type = SOF_SDW_DAI_TYPE_JACK, + .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, + .init = NULL, + }, + }, + .dai_num = 1, }, { .part_id = 0x55aa, /* amplifier mockup */ .version_id = 0, - .direction = {true, false}, - .dai_name = "sdw-mockup-aif1", - .init = NULL, - .codec_type = SOF_SDW_CODEC_TYPE_AMP, + .dais = { + { + .direction = {true, false}, + .dai_name = "sdw-mockup-aif1", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .init = NULL, + }, + }, + .dai_num = 1, }, { .part_id = 0x5555, .version_id = 0, - .direction = {false, true}, - .dai_name = "sdw-mockup-aif1", - .codec_type = SOF_SDW_CODEC_TYPE_MIC, + .dais = { + { + .dai_name = "sdw-mockup-aif1", + .direction = {false, true}, + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = NULL, + }, + }, + .dai_num = 1, }, }; @@ -711,10 +1013,10 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li int *sdw_be_num, int *sdw_cpu_dai_num) { const struct snd_soc_acpi_link_adr *link; - int _codec_type = SOF_SDW_CODEC_TYPE_JACK; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; + int j; no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; *sdw_cpu_dai_num = 0; @@ -728,6 +1030,7 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li for (link = links; link->num_adr; link++) { const struct snd_soc_acpi_endpoint *endpoint; + struct sof_sdw_codec_info *codec_info; int codec_index; int stream; u64 adr; @@ -737,26 +1040,23 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li codec_index = find_codec_info_part(adr); if (codec_index < 0) return codec_index; - - if (codec_info_list[codec_index].codec_type < _codec_type) - dev_warn(dev, - "Unexpected address table ordering. Expected order: jack -> amp -> mic\n"); - - _codec_type = codec_info_list[codec_index].codec_type; + codec_info = &codec_info_list[codec_index]; endpoint = link->adr_d[i].endpoints; - /* count DAI number for playback and capture */ - for_each_pcm_streams(stream) { - if (!codec_info_list[codec_index].direction[stream]) - continue; + for (j = 0; j < codec_info->dai_num; j++) { + /* count DAI number for playback and capture */ + for_each_pcm_streams(stream) { + if (!codec_info->dais[j].direction[stream]) + continue; - (*sdw_cpu_dai_num)++; + (*sdw_cpu_dai_num)++; - /* count BE for each non-aggregated slave or group */ - if (!endpoint->aggregated || no_aggregation || - !group_visited[endpoint->group_id]) - (*sdw_be_num)++; + /* count BE for each non-aggregated slave or group */ + if (!endpoint->aggregated || no_aggregation || + !group_visited[endpoint->group_id]) + (*sdw_be_num)++; + } } if (endpoint->aggregated) @@ -832,7 +1132,8 @@ static int create_codec_dai_name(struct device *dev, struct snd_soc_codec_conf *codec_conf, int codec_count, int *codec_conf_index, - int adr_index) + int adr_index, + int dai_index) { int _codec_index = -1; int i; @@ -888,7 +1189,7 @@ static int create_codec_dai_name(struct device *dev, _codec_index = codec_index; codec[comp_index].dai_name = - codec_info_list[codec_index].dai_name; + codec_info_list[codec_index].dais[dai_index].dai_name; codec_conf[*codec_conf_index].dlc = codec[comp_index]; codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix; @@ -902,35 +1203,43 @@ static int create_codec_dai_name(struct device *dev, static int set_codec_init_func(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, - bool playback, int group_id) + bool playback, int group_id, int adr_index, int dai_index) { - int i; + int i = adr_index; do { /* * Initialize the codec. If codec is part of an aggregated * group (group_id>0), initialize all codecs belonging to * same group. + * The first link should start with link->adr_d[adr_index] + * because that is the device that we want to initialize and + * we should end immediately if it is not aggregated (group_id=0) */ - for (i = 0; i < link->num_adr; i++) { + for ( ; i < link->num_adr; i++) { int codec_index; codec_index = find_codec_info_part(link->adr_d[i].adr); if (codec_index < 0) return codec_index; + /* The group_id is > 0 iff the codec is aggregated */ if (link->adr_d[i].endpoints->group_id != group_id) continue; - if (codec_info_list[codec_index].init) - codec_info_list[codec_index].init(card, + + if (codec_info_list[codec_index].dais[dai_index].init) + codec_info_list[codec_index].dais[dai_index].init(card, link, dai_links, &codec_info_list[codec_index], playback); + if (!group_id) + return 0; } + i = 0; link++; - } while (link->mask && group_id); + } while (link->mask); return 0; } @@ -1022,6 +1331,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, return 0; } +static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps, + int codec_num, int cpu_num) +{ + int step; + int i; + + step = codec_num / cpu_num; + for (i = 0; i < codec_num; i++) + sdw_codec_ch_maps[i].connected_cpu_id = i / step; +} + static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; static int create_sdw_dailink(struct snd_soc_card *card, @@ -1035,19 +1355,21 @@ static int create_sdw_dailink(struct snd_soc_card *card, int codec_count, int *link_id, int *codec_conf_index, bool *ignore_pch_dmic, - bool append_codec_type, - int adr_index) + bool append_dai_type, + int adr_index, + int dai_index) { const struct snd_soc_acpi_link_adr *link_next; struct snd_soc_dai_link_component *codecs; + struct sof_sdw_codec_info *codec_info; int cpu_dai_id[SDW_MAX_CPU_DAIS]; int cpu_dai_num, cpu_dai_index; unsigned int group_id; int codec_idx = 0; - int i = 0, j = 0; int codec_index; int codec_num; int stream; + int i = 0; int ret; int k; @@ -1075,7 +1397,8 @@ static int create_sdw_dailink(struct snd_soc_card *card, continue; ret = create_codec_dai_name(dev, link_next, codecs, codec_idx, - codec_conf, codec_count, codec_conf_index, adr_index); + codec_conf, codec_count, codec_conf_index, + adr_index, dai_index); if (ret < 0) return ret; @@ -1088,25 +1411,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, codec_index = find_codec_info_part(link->adr_d[adr_index].adr); if (codec_index < 0) return codec_index; + codec_info = &codec_info_list[codec_index]; - if (codec_info_list[codec_index].ignore_pch_dmic) + if (codec_info->ignore_pch_dmic) *ignore_pch_dmic = true; - /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */ - if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP && - *link_id < SDW_AMP_DAI_ID) - *link_id = SDW_AMP_DAI_ID; - - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC && - *link_id < SDW_DMIC_DAI_ID) - *link_id = SDW_DMIC_DAI_ID; - cpu_dai_index = *cpu_id; for_each_pcm_streams(stream) { + struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps; char *name, *cpu_name; int playback, capture; static const char * const sdw_stream_name[] = { @@ -1116,14 +1428,25 @@ static int create_sdw_dailink(struct snd_soc_card *card, "SDW%d-Capture-%s", }; - if (!codec_info_list[codec_index].direction[stream]) + if (!codec_info->dais[dai_index].direction[stream]) continue; + *link_id = codec_info->dais[dai_index].dailink[stream]; + if (*link_id < 0) { + dev_err(dev, "Invalid dailink id %d\n", *link_id); + return -EINVAL; + } + + sdw_codec_ch_maps = devm_kcalloc(dev, codec_num, + sizeof(*sdw_codec_ch_maps), GFP_KERNEL); + if (!sdw_codec_ch_maps) + return -ENOMEM; + /* create stream name according to first link id */ - if (append_codec_type) { + if (append_dai_type) { name = devm_kasprintf(dev, GFP_KERNEL, sdw_stream_name[stream + 2], cpu_dai_id[0], - type_strings[codec_info_list[codec_index].codec_type]); + type_strings[codec_info->dais[dai_index].dai_type]); } else { name = devm_kasprintf(dev, GFP_KERNEL, sdw_stream_name[stream], cpu_dai_id[0]); @@ -1138,7 +1461,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, for (k = 0; k < cpu_dai_num; k++) { cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", cpu_dai_id[k], - j + SDW_INTEL_BIDIR_PDI_BASE); + sdw_pin_index[cpu_dai_id[k]]++); if (!cpu_name) return -ENOMEM; @@ -1179,15 +1502,16 @@ static int create_sdw_dailink(struct snd_soc_card *card, */ dai_links[*link_index].nonatomic = true; + set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); + dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; ret = set_codec_init_func(card, link, dai_links + (*link_index)++, - playback, group_id); + playback, group_id, adr_index, dai_index); if (ret < 0) { dev_err(dev, "failed to init codec %d", codec_index); return ret; } *cpu_id += cpu_dai_num; - j++; } return 0; @@ -1203,6 +1527,7 @@ static int sof_card_codec_conf_alloc(struct device *dev, const struct snd_soc_acpi_link_adr *adr_link; struct snd_soc_codec_conf *c_conf; int num_codecs = 0; + int codec_index; int i; adr_link = mach_params->links; @@ -1217,8 +1542,11 @@ static int sof_card_codec_conf_alloc(struct device *dev, adr_link->adr_d[i].adr); return -EINVAL; } + codec_index = find_codec_info_part(adr_link->adr_d[i].adr); + if (codec_index < 0) + return codec_index; + num_codecs += codec_info_list[codec_index].dai_num; } - num_codecs += adr_link->num_adr; } c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL); @@ -1243,7 +1571,7 @@ static int sof_card_dai_links_create(struct device *dev, const struct snd_soc_acpi_link_adr *adr_link; struct snd_soc_dai_link_component *cpus; struct snd_soc_codec_conf *codec_conf; - bool append_codec_type = false; + bool append_dai_type = false; bool ignore_pch_dmic = false; int codec_conf_count; int codec_conf_index = 0; @@ -1255,6 +1583,7 @@ static int sof_card_dai_links_create(struct device *dev, int total_cpu_dai_num; int sdw_cpu_dai_num; int i, j, be_id = 0; + int codec_index; int cpu_id = 0; int comp_num; int ret; @@ -1335,6 +1664,9 @@ static int sof_card_dai_links_create(struct device *dev, for (i = 0; i < SDW_MAX_GROUPS; i++) group_generated[i] = false; + for (i = 0; i < SDW_MAX_LINKS; i++) + sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE; + for (; adr_link->num_adr; adr_link++) { /* * If there are two or more different devices on the same sdw link, we have to @@ -1343,12 +1675,20 @@ static int sof_card_dai_links_create(struct device *dev, * snd_soc_acpi_adr_device array. They won't be described in different adr_links. */ for (i = 0; i < adr_link->num_adr; i++) { + /* find codec info to get dai_num */ + codec_index = find_codec_info_part(adr_link->adr_d[i].adr); + if (codec_index < 0) + return codec_index; + if (codec_info_list[codec_index].dai_num > 1) { + append_dai_type = true; + goto out; + } for (j = 0; j < i; j++) { if ((SDW_PART_ID(adr_link->adr_d[i].adr) != SDW_PART_ID(adr_link->adr_d[j].adr)) || (SDW_MFG_ID(adr_link->adr_d[i].adr) != SDW_MFG_ID(adr_link->adr_d[j].adr))) { - append_codec_type = true; + append_dai_type = true; goto out; } } @@ -1373,15 +1713,22 @@ out: group_generated[endpoint->group_id]) continue; - ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, - sdw_cpu_dai_num, cpus, adr_link, - &cpu_id, group_generated, - codec_conf, codec_conf_count, - &be_id, &codec_conf_index, - &ignore_pch_dmic, append_codec_type, i); - if (ret < 0) { - dev_err(dev, "failed to create dai link %d", link_index); - return ret; + /* find codec info to get dai_num */ + codec_index = find_codec_info_part(adr_link->adr_d[i].adr); + if (codec_index < 0) + return codec_index; + + for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { + ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, + sdw_cpu_dai_num, cpus, adr_link, + &cpu_id, group_generated, + codec_conf, codec_conf_count, + &be_id, &codec_conf_index, + &ignore_pch_dmic, append_dai_type, i, j); + if (ret < 0) { + dev_err(dev, "failed to create dai link %d", link_index); + return ret; + } } } } @@ -1420,18 +1767,19 @@ SSP: return -ENOMEM; ssp_components->name = codec_name; - ssp_components->dai_name = info->dai_name; + /* TODO: support multi codec dai on SSP when it is needed */ + ssp_components->dai_name = info->dais[0].dai_name; cpus[cpu_id].dai_name = cpu_name; - playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK]; - capture = info->direction[SNDRV_PCM_STREAM_CAPTURE]; + playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK]; + capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE]; init_dai_link(dev, links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->init(card, NULL, links + link_index, info, 0); + ret = info->dais[0].init(card, NULL, links + link_index, info, 0); if (ret < 0) return ret; @@ -1488,8 +1836,7 @@ HDMI: if (!idisp_components[i].dai_name) return -ENOMEM; } else { - idisp_components[i].name = "snd-soc-dummy"; - idisp_components[i].dai_name = "snd-soc-dummy-dai"; + idisp_components[i] = asoc_dummy_dlc; } cpu_name = devm_kasprintf(dev, GFP_KERNEL, @@ -1514,21 +1861,13 @@ HDMI: if (!name) return -ENOMEM; - ssp_components = devm_kzalloc(dev, sizeof(*ssp_components), - GFP_KERNEL); - if (!ssp_components) - return -ENOMEM; - - ssp_components->name = "snd-soc-dummy"; - ssp_components->dai_name = "snd-soc-dummy-dai"; - cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port); if (!cpu_name) return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; init_dai_link(dev, links + link_index, be_id, name, 1, 1, - cpus + cpu_id, 1, ssp_components, 1, NULL, NULL); + cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); } card->dai_link = links; @@ -1570,6 +1909,24 @@ static struct snd_soc_card card_sof_sdw = { .late_probe = sof_sdw_card_late_probe, }; +/* helper to get the link that the codec DAI is used */ +static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_dai_link *link; + int i; + int j; + + for_each_card_prelinks(card, i, link) { + for (j = 0; j < link->num_codecs; j++) { + /* Check each codec in a link */ + if (!strcmp(link->codecs[j].dai_name, dai_name)) + return link; + } + } + return NULL; +} + static void mc_dailink_exit_loop(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -1577,16 +1934,18 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) int i, j; for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { - if (!codec_info_list[i].exit) - continue; - /* - * We don't need to call .exit function if there is no matched - * dai link found. - */ - for_each_card_prelinks(card, j, link) { - if (!strcmp(link->codecs[0].dai_name, - codec_info_list[i].dai_name)) { - ret = codec_info_list[i].exit(card, link); + for (j = 0; j < codec_info_list[i].dai_num; j++) { + /* Check each dai in codec_info_lis to see if it is used in the link */ + if (!codec_info_list[i].dais[j].exit) + continue; + /* + * We don't need to call .exit function if there is no matched + * dai link found. + */ + link = mc_find_codec_dai_used(card, codec_info_list[i].dais[j].dai_name); + if (link) { + /* Do the .exit function if the codec dai is used in the link */ + ret = codec_info_list[i].dais[j].exit(card, link); if (ret) dev_warn(card->dev, "codec exit failed %d\n", diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 081ab7eac5b6..37402170d5f9 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -15,7 +15,11 @@ #define MAX_NO_PROPS 2 #define MAX_HDMI_NUM 4 -#define SDW_AMP_DAI_ID 2 +#define SDW_UNUSED_DAI_ID -1 +#define SDW_JACK_OUT_DAI_ID 0 +#define SDW_JACK_IN_DAI_ID 1 +#define SDW_AMP_OUT_DAI_ID 2 +#define SDW_AMP_IN_DAI_ID 3 #define SDW_DMIC_DAI_ID 4 #define SDW_MAX_CPU_DAIS 16 #define SDW_INTEL_BIDIR_PDI_BASE 2 @@ -37,7 +41,7 @@ enum { SOF_I2S_SSP5 = BIT(5), }; -#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(3, 0)) +#define SOF_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0)) #define SOF_SDW_FOUR_SPK BIT(4) #define SOF_SDW_TGL_HDMI BIT(5) #define SOF_SDW_PCH_DMIC BIT(6) @@ -52,28 +56,37 @@ enum { (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18) -#define SOF_SDW_CODEC_TYPE_JACK 0 -#define SOF_SDW_CODEC_TYPE_AMP 1 -#define SOF_SDW_CODEC_TYPE_MIC 2 +#define SOF_SDW_DAI_TYPE_JACK 0 +#define SOF_SDW_DAI_TYPE_AMP 1 +#define SOF_SDW_DAI_TYPE_MIC 2 + +#define SOF_SDW_MAX_DAI_NUM 3 + +struct sof_sdw_codec_info; + +struct sof_sdw_dai_info { + const bool direction[2]; /* playback & capture support */ + const char *dai_name; + const int dai_type; + const int dailink[2]; /* dailink id for each direction */ + int (*init)(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +}; struct sof_sdw_codec_info { const int part_id; const int version_id; - const int codec_type; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; - const bool direction[2]; // playback & capture support const bool ignore_pch_dmic; - const char *dai_name; const struct snd_soc_ops *ops; + struct sof_sdw_dai_info dais[SOF_SDW_MAX_DAI_NUM]; + const int dai_num; - int (*init)(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - - int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); int (*codec_card_late_probe)(struct snd_soc_card *card); }; @@ -90,6 +103,8 @@ extern unsigned long sof_sdw_quirk; int sdw_startup(struct snd_pcm_substream *substream); int sdw_prepare(struct snd_pcm_substream *substream); int sdw_trigger(struct snd_pcm_substream *substream, int cmd); +int sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); int sdw_hw_free(struct snd_pcm_substream *substream); void sdw_shutdown(struct snd_pcm_substream *substream); @@ -110,12 +125,30 @@ int sof_sdw_rt711_init(struct snd_soc_card *card, int sof_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* RT711-SDCA support */ -int sof_sdw_rt711_sdca_init(struct snd_soc_card *card, +int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); +int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); + +/* RT712-SDCA support */ +int sof_sdw_rt712_sdca_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); -int sof_sdw_rt711_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +int sof_sdw_rt712_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +int sof_sdw_rt712_spk_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); +int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); /* RT700 support */ int sof_sdw_rt700_init(struct snd_soc_card *card, @@ -151,12 +184,12 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); -/* MAX98373 support */ -int sof_sdw_mx8373_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); +/* MAXIM codec support */ +int sof_sdw_maxim_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); /* RT5682 support */ int sof_sdw_rt5682_init(struct snd_soc_card *card, @@ -165,4 +198,11 @@ int sof_sdw_rt5682_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); +/* CS42L42 support */ +int sof_sdw_cs42l42_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + #endif diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c new file mode 100644 index 000000000000..c4a16e4c9f69 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023 Intel Corporation + +/* + * sof_sdw_cs42l42 - Helpers to handle CS42L42 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/control.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget cs42l42_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route cs42l42_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + {"Headphone", NULL, "cs42l42 HP"}, + + /* other jacks */ + {"cs42l42 HS", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new cs42l42_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static struct snd_soc_jack_pin cs42l42_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s hs:cs42l42", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, cs42l42_controls, + ARRAY_SIZE(cs42l42_controls)); + if (ret) { + dev_err(card->dev, "cs42l42 control addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, cs42l42_widgets, + ARRAY_SIZE(cs42l42_widgets)); + if (ret) { + dev_err(card->dev, "cs42l42 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, cs42l42_map, + ARRAY_SIZE(cs42l42_map)); + + if (ret) { + dev_err(card->dev, "cs42l42 map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + cs42l42_jack_pins, + ARRAY_SIZE(cs42l42_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +int sof_sdw_cs42l42_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* + * headset should be initialized once. + * Do it with dai link for playback. + */ + if (!playback) + return 0; + + dai_links->init = cs42l42_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_max98373.c b/sound/soc/intel/boards/sof_sdw_maxim.c index 3d7df58c0f1d..414c4d8dac77 100644 --- a/sound/soc/intel/boards/sof_sdw_max98373.c +++ b/sound/soc/intel/boards/sof_sdw_maxim.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2020 Intel Corporation // -// sof_sdw_max98373 - Helpers to handle 2x MAX98373 +// sof_sdw_maxim - Helpers to handle maxim codecs // codec devices from generic machine driver #include <linux/device.h> @@ -13,12 +13,16 @@ #include "sof_sdw_common.h" #include "sof_maxim_common.h" -static const struct snd_soc_dapm_widget mx8373_widgets[] = { +static int maxim_part_id; +#define SOF_SDW_PART_ID_MAX98363 0x8363 +#define SOF_SDW_PART_ID_MAX98373 0x8373 + +static const struct snd_soc_dapm_widget maxim_widgets[] = { SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), }; -static const struct snd_kcontrol_new mx8373_controls[] = { +static const struct snd_kcontrol_new maxim_controls[] = { SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), }; @@ -29,22 +33,25 @@ static int spk_init(struct snd_soc_pcm_runtime *rtd) int ret; card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:mx8373", - card->components); + "%s spk:mx%04x", + card->components, maxim_part_id); if (!card->components) return -ENOMEM; - ret = snd_soc_add_card_controls(card, mx8373_controls, - ARRAY_SIZE(mx8373_controls)); + dev_dbg(card->dev, "soundwire maxim card components assigned : %s\n", + card->components); + + ret = snd_soc_add_card_controls(card, maxim_controls, + ARRAY_SIZE(maxim_controls)); if (ret) { - dev_err(card->dev, "mx8373 ctrls addition failed: %d\n", ret); + dev_err(card->dev, "mx%04x ctrls addition failed: %d\n", maxim_part_id, ret); return ret; } - ret = snd_soc_dapm_new_controls(&card->dapm, mx8373_widgets, - ARRAY_SIZE(mx8373_widgets)); + ret = snd_soc_dapm_new_controls(&card->dapm, maxim_widgets, + ARRAY_SIZE(maxim_widgets)); if (ret) { - dev_err(card->dev, "mx8373 widgets addition failed: %d\n", ret); + dev_err(card->dev, "mx%04x widgets addition failed: %d\n", maxim_part_id, ret); return ret; } @@ -116,6 +123,7 @@ static const struct snd_soc_ops max_98373_sdw_ops = { .startup = sdw_startup, .prepare = mx8373_sdw_prepare, .trigger = sdw_trigger, + .hw_params = sdw_hw_params, .hw_free = mx8373_sdw_hw_free, .shutdown = sdw_shutdown, }; @@ -130,19 +138,30 @@ static int mx8373_sdw_late_probe(struct snd_soc_card *card) return snd_soc_dapm_sync(dapm); } -int sof_sdw_mx8373_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) +int sof_sdw_maxim_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) { info->amp_num++; if (info->amp_num == 2) dai_links->init = spk_init; - info->codec_card_late_probe = mx8373_sdw_late_probe; - - dai_links->ops = &max_98373_sdw_ops; - + maxim_part_id = info->part_id; + switch (maxim_part_id) { + case SOF_SDW_PART_ID_MAX98363: + /* Default ops are set in function init_dai_link. + * called as part of function create_sdw_dailink + */ + break; + case SOF_SDW_PART_ID_MAX98373: + info->codec_card_late_probe = mx8373_sdw_late_probe; + dai_links->ops = &max_98373_sdw_ops; + break; + default: + dev_err(card->dev, "Invalid maxim_part_id %#x\n", maxim_part_id); + return -EINVAL; + } return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index 8291967f23f3..2b05e2a707de 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -27,9 +27,9 @@ static int rt711_add_codec_device_props(struct device *sdw_dev) struct fwnode_handle *fwnode; int ret; - if (!SOF_RT711_JDSRC(sof_sdw_quirk)) + if (!SOF_JACK_JDSRC(sof_sdw_quirk)) return 0; - props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_RT711_JDSRC(sof_sdw_quirk)); + props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_JACK_JDSRC(sof_sdw_quirk)); fwnode = fwnode_create_software_node(props, NULL); if (IS_ERR(fwnode)) diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c new file mode 100644 index 000000000000..84c8025d24e3 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023 Intel Corporation + +/* + * sof_sdw_rt712_sdca - Helpers to handle RT712-SDCA from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/control.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/soc-dapm.h> +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget rt712_spk_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +/* + * dapm routes for rt712 spk will be registered dynamically according + * to the number of rt712 spk used. The first two entries will be registered + * for one codec case, and the last two entries are also registered + * if two rt712s are used. + */ +static const struct snd_soc_dapm_route rt712_spk_map[] = { + { "Speaker", NULL, "rt712 SPOL" }, + { "Speaker", NULL, "rt712 SPOR" }, +}; + +static const struct snd_kcontrol_new rt712_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s spk:rt712", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt712_spk_controls, + ARRAY_SIZE(rt712_spk_controls)); + if (ret) { + dev_err(card->dev, "rt712 spk controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt712_spk_widgets, + ARRAY_SIZE(rt712_spk_widgets)); + if (ret) { + dev_err(card->dev, "rt712 spk widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt712_spk_map, ARRAY_SIZE(rt712_spk_map)); + if (ret) + dev_err(rtd->dev, "failed to add SPK map: %d\n", ret); + + return ret; +} + +int sof_sdw_rt712_spk_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + dai_links->init = rt712_spk_init; + + return 0; +} + +static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:rt712-sdca-dmic", + card->components); + if (!card->components) + return -ENOMEM; + + return 0; +} + +int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + dai_links->init = rt712_sdca_dmic_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index 7f16304d025b..623e3bebb888 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -21,16 +21,16 @@ * Note this MUST be called before snd_soc_register_card(), so that the props * are in place before the codec component driver's probe function parses them. */ -static int rt711_sdca_add_codec_device_props(struct device *sdw_dev) +static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev) { struct property_entry props[MAX_NO_PROPS] = {}; struct fwnode_handle *fwnode; int ret; - if (!SOF_RT711_JDSRC(sof_sdw_quirk)) + if (!SOF_JACK_JDSRC(sof_sdw_quirk)) return 0; - props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_RT711_JDSRC(sof_sdw_quirk)); + props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_JACK_JDSRC(sof_sdw_quirk)); fwnode = fwnode_create_software_node(props, NULL); if (IS_ERR(fwnode)) @@ -43,23 +43,27 @@ static int rt711_sdca_add_codec_device_props(struct device *sdw_dev) return ret; } -static const struct snd_soc_dapm_widget rt711_sdca_widgets[] = { +static const struct snd_soc_dapm_widget rt_sdca_jack_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), }; static const struct snd_soc_dapm_route rt711_sdca_map[] = { - /* Headphones */ { "Headphone", NULL, "rt711 HP" }, { "rt711 MIC2", NULL, "Headset Mic" }, }; -static const struct snd_kcontrol_new rt711_sdca_controls[] = { +static const struct snd_soc_dapm_route rt712_sdca_map[] = { + { "Headphone", NULL, "rt712 HP" }, + { "rt712 MIC2", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new rt_sdca_jack_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), }; -static struct snd_soc_jack_pin rt711_sdca_jack_pins[] = { +static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { { .pin = "Headphone", .mask = SND_JACK_HEADPHONE, @@ -70,7 +74,7 @@ static struct snd_soc_jack_pin rt711_sdca_jack_pins[] = { }, }; -static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) +static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -80,30 +84,38 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) int ret; card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s hs:rt711-sdca", - card->components); + "%s hs:%s-sdca", + card->components, component->name_prefix); if (!card->components) return -ENOMEM; - ret = snd_soc_add_card_controls(card, rt711_sdca_controls, - ARRAY_SIZE(rt711_sdca_controls)); + ret = snd_soc_add_card_controls(card, rt_sdca_jack_controls, + ARRAY_SIZE(rt_sdca_jack_controls)); if (ret) { - dev_err(card->dev, "rt711-sdca controls addition failed: %d\n", ret); + dev_err(card->dev, "rt sdca jack controls addition failed: %d\n", ret); return ret; } - ret = snd_soc_dapm_new_controls(&card->dapm, rt711_sdca_widgets, - ARRAY_SIZE(rt711_sdca_widgets)); + ret = snd_soc_dapm_new_controls(&card->dapm, rt_sdca_jack_widgets, + ARRAY_SIZE(rt_sdca_jack_widgets)); if (ret) { - dev_err(card->dev, "rt711-sdca widgets addition failed: %d\n", ret); + dev_err(card->dev, "rt sdca jack widgets addition failed: %d\n", ret); return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map, - ARRAY_SIZE(rt711_sdca_map)); + if (strstr(component->name_prefix, "rt711")) { + ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map, + ARRAY_SIZE(rt711_sdca_map)); + } else if (strstr(component->name_prefix, "rt712")) { + ret = snd_soc_dapm_add_routes(&card->dapm, rt712_sdca_map, + ARRAY_SIZE(rt712_sdca_map)); + } else { + dev_err(card->dev, "%s is not supported\n", component->name_prefix); + return -EINVAL; + } if (ret) { - dev_err(card->dev, "rt711-sdca map addition failed: %d\n", ret); + dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret); return ret; } @@ -112,8 +124,8 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->sdw_headset, - rt711_sdca_jack_pins, - ARRAY_SIZE(rt711_sdca_jack_pins)); + rt_sdca_jack_pins, + ARRAY_SIZE(rt_sdca_jack_pins)); if (ret) { dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", ret); @@ -136,24 +148,27 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_rt711_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) +int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct mc_private *ctx = snd_soc_card_get_drvdata(card); if (!ctx->headset_codec_dev) return 0; + if (!SOF_JACK_JDSRC(sof_sdw_quirk)) + return 0; + device_remove_software_node(ctx->headset_codec_dev); put_device(ctx->headset_codec_dev); return 0; } -int sof_sdw_rt711_sdca_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) +int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) { struct mc_private *ctx = snd_soc_card_get_drvdata(card); struct device *sdw_dev; @@ -170,14 +185,14 @@ int sof_sdw_rt711_sdca_init(struct snd_soc_card *card, if (!sdw_dev) return -EPROBE_DEFER; - ret = rt711_sdca_add_codec_device_props(sdw_dev); + ret = rt_sdca_jack_add_codec_device_props(sdw_dev); if (ret < 0) { put_device(sdw_dev); return ret; } ctx->headset_codec_dev = sdw_dev; - dai_links->init = rt711_sdca_rtd_init; + dai_links->init = rt_sdca_jack_rtd_init; return 0; } diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index ffd9c583dab1..0aef718e82b2 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -167,13 +167,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) { struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); @@ -233,8 +226,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (!links[id].name) return NULL; links[id].id = id; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].dpcm_capture = 1; @@ -331,8 +324,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (!idisp_components[i - 1].dai_name) goto devm_err; } else { - idisp_components[i - 1].name = "snd-soc-dummy"; - idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + idisp_components[i - 1] = asoc_dummy_dlc; } links[id].codecs = &idisp_components[i - 1]; @@ -360,8 +352,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); if (!links[id].name) goto devm_err; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].dpcm_playback = 1; @@ -471,6 +463,15 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT | SOF_CS35L41_SPEAKER_AMP_PRESENT), }, + { + .name = "adl_lt6911_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_NO_OF_HDMI_PLAYBACK(3) | + SOF_HDMI_PLAYBACK_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); @@ -486,7 +487,7 @@ static struct platform_driver sof_ssp_amp_driver = { module_platform_driver(sof_ssp_amp_driver); MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver"); -MODULE_AUTHOR("balamurugan.c <balamurugan.c@intel.com>"); +MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>"); MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 41054cf09ec9..07aa37dd90e9 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -10,6 +10,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-tgl-match.o soc-acpi-intel-ehl-match.o \ soc-acpi-intel-jsl-match.o soc-acpi-intel-adl-match.o \ soc-acpi-intel-rpl-match.o soc-acpi-intel-mtl-match.o \ + soc-acpi-intel-lnl-match.o \ soc-acpi-intel-hda-match.o \ soc-acpi-intel-sdw-mockup-match.o diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index d8c80041388a..bcd66e0094b4 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -133,6 +133,15 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = { + { + .adr = 0x000130025D131601ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt1316-1" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = { { .adr = 0x000230025D131601ull, @@ -312,6 +321,20 @@ static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] = {} }; +static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link1_rt714_link0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_single_adr), + .adr_d = rt1316_1_single_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link3[] = { { .mask = BIT(2), @@ -557,12 +580,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .quirk_data = &adl_max98360a_amp, .sof_tplg_filename = "sof-adl-max98360a-cs42l42.tplg", }, - /* place amp-only boards in the end of table */ - { - .id = "CSC3541", - .drv_name = "adl_cs35l41", - .sof_tplg_filename = "sof-adl-cs35l41.tplg", - }, { .comp_ids = &essx_83x6, .drv_name = "adl_es83x6_c1_h02", @@ -578,6 +595,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + /* place amp-only boards in the end of table */ + { + .id = "CSC3541", + .drv_name = "adl_cs35l41", + .sof_tplg_filename = "sof-adl-cs35l41.tplg", + }, + { + .id = "INTC10B0", + .drv_name = "adl_lt6911_hdmi_ssp", + .sof_tplg_filename = "sof-adl-nocodec-hdmi-ssp02.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); @@ -621,6 +649,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg", }, { + .link_mask = 0x3, /* rt1316 on link1 & rt714 on link0 */ + .links = adl_sdw_rt1316_link1_rt714_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-adl-rt1316-l1-mono-rt714-l0.tplg", + }, + { .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */ .links = adl_sdw_rt1316_link12_rt714_link0, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c new file mode 100644 index 000000000000..9f35b77deb11 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * soc-acpi-intel-lnl-match.c - tables and support for LNL ACPI enumeration. + * + * Copyright (c) 2023, Intel Corporation. All rights reserved. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "soc-acpi-intel-sdw-mockup-match.h" + +struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_machines); + +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, +}; + +static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { + { + .adr = 0x000030025D071101ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt711" + } +}; + +static const struct snd_soc_acpi_link_adr lnl_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + {} +}; + +/* this table is used when there is no I2S codec present */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + /* mockup tests need to be first */ + { + .link_mask = GENMASK(3, 0), + .links = sdw_mockup_headset_2amps_mic, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = BIT(0) | BIT(1) | BIT(3), + .links = sdw_mockup_headset_1amp_mic, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711-rt1308-mono-rt715.tplg", + }, + { + .link_mask = GENMASK(2, 0), + .links = sdw_mockup_mic_headset_1amp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg", + }, + { + .link_mask = BIT(0), + .links = lnl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 7911c3af8071..ed9821adc1d9 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -20,6 +20,11 @@ static const struct snd_soc_acpi_codecs mtl_max98360a_amp = { .codecs = {"MX98360A"} }; +static const struct snd_soc_acpi_codecs mtl_rt1019p_amp = { + .num_codecs = 1, + .codecs = {"RTL1019"} +}; + static const struct snd_soc_acpi_codecs mtl_rt5682_rt5682s_hp = { .num_codecs = 2, .codecs = {"10EC5682", "RTL5682"}, @@ -40,6 +45,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { .quirk_data = &mtl_max98360a_amp, .sof_tplg_filename = "sof-mtl-max98360a-rt5682.tplg", }, + { + .comp_ids = &mtl_rt5682_rt5682s_hp, + .drv_name = "mtl_rt1019_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &mtl_rt1019p_amp, + .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_machines); @@ -65,6 +77,21 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -74,6 +101,24 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = { + { + .adr = 0x000030025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_endpoints), + .endpoints = rt712_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { + { + .adr = 0x000330025D171201ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt712-dmic" + } +}; + static const struct snd_soc_acpi_adr_device mx8373_0_adr[] = { { .adr = 0x000023019F837300ull, @@ -98,6 +143,47 @@ static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { + { + .adr = 0x000230025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1316-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = { + { + .adr = 0x000331025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" + } +}; + +static const struct snd_soc_acpi_adr_device rt714_1_adr[] = { + { + .adr = 0x000130025D071401ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt714" + } +}; + +static const struct snd_soc_acpi_link_adr mtl_712_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt712_0_single_adr), + .adr_d = rt712_0_single_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1712_3_single_adr), + .adr_d = rt1712_3_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = { /* Expected order: jack -> amp */ { @@ -122,6 +208,69 @@ static const struct snd_soc_acpi_link_adr mtl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group1_adr), + .adr_d = rt1316_2_group1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1316_3_group1_adr), + .adr_d = rt1316_3_group1_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt714_1_adr), + .adr_d = rt714_1_adr, + }, + {} +}; + +static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = { + { + .adr = 0x000230019F836300ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "Left" + }, + { + .adr = 0x000231019F836300ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "Right" + } +}; + +static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = { + { + .adr = 0x00001001FA424200ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "cs42l42" + } +}; + +static const struct snd_soc_acpi_link_adr cs42l42_link0_max98363_link2[] = { + /* Expected order: jack -> amp */ + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l42_0_adr), + .adr_d = cs42l42_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(mx8363_2_adr), + .adr_d = mx8363_2_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { /* mockup tests need to be first */ @@ -144,6 +293,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-rt715-rt711-rt1308-mono.tplg", }, { + .link_mask = BIT(3) | BIT(0), + .links = mtl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", + }, + { + .link_mask = GENMASK(3, 0), + .links = mtl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", + }, + { .link_mask = BIT(0), .links = mtl_rvp, .drv_name = "sof_sdw", @@ -155,6 +316,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", }, + { + .link_mask = BIT(0) | BIT(2), + .links = cs42l42_link0_max98363_link2, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index 749d371a86ae..302a08018572 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -179,6 +179,30 @@ static const struct snd_soc_acpi_link_adr rpl_sdca_3_in_1[] = { {} }; +static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1316_link12_rt714_link3[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group1_adr), + .adr_d = rt1316_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group1_adr), + .adr_d = rt1316_2_group1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt714_3_adr), + .adr_d = rt714_3_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = { { .mask = BIT(2), @@ -303,6 +327,16 @@ static const struct snd_soc_acpi_codecs rpl_max98360a_amp = { .codecs = {"MX98360A"}, }; +static const struct snd_soc_acpi_codecs rpl_max98373_amp = { + .num_codecs = 1, + .codecs = {"MX98373"} +}; + +static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = { + .num_codecs = 1, + .codecs = {"RTL1019"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { { .comp_ids = &rpl_rt5682_hp, @@ -311,6 +345,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { .quirk_data = &rpl_max98360a_amp, .sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg", }, + { + .id = "10508825", + .drv_name = "rpl_max98373_8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_max98373_amp, + .sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg", + }, + { + .comp_ids = &rpl_rt5682_hp, + .drv_name = "rpl_rt1019_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_rt1019p_amp, + .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines); @@ -331,6 +379,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = { }, { .link_mask = 0xF, /* 4 active links required */ + .links = rpl_sdw_rt711_link0_rt1316_link12_rt714_link3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-rpl-rt711-l0-rt1316-l12-rt714-l3.tplg", + }, + { + .link_mask = 0xF, /* 4 active links required */ .links = rpl_sdw_rt711_link0_rt1318_link12_rt714_link3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12-rt714-l3.tplg", diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index ef19150e7b2e..5804926c8b56 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -41,6 +41,21 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { .adr = 0x000020025D071100ull, @@ -170,6 +185,24 @@ static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = { + { + .adr = 0x000030025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_endpoints), + .endpoints = rt712_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt1712_1_single_adr[] = { + { + .adr = 0x000130025D171201ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt712-dmic" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { { .adr = 0x000131025D131601ull, /* unique ID is set for some reason */ @@ -353,6 +386,20 @@ static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca_mono[] = { {} }; +static const struct snd_soc_acpi_link_adr tgl_712_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt712_0_single_adr), + .adr_d = rt712_0_single_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1712_1_single_adr), + .adr_d = rt1712_1_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_codecs tgl_max98373_amp = { .num_codecs = 1, .codecs = {"MX98373"} @@ -436,6 +483,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { .sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg", }, { + .link_mask = 0xF, /* 4 active links required */ + .links = tgl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-tgl-rt712.tplg", + }, + { .link_mask = 0x7, .links = tgl_sdw_rt711_link1_rt1308_link2_rt715_link0, .drv_name = "sof_sdw", diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index d0f6c945d9ae..578af21769c9 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -375,6 +375,14 @@ static const struct i2s_soc_info jz4760_i2s_soc_info = { .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), }; +static const struct i2s_soc_info x1000_i2s_soc_info = { + .dai = &jz4740_i2s_dai, + .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), + .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), + .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8), + .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8), +}; + static struct snd_soc_dai_driver jz4770_i2s_dai = { .probe = jz4740_i2s_dai_probe, .playback = { @@ -486,6 +494,7 @@ static const struct of_device_id jz4740_of_matches[] = { { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info }, { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info }, { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info }, + { .compatible = "ingenic,x1000-i2s", .data = &x1000_i2s_soc_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, jz4740_of_matches); diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig new file mode 100644 index 000000000000..b8d7e2bade24 --- /dev/null +++ b/sound/soc/loongson/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "SoC Audio for Loongson CPUs" + depends on LOONGARCH || COMPILE_TEST + +config SND_SOC_LOONGSON_I2S_PCI + tristate "Loongson I2S-PCI Device Driver" + select REGMAP_MMIO + depends on PCI + help + Say Y or M if you want to add support for I2S driver for + Loongson I2S controller. + + The controller is found in loongson bridge chips or SoCs, + and work as a PCI device. + +config SND_SOC_LOONGSON_CARD + tristate "Loongson Sound Card Driver" + select SND_SOC_LOONGSON_I2S_PCI + depends on PCI + help + Say Y or M if you want to add support for SoC audio using + loongson I2S controller. + + The driver add support for ALSA SoC Audio support using + loongson I2S controller. + +endmenu diff --git a/sound/soc/loongson/Makefile b/sound/soc/loongson/Makefile new file mode 100644 index 000000000000..601a905a4860 --- /dev/null +++ b/sound/soc/loongson/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +#Platform Support +snd-soc-loongson-i2s-pci-objs := loongson_i2s_pci.o loongson_i2s.o loongson_dma.o +obj-$(CONFIG_SND_SOC_LOONGSON_I2S_PCI) += snd-soc-loongson-i2s-pci.o + +#Machine Support +snd-soc-loongson-card-objs := loongson_card.o +obj-$(CONFIG_SND_SOC_LOONGSON_CARD) += snd-soc-loongson-card.o diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c new file mode 100644 index 000000000000..9ded16329747 --- /dev/null +++ b/sound/soc/loongson/loongson_card.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Loongson ASoC Audio Machine driver +// +// Copyright (C) 2023 Loongson Technology Corporation Limited +// Author: Yingkun Meng <mengyingkun@loongson.cn> +// + +#include <linux/module.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <sound/pcm_params.h> + +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +struct loongson_card_data { + struct snd_soc_card snd_card; + unsigned int mclk_fs; +}; + +static int loongson_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct loongson_card_data *ls_card = snd_soc_card_get_drvdata(rtd->card); + int ret, mclk; + + if (ls_card->mclk_fs) { + mclk = ls_card->mclk_fs * params_rate(params); + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(codec_dai->dev, "cpu_dai clock not set\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "codec_dai clock not set\n"); + return ret; + } + } + return 0; +} + +static const struct snd_soc_ops loongson_ops = { + .hw_params = loongson_card_hw_params, +}; + +SND_SOC_DAILINK_DEFS(analog, + DAILINK_COMP_ARRAY(COMP_CPU("loongson-i2s")), + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link loongson_dai_links[] = { + { + .name = "Loongson Audio Port", + .stream_name = "Loongson Audio", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBC_CFC, + SND_SOC_DAILINK_REG(analog), + .ops = &loongson_ops, + }, +}; + +static int loongson_card_parse_acpi(struct loongson_card_data *data) +{ + struct snd_soc_card *card = &data->snd_card; + struct fwnode_handle *fwnode = card->dev->fwnode; + struct fwnode_reference_args args; + const char *codec_dai_name; + struct acpi_device *adev; + struct device *phy_dev; + int ret, i; + + /* fixup platform name based on reference node */ + memset(&args, 0, sizeof(args)); + ret = acpi_node_get_property_reference(fwnode, "cpu", 0, &args); + if (ret || !is_acpi_device_node(args.fwnode)) { + dev_err(card->dev, "No matching phy in ACPI table\n"); + return ret ?: -ENOENT; + } + adev = to_acpi_device_node(args.fwnode); + phy_dev = acpi_get_first_physical_node(adev); + if (!phy_dev) + return -EPROBE_DEFER; + for (i = 0; i < card->num_links; i++) + loongson_dai_links[i].platforms->name = dev_name(phy_dev); + + /* fixup codec name based on reference node */ + memset(&args, 0, sizeof(args)); + ret = acpi_node_get_property_reference(fwnode, "codec", 0, &args); + if (ret || !is_acpi_device_node(args.fwnode)) { + dev_err(card->dev, "No matching phy in ACPI table\n"); + return ret ?: -ENOENT; + } + adev = to_acpi_device_node(args.fwnode); + snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); + for (i = 0; i < card->num_links; i++) + loongson_dai_links[i].codecs->name = codec_name; + + device_property_read_string(card->dev, "codec-dai-name", + &codec_dai_name); + for (i = 0; i < card->num_links; i++) + loongson_dai_links[i].codecs->dai_name = codec_dai_name; + + return 0; +} + +static int loongson_card_parse_of(struct loongson_card_data *data) +{ + struct device_node *cpu, *codec; + struct snd_soc_card *card = &data->snd_card; + struct device *dev = card->dev; + int ret, i; + + cpu = of_get_child_by_name(dev->of_node, "cpu"); + if (!cpu) { + dev_err(dev, "platform property missing or invalid\n"); + return -EINVAL; + } + codec = of_get_child_by_name(dev->of_node, "codec"); + if (!codec) { + dev_err(dev, "audio-codec property missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < card->num_links; i++) { + ret = snd_soc_of_get_dlc(cpu, NULL, loongson_dai_links[i].cpus, 0); + if (ret < 0) { + dev_err(dev, "getting cpu dlc error (%d)\n", ret); + goto err; + } + + ret = snd_soc_of_get_dlc(codec, NULL, loongson_dai_links[i].codecs, 0); + if (ret < 0) { + dev_err(dev, "getting codec dlc error (%d)\n", ret); + goto err; + } + } + + of_node_put(cpu); + of_node_put(codec); + + return 0; + +err: + of_node_put(cpu); + of_node_put(codec); + return ret; +} + +static int loongson_asoc_card_probe(struct platform_device *pdev) +{ + struct loongson_card_data *ls_priv; + struct snd_soc_card *card; + int ret; + + ls_priv = devm_kzalloc(&pdev->dev, sizeof(*ls_priv), GFP_KERNEL); + if (!ls_priv) + return -ENOMEM; + + card = &ls_priv->snd_card; + + card->dev = &pdev->dev; + card->owner = THIS_MODULE; + card->dai_link = loongson_dai_links; + card->num_links = ARRAY_SIZE(loongson_dai_links); + snd_soc_card_set_drvdata(card, ls_priv); + + ret = device_property_read_string(&pdev->dev, "model", &card->name); + if (ret) { + dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); + return ret; + } + ret = device_property_read_u32(&pdev->dev, "mclk-fs", &ls_priv->mclk_fs); + if (ret) { + dev_err(&pdev->dev, "Error parsing mclk-fs: %d\n", ret); + return ret; + } + + if (has_acpi_companion(&pdev->dev)) + ret = loongson_card_parse_acpi(ls_priv); + else + ret = loongson_card_parse_of(ls_priv); + if (ret < 0) + return ret; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + + return ret; +} + +static const struct of_device_id loongson_asoc_dt_ids[] = { + { .compatible = "loongson,ls-audio-card" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); + +static struct platform_driver loongson_audio_driver = { + .probe = loongson_asoc_card_probe, + .driver = { + .name = "loongson-asoc-card", + .pm = &snd_soc_pm_ops, + .of_match_table = of_match_ptr(loongson_asoc_dt_ids), + }, +}; +module_platform_driver(loongson_audio_driver); + +MODULE_DESCRIPTION("Loongson ASoc Sound Card driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/loongson/loongson_dma.c b/sound/soc/loongson/loongson_dma.c new file mode 100644 index 000000000000..65b6719e61c5 --- /dev/null +++ b/sound/soc/loongson/loongson_dma.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Loongson ALSA SoC Platform (DMA) driver +// +// Copyright (C) 2023 Loongson Technology Corporation Limited +// Author: Yingkun Meng <mengyingkun@loongson.cn> +// + +#include <linux/module.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "loongson_i2s.h" + +/* DMA dma_order Register */ +#define DMA_ORDER_STOP (1 << 4) /* DMA stop */ +#define DMA_ORDER_START (1 << 3) /* DMA start */ +#define DMA_ORDER_ASK_VALID (1 << 2) /* DMA ask valid flag */ +#define DMA_ORDER_AXI_UNCO (1 << 1) /* Uncache access */ +#define DMA_ORDER_ADDR_64 (1 << 0) /* 64bits address support */ + +#define DMA_ORDER_ASK_MASK (~0x1fUL) /* Ask addr mask */ +#define DMA_ORDER_CTRL_MASK (0x0fUL) /* Control mask */ + +/* + * DMA registers descriptor. + */ +struct loongson_dma_desc { + u32 order; /* Next descriptor address register */ + u32 saddr; /* Source address register */ + u32 daddr; /* Device address register */ + u32 length; /* Total length register */ + u32 step_length; /* Memory stride register */ + u32 step_times; /* Repeat time register */ + u32 cmd; /* Command register */ + u32 stats; /* Status register */ + u32 order_hi; /* Next descriptor high address register */ + u32 saddr_hi; /* High source address register */ + u32 res[6]; /* Reserved */ +} __packed; + +struct loongson_runtime_data { + struct loongson_dma_data *dma_data; + + struct loongson_dma_desc *dma_desc_arr; + dma_addr_t dma_desc_arr_phy; + int dma_desc_arr_size; + + struct loongson_dma_desc *dma_pos_desc; + dma_addr_t dma_pos_desc_phy; +}; + +static const struct snd_pcm_hardware ls_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE, + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + .period_bytes_min = 128, + .period_bytes_max = 128 * 1024, + .periods_min = 1, + .periods_max = PAGE_SIZE / sizeof(struct loongson_dma_desc), + .buffer_bytes_max = 1024 * 1024, +}; + +static struct +loongson_dma_desc *dma_desc_save(struct loongson_runtime_data *prtd) +{ + void __iomem *order_reg = prtd->dma_data->order_addr; + u64 val; + + val = (u64)prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK; + val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK); + val |= DMA_ORDER_ASK_VALID; + writeq(val, order_reg); + + while (readl(order_reg) & DMA_ORDER_ASK_VALID) + udelay(2); + + return prtd->dma_pos_desc; +} + +static int loongson_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct loongson_runtime_data *prtd = substream->runtime->private_data; + struct device *dev = substream->pcm->card->dev; + void __iomem *order_reg = prtd->dma_data->order_addr; + u64 val; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK; + if (dev->coherent_dma_mask == DMA_BIT_MASK(64)) + val |= DMA_ORDER_ADDR_64; + else + val &= ~DMA_ORDER_ADDR_64; + val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK); + val |= DMA_ORDER_START; + writeq(val, order_reg); + + while ((readl(order_reg) & DMA_ORDER_START)) + udelay(2); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma_desc_save(prtd); + + /* dma stop */ + val = readq(order_reg) | DMA_ORDER_STOP; + writeq(val, order_reg); + udelay(1000); + + break; + default: + dev_err(dev, "Invalid pcm trigger operation\n"); + return -EINVAL; + } + + return ret; +} + +static int loongson_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct device *dev = substream->pcm->card->dev; + struct loongson_runtime_data *prtd = runtime->private_data; + size_t buf_len = params_buffer_bytes(params); + size_t period_len = params_period_bytes(params); + dma_addr_t order_addr, mem_addr; + struct loongson_dma_desc *desc; + u32 num_periods; + int i; + + if (buf_len % period_len) { + dev_err(dev, "buf len not multiply of period len\n"); + return -EINVAL; + } + + num_periods = buf_len / period_len; + if (!num_periods || num_periods > prtd->dma_desc_arr_size) { + dev_err(dev, "dma data too small or too big\n"); + return -EINVAL; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = buf_len; + + /* initialize dma descriptor array */ + mem_addr = runtime->dma_addr; + order_addr = prtd->dma_desc_arr_phy; + for (i = 0; i < num_periods; i++) { + desc = &prtd->dma_desc_arr[i]; + + /* next descriptor physical address */ + order_addr += sizeof(*desc); + desc->order = lower_32_bits(order_addr | BIT(0)); + desc->order_hi = upper_32_bits(order_addr); + + desc->saddr = lower_32_bits(mem_addr); + desc->saddr_hi = upper_32_bits(mem_addr); + desc->daddr = prtd->dma_data->dev_addr; + + desc->cmd = BIT(0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + desc->cmd |= BIT(12); + + desc->length = period_len >> 2; + desc->step_length = 0; + desc->step_times = 1; + + mem_addr += period_len; + } + desc = &prtd->dma_desc_arr[num_periods - 1]; + desc->order = lower_32_bits(prtd->dma_desc_arr_phy | BIT(0)); + desc->order_hi = upper_32_bits(prtd->dma_desc_arr_phy); + + /* init position descriptor */ + *prtd->dma_pos_desc = *prtd->dma_desc_arr; + + return 0; +} + +static snd_pcm_uframes_t +loongson_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loongson_runtime_data *prtd = runtime->private_data; + struct loongson_dma_desc *desc; + snd_pcm_uframes_t x; + u64 addr; + + desc = dma_desc_save(prtd); + addr = ((u64)desc->saddr_hi << 32) | desc->saddr; + + x = bytes_to_frames(runtime, addr - runtime->dma_addr); + if (x == runtime->buffer_size) + x = 0; + return x; +} + +static irqreturn_t loongson_pcm_dma_irq(int irq, void *devid) +{ + struct snd_pcm_substream *substream = devid; + + snd_pcm_period_elapsed(substream); + return IRQ_HANDLED; +} + +static int loongson_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_card *card = substream->pcm->card; + struct loongson_runtime_data *prtd; + struct loongson_dma_data *dma_data; + int ret; + + /* + * For mysterious reasons (and despite what the manual says) + * playback samples are lost if the DMA count is not a multiple + * of the DMA burst size. Let's add a rule to enforce that. + */ + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128); + snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + snd_soc_set_runtime_hwparams(substream, &ls_pcm_hardware); + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->dma_desc_arr = dma_alloc_coherent(card->dev, PAGE_SIZE, + &prtd->dma_desc_arr_phy, + GFP_KERNEL); + if (!prtd->dma_desc_arr) { + ret = -ENOMEM; + goto desc_err; + } + prtd->dma_desc_arr_size = PAGE_SIZE / sizeof(*prtd->dma_desc_arr); + + prtd->dma_pos_desc = dma_alloc_coherent(card->dev, + sizeof(*prtd->dma_pos_desc), + &prtd->dma_pos_desc_phy, + GFP_KERNEL); + if (!prtd->dma_pos_desc) { + ret = -ENOMEM; + goto pos_err; + } + + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + prtd->dma_data = dma_data; + + substream->runtime->private_data = prtd; + + return 0; +pos_err: + dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr, + prtd->dma_desc_arr_phy); +desc_err: + kfree(prtd); + + return ret; +} + +static int loongson_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_card *card = substream->pcm->card; + struct loongson_runtime_data *prtd = substream->runtime->private_data; + + dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr, + prtd->dma_desc_arr_phy); + + dma_free_coherent(card->dev, sizeof(*prtd->dma_pos_desc), + prtd->dma_pos_desc, prtd->dma_pos_desc_phy); + + kfree(prtd); + return 0; +} + +static int loongson_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int loongson_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm_substream *substream; + struct loongson_dma_data *dma_data; + unsigned int i; + int ret; + + for_each_pcm_streams(i) { + substream = rtd->pcm->streams[i].substream; + if (!substream) + continue; + + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), + substream); + ret = devm_request_irq(card->dev, dma_data->irq, + loongson_pcm_dma_irq, + IRQF_TRIGGER_HIGH, LS_I2S_DRVNAME, + substream); + if (ret < 0) { + dev_err(card->dev, "request irq for DMA failed\n"); + return ret; + } + } + + return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + card->dev, + ls_pcm_hardware.buffer_bytes_max); +} + +const struct snd_soc_component_driver loongson_i2s_component = { + .name = LS_I2S_DRVNAME, + .open = loongson_pcm_open, + .close = loongson_pcm_close, + .hw_params = loongson_pcm_hw_params, + .trigger = loongson_pcm_trigger, + .pointer = loongson_pcm_pointer, + .mmap = loongson_pcm_mmap, + .pcm_construct = loongson_pcm_new, +}; diff --git a/sound/soc/loongson/loongson_dma.h b/sound/soc/loongson/loongson_dma.h new file mode 100644 index 000000000000..073ee8c0c046 --- /dev/null +++ b/sound/soc/loongson/loongson_dma.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ALSA ASoC interface for the Loongson platform + * + * Copyright (C) 2023 Loongson Technology Corporation Limited + * Author: Yingkun Meng <mengyingkun@loongson.cn> + */ + +#ifndef _LOONGSON_DMA_H +#define _LOONGSON_DMA_H + +#include <sound/soc.h> + +extern const struct snd_soc_component_driver loongson_i2s_component; + +#endif diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c new file mode 100644 index 000000000000..b919f0fe8361 --- /dev/null +++ b/sound/soc/loongson/loongson_i2s.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Common functions for loongson I2S controller driver +// +// Copyright (C) 2023 Loongson Technology Corporation Limited. +// Author: Yingkun Meng <mengyingkun@loongson.cn> +// + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> +#include <sound/soc.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "loongson_i2s.h" + +#define LOONGSON_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static int loongson_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN, + I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN); + else + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN, + I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN, 0); + else + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN, 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int loongson_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 clk_rate = i2s->clk_rate; + u32 sysclk = i2s->sysclk; + u32 bits = params_width(params); + u32 chans = params_channels(params); + u32 fs = params_rate(params); + u32 bclk_ratio, mclk_ratio; + u32 mclk_ratio_frac; + u32 val = 0; + + switch (i2s->rev_id) { + case 0: + bclk_ratio = DIV_ROUND_CLOSEST(clk_rate, + (bits * chans * fs * 2)) - 1; + mclk_ratio = DIV_ROUND_CLOSEST(clk_rate, (sysclk * 2)) - 1; + + /* According to 2k1000LA user manual, set bits == depth */ + val |= (bits << 24); + val |= (bits << 16); + val |= (bclk_ratio << 8); + val |= mclk_ratio; + regmap_write(i2s->regmap, LS_I2S_CFG, val); + + break; + case 1: + bclk_ratio = DIV_ROUND_CLOSEST(sysclk, + (bits * chans * fs * 2)) - 1; + mclk_ratio = clk_rate / sysclk; + mclk_ratio_frac = DIV_ROUND_CLOSEST_ULL(((u64)clk_rate << 16), + sysclk) - (mclk_ratio << 16); + + regmap_read(i2s->regmap, LS_I2S_CFG, &val); + val |= (bits << 24); + val |= (bclk_ratio << 8); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + val |= (bits << 16); + else + val |= bits; + regmap_write(i2s->regmap, LS_I2S_CFG, val); + + val = (mclk_ratio_frac << 16) | mclk_ratio; + regmap_write(i2s->regmap, LS_I2S_CFG1, val); + + break; + default: + dev_err(i2s->dev, "I2S revision invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int loongson_i2s_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + i2s->sysclk = freq; + + return 0; +} + +static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 val; + int ret; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_RIGHT_J: + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MSB, + I2S_CTRL_MSB); + break; + default: + return -EINVAL; + } + + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + case SND_SOC_DAIFMT_BP_FC: + /* Enable master mode */ + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER, + I2S_CTRL_MASTER); + if (i2s->rev_id == 1) { + ret = regmap_read_poll_timeout_atomic(i2s->regmap, + LS_I2S_CTRL, val, + val & I2S_CTRL_CLK_READY, + 10, 500000); + if (ret < 0) + dev_warn(dai->dev, "wait BCLK ready timeout\n"); + } + break; + case SND_SOC_DAIFMT_BC_FP: + /* Enable MCLK */ + if (i2s->rev_id == 1) { + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_MCLK_EN, + I2S_CTRL_MCLK_EN); + ret = regmap_read_poll_timeout_atomic(i2s->regmap, + LS_I2S_CTRL, val, + val & I2S_CTRL_MCLK_READY, + 10, 500000); + if (ret < 0) + dev_warn(dai->dev, "wait MCLK ready timeout\n"); + } + break; + case SND_SOC_DAIFMT_BP_FP: + /* Enable MCLK */ + if (i2s->rev_id == 1) { + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, + I2S_CTRL_MCLK_EN, + I2S_CTRL_MCLK_EN); + ret = regmap_read_poll_timeout_atomic(i2s->regmap, + LS_I2S_CTRL, val, + val & I2S_CTRL_MCLK_READY, + 10, 500000); + if (ret < 0) + dev_warn(dai->dev, "wait MCLK ready timeout\n"); + } + + /* Enable master mode */ + regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER, + I2S_CTRL_MASTER); + if (i2s->rev_id == 1) { + ret = regmap_read_poll_timeout_atomic(i2s->regmap, + LS_I2S_CTRL, val, + val & I2S_CTRL_CLK_READY, + 10, 500000); + if (ret < 0) + dev_warn(dai->dev, "wait BCLK ready timeout\n"); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops loongson_i2s_dai_ops = { + .trigger = loongson_i2s_trigger, + .hw_params = loongson_i2s_hw_params, + .set_sysclk = loongson_i2s_set_dai_sysclk, + .set_fmt = loongson_i2s_set_fmt, +}; + +static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + + snd_soc_dai_init_dma_data(cpu_dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); + snd_soc_dai_set_drvdata(cpu_dai, i2s); + + return 0; +} + +struct snd_soc_dai_driver loongson_i2s_dai = { + .name = "loongson-i2s", + .probe = loongson_i2s_dai_probe, + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = LOONGSON_I2S_FORMATS, + }, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = LOONGSON_I2S_FORMATS, + }, + .ops = &loongson_i2s_dai_ops, + .symmetric_rate = 1, +}; + +static int i2s_suspend(struct device *dev) +{ + struct loongson_i2s *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, true); + + return 0; +} + +static int i2s_resume(struct device *dev) +{ + struct loongson_i2s *i2s = dev_get_drvdata(dev); + int ret; + + regcache_cache_only(i2s->regmap, false); + regcache_mark_dirty(i2s->regmap); + ret = regcache_sync(i2s->regmap); + + return ret; +} + +const struct dev_pm_ops loongson_i2s_pm = { + SYSTEM_SLEEP_PM_OPS(i2s_suspend, i2s_resume) +}; diff --git a/sound/soc/loongson/loongson_i2s.h b/sound/soc/loongson/loongson_i2s.h new file mode 100644 index 000000000000..89492eebf834 --- /dev/null +++ b/sound/soc/loongson/loongson_i2s.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ALSA I2S interface for the Loongson platform + * + * Copyright (C) 2023 Loongson Technology Corporation Limited + * Author: Yingkun Meng <mengyingkun@loongson.cn> + */ + +#ifndef _LOONGSON_I2S_H +#define _LOONGSON_I2S_H + +#include <linux/regmap.h> +#include <sound/dmaengine_pcm.h> + +/* I2S Common Registers */ +#define LS_I2S_VER 0x00 /* I2S Version */ +#define LS_I2S_CFG 0x04 /* I2S Config */ +#define LS_I2S_CTRL 0x08 /* I2S Control */ +#define LS_I2S_RX_DATA 0x0C /* I2S DMA RX Address */ +#define LS_I2S_TX_DATA 0x10 /* I2S DMA TX Address */ + +/* 2K2000 I2S Specify Registers */ +#define LS_I2S_CFG1 0x14 /* I2S Config1 */ + +/* 7A2000 I2S Specify Registers */ +#define LS_I2S_TX_ORDER 0x100 /* TX DMA Order */ +#define LS_I2S_RX_ORDER 0x110 /* RX DMA Order */ + +/* Loongson I2S Control Register */ +#define I2S_CTRL_MCLK_READY (1 << 16) /* MCLK ready */ +#define I2S_CTRL_MASTER (1 << 15) /* Master mode */ +#define I2S_CTRL_MSB (1 << 14) /* MSB bit order */ +#define I2S_CTRL_RX_EN (1 << 13) /* RX enable */ +#define I2S_CTRL_TX_EN (1 << 12) /* TX enable */ +#define I2S_CTRL_RX_DMA_EN (1 << 11) /* DMA RX enable */ +#define I2S_CTRL_CLK_READY (1 << 8) /* BCLK ready */ +#define I2S_CTRL_TX_DMA_EN (1 << 7) /* DMA TX enable */ +#define I2S_CTRL_RESET (1 << 4) /* Controller soft reset */ +#define I2S_CTRL_MCLK_EN (1 << 3) /* Enable MCLK */ +#define I2S_CTRL_RX_INT_EN (1 << 1) /* RX interrupt enable */ +#define I2S_CTRL_TX_INT_EN (1 << 0) /* TX interrupt enable */ + +#define LS_I2S_DRVNAME "loongson-i2s" + +struct loongson_dma_data { + dma_addr_t dev_addr; /* device physical address for DMA */ + void __iomem *order_addr; /* DMA order register */ + int irq; /* DMA irq */ +}; + +struct loongson_i2s { + struct device *dev; + union { + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct loongson_dma_data tx_dma_data; + }; + union { + struct snd_dmaengine_dai_dma_data capture_dma_data; + struct loongson_dma_data rx_dma_data; + }; + struct regmap *regmap; + void __iomem *reg_base; + u32 rev_id; + u32 clk_rate; + u32 sysclk; +}; + +extern const struct dev_pm_ops loongson_i2s_pm; +extern struct snd_soc_dai_driver loongson_i2s_dai; + +#endif diff --git a/sound/soc/loongson/loongson_i2s_pci.c b/sound/soc/loongson/loongson_i2s_pci.c new file mode 100644 index 000000000000..fa90361865c6 --- /dev/null +++ b/sound/soc/loongson/loongson_i2s_pci.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// loongson_i2s_pci.c -- Loongson I2S controller driver +// +// Copyright (C) 2023 Loongson Technology Corporation Limited +// Author: Yingkun Meng <mengyingkun@loongson.cn> +// + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <sound/soc.h> +#include "loongson_i2s.h" +#include "loongson_dma.h" + +static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LS_I2S_CFG: + case LS_I2S_CTRL: + case LS_I2S_RX_DATA: + case LS_I2S_TX_DATA: + case LS_I2S_CFG1: + return true; + default: + return false; + }; +} + +static bool loongson_i2s_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LS_I2S_VER: + case LS_I2S_CFG: + case LS_I2S_CTRL: + case LS_I2S_RX_DATA: + case LS_I2S_TX_DATA: + case LS_I2S_CFG1: + return true; + default: + return false; + }; +} + +static bool loongson_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LS_I2S_CFG: + case LS_I2S_CTRL: + case LS_I2S_RX_DATA: + case LS_I2S_TX_DATA: + case LS_I2S_CFG1: + return true; + default: + return false; + }; +} + +static const struct regmap_config loongson_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LS_I2S_CFG1, + .writeable_reg = loongson_i2s_wr_reg, + .readable_reg = loongson_i2s_rd_reg, + .volatile_reg = loongson_i2s_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int loongson_i2s_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pid) +{ + const struct fwnode_handle *fwnode = pdev->dev.fwnode; + struct loongson_dma_data *tx_data, *rx_data; + struct loongson_i2s *i2s; + int ret; + + if (pcim_enable_device(pdev)) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + i2s->rev_id = pdev->revision; + i2s->dev = &pdev->dev; + pci_set_drvdata(pdev, i2s); + + ret = pcim_iomap_regions(pdev, 1 << 0, dev_name(&pdev->dev)); + if (ret < 0) { + dev_err(&pdev->dev, "iomap_regions failed\n"); + return ret; + } + i2s->reg_base = pcim_iomap_table(pdev)[0]; + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->reg_base, + &loongson_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap_init_mmio failed\n"); + return PTR_ERR(i2s->regmap); + } + + tx_data = &i2s->tx_dma_data; + rx_data = &i2s->rx_dma_data; + + tx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_TX_DATA; + tx_data->order_addr = i2s->reg_base + LS_I2S_TX_ORDER; + + rx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_RX_DATA; + rx_data->order_addr = i2s->reg_base + LS_I2S_RX_ORDER; + + tx_data->irq = fwnode_irq_get_byname(fwnode, "tx"); + if (tx_data->irq < 0) { + dev_err(&pdev->dev, "dma tx irq invalid\n"); + return tx_data->irq; + } + + rx_data->irq = fwnode_irq_get_byname(fwnode, "rx"); + if (rx_data->irq < 0) { + dev_err(&pdev->dev, "dma rx irq invalid\n"); + return rx_data->irq; + } + + device_property_read_u32(&pdev->dev, "clock-frequency", &i2s->clk_rate); + if (!i2s->clk_rate) { + dev_err(&pdev->dev, "clock-frequency property invalid\n"); + return -EINVAL; + } + + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + + if (i2s->rev_id == 1) { + regmap_write(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET); + udelay(200); + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &loongson_i2s_component, + &loongson_i2s_dai, 1); + if (ret) { + dev_err(&pdev->dev, "register DAI failed %d\n", ret); + return ret; + } + + return 0; +} + +static const struct pci_device_id loongson_i2s_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a27) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, loongson_i2s_ids); + +static struct pci_driver loongson_i2s_driver = { + .name = "loongson-i2s-pci", + .id_table = loongson_i2s_ids, + .probe = loongson_i2s_pci_probe, + .driver = { + .owner = THIS_MODULE, + .pm = pm_sleep_ptr(&loongson_i2s_pm), + }, +}; +module_pci_driver(loongson_i2s_driver); + +MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 4baac72677d9..90db67e0ce4f 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -223,8 +223,13 @@ config SND_SOC_MT8188 config SND_SOC_MT8188_MT6359 tristate "ASoC Audio driver for MT8188 with MT6359 and I2S codecs" depends on SND_SOC_MT8188 && MTK_PMIC_WRAP + depends on I2C select SND_SOC_MT6359 select SND_SOC_HDMI_CODEC + select SND_SOC_DMIC + select SND_SOC_MAX98390 + select SND_SOC_NAU8315 + select SND_SOC_NAU8825 help This adds support for ASoC machine driver for MediaTek MT8188 boards with the MT6359 and other I2S audio codecs. diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c index 738093451ccb..a58e1e3674de 100644 --- a/sound/soc/mediatek/common/mtk-soundcard-driver.c +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -21,8 +21,10 @@ static int set_card_codec_info(struct snd_soc_card *card, int ret; codec_node = of_get_child_by_name(sub_node, "codec"); - if (!codec_node) - return -EINVAL; + if (!codec_node) { + dev_dbg(dev, "%s no specified codec\n", dai_link->name); + return 0; + } /* set card codec info */ ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); @@ -36,6 +38,47 @@ static int set_card_codec_info(struct snd_soc_card *card, return 0; } +static int set_dailink_daifmt(struct snd_soc_card *card, + struct device_node *sub_node, + struct snd_soc_dai_link *dai_link) +{ + unsigned int daifmt; + const char *str; + int ret; + struct { + char *name; + unsigned int val; + } of_clk_table[] = { + { "cpu", SND_SOC_DAIFMT_CBC_CFC }, + { "codec", SND_SOC_DAIFMT_CBP_CFP }, + }; + + daifmt = snd_soc_daifmt_parse_format(sub_node, NULL); + if (daifmt) { + dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + dai_link->dai_fmt |= daifmt; + } + + /* + * check "mediatek,clk-provider = xxx" + * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area + */ + ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str); + if (ret == 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) { + if (strcmp(str, of_clk_table[i].name) == 0) { + dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + dai_link->dai_fmt |= of_clk_table[i].val; + break; + } + } + } + + return 0; +} + int parse_dai_link_info(struct snd_soc_card *card) { struct device *dev = card->dev; @@ -67,6 +110,12 @@ int parse_dai_link_info(struct snd_soc_card *card) of_node_put(sub_node); return ret; } + + ret = set_dailink_daifmt(card, sub_node, dai_link); + if (ret < 0) { + of_node_put(sub_node); + return ret; + } } return 0; diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index f93c2ec8beb7..06269f7e3756 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1070,6 +1070,10 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; + irq_id = platform_get_irq(pdev, 0); + if (irq_id <= 0) + return irq_id < 0 ? irq_id : -ENXIO; + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); @@ -1156,14 +1160,14 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL); if (!comp_hdmi) { ret = -ENOMEM; - goto err_pm_disable; + goto err_cleanup_components; } ret = snd_soc_component_initialize(comp_hdmi, &mt8173_afe_hdmi_dai_component, &pdev->dev); if (ret) - goto err_pm_disable; + goto err_cleanup_components; #ifdef CONFIG_DEBUG_FS comp_hdmi->debugfs_prefix = "hdmi"; @@ -1175,14 +1179,11 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_cleanup_components; - irq_id = platform_get_irq(pdev, 0); - if (irq_id <= 0) - return irq_id < 0 ? irq_id : -ENXIO; ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, 0, "Afe_ISR_Handle", (void *)afe); if (ret) { dev_err(afe->dev, "could not request_irq\n"); - goto err_pm_disable; + goto err_cleanup_components; } dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index e05f2b0231fe..3ece4b5eaca2 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -288,7 +288,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) np = of_get_child_by_name(pdev->dev.of_node, "codec-capture"); if (np) { - ret = snd_soc_of_get_dai_name(np, &codec_capture_dai); + ret = snd_soc_of_get_dai_name(np, &codec_capture_dai, 0); of_node_put(np); if (ret < 0) { dev_err(&pdev->dev, diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-control.c b/sound/soc/mediatek/mt8186/mt8186-afe-control.c index d714e9641571..55edf6374578 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-control.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-control.c @@ -6,7 +6,6 @@ // Author: Jiaxin Yu <jiaxin.yu@mediatek.com> #include "mt8186-afe-common.h" -#include <linux/pm_runtime.h> enum { MTK_AFE_RATE_8K = 0, diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c index cdf54d1eb50d..0432f9d89020 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c @@ -10,7 +10,6 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/of_device.h> -#include <linux/pm_runtime.h> #include <sound/jack.h> #include <sound/pcm_params.h> #include <sound/soc.h> diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c index 7538274641fd..9c11016f032c 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c @@ -12,7 +12,6 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/of_device.h> -#include <linux/pm_runtime.h> #include <sound/jack.h> #include <sound/pcm_params.h> #include <sound/rt5682.h> diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c index 0fb97517f82c..e69c1bb2cb23 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c @@ -24,14 +24,19 @@ static const char *aud_clks[MT8188_CLK_NUM] = { [MT8188_CLK_APMIXED_APLL2] = "apll2", /* divider */ + [MT8188_CLK_TOP_APLL1_D4] = "apll1_d4", + [MT8188_CLK_TOP_APLL2_D4] = "apll2_d4", [MT8188_CLK_TOP_APLL12_DIV0] = "apll12_div0", [MT8188_CLK_TOP_APLL12_DIV1] = "apll12_div1", [MT8188_CLK_TOP_APLL12_DIV2] = "apll12_div2", [MT8188_CLK_TOP_APLL12_DIV3] = "apll12_div3", + [MT8188_CLK_TOP_APLL12_DIV4] = "apll12_div4", [MT8188_CLK_TOP_APLL12_DIV9] = "apll12_div9", /* mux */ [MT8188_CLK_TOP_A1SYS_HP_SEL] = "top_a1sys_hp", + [MT8188_CLK_TOP_A2SYS_SEL] = "top_a2sys", + [MT8188_CLK_TOP_AUD_IEC_SEL] = "top_aud_iec", [MT8188_CLK_TOP_AUD_INTBUS_SEL] = "top_aud_intbus", [MT8188_CLK_TOP_AUDIO_H_SEL] = "top_audio_h", [MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "top_audio_local_bus", @@ -378,6 +383,19 @@ int mt8188_afe_get_default_mclk_source_by_rate(int rate) MT8188_MCK_SEL_APLL1 : MT8188_MCK_SEL_APLL2; } +int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate) +{ + return ((rate % 8000) == 0) ? MT8188_AUD_PLL1 : MT8188_AUD_PLL2; +} + +int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name) +{ + if (strcmp(name, APLL1_W_NAME) == 0) + return MT8188_AUD_PLL1; + + return MT8188_AUD_PLL2; +} + int mt8188_afe_init_clock(struct mtk_base_afe *afe) { struct mt8188_afe_private *afe_priv = afe->platform_priv; @@ -470,8 +488,8 @@ int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk, if (clk && parent) { ret = clk_set_parent(clk, parent); if (ret) { - dev_dbg(afe->dev, "%s(), failed to set clk parent\n", - __func__); + dev_dbg(afe->dev, "%s(), failed to set clk parent %d\n", + __func__, ret); return ret; } } @@ -598,54 +616,132 @@ static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe) return 0; } -static int mt8188_afe_enable_timing_sys(struct mtk_base_afe *afe) +static int mt8188_afe_enable_a1sys(struct mtk_base_afe *afe) { struct mt8188_afe_private *afe_priv = afe->platform_priv; + int ret; - mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); - mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); + ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); + if (ret) + return ret; - mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); - mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); - mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); + return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); +} + +static int mt8188_afe_disable_a1sys(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); return 0; } -static int mt8188_afe_disable_timing_sys(struct mtk_base_afe *afe) +static int mt8188_afe_enable_a2sys(struct mtk_base_afe *afe) { struct mt8188_afe_private *afe_priv = afe->platform_priv; + int ret; - mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); - mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); + ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); + if (ret) + return ret; - mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); - mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); - mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); + return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); +} + +static int mt8188_afe_disable_a2sys(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); return 0; } -int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe) +int mt8188_apll1_enable(struct mtk_base_afe *afe) { - mt8188_afe_enable_timing_sys(afe); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int ret; - mt8188_afe_enable_afe_on(afe); + ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]); + if (ret) + return ret; + + ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL], + afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]); + if (ret) + goto err_clk_parent; - mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1); - mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2); + ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1); + if (ret) + goto err_apll_tuner; + + ret = mt8188_afe_enable_a1sys(afe); + if (ret) + goto err_a1sys; return 0; + +err_a1sys: + mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1); +err_apll_tuner: + mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL], + afe_priv->clk[MT8188_CLK_XTAL_26M]); +err_clk_parent: + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]); + + return ret; } -int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe) +int mt8188_apll1_disable(struct mtk_base_afe *afe) { - mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + mt8188_afe_disable_a1sys(afe); mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1); + mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL], + afe_priv->clk[MT8188_CLK_XTAL_26M]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]); - mt8188_afe_disable_afe_on(afe); + return 0; +} + +int mt8188_apll2_enable(struct mtk_base_afe *afe) +{ + int ret; - mt8188_afe_disable_timing_sys(afe); + ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2); + if (ret) + return ret; + ret = mt8188_afe_enable_a2sys(afe); + if (ret) + goto err_a2sys; + + return 0; +err_a2sys: + mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2); + + return ret; +} + +int mt8188_apll2_disable(struct mtk_base_afe *afe) +{ + mt8188_afe_disable_a2sys(afe); + mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2); + return 0; +} + +int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe) +{ + mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); + mt8188_afe_enable_afe_on(afe); + return 0; +} + +int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe) +{ + mt8188_afe_disable_afe_on(afe); + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); return 0; } diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h index a4203a87a1e3..ec53c171c170 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h @@ -11,6 +11,10 @@ #ifndef _MT8188_AFE_CLK_H_ #define _MT8188_AFE_CLK_H_ +/* APLL */ +#define APLL1_W_NAME "APLL1" +#define APLL2_W_NAME "APLL2" + enum { /* xtal */ MT8188_CLK_XTAL_26M, @@ -18,13 +22,18 @@ enum { MT8188_CLK_APMIXED_APLL1, MT8188_CLK_APMIXED_APLL2, /* divider */ + MT8188_CLK_TOP_APLL1_D4, + MT8188_CLK_TOP_APLL2_D4, MT8188_CLK_TOP_APLL12_DIV0, MT8188_CLK_TOP_APLL12_DIV1, MT8188_CLK_TOP_APLL12_DIV2, MT8188_CLK_TOP_APLL12_DIV3, + MT8188_CLK_TOP_APLL12_DIV4, MT8188_CLK_TOP_APLL12_DIV9, /* mux */ MT8188_CLK_TOP_A1SYS_HP_SEL, + MT8188_CLK_TOP_A2SYS_SEL, + MT8188_CLK_TOP_AUD_IEC_SEL, MT8188_CLK_TOP_AUD_INTBUS_SEL, MT8188_CLK_TOP_AUDIO_H_SEL, MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL, @@ -99,6 +108,8 @@ struct mtk_base_afe; int mt8188_afe_get_mclk_source_clk_id(int sel); int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll); int mt8188_afe_get_default_mclk_source_by_rate(int rate); +int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate); +int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name); int mt8188_afe_init_clock(struct mtk_base_afe *afe); int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk); void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk); @@ -106,6 +117,10 @@ int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk, unsigned int rate); int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk, struct clk *parent); +int mt8188_apll1_enable(struct mtk_base_afe *afe); +int mt8188_apll1_disable(struct mtk_base_afe *afe); +int mt8188_apll2_enable(struct mtk_base_afe *afe); +int mt8188_apll2_disable(struct mtk_base_afe *afe); int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe); int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe); int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe); diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h index eb7e57c239bd..1304d685a306 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-common.h +++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h @@ -39,7 +39,7 @@ enum { MT8188_AFE_MEMIF_END, MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START), MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END, - MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START, + MT8188_AFE_IO_DL_SRC = MT8188_AFE_IO_START, MT8188_AFE_IO_DMIC_IN, MT8188_AFE_IO_DPTX, MT8188_AFE_IO_ETDM_START, @@ -52,6 +52,7 @@ enum { MT8188_AFE_IO_ETDM_NUM = (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START), MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END, + MT8188_AFE_IO_UL_SRC, MT8188_AFE_IO_END, MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START), MT8188_DAI_END = MT8188_AFE_IO_END, diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c index bcf7025886df..6a24b339444b 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c +++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c @@ -17,6 +17,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/soc/mediatek/infracfg.h> #include <linux/reset.h> #include <sound/pcm_params.h> #include "mt8188-afe-common.h" @@ -1898,10 +1899,6 @@ static const struct snd_kcontrol_new mt8188_memif_controls[] = { MT8188_AFE_MEMIF_UL10), }; -static const struct snd_soc_component_driver mt8188_afe_pcm_dai_component = { - .name = "mt8188-afe-pcm-dai", -}; - static const struct mtk_base_memif_data memif_data[MT8188_AFE_MEMIF_NUM] = { [MT8188_AFE_MEMIF_DL2] = { .name = "DL2", @@ -3137,14 +3134,69 @@ static int mt8188_afe_parse_of(struct mtk_base_afe *afe, return 0; } +#define MT8188_DELAY_US 10 +#define MT8188_TIMEOUT_US USEC_PER_SEC + +static int bus_protect_enable(struct regmap *regmap) +{ + int ret; + u32 val; + u32 mask; + + val = 0; + mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1; + regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask); + + ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA, + val, (val & mask) == mask, + MT8188_DELAY_US, MT8188_TIMEOUT_US); + if (ret) + return ret; + + val = 0; + mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2; + regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask); + + ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA, + val, (val & mask) == mask, + MT8188_DELAY_US, MT8188_TIMEOUT_US); + return ret; +} + +static int bus_protect_disable(struct regmap *regmap) +{ + int ret; + u32 val; + u32 mask; + + val = 0; + mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2; + regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask); + + ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA, + val, !(val & mask), + MT8188_DELAY_US, MT8188_TIMEOUT_US); + if (ret) + return ret; + + val = 0; + mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1; + regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask); + + ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA, + val, !(val & mask), + MT8188_DELAY_US, MT8188_TIMEOUT_US); + return ret; +} + static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8188_afe_private *afe_priv; struct device *dev; - int i, irq_id, ret; - struct snd_soc_component *component; struct reset_control *rstc; + struct regmap *infra_ao; + int i, irq_id, ret; ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); if (ret) @@ -3168,18 +3220,37 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(afe->base_addr), "AFE base_addr not found\n"); + infra_ao = syscon_regmap_lookup_by_phandle(dev->of_node, + "mediatek,infracfg"); + if (IS_ERR(infra_ao)) + return dev_err_probe(dev, PTR_ERR(infra_ao), + "%s() Cannot find infra_ao controller\n", + __func__); + /* reset controller to reset audio regs before regmap cache */ rstc = devm_reset_control_get_exclusive(dev, "audiosys"); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n"); + ret = bus_protect_enable(infra_ao); + if (ret) { + dev_err(dev, "bus_protect_enable failed\n"); + return ret; + } + ret = reset_control_reset(rstc); if (ret) { dev_err(dev, "failed to trigger audio reset:%d\n", ret); return ret; } + ret = bus_protect_disable(infra_ao); + if (ret) { + dev_err(dev, "bus_protect_disable failed\n"); + return ret; + } + /* initial audio related clock */ ret = mt8188_afe_init_clock(afe); if (ret) @@ -3276,34 +3347,12 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) /* register component */ ret = devm_snd_soc_register_component(dev, &mt8188_afe_component, - NULL, 0); + afe->dai_drivers, afe->num_dai_drivers); if (ret) { dev_warn(dev, "err_platform\n"); goto err_pm_put; } - component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL); - if (!component) { - ret = -ENOMEM; - goto err_pm_put; - } - - ret = snd_soc_component_initialize(component, - &mt8188_afe_pcm_dai_component, - &pdev->dev); - if (ret) - goto err_pm_put; -#ifdef CONFIG_DEBUG_FS - component->debugfs_prefix = "pcm"; -#endif - ret = snd_soc_add_component(component, - afe->dai_drivers, - afe->num_dai_drivers); - if (ret) { - dev_warn(dev, "err_add_component\n"); - goto err_pm_put; - } - mt8188_afe_init_registers(afe); pm_runtime_put_sync(&pdev->dev); @@ -3319,11 +3368,6 @@ err_pm_put: return ret; } -static void mt8188_afe_pcm_dev_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); -} - static const struct of_device_id mt8188_afe_pcm_dt_match[] = { { .compatible = "mediatek,mt8188-afe", }, {}, @@ -3342,7 +3386,6 @@ static struct platform_driver mt8188_afe_pcm_driver = { .pm = &mt8188_afe_pm_ops, }, .probe = mt8188_afe_pcm_dev_probe, - .remove_new = mt8188_afe_pcm_dev_remove, }; module_platform_driver(mt8188_afe_pcm_driver); diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c index d71696901553..7dc029f2b428 100644 --- a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c +++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c @@ -18,7 +18,6 @@ #define ADDA_HIRES_THRES 48000 enum { - SUPPLY_SEQ_CLOCK_SEL, SUPPLY_SEQ_ADDA_DL_ON, SUPPLY_SEQ_ADDA_MTKAIF_CFG, SUPPLY_SEQ_ADDA_UL_ON, @@ -54,8 +53,7 @@ enum { }; struct mtk_dai_adda_priv { - unsigned int dl_rate; - unsigned int ul_rate; + bool hires_required; }; static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe, @@ -242,70 +240,35 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w, return 0; } -static int mtk_audio_hires_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) +static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe, + const char *name) { - struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct clk *clk = afe_priv->clk[MT8188_CLK_TOP_AUDIO_H_SEL]; - struct clk *clk_parent; - - dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", - __func__, w->name, event); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - clk_parent = afe_priv->clk[MT8188_CLK_APMIXED_APLL1]; - break; - case SND_SOC_DAPM_POST_PMD: - clk_parent = afe_priv->clk[MT8188_CLK_XTAL_26M]; - break; - default: - return 0; - } - mt8188_afe_set_clk_parent(afe, clk, clk_parent); - - return 0; -} - -static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) -{ - struct snd_soc_dapm_widget *w = source; - struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_adda_priv *adda_priv; - - adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; - if (!adda_priv) { - dev_err(afe->dev, "%s adda_priv == NULL", __func__); - return 0; - } - - return !!(adda_priv->ul_rate > ADDA_HIRES_THRES); + if (strstr(name, "aud_adc_hires")) + return afe_priv->dai_priv[MT8188_AFE_IO_UL_SRC]; + else if (strstr(name, "aud_dac_hires")) + return afe_priv->dai_priv[MT8188_AFE_IO_DL_SRC]; + else + return NULL; } -static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) +static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) { struct snd_soc_dapm_widget *w = source; struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); - struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_adda_priv *adda_priv; - adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; + adda_priv = get_adda_priv_by_name(afe, w->name); if (!adda_priv) { - dev_err(afe->dev, "%s adda_priv == NULL", __func__); + dev_dbg(afe->dev, "adda_priv == NULL"); return 0; } - return !!(adda_priv->dl_rate > ADDA_HIRES_THRES); + return (adda_priv->hires_required) ? 1 : 0; } static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = { @@ -364,12 +327,6 @@ static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { mtk_adda_ul_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY_S("AUDIO_HIRES", SUPPLY_SEQ_CLOCK_SEL, - SND_SOC_NOPM, - 0, 0, - mtk_audio_hires_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG, SND_SOC_NOPM, 0, 0, @@ -396,8 +353,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { {"ADDA Capture", NULL, "ADDA Capture Enable"}, {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"}, {"ADDA Capture", NULL, "aud_adc"}, - {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect}, - {"aud_adc_hires", NULL, "AUDIO_HIRES"}, + {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect}, {"I168", NULL, "ADDA Capture"}, {"I169", NULL, "ADDA Capture"}, @@ -405,8 +361,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { {"ADDA Playback", NULL, "ADDA Enable"}, {"ADDA Playback", NULL, "ADDA Playback Enable"}, {"ADDA Playback", NULL, "aud_dac"}, - {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect}, - {"aud_dac_hires", NULL, "AUDIO_HIRES"}, + {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect}, {"DL_GAIN", NULL, "O176"}, {"DL_GAIN", NULL, "O177"}, @@ -540,13 +495,12 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream, dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n", __func__, id, substream->stream, rate); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - adda_priv->dl_rate = rate; + adda_priv->hires_required = (rate > ADDA_HIRES_THRES); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mtk_dai_da_configure(afe, rate, id); - } else { - adda_priv->ul_rate = rate; + else ret = mtk_dai_ad_configure(afe, rate, id); - } return ret; } @@ -573,8 +527,8 @@ static const struct snd_soc_dai_ops mtk_dai_adda_ops = { static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { { - .name = "ADDA", - .id = MT8188_AFE_IO_ADDA, + .name = "DL_SRC", + .id = MT8188_AFE_IO_DL_SRC, .playback = { .stream_name = "ADDA Playback", .channels_min = 1, @@ -582,6 +536,11 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { .rates = MTK_ADDA_PLAYBACK_RATES, .formats = MTK_ADDA_FORMATS, }, + .ops = &mtk_dai_adda_ops, + }, + { + .name = "UL_SRC", + .id = MT8188_AFE_IO_UL_SRC, .capture = { .stream_name = "ADDA Capture", .channels_min = 1, @@ -597,13 +556,18 @@ static int init_adda_priv_data(struct mtk_base_afe *afe) { struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_adda_priv *adda_priv; + int adda_dai_list[] = {MT8188_AFE_IO_DL_SRC, MT8188_AFE_IO_UL_SRC}; + int i; - adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv), - GFP_KERNEL); - if (!adda_priv) - return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) { + adda_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_dai_adda_priv), + GFP_KERNEL); + if (!adda_priv) + return -ENOMEM; - afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv; + afe_priv->dai_priv[adda_dai_list[i]] = adda_priv; + } return 0; } diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c index 7a37752d4244..16440dd0a89c 100644 --- a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c +++ b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c @@ -22,6 +22,14 @@ #define ENUM_TO_STR(x) #x enum { + SUPPLY_SEQ_APLL, + SUPPLY_SEQ_ETDM_MCLK, + SUPPLY_SEQ_ETDM_CG, + SUPPLY_SEQ_DPTX_EN, + SUPPLY_SEQ_ETDM_EN, +}; + +enum { MTK_DAI_ETDM_FORMAT_I2S = 0, MTK_DAI_ETDM_FORMAT_LJ, MTK_DAI_ETDM_FORMAT_RJ, @@ -84,11 +92,11 @@ struct mtk_dai_etdm_rate { }; struct mtk_dai_etdm_priv { - unsigned int clock_mode; unsigned int data_mode; bool slave_mode; bool lrck_inv; bool bck_inv; + unsigned int rate; unsigned int format; unsigned int slots; unsigned int lrck_width; @@ -100,8 +108,6 @@ struct mtk_dai_etdm_priv { unsigned int cowork_slv_count; int cowork_slv_id[MT8188_AFE_IO_ETDM_NUM - 1]; //dai_id bool in_disable_ch[MT8188_ETDM_MAX_CHANNELS]; - unsigned int en_ref_cnt; - bool is_prepared; }; static const struct mtk_dai_etdm_rate mt8188_etdm_rates[] = { @@ -345,13 +351,85 @@ static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id) } } +static int get_etdm_id_by_name(struct mtk_base_afe *afe, + const char *name) +{ + if (!strncmp(name, "ETDM1_IN", strlen("ETDM1_IN"))) + return MT8188_AFE_IO_ETDM1_IN; + else if (!strncmp(name, "ETDM2_IN", strlen("ETDM2_IN"))) + return MT8188_AFE_IO_ETDM2_IN; + else if (!strncmp(name, "ETDM1_OUT", strlen("ETDM1_OUT"))) + return MT8188_AFE_IO_ETDM1_OUT; + else if (!strncmp(name, "ETDM2_OUT", strlen("ETDM2_OUT"))) + return MT8188_AFE_IO_ETDM2_OUT; + else if (!strncmp(name, "ETDM3_OUT", strlen("ETDM3_OUT"))) + return MT8188_AFE_IO_ETDM3_OUT; + else if (!strncmp(name, "DPTX", strlen("DPTX"))) + return MT8188_AFE_IO_ETDM3_OUT; + else + return -EINVAL; +} + +static struct mtk_dai_etdm_priv *get_etdm_priv_by_name(struct mtk_base_afe *afe, + const char *name) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int dai_id = get_etdm_id_by_name(afe, name); + + if (dai_id < MT8188_AFE_IO_ETDM_START || + dai_id >= MT8188_AFE_IO_ETDM_END) + return NULL; + + return afe_priv->dai_priv[dai_id]; +} + static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id) { struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + unsigned int val = 0; + unsigned int mask; + int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id); int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); + int apll_clk_id; + int apll; + int ret; - if (clkdiv_id < 0) + if (!is_valid_etdm_dai(dai_id)) return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + apll = etdm_data->mclk_apll; + apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll); + + if (clkmux_id < 0 || clkdiv_id < 0) + return -EINVAL; + + if (apll_clk_id < 0) + return apll_clk_id; + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + mask = ETDM_CON1_MCLK_OUTPUT; + if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT) + val = ETDM_CON1_MCLK_OUTPUT; + regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val); + + /* enable parent clock before select apll*/ + mt8188_afe_enable_clk(afe, afe_priv->clk[clkmux_id]); + + /* select apll */ + ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clkmux_id], + afe_priv->clk[apll_clk_id]); + if (ret) + return ret; + + /* set rate */ + ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id], + etdm_data->mclk_freq); mt8188_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]); @@ -361,12 +439,275 @@ static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id) static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id) { struct mt8188_afe_private *afe_priv = afe->platform_priv; + int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id); int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); - if (clkdiv_id < 0) + if (clkmux_id < 0 || clkdiv_id < 0) return -EINVAL; mt8188_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]); + mt8188_afe_disable_clk(afe, afe_priv->clk[clkmux_id]); + + return 0; +} + +static int mtk_afe_etdm_apll_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_dai_etdm_priv *etdm_priv; + int cur_apll; + int need_apll; + + etdm_priv = get_etdm_priv_by_name(afe, w->name); + if (!etdm_priv) { + dev_dbg(afe->dev, "etdm_priv == NULL\n"); + return 0; + } + + cur_apll = mt8188_get_apll_by_name(afe, source->name); + need_apll = mt8188_get_apll_by_rate(afe, etdm_priv->rate); + + return (need_apll == cur_apll) ? 1 : 0; +} + +static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_dai_etdm_priv *etdm_priv; + int cur_apll; + + etdm_priv = get_etdm_priv_by_name(afe, w->name); + + cur_apll = mt8188_get_apll_by_name(afe, source->name); + + return (etdm_priv->mclk_apll == cur_apll) ? 1 : 0; +} + +static int mtk_etdm_mclk_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_priv; + int mclk_id; + + mclk_id = get_etdm_id_by_name(afe, source->name); + if (mclk_id < 0) { + dev_dbg(afe->dev, "mclk_id < 0\n"); + return 0; + } + + etdm_priv = get_etdm_priv_by_name(afe, w->name); + if (!etdm_priv) { + dev_dbg(afe->dev, "etdm_priv == NULL\n"); + return 0; + } + + if (get_etdm_id_by_name(afe, sink->name) == mclk_id) + return !!(etdm_priv->mclk_freq > 0); + + if (etdm_priv->cowork_source_id == mclk_id) { + etdm_priv = afe_priv->dai_priv[mclk_id]; + return !!(etdm_priv->mclk_freq > 0); + } + + return 0; +} + +static int mtk_etdm_cowork_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_priv; + int source_id; + int i; + + source_id = get_etdm_id_by_name(afe, source->name); + if (source_id < 0) { + dev_dbg(afe->dev, "%s() source_id < 0\n", __func__); + return 0; + } + + etdm_priv = get_etdm_priv_by_name(afe, w->name); + if (!etdm_priv) { + dev_dbg(afe->dev, "%s() etdm_priv == NULL\n", __func__); + return 0; + } + + if (etdm_priv->cowork_source_id != COWORK_ETDM_NONE) { + if (etdm_priv->cowork_source_id == source_id) + return 1; + + etdm_priv = afe_priv->dai_priv[etdm_priv->cowork_source_id]; + for (i = 0; i < etdm_priv->cowork_slv_count; i++) { + if (etdm_priv->cowork_slv_id[i] == source_id) + return 1; + } + } else { + for (i = 0; i < etdm_priv->cowork_slv_count; i++) { + if (etdm_priv->cowork_slv_id[i] == source_id) + return 1; + } + } + + return 0; +} + +static int mtk_apll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strcmp(w->name, APLL1_W_NAME) == 0) + mt8188_apll1_enable(afe); + else + mt8188_apll2_enable(afe); + break; + case SND_SOC_DAPM_POST_PMD: + if (strcmp(w->name, APLL1_W_NAME) == 0) + mt8188_apll1_disable(afe); + else + mt8188_apll2_disable(afe); + break; + default: + break; + } + + return 0; +} + +static int mtk_etdm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + int mclk_id = get_etdm_id_by_name(afe, w->name); + + if (mclk_id < 0) { + dev_dbg(afe->dev, "%s() mclk_id < 0\n", __func__); + return 0; + } + + dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mtk_dai_etdm_enable_mclk(afe, mclk_id); + break; + case SND_SOC_DAPM_POST_PMD: + mtk_dai_etdm_disable_mclk(afe, mclk_id); + break; + default: + break; + } + + return 0; +} + +static int mtk_dptx_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mtk_dai_etdm_enable_mclk(afe, MT8188_AFE_IO_DPTX); + break; + case SND_SOC_DAPM_POST_PMD: + mtk_dai_etdm_disable_mclk(afe, MT8188_AFE_IO_DPTX); + break; + default: + break; + } + + return 0; +} + +static int mtk_etdm_cg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int etdm_id; + int cg_id; + + etdm_id = get_etdm_id_by_name(afe, w->name); + if (etdm_id < 0) { + dev_dbg(afe->dev, "%s() etdm_id < 0\n", __func__); + return 0; + } + + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(etdm_id); + if (cg_id < 0) { + dev_dbg(afe->dev, "%s() cg_id < 0\n", __func__); + return 0; + } + + dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); + break; + case SND_SOC_DAPM_POST_PMD: + mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); + break; + default: + break; + } + + return 0; +} + +static int mtk_etdm3_cg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]); + break; + case SND_SOC_DAPM_POST_PMD: + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]); + break; + default: + break; + } return 0; } @@ -906,11 +1247,181 @@ static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0, &hdmi_ch7_mux_control), + /* mclk en */ + SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK, + SND_SOC_NOPM, 0, 0, + mtk_etdm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK, + SND_SOC_NOPM, 0, 0, + mtk_etdm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK, + SND_SOC_NOPM, 0, 0, + mtk_etdm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK, + SND_SOC_NOPM, 0, 0, + mtk_etdm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("DPTX_MCLK", SUPPLY_SEQ_ETDM_MCLK, + SND_SOC_NOPM, 0, 0, + mtk_dptx_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* cg */ + SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_CG", SUPPLY_SEQ_ETDM_CG, + SND_SOC_NOPM, 0, 0, + mtk_etdm_cg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_CG", SUPPLY_SEQ_ETDM_CG, + SND_SOC_NOPM, 0, 0, + mtk_etdm_cg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_CG", SUPPLY_SEQ_ETDM_CG, + SND_SOC_NOPM, 0, 0, + mtk_etdm_cg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_CG", SUPPLY_SEQ_ETDM_CG, + SND_SOC_NOPM, 0, 0, + mtk_etdm_cg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_CG", SUPPLY_SEQ_ETDM_CG, + SND_SOC_NOPM, 0, 0, + mtk_etdm3_cg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* en */ + SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_EN", SUPPLY_SEQ_ETDM_EN, + ETDM_IN1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_EN", SUPPLY_SEQ_ETDM_EN, + ETDM_IN2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_EN", SUPPLY_SEQ_ETDM_EN, + ETDM_OUT1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_EN", SUPPLY_SEQ_ETDM_EN, + ETDM_OUT2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_EN", SUPPLY_SEQ_ETDM_EN, + ETDM_OUT3_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DPTX_EN", SUPPLY_SEQ_DPTX_EN, + AFE_DPTX_CON, AFE_DPTX_CON_ON_SHIFT, 0, NULL, 0), + + /* apll */ + SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL, + SND_SOC_NOPM, 0, 0, + mtk_apll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL, + SND_SOC_NOPM, 0, 0, + mtk_apll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("ETDM_INPUT"), SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"), }; static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { + /* mclk */ + {"ETDM1_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect}, + + {"ETDM2_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect}, + + {"ETDM1_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect}, + {"ETDM1_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect}, + + {"ETDM2_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect}, + {"ETDM2_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect}, + + {"DPTX", NULL, "DPTX_MCLK"}, + + {"ETDM1_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {"ETDM1_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + {"ETDM2_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {"ETDM2_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + {"ETDM1_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {"ETDM1_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + {"ETDM2_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {"ETDM2_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + {"DPTX_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {"DPTX_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + /* cg */ + {"ETDM1_IN", NULL, "ETDM1_IN_CG"}, + {"ETDM1_IN", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM1_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect}, + {"ETDM1_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect}, + + {"ETDM2_IN", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM2_IN", NULL, "ETDM2_IN_CG"}, + {"ETDM2_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect}, + {"ETDM2_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect}, + + {"ETDM1_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM1_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM1_OUT", NULL, "ETDM1_OUT_CG"}, + {"ETDM1_OUT", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect}, + + {"ETDM2_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM2_OUT_CG"}, + + {"ETDM3_OUT", NULL, "ETDM3_OUT_CG"}, + {"DPTX", NULL, "ETDM3_OUT_CG"}, + + /* en */ + {"ETDM1_IN", NULL, "ETDM1_IN_EN"}, + {"ETDM1_IN", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM1_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect}, + {"ETDM1_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect}, + + {"ETDM2_IN", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM2_IN", NULL, "ETDM2_IN_EN"}, + {"ETDM2_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect}, + {"ETDM2_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect}, + + {"ETDM1_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM1_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM1_OUT", NULL, "ETDM1_OUT_EN"}, + {"ETDM1_OUT", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect}, + + {"ETDM2_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect}, + {"ETDM2_OUT", NULL, "ETDM2_OUT_EN"}, + + {"ETDM3_OUT", NULL, "ETDM3_OUT_EN"}, + {"DPTX", NULL, "ETDM3_OUT_EN"}, + {"DPTX", NULL, "DPTX_EN"}, + + {"ETDM1_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect}, + {"ETDM1_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect}, + + {"ETDM2_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect}, + {"ETDM2_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect}, + + {"ETDM1_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect}, + {"ETDM1_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect}, + + {"ETDM2_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect}, + {"ETDM2_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect}, + + {"ETDM3_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect}, + {"ETDM3_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect}, + {"I012", NULL, "ETDM2_IN"}, {"I013", NULL, "ETDM2_IN"}, {"I014", NULL, "ETDM2_IN"}, @@ -1163,64 +1674,6 @@ static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { {"ETDM2_IN", NULL, "ETDM_INPUT"}, }; -static int mt8188_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id) -{ - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *etdm_data; - struct etdm_con_reg etdm_reg; - unsigned long flags; - int ret = 0; - - if (!is_valid_etdm_dai(dai_id)) - return -EINVAL; - etdm_data = afe_priv->dai_priv[dai_id]; - - dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt); - spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); - etdm_data->en_ref_cnt++; - if (etdm_data->en_ref_cnt == 1) { - ret = get_etdm_reg(dai_id, &etdm_reg); - if (ret < 0) - goto out; - - regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_EN); - } - -out: - spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); - return ret; -} - -static int mt8188_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id) -{ - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *etdm_data; - struct etdm_con_reg etdm_reg; - unsigned long flags; - int ret = 0; - - if (!is_valid_etdm_dai(dai_id)) - return -EINVAL; - etdm_data = afe_priv->dai_priv[dai_id]; - - dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt); - spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); - if (etdm_data->en_ref_cnt > 0) { - etdm_data->en_ref_cnt--; - if (etdm_data->en_ref_cnt == 0) { - ret = get_etdm_reg(dai_id, &etdm_reg); - if (ret < 0) - goto out; - regmap_clear_bits(afe->regmap, etdm_reg.con0, - ETDM_CON0_EN); - } - } - -out: - spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); - return ret; -} - static int etdm_cowork_slv_sel(int id, int slave_mode) { if (slave_mode) { @@ -1408,121 +1861,6 @@ static int mt8188_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id) } /* dai ops */ -static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *mst_etdm_data; - int mst_dai_id; - int slv_dai_id; - int cg_id; - int i; - - if (is_cowork_mode(dai)) { - mst_dai_id = get_etdm_cowork_master_id(dai); - if (!is_valid_etdm_dai(mst_dai_id)) - return -EINVAL; - mtk_dai_etdm_enable_mclk(afe, mst_dai_id); - - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id); - if (cg_id >= 0) - mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); - - mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; - - for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { - slv_dai_id = mst_etdm_data->cowork_slv_id[i]; - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id); - if (cg_id >= 0) - mt8188_afe_enable_clk(afe, - afe_priv->clk[cg_id]); - } - } else { - mtk_dai_etdm_enable_mclk(afe, dai->id); - - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); - if (cg_id >= 0) - mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); - } - - return 0; -} - -static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *mst_etdm_data; - int mst_dai_id; - int slv_dai_id; - int cg_id; - int ret; - int i; - - if (!is_valid_etdm_dai(dai->id)) - return; - mst_etdm_data = afe_priv->dai_priv[dai->id]; - - dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, - mst_etdm_data->is_prepared); - - if (mst_etdm_data->is_prepared) { - mst_etdm_data->is_prepared = false; - - if (is_cowork_mode(dai)) { - mst_dai_id = get_etdm_cowork_master_id(dai); - if (!is_valid_etdm_dai(mst_dai_id)) - return; - mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; - - ret = mt8188_afe_disable_etdm(afe, mst_dai_id); - if (ret) - dev_dbg(afe->dev, "%s disable %d failed\n", - __func__, mst_dai_id); - - for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { - slv_dai_id = mst_etdm_data->cowork_slv_id[i]; - ret = mt8188_afe_disable_etdm(afe, slv_dai_id); - if (ret) - dev_dbg(afe->dev, "%s disable %d failed\n", - __func__, slv_dai_id); - } - } else { - ret = mt8188_afe_disable_etdm(afe, dai->id); - if (ret) - dev_dbg(afe->dev, "%s disable %d failed\n", - __func__, dai->id); - } - } - - if (is_cowork_mode(dai)) { - mst_dai_id = get_etdm_cowork_master_id(dai); - if (!is_valid_etdm_dai(mst_dai_id)) - return; - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id); - if (cg_id >= 0) - mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); - - mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; - for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { - slv_dai_id = mst_etdm_data->cowork_slv_id[i]; - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id); - if (cg_id >= 0) - mt8188_afe_disable_clk(afe, - afe_priv->clk[cg_id]); - } - mtk_dai_etdm_disable_mclk(afe, mst_dai_id); - } else { - cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); - if (cg_id >= 0) - mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); - - mtk_dai_etdm_disable_mclk(afe, dai->id); - } -} - static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe, int dai_id, unsigned int rate) { @@ -1759,60 +2097,6 @@ static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe, return 0; } -static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id) -{ - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *etdm_data; - struct etdm_con_reg etdm_reg; - int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id); - int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); - int apll_clk_id; - int apll; - int ret; - - if (clk_id < 0 || clkdiv_id < 0) - return -EINVAL; - - if (!is_valid_etdm_dai(dai_id)) - return -EINVAL; - etdm_data = afe_priv->dai_priv[dai_id]; - - ret = get_etdm_reg(dai_id, &etdm_reg); - if (ret < 0) - return ret; - - if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT) - regmap_set_bits(afe->regmap, etdm_reg.con1, - ETDM_CON1_MCLK_OUTPUT); - else - regmap_clear_bits(afe->regmap, etdm_reg.con1, - ETDM_CON1_MCLK_OUTPUT); - - if (etdm_data->mclk_freq) { - apll = etdm_data->mclk_apll; - apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll); - if (apll_clk_id < 0) - return apll_clk_id; - - /* select apll */ - ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clk_id], - afe_priv->clk[apll_clk_id]); - if (ret) - return ret; - - /* set rate */ - ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id], - etdm_data->mclk_freq); - if (ret) - return ret; - } else { - if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT) - dev_dbg(afe->dev, "%s mclk freq = 0\n", __func__); - } - - return 0; -} - static int mtk_dai_etdm_configure(struct mtk_base_afe *afe, unsigned int rate, unsigned int channels, @@ -1834,15 +2118,16 @@ static int mtk_dai_etdm_configure(struct mtk_base_afe *afe, return -EINVAL; etdm_data = afe_priv->dai_priv[dai_id]; slave_mode = etdm_data->slave_mode; + etdm_data->rate = rate; ret = get_etdm_reg(dai_id, &etdm_reg); if (ret < 0) return ret; - dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, clock %u slv %u\n", + dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, slv %u\n", __func__, etdm_data->format, etdm_data->data_mode, etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv, - etdm_data->clock_mode, etdm_data->slave_mode); + etdm_data->slave_mode); dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n", __func__, rate, channels, bit_width, dai_id); @@ -1909,16 +2194,15 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, if (!is_valid_etdm_dai(mst_dai_id)) return -EINVAL; - ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id); - if (ret) - return ret; + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + if (mst_etdm_data->slots) + channels = mst_etdm_data->slots; ret = mtk_dai_etdm_configure(afe, rate, channels, bit_width, mst_dai_id); if (ret) return ret; - mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { slv_dai_id = mst_etdm_data->cowork_slv_id[i]; ret = mtk_dai_etdm_configure(afe, rate, channels, @@ -1931,9 +2215,11 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } } else { - ret = mtk_dai_etdm_mclk_configure(afe, dai->id); - if (ret) - return ret; + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + mst_etdm_data = afe_priv->dai_priv[dai->id]; + if (mst_etdm_data->slots) + channels = mst_etdm_data->slots; ret = mtk_dai_etdm_configure(afe, rate, channels, bit_width, dai->id); @@ -1944,66 +2230,6 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int mtk_dai_etdm_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *mst_etdm_data; - int mst_dai_id; - int slv_dai_id; - int ret; - int i; - - if (!is_valid_etdm_dai(dai->id)) - return -EINVAL; - mst_etdm_data = afe_priv->dai_priv[dai->id]; - - dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, - mst_etdm_data->is_prepared); - - if (mst_etdm_data->is_prepared) - return 0; - - mst_etdm_data->is_prepared = true; - - if (is_cowork_mode(dai)) { - mst_dai_id = get_etdm_cowork_master_id(dai); - if (!is_valid_etdm_dai(mst_dai_id)) - return -EINVAL; - mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; - - for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { - slv_dai_id = mst_etdm_data->cowork_slv_id[i]; - ret = mt8188_afe_enable_etdm(afe, slv_dai_id); - if (ret) { - dev_dbg(afe->dev, "%s enable %d failed\n", - __func__, slv_dai_id); - - return ret; - } - } - - ret = mt8188_afe_enable_etdm(afe, mst_dai_id); - if (ret) { - dev_dbg(afe->dev, "%s enable %d failed\n", - __func__, mst_dai_id); - - return ret; - } - } else { - ret = mt8188_afe_enable_etdm(afe, dai->id); - if (ret) { - dev_dbg(afe->dev, "%s enable %d failed\n", - __func__, dai->id); - - return ret; - } - } - - return 0; -} - static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id) { struct mt8188_afe_private *afe_priv = afe->platform_priv; @@ -2073,10 +2299,16 @@ static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai, struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_etdm_priv *etdm_data; + int dai_id; - if (!is_valid_etdm_dai(dai->id)) + if (is_cowork_mode(dai)) + dai_id = get_etdm_cowork_master_id(dai); + else + dai_id = dai->id; + + if (!is_valid_etdm_dai(dai_id)) return -EINVAL; - etdm_data = afe_priv->dai_priv[dai->id]; + etdm_data = afe_priv->dai_priv[dai_id]; dev_dbg(dai->dev, "%s id %d slot_width %d\n", __func__, dai->id, slot_width); @@ -2151,53 +2383,6 @@ static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int mtk_dai_hdmitx_dptx_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); - - if (cg_id >= 0) - mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); - - mtk_dai_etdm_enable_mclk(afe, dai->id); - - return 0; -} - -static void mtk_dai_hdmitx_dptx_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); - struct mtk_dai_etdm_priv *etdm_data; - int ret; - - if (!is_valid_etdm_dai(dai->id)) - return; - etdm_data = afe_priv->dai_priv[dai->id]; - - if (etdm_data->is_prepared) { - etdm_data->is_prepared = false; - /* disable etdm_out3 */ - ret = mt8188_afe_disable_etdm(afe, dai->id); - if (ret) - dev_dbg(afe->dev, "%s disable failed\n", __func__); - - /* disable dptx interface */ - if (dai->id == MT8188_AFE_IO_DPTX) - regmap_clear_bits(afe->regmap, AFE_DPTX_CON, - AFE_DPTX_CON_ON); - } - - mtk_dai_etdm_disable_mclk(afe, dai->id); - - if (cg_id >= 0) - mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); -} - static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel) { switch (channel) { @@ -2265,42 +2450,11 @@ static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream, etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN; } - ret = mtk_dai_etdm_mclk_configure(afe, dai->id); - if (ret) - return ret; - ret = mtk_dai_etdm_configure(afe, rate, channels, width, dai->id); return ret; } -static int mtk_dai_hdmitx_dptx_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mt8188_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *etdm_data; - - if (!is_valid_etdm_dai(dai->id)) - return -EINVAL; - etdm_data = afe_priv->dai_priv[dai->id]; - - dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, - etdm_data->is_prepared); - - if (etdm_data->is_prepared) - return 0; - - etdm_data->is_prepared = true; - - /* enable dptx interface */ - if (dai->id == MT8188_AFE_IO_DPTX) - regmap_set_bits(afe->regmap, AFE_DPTX_CON, AFE_DPTX_CON_ON); - - /* enable etdm_out3 */ - return mt8188_afe_enable_etdm(afe, dai->id); -} - static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, @@ -2322,20 +2476,14 @@ static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai, } static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { - .startup = mtk_dai_etdm_startup, - .shutdown = mtk_dai_etdm_shutdown, .hw_params = mtk_dai_etdm_hw_params, - .prepare = mtk_dai_etdm_prepare, .set_sysclk = mtk_dai_etdm_set_sysclk, .set_fmt = mtk_dai_etdm_set_fmt, .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, }; static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { - .startup = mtk_dai_hdmitx_dptx_startup, - .shutdown = mtk_dai_hdmitx_dptx_shutdown, .hw_params = mtk_dai_hdmitx_dptx_hw_params, - .prepare = mtk_dai_hdmitx_dptx_prepare, .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, .set_fmt = mtk_dai_etdm_set_fmt, }; diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 919d74ea1934..ac69c23e0da1 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -6,6 +6,8 @@ * Author: Trevor Wu <trevor.wu@mediatek.com> */ +#include <linux/bitfield.h> +#include <linux/input.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> @@ -13,10 +15,36 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "mt8188-afe-common.h" +#include "../../codecs/nau8825.h" #include "../../codecs/mt6359.h" #include "../common/mtk-afe-platform-driver.h" #include "../common/mtk-soundcard-driver.h" +#define CKSYS_AUD_TOP_CFG 0x032c + #define RG_TEST_ON BIT(0) + #define RG_TEST_TYPE BIT(2) +#define CKSYS_AUD_TOP_MON 0x0330 + #define TEST_MISO_COUNT_1 GENMASK(3, 0) + #define TEST_MISO_COUNT_2 GENMASK(7, 4) + #define TEST_MISO_DONE_1 BIT(28) + #define TEST_MISO_DONE_2 BIT(29) + +#define NAU8825_HS_PRESENT BIT(0) + +/* + * Maxim MAX98390 + */ +#define MAX98390_CODEC_DAI "max98390-aif1" +#define MAX98390_DEV0_NAME "max98390.0-0038" /* rear right */ +#define MAX98390_DEV1_NAME "max98390.0-0039" /* rear left */ +#define MAX98390_DEV2_NAME "max98390.0-003a" /* front right */ +#define MAX98390_DEV3_NAME "max98390.0-003b" /* front left */ + +/* + * Nau88l25 + */ +#define NAU8825_CODEC_DAI "nau8825-hifi" + /* FE */ SND_SOC_DAILINK_DEFS(playback2, DAILINK_COMP_ARRAY(COMP_CPU("DL2")), @@ -99,8 +127,8 @@ SND_SOC_DAILINK_DEFS(capture10, DAILINK_COMP_ARRAY(COMP_EMPTY())); /* BE */ -SND_SOC_DAILINK_DEFS(adda, - DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), +SND_SOC_DAILINK_DEFS(dl_src, + DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")), DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", "mt6359-snd-codec-aif1")), DAILINK_COMP_ARRAY(COMP_EMPTY())); @@ -140,9 +168,44 @@ SND_SOC_DAILINK_DEFS(pcm1, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(ul_src, + DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC")), + DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", + "mt6359-snd-codec-aif1"), + COMP_CODEC("dmic-codec", + "dmic-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + struct mt8188_mt6359_priv { struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct snd_soc_jack headset_jack; + void *private_data; +}; + +static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = { + { + .pin = "HDMI", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = { + { + .pin = "DP", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin nau8825_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, }; struct mt8188_card_data { @@ -150,9 +213,39 @@ struct mt8188_card_data { unsigned long quirk; }; +static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_dumb_spk_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_kcontrol_new mt8188_dual_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_dual_spk_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_kcontrol_new mt8188_rear_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Rear Left Spk"), + SOC_DAPM_PIN_SWITCH("Rear Right Spk"), +}; + +static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = { + SND_SOC_DAPM_SPK("Rear Left Spk", NULL), + SND_SOC_DAPM_SPK("Rear Right Spk", NULL), +}; + static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SINK("HDMI"), + SND_SOC_DAPM_SINK("DP"), }; static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { @@ -160,8 +253,13 @@ static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; -#define CKSYS_AUD_TOP_CFG 0x032c -#define CKSYS_AUD_TOP_MON 0x0330 +static const struct snd_soc_dapm_widget mt8188_nau8825_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), +}; + +static const struct snd_kcontrol_new mt8188_nau8825_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), +}; static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) { @@ -174,13 +272,13 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) struct mtkaif_param *param; int chosen_phase_1, chosen_phase_2; int prev_cycle_1, prev_cycle_2; - int test_done_1, test_done_2; + u8 test_done_1, test_done_2; int cycle_1, cycle_2; int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM]; int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM]; int mtkaif_calibration_num_phase; bool mtkaif_calibration_ok; - unsigned int monitor = 0; + u32 monitor = 0; int counter; int phase; int i; @@ -212,8 +310,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) mt6359_mtkaif_calibration_enable(cmpnt_codec); /* set test type to synchronizer pulse */ - regmap_update_bits(afe_priv->topckgen, - CKSYS_AUD_TOP_CFG, 0xffff, 0x4); + regmap_write(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_TYPE); mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */ mtkaif_calibration_ok = true; @@ -223,7 +320,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) mt6359_set_mtkaif_calibration_phase(cmpnt_codec, phase, phase, phase); - regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1); + regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON); test_done_1 = 0; test_done_2 = 0; @@ -235,20 +332,19 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) while (!(test_done_1 & test_done_2)) { regmap_read(afe_priv->topckgen, CKSYS_AUD_TOP_MON, &monitor); - test_done_1 = (monitor >> 28) & 0x1; - test_done_2 = (monitor >> 29) & 0x1; + test_done_1 = FIELD_GET(TEST_MISO_DONE_1, monitor); + test_done_2 = FIELD_GET(TEST_MISO_DONE_2, monitor); if (test_done_1 == 1) - cycle_1 = monitor & 0xf; + cycle_1 = FIELD_GET(TEST_MISO_COUNT_1, monitor); if (test_done_2 == 1) - cycle_2 = (monitor >> 4) & 0xf; + cycle_2 = FIELD_GET(TEST_MISO_COUNT_2, monitor); /* handle if never test done */ if (++counter > 10000) { - dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n", - __func__, - cycle_1, cycle_2, monitor); + dev_err(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n", + __func__, cycle_1, cycle_2, monitor); mtkaif_calibration_ok = false; break; } @@ -271,7 +367,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] = prev_cycle_2; } - regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1); + regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON); if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] >= 0 && mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] >= 0) @@ -307,8 +403,8 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++) param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i]; - dev_info(afe->dev, "%s(), end, calibration ok %d\n", - __func__, param->mtkaif_calibration_ok); + dev_dbg(afe->dev, "%s(), end, calibration ok %d\n", + __func__, param->mtkaif_calibration_ok); return 0; } @@ -345,7 +441,7 @@ enum { DAI_LINK_UL8_FE, DAI_LINK_UL9_FE, DAI_LINK_UL10_FE, - DAI_LINK_ADDA_BE, + DAI_LINK_DL_SRC_BE, DAI_LINK_DPTX_BE, DAI_LINK_ETDM1_IN_BE, DAI_LINK_ETDM2_IN_BE, @@ -353,6 +449,7 @@ enum { DAI_LINK_ETDM2_OUT_BE, DAI_LINK_ETDM3_OUT_BE, DAI_LINK_PCM1_BE, + DAI_LINK_UL_SRC_BE, }; static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream, @@ -389,19 +486,23 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack", + SND_JACK_LINEOUT, &priv->hdmi_jack, + mt8188_hdmi_jack_pins, + ARRAY_SIZE(mt8188_hdmi_jack_pins)); if (ret) { - dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); + dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); return ret; } ret = snd_soc_component_set_jack(component, &priv->hdmi_jack, NULL); - if (ret) - dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", - __func__, component->name, ret); + if (ret) { + dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", + __func__, component->name, ret); + return ret; + } - return ret; + return 0; } static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) @@ -410,21 +511,207 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, - &priv->dp_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT, + &priv->dp_jack, mt8188_dp_jack_pins, + ARRAY_SIZE(mt8188_dp_jack_pins)); if (ret) { - dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); + dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); return ret; } ret = snd_soc_component_set_jack(component, &priv->dp_jack, NULL); - if (ret) - dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", - __func__, component->name, ret); + if (ret) { + dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", + __func__, component->name, ret); + return ret; + } - return ret; + return 0; +} + +static int mt8188_dumb_amp_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret = 0; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dumb_spk_widgets, + ARRAY_SIZE(mt8188_dumb_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_dumb_spk_controls, + ARRAY_SIZE(mt8188_dumb_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret); + return ret; + } + + return 0; +} + +static int mt8188_max98390_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int bit_width = params_width(params); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + int i; + + snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0xf, 4, bit_width); + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!strcmp(codec_dai->component->name, MAX98390_DEV0_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV1_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV2_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x2, 0x3, 4, bit_width); + + if (!strcmp(codec_dai->component->name, MAX98390_DEV3_NAME)) + snd_soc_dai_set_tdm_slot(codec_dai, 0x1, 0x3, 4, bit_width); + } + return 0; +} + +static const struct snd_soc_ops mt8188_max98390_ops = { + .hw_params = mt8188_max98390_hw_params, +}; + +static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + /* add regular speakers dapm route */ + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dual_spk_widgets, + ARRAY_SIZE(mt8188_dual_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right Speaker widget, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_dual_spk_controls, + ARRAY_SIZE(mt8188_dual_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right card controls, ret %d\n", ret); + return ret; + } + + if (rtd->dai_link->num_codecs <= 2) + return 0; + + /* add widgets/controls/dapm for rear speakers */ + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_rear_spk_widgets, + ARRAY_SIZE(mt8188_rear_spk_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add Rear Speaker widget, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_rear_spk_controls, + ARRAY_SIZE(mt8188_rear_spk_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add Rear card controls, ret %d\n", ret); + return ret; + } + + return 0; +} + +static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_jack *jack = &priv->headset_jack; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_nau8825_widgets, + ARRAY_SIZE(mt8188_nau8825_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls, + ARRAY_SIZE(mt8188_nau8825_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + jack, + nau8825_jack_pins, + ARRAY_SIZE(nau8825_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return 0; +}; + +static void mt8188_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + + snd_soc_component_set_jack(component, NULL, NULL); } +static int mt8188_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_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int rate = params_rate(params); + unsigned int bit_width = params_width(params); + int clk_freq, ret; + + clk_freq = rate * 2 * bit_width; + + /* Configure clock for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret); + return ret; + } + + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq, + params_rate(params) * 256); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_ops mt8188_nau8825_ops = { + .hw_params = mt8188_nau8825_hw_params, +}; static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -604,13 +891,11 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { SND_SOC_DAILINK_REG(capture10), }, /* BE */ - [DAI_LINK_ADDA_BE] = { - .name = "ADDA_BE", + [DAI_LINK_DL_SRC_BE] = { + .name = "DL_SRC_BE", .no_pcm = 1, .dpcm_playback = 1, - .dpcm_capture = 1, - .init = mt8188_mt6359_init, - SND_SOC_DAILINK_REG(adda), + SND_SOC_DAILINK_REG(dl_src), }, [DAI_LINK_DPTX_BE] = { .name = "DPTX_BE", @@ -676,8 +961,48 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { .dpcm_capture = 1, SND_SOC_DAILINK_REG(pcm1), }, + [DAI_LINK_UL_SRC_BE] = { + .name = "UL_SRC_BE", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(ul_src), + }, }; +static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) +{ + struct snd_ctl_elem_id sid; + + memset(&sid, 0, sizeof(sid)); + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_find_id(card, &sid); +} + +static void mt8188_fixup_controls(struct snd_soc_card *card) +{ + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card); + struct mt8188_card_data *card_data = (struct mt8188_card_data *)priv->private_data; + struct snd_kcontrol *kctl; + + if (card_data->quirk & NAU8825_HS_PRESENT) { + struct snd_soc_dapm_widget *w, *next_w; + + for_each_card_widgets_safe(card, w, next_w) { + if (strcmp(w->name, "Headphone")) + continue; + + snd_soc_dapm_free_widget(w); + } + + kctl = ctl_find(card->snd_card, "Headphone Switch"); + if (kctl) + snd_ctl_remove(card->snd_card, kctl); + else + dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n"); + } +} + static struct snd_soc_card mt8188_mt6359_soc_card = { .owner = THIS_MODULE, .dai_link = mt8188_mt6359_dai_links, @@ -686,6 +1011,7 @@ static struct snd_soc_card mt8188_mt6359_soc_card = { .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets), .controls = mt8188_mt6359_controls, .num_controls = ARRAY_SIZE(mt8188_mt6359_controls), + .fixup_controls = mt8188_fixup_controls, }; static int mt8188_mt6359_dev_probe(struct platform_device *pdev) @@ -695,6 +1021,10 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) struct mt8188_mt6359_priv *priv; struct mt8188_card_data *card_data; struct snd_soc_dai_link *dai_link; + bool init_mt6359 = false; + bool init_nau8825 = false; + bool init_max98390 = false; + bool init_dumb = false; int ret, i; card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev); @@ -739,9 +1069,41 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8188_hdmi_codec_init; + } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || + strcmp(dai_link->name, "UL_SRC_BE") == 0) { + if (!init_mt6359) { + dai_link->init = mt8188_mt6359_init; + init_mt6359 = true; + } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM1_IN_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) { + dai_link->ops = &mt8188_max98390_ops; + if (!init_max98390) { + dai_link->init = mt8188_max98390_codec_init; + init_max98390 = true; + } + } else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) { + dai_link->ops = &mt8188_nau8825_ops; + if (!init_nau8825) { + dai_link->init = mt8188_nau8825_codec_init; + dai_link->exit = mt8188_nau8825_codec_exit; + init_nau8825 = true; + } + } else { + if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) { + if (!init_dumb) { + dai_link->init = mt8188_dumb_amp_init; + init_dumb = true; + } + } + } } } + priv->private_data = card_data; snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); @@ -758,12 +1120,15 @@ static struct mt8188_card_data mt8188_evb_card = { .name = "mt8188_mt6359", }; +static struct mt8188_card_data mt8188_nau8825_card = { + .name = "mt8188_nau8825", + .quirk = NAU8825_HS_PRESENT, +}; + static const struct of_device_id mt8188_mt6359_dt_match[] = { - { - .compatible = "mediatek,mt8188-mt6359-evb", - .data = &mt8188_evb_card, - }, - {}, + { .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, }, + { .compatible = "mediatek,mt8188-nau8825", .data = &mt8188_nau8825_card, }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match); diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h index 51cd1a83dd9d..bdd885419ff3 100644 --- a/sound/soc/mediatek/mt8188/mt8188-reg.h +++ b/sound/soc/mediatek/mt8188/mt8188-reg.h @@ -3007,6 +3007,7 @@ #define ETDM_CON0_SLAVE_MODE BIT(5) #define ETDM_CON0_SYNC_MODE BIT(1) #define ETDM_CON0_EN BIT(0) +#define ETDM_CON0_EN_SHIFT 0 #define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28) @@ -3108,6 +3109,7 @@ #define AFE_DPTX_CON_CH_NUM_8CH (0x1 << 1) #define AFE_DPTX_CON_CH_NUM_MASK BIT(1) #define AFE_DPTX_CON_ON BIT(0) +#define AFE_DPTX_CON_ON_SHIFT 0 /* AFE_ADDA_DL_SRC2_CON0 */ #define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28) diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-control.c b/sound/soc/mediatek/mt8192/mt8192-afe-control.c index 9163e05e54e1..d01b62e10088 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-control.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-control.c @@ -6,8 +6,6 @@ // Author: Shane Chien <shane.chien@mediatek.com> // -#include <linux/pm_runtime.h> - #include "mt8192-afe-common.h" enum { diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 03dabc056b91..a0f2012211fb 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3030,28 +3030,6 @@ static const struct reg_sequence mt8195_cg_patch[] = { { AUDIO_TOP_CON1, 0xfffffff8 }, }; -static int mt8195_afe_init_registers(struct mtk_base_afe *afe) -{ - return regmap_multi_reg_write(afe->regmap, - mt8195_afe_reg_defaults, - ARRAY_SIZE(mt8195_afe_reg_defaults)); -} - -static void mt8195_afe_parse_of(struct mtk_base_afe *afe, - struct device_node *np) -{ -#if IS_ENABLED(CONFIG_SND_SOC_MT6359) - struct mt8195_afe_private *afe_priv = afe->platform_priv; - - afe_priv->topckgen = syscon_regmap_lookup_by_phandle(afe->dev->of_node, - "mediatek,topckgen"); - if (IS_ERR(afe_priv->topckgen)) { - dev_info(afe->dev, "%s() Cannot find topckgen controller: %ld\n", - __func__, PTR_ERR(afe_priv->topckgen)); - } -#endif -} - static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -3062,10 +3040,8 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) struct snd_soc_component *component; ret = of_reserved_mem_device_init(dev); - if (ret) { - dev_err(dev, "failed to assign memory region: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to assign memory region\n"); ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) @@ -3089,24 +3065,17 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) /* initial audio related clock */ ret = mt8195_afe_init_clock(afe); - if (ret) { - dev_err(dev, "init clock error\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "init clock error\n"); /* reset controller to reset audio regs before regmap cache */ rstc = devm_reset_control_get_exclusive(dev, "audiosys"); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - dev_err(dev, "could not get audiosys reset:%d\n", ret); - return ret; - } + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n"); ret = reset_control_reset(rstc); - if (ret) { - dev_err(dev, "failed to trigger audio reset:%d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to trigger audio reset\n"); spin_lock_init(&afe_priv->afe_ctrl_lock); @@ -3143,30 +3112,22 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq_id, mt8195_afe_irq_handler, IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); - if (ret) { - dev_err(dev, "could not request_irq for asys-isr\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n"); /* init sub_dais */ INIT_LIST_HEAD(&afe->sub_dais); for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { ret = dai_register_cbs[i](afe); - if (ret) { - dev_warn(dev, "dai register i %d fail, ret %d\n", - i, ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "dai cb%i register fail\n", i); } /* init dai_driver and component_driver */ ret = mtk_afe_combine_sub_dai(afe); - if (ret) { - dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n"); afe->mtk_afe_hardware = &mt8195_afe_hardware; afe->memif_fs = mt8195_memif_fs; @@ -3177,18 +3138,21 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) platform_set_drvdata(pdev, afe); - mt8195_afe_parse_of(afe, pdev->dev.of_node); - - pm_runtime_enable(dev); - if (!pm_runtime_enabled(dev)) { - ret = mt8195_afe_runtime_resume(dev); - if (ret) - return ret; - } + afe_priv->topckgen = syscon_regmap_lookup_by_phandle(dev->of_node, "mediatek,topckgen"); + if (IS_ERR(afe_priv->topckgen)) + dev_dbg(afe->dev, "Cannot find topckgen controller: %ld\n", + PTR_ERR(afe_priv->topckgen)); /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to resume device\n"); afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, &mt8195_afe_regmap_config); @@ -3236,9 +3200,15 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) goto err_pm_put; } - mt8195_afe_init_registers(afe); + ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults, + ARRAY_SIZE(mt8195_afe_reg_defaults)); + if (ret) + goto err_pm_put; + + ret = pm_runtime_put_sync(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to suspend device\n"); - pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; regcache_cache_only(afe->regmap, true); @@ -3248,7 +3218,6 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) err_pm_put: pm_runtime_put_sync(dev); - pm_runtime_disable(dev); return ret; } diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index a25c397c66c5..f10c0c17863e 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -120,20 +120,18 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, if (!lb->name) return -ENOMEM; - dlc = devm_kzalloc(card->dev, 2 * sizeof(*dlc), GFP_KERNEL); + dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; - lb->cpus = &dlc[0]; - lb->codecs = &dlc[1]; + lb->cpus = dlc; + lb->codecs = &asoc_dummy_dlc; lb->num_cpus = 1; lb->num_codecs = 1; lb->stream_name = lb->name; lb->cpus->of_node = pad->cpus->of_node; lb->cpus->dai_name = "TDM Loopback"; - lb->codecs->name = "snd-soc-dummy"; - lb->codecs->dai_name = "snd-soc-dummy-dai"; lb->dpcm_capture = 1; lb->no_pcm = 1; lb->ops = &axg_card_tdm_be_ops; @@ -321,8 +319,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; - ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, - &dai_link->cpus->dai_name); + ret = meson_card_parse_dai(card, np, dai_link->cpus); if (ret) return ret; diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c index 58c411d3c489..a26b620fc177 100644 --- a/sound/soc/meson/gx-card.c +++ b/sound/soc/meson/gx-card.c @@ -90,8 +90,7 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; - ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, - &dai_link->cpus->dai_name); + ret = meson_card_parse_dai(card, np, dai_link->cpus); if (ret) return ret; diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 2d8d5717fd8b..f7fd9c013e19 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -74,23 +74,18 @@ EXPORT_SYMBOL_GPL(meson_card_reallocate_links); int meson_card_parse_dai(struct snd_soc_card *card, struct device_node *node, - struct device_node **dai_of_node, - const char **dai_name) + struct snd_soc_dai_link_component *dlc) { - struct of_phandle_args args; int ret; - if (!dai_name || !dai_of_node || !node) + if (!dlc || !node) return -EINVAL; - ret = of_parse_phandle_with_args(node, "sound-dai", - "#sound-dai-cells", 0, &args); + ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); if (ret) return dev_err_probe(card->dev, ret, "can't parse dai\n"); - *dai_of_node = args.np; - - return snd_soc_get_dai_name(&args, dai_name); + return ret; } EXPORT_SYMBOL_GPL(meson_card_parse_dai); @@ -160,8 +155,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, link->num_codecs = num_codecs; for_each_child_of_node(node, np) { - ret = meson_card_parse_dai(card, np, &codec->of_node, - &codec->dai_name); + ret = meson_card_parse_dai(card, np, codec); if (ret) { of_node_put(np); return ret; @@ -183,21 +177,13 @@ int meson_card_set_fe_link(struct snd_soc_card *card, struct device_node *node, bool is_playback) { - struct snd_soc_dai_link_component *codec; - - codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - link->codecs = codec; + link->codecs = &asoc_dummy_dlc; link->num_codecs = 1; link->dynamic = 1; link->dpcm_merged_format = 1; link->dpcm_merged_chan = 1; link->dpcm_merged_rate = 1; - link->codecs->dai_name = "snd-soc-dummy-dai"; - link->codecs->name = "snd-soc-dummy"; if (is_playback) link->dpcm_playback = 1; diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h index 74314071c80d..a5374324a189 100644 --- a/sound/soc/meson/meson-card.h +++ b/sound/soc/meson/meson-card.h @@ -39,8 +39,7 @@ int meson_card_reallocate_links(struct snd_soc_card *card, unsigned int num_links); int meson_card_parse_dai(struct snd_soc_card *card, struct device_node *node, - struct device_node **dai_of_node, - const char **dai_name); + struct snd_soc_dai_link_component *dlc); int meson_card_set_be_link(struct snd_soc_card *card, struct snd_soc_dai_link *link, struct device_node *node); diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index c1f24af17506..e2d8c41945fa 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -8,6 +8,11 @@ #include "qdsp6/q6afe.h" #include "common.h" +static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + int qcom_snd_parse_of(struct snd_soc_card *card) { struct device_node *np; @@ -96,22 +101,15 @@ int qcom_snd_parse_of(struct snd_soc_card *card) goto err; } - ret = of_parse_phandle_with_args(cpu, "sound-dai", - "#sound-dai-cells", 0, &args); - if (ret) { - dev_err(card->dev, "%s: error getting cpu phandle\n", link->name); - goto err; - } - link->cpus->of_node = args.np; - link->id = args.args[0]; - - ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); + ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { dev_err_probe(card->dev, ret, "%s: error getting cpu dai name\n", link->name); goto err; } + link->id = args.args[0]; + if (platform) { link->platforms->of_node = of_parse_phandle(platform, "sound-dai", @@ -140,17 +138,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card) } } else { /* DPCM frontend */ - dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); - if (!dlc) { - ret = -ENOMEM; - goto err; - } - - link->codecs = dlc; + link->codecs = &asoc_dummy_dlc; link->num_codecs = 1; - - link->codecs->dai_name = "snd-soc-dummy-dai"; - link->codecs->name = "snd-soc-dummy"; link->dynamic = 1; } @@ -169,6 +158,11 @@ int qcom_snd_parse_of(struct snd_soc_card *card) of_node_put(platform); } + if (!card->dapm_widgets) { + card->dapm_widgets = qcom_jack_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets); + } + return 0; err: of_node_put(cpu); diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index 41db6617e2ed..56db852f4eab 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> +#include <linux/pm.h> #include <dt-bindings/sound/sc7180-lpass.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c index d43f480cbae3..bcf18fe8e14d 100644 --- a/sound/soc/qcom/lpass-sc7280.c +++ b/sound/soc/qcom/lpass-sc7280.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <linux/pm_runtime.h> +#include <linux/pm.h> #include <dt-bindings/sound/sc7180-lpass.h> diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 1e0c918eb576..5974c7929dd3 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -196,6 +196,12 @@ struct apm_codec_dma_module_intf_cfg { #define APM_CDMA_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_codec_dma_module_intf_cfg), 8) +struct apm_display_port_module_intf_cfg { + struct apm_module_param_data param_data; + struct param_id_display_port_intf_cfg cfg; +} __packed; +#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8) + static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr) { @@ -582,6 +588,72 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk } EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync); +static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, + struct audioreach_module *module, + struct audioreach_module_config *cfg) +{ + struct apm_display_port_module_intf_cfg *intf_cfg; + struct apm_module_frame_size_factor_cfg *fs_cfg; + struct apm_module_param_data *param_data; + struct apm_module_hw_ep_mf_cfg *hw_cfg; + int ic_sz, ep_sz, fs_sz, dl_sz; + int rc, payload_size; + struct gpr_pkt *pkt; + void *p; + + ic_sz = APM_DP_INTF_CFG_PSIZE; + ep_sz = APM_HW_EP_CFG_PSIZE; + fs_sz = APM_FS_CFG_PSIZE; + dl_sz = 0; + + payload_size = ic_sz + ep_sz + fs_sz + dl_sz; + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + + hw_cfg = p; + param_data = &hw_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_MF_CFG; + param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; + + hw_cfg->mf.sample_rate = cfg->sample_rate; + hw_cfg->mf.bit_width = cfg->bit_width; + hw_cfg->mf.num_channels = cfg->num_channels; + hw_cfg->mf.data_format = module->data_format; + p += ep_sz; + + fs_cfg = p; + param_data = &fs_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR; + param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE; + fs_cfg->frame_size_factor = 1; + p += fs_sz; + + intf_cfg = p; + param_data = &intf_cfg->param_data; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_DISPLAY_PORT_INTF_CFG; + param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; + + intf_cfg->cfg.channel_allocation = cfg->channel_allocation; + intf_cfg->cfg.mst_idx = 0; + intf_cfg->cfg.dptx_idx = cfg->dp_idx; + + rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); + + kfree(pkt); + + return rc; +} + /* LPASS Codec DMA port Module Media Format Setup */ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, @@ -660,33 +732,32 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, return rc; } -static int audioreach_sal_limiter_enable(struct q6apm_graph *graph, - struct audioreach_module *module, bool enable) +int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module, + uint32_t param_id, uint32_t param_val) { struct apm_module_param_data *param_data; - struct param_id_sal_limiter_enable *limiter_enable; - int payload_size; struct gpr_pkt *pkt; - int rc; + uint32_t *param; + int rc, payload_size; void *p; - payload_size = sizeof(*limiter_enable) + APM_MODULE_PARAM_DATA_SIZE; + payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE; + p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(p)) + return -ENOMEM; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + pkt = p; + p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; param_data = p; param_data->module_instance_id = module->instance_id; param_data->error_code = 0; - param_data->param_id = PARAM_ID_SAL_LIMITER_ENABLE; - param_data->param_size = sizeof(*limiter_enable); - p = p + APM_MODULE_PARAM_DATA_SIZE; - limiter_enable = p; + param_data->param_id = param_id; + param_data->param_size = sizeof(uint32_t); - limiter_enable->enable_lim = enable; + p = p + APM_MODULE_PARAM_DATA_SIZE; + param = p; + *param = param_val; rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -694,77 +765,34 @@ static int audioreach_sal_limiter_enable(struct q6apm_graph *graph, return rc; } +EXPORT_SYMBOL_GPL(audioreach_send_u32_param); + +static int audioreach_sal_limiter_enable(struct q6apm_graph *graph, + struct audioreach_module *module, bool enable) +{ + return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable); +} static int audioreach_sal_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, struct audioreach_module_config *cfg) { - struct apm_module_param_data *param_data; - struct param_id_sal_output_config *media_format; - int payload_size; - struct gpr_pkt *pkt; - int rc; - void *p; - - payload_size = sizeof(*media_format) + APM_MODULE_PARAM_DATA_SIZE; - - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; - - param_data = p; - param_data->module_instance_id = module->instance_id; - param_data->error_code = 0; - param_data->param_id = PARAM_ID_SAL_OUTPUT_CFG; - param_data->param_size = sizeof(*media_format); - p = p + APM_MODULE_PARAM_DATA_SIZE; - media_format = p; - - media_format->bits_per_sample = cfg->bit_width; - - rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); - - kfree(pkt); - - return rc; + return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width); } static int audioreach_module_enable(struct q6apm_graph *graph, struct audioreach_module *module, bool enable) { - struct apm_module_param_data *param_data; - struct param_id_module_enable *param; - int payload_size; - struct gpr_pkt *pkt; - int rc; - void *p; - - payload_size = sizeof(*param) + APM_MODULE_PARAM_DATA_SIZE; - - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; - - param_data = p; - param_data->module_instance_id = module->instance_id; - param_data->error_code = 0; - param_data->param_id = PARAM_ID_MODULE_ENABLE; - param_data->param_size = sizeof(*param); - p = p + APM_MODULE_PARAM_DATA_SIZE; - param = p; - - param->enable = enable; - - rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); - - kfree(pkt); + return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable); +} - return rc; +static int audioreach_gapless_set_media_format(struct q6apm_graph *graph, + struct audioreach_module *module, + struct audioreach_module_config *cfg) +{ + return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY, + EARLY_EOS_DELAY_MS); } static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, @@ -814,6 +842,99 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, return rc; } +static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, + void *p, struct audioreach_module_config *mcfg) +{ + struct payload_media_fmt_aac_t *aac_cfg; + struct payload_media_fmt_pcm *mp3_cfg; + struct payload_media_fmt_flac_t *flac_cfg; + + switch (mcfg->fmt) { + case SND_AUDIOCODEC_MP3: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3; + media_fmt_hdr->payload_size = 0; + p = p + sizeof(*media_fmt_hdr); + mp3_cfg = p; + mp3_cfg->sample_rate = mcfg->sample_rate; + mp3_cfg->bit_width = mcfg->bit_width; + mp3_cfg->alignment = PCM_LSB_ALIGNED; + mp3_cfg->bits_per_sample = mcfg->bit_width; + mp3_cfg->q_factor = mcfg->bit_width - 1; + mp3_cfg->endianness = PCM_LITTLE_ENDIAN; + mp3_cfg->num_channels = mcfg->num_channels; + + if (mcfg->num_channels == 1) { + mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L; + } else if (mcfg->num_channels == 2) { + mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L; + mp3_cfg->channel_mapping[1] = PCM_CHANNEL_R; + } + break; + case SND_AUDIOCODEC_AAC: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC; + media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_aac_t); + p = p + sizeof(*media_fmt_hdr); + aac_cfg = p; + aac_cfg->aac_fmt_flag = 0; + aac_cfg->audio_obj_type = 5; + aac_cfg->num_channels = mcfg->num_channels; + aac_cfg->total_size_of_PCE_bits = 0; + aac_cfg->sample_rate = mcfg->sample_rate; + break; + case SND_AUDIOCODEC_FLAC: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC; + media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_flac_t); + p = p + sizeof(*media_fmt_hdr); + flac_cfg = p; + flac_cfg->sample_size = mcfg->codec.options.flac_d.sample_size; + flac_cfg->num_channels = mcfg->num_channels; + flac_cfg->min_blk_size = mcfg->codec.options.flac_d.min_blk_size; + flac_cfg->max_blk_size = mcfg->codec.options.flac_d.max_blk_size; + flac_cfg->sample_rate = mcfg->sample_rate; + flac_cfg->min_frame_size = mcfg->codec.options.flac_d.min_frame_size; + flac_cfg->max_frame_size = mcfg->codec.options.flac_d.max_frame_size; + break; + default: + return -EINVAL; + } + + return 0; +} + +int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg) +{ + struct media_format *header; + struct gpr_pkt *pkt; + int iid, payload_size, rc; + void *p; + + payload_size = sizeof(struct apm_sh_module_media_fmt_cmd); + + iid = q6apm_graph_get_rx_shmem_module_iid(graph); + pkt = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, + 0, graph->port->id, iid); + + if (IS_ERR(pkt)) + return -ENOMEM; + + p = (void *)pkt + GPR_HDR_SIZE; + header = p; + rc = audioreach_set_compr_media_format(header, p, mcfg); + if (rc) { + kfree(pkt); + return rc; + } + + rc = gpr_send_port_pkt(graph->port, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(audioreach_compr_set_param); + static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, struct audioreach_module_config *cfg) @@ -1017,25 +1138,33 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, p = p + APM_MODULE_PARAM_DATA_SIZE; header = p; - header->data_format = DATA_FORMAT_FIXED_POINT; - header->fmt_id = MEDIA_FMT_ID_PCM; - header->payload_size = payload_size - sizeof(*header); - - p = p + sizeof(*header); - cfg = p; - cfg->sample_rate = mcfg->sample_rate; - cfg->bit_width = mcfg->bit_width; - cfg->alignment = PCM_LSB_ALIGNED; - cfg->bits_per_sample = mcfg->bit_width; - cfg->q_factor = mcfg->bit_width - 1; - cfg->endianness = PCM_LITTLE_ENDIAN; - cfg->num_channels = mcfg->num_channels; - - if (mcfg->num_channels == 1) { - cfg->channel_mapping[0] = PCM_CHANNEL_L; - } else if (num_channels == 2) { - cfg->channel_mapping[0] = PCM_CHANNEL_L; - cfg->channel_mapping[1] = PCM_CHANNEL_R; + if (mcfg->fmt == SND_AUDIOCODEC_PCM) { + header->data_format = DATA_FORMAT_FIXED_POINT; + header->fmt_id = MEDIA_FMT_ID_PCM; + header->payload_size = payload_size - sizeof(*header); + + p = p + sizeof(*header); + cfg = p; + cfg->sample_rate = mcfg->sample_rate; + cfg->bit_width = mcfg->bit_width; + cfg->alignment = PCM_LSB_ALIGNED; + cfg->bits_per_sample = mcfg->bit_width; + cfg->q_factor = mcfg->bit_width - 1; + cfg->endianness = PCM_LITTLE_ENDIAN; + cfg->num_channels = mcfg->num_channels; + + if (mcfg->num_channels == 1) + cfg->channel_mapping[0] = PCM_CHANNEL_L; + else if (num_channels == 2) { + cfg->channel_mapping[0] = PCM_CHANNEL_L; + cfg->channel_mapping[1] = PCM_CHANNEL_R; + } + } else { + rc = audioreach_set_compr_media_format(header, p, mcfg); + if (rc) { + kfree(pkt); + return rc; + } } rc = audioreach_graph_send_cmd_sync(graph, pkt, 0); @@ -1120,8 +1249,13 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_PCM_DEC: case MODULE_ID_PCM_ENC: case MODULE_ID_PCM_CNV: + case MODULE_ID_PLACEHOLDER_DECODER: + case MODULE_ID_PLACEHOLDER_ENCODER: rc = audioreach_pcm_set_media_format(graph, module, cfg); break; + case MODULE_ID_DISPLAY_PORT_SINK: + rc = audioreach_display_port_set_media_format(graph, module, cfg); + break; case MODULE_ID_I2S_SOURCE: case MODULE_ID_I2S_SINK: rc = audioreach_i2s_set_media_format(graph, module, cfg); @@ -1144,6 +1278,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_MFC: rc = audioreach_mfc_set_media_format(graph, module, cfg); break; + case MODULE_ID_GAPLESS: + rc = audioreach_gapless_set_media_format(graph, module, cfg); + break; default: rc = 0; } diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 1d1d47d47d40..e38111ffd7b9 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -15,6 +15,8 @@ struct q6apm_graph; #define MODULE_ID_PCM_CNV 0x07001003 #define MODULE_ID_PCM_ENC 0x07001004 #define MODULE_ID_PCM_DEC 0x07001005 +#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008 +#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009 #define MODULE_ID_SAL 0x07001010 #define MODULE_ID_MFC 0x07001015 #define MODULE_ID_CODEC_DMA_SINK 0x07001023 @@ -22,6 +24,11 @@ struct q6apm_graph; #define MODULE_ID_I2S_SINK 0x0700100A #define MODULE_ID_I2S_SOURCE 0x0700100B #define MODULE_ID_DATA_LOGGING 0x0700101A +#define MODULE_ID_AAC_DEC 0x0700101F +#define MODULE_ID_FLAC_DEC 0x0700102F +#define MODULE_ID_MP3_DECODE 0x0700103B +#define MODULE_ID_GAPLESS 0x0700104D +#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069 #define APM_CMD_GET_SPF_STATE 0x01001021 #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 @@ -142,12 +149,15 @@ struct param_id_enc_bitrate_param { } __packed; #define DATA_FORMAT_FIXED_POINT 1 +#define DATA_FORMAT_GENERIC_COMPRESSED 5 +#define DATA_FORMAT_RAW_COMPRESSED 6 #define PCM_LSB_ALIGNED 1 #define PCM_MSB_ALIGNED 2 #define PCM_LITTLE_ENDIAN 1 #define PCM_BIT_ENDIAN 2 #define MEDIA_FMT_ID_PCM 0x09001000 +#define MEDIA_FMT_ID_MP3 0x09001009 #define PCM_CHANNEL_L 1 #define PCM_CHANNEL_R 2 #define SAMPLE_RATE_48K 48000 @@ -225,6 +235,28 @@ struct apm_media_format { uint32_t payload_size; } __packed; +#define MEDIA_FMT_ID_FLAC 0x09001004 + +struct payload_media_fmt_flac_t { + uint16_t num_channels; + uint16_t sample_size; + uint16_t min_blk_size; + uint16_t max_blk_size; + uint32_t sample_rate; + uint32_t min_frame_size; + uint32_t max_frame_size; +} __packed; + +#define MEDIA_FMT_ID_AAC 0x09001001 + +struct payload_media_fmt_aac_t { + uint16_t aac_fmt_flag; + uint16_t audio_obj_type; + uint16_t num_channels; + uint16_t total_size_of_PCE_bits; + uint32_t sample_rate; +} __packed; + #define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002 #define WR_SH_MEM_EP_EOS_POLICY_LAST 1 #define WR_SH_MEM_EP_EOS_POLICY_EACH 2 @@ -444,6 +476,15 @@ struct param_id_i2s_intf_cfg { #define PORT_ID_I2S_OUPUT 1 #define I2S_STACK_SIZE 2048 +#define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 + +struct param_id_display_port_intf_cfg { + uint32_t channel_allocation; + /* Multi-Steam Transport index */ + uint32_t mst_idx; + uint32_t dptx_idx; +} __packed; + #define PARAM_ID_HW_EP_MF_CFG 0x08001017 struct param_id_hw_ep_mf { uint32_t sample_rate; @@ -512,6 +553,8 @@ struct param_id_sal_limiter_enable { } __packed; #define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024 +#define PARAM_ID_EARLY_EOS_DELAY 0x0800114C +#define EARLY_EOS_DELAY_MS 150 struct param_id_mfc_media_format { uint32_t sample_rate; @@ -520,6 +563,10 @@ struct param_id_mfc_media_format { uint16_t channel_mapping[]; } __packed; +struct param_id_gapless_early_eos_delay_t { + uint32_t early_eos_delay_ms; +} __packed; + struct media_format { uint32_t data_format; uint32_t fmt_id; @@ -598,6 +645,15 @@ struct param_id_vol_ctrl_master_gain { } __packed; +#define PARAM_ID_REMOVE_INITIAL_SILENCE 0x0800114B +#define PARAM_ID_REMOVE_TRAILING_SILENCE 0x0800115D + +#define PARAM_ID_REAL_MODULE_ID 0x0800100B + +struct param_id_placeholder_real_module_id { + uint32_t real_module_id; +} __packed; + /* Graph */ struct audioreach_connection { /* Connections */ @@ -702,8 +758,11 @@ struct audioreach_module_config { u16 data_format; u16 num_channels; u16 active_channels_mask; + u16 dp_idx; + u32 channel_allocation; u32 sd_line_mask; int fmt; + struct snd_codec codec; u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; }; @@ -740,4 +799,8 @@ int audioreach_set_media_format(struct q6apm_graph *graph, int audioreach_shared_memory_send_eos(struct q6apm_graph *graph); int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol); +int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module, + uint32_t param_id, uint32_t param_val); +int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg); + #endif /* __AUDIOREACH_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 8bb7452b8f18..31e0bad71e95 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -12,6 +12,7 @@ #include <sound/soc.h> #include <sound/pcm_params.h> #include "q6dsp-lpass-ports.h" +#include "q6dsp-common.h" #include "q6afe.h" @@ -69,6 +70,7 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream, struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); int channels = params_channels(params); struct q6afe_hdmi_cfg *hdmi = &dai_data->port_config[dai->id].hdmi; + int ret; hdmi->sample_rate = params_rate(params); switch (params_format(params)) { @@ -80,33 +82,11 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream, break; } - /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */ - switch (channels) { - case 2: - hdmi->channel_allocation = 0; - break; - case 3: - hdmi->channel_allocation = 0x02; - break; - case 4: - hdmi->channel_allocation = 0x06; - break; - case 5: - hdmi->channel_allocation = 0x0A; - break; - case 6: - hdmi->channel_allocation = 0x0B; - break; - case 7: - hdmi->channel_allocation = 0x12; - break; - case 8: - hdmi->channel_allocation = 0x13; - break; - default: - dev_err(dai->dev, "invalid Channels = %u\n", channels); - return -EINVAL; - } + ret = q6dsp_get_channel_allocation(channels); + if (ret < 0) + return ret; + + hdmi->channel_allocation = (u16) ret; return 0; } diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 7f02f5b2c33f..5eb0b864c740 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -28,8 +28,27 @@ #define CAPTURE_MIN_PERIOD_SIZE 320 #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE) #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) #define SID_MASK_DEFAULT 0xF +static const struct snd_compr_codec_caps q6apm_compr_caps = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 88200, + 96000, 176400, 192000 }, + .descriptor[0].num_sample_rates = 13, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 128, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + enum stream_state { Q6APM_STREAM_IDLE = 0, Q6APM_STREAM_STOPPED, @@ -39,6 +58,7 @@ enum stream_state { struct q6apm_dai_rtd { struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; + struct snd_codec codec; struct snd_compr_params codec_param; struct snd_dma_buffer dma_buffer; phys_addr_t phys; @@ -52,9 +72,13 @@ struct q6apm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ uint16_t session_id; + bool next_track; enum stream_state state; struct q6apm_graph *graph; spinlock_t lock; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + bool notify_on_drain; }; struct q6apm_dai_data { @@ -132,6 +156,69 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo } } +static void event_handler_compr(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6apm_dai_rtd *prtd = priv; + struct snd_compr_stream *substream = prtd->cstream; + unsigned long flags; + uint32_t wflags = 0; + uint64_t avail; + uint32_t bytes_written, bytes_to_write; + bool is_last_buffer = false; + + switch (opcode) { + case APM_CLIENT_EVENT_CMD_EOS_DONE: + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->notify_on_drain) { + snd_compr_drain_notify(prtd->cstream); + prtd->notify_on_drain = false; + } else { + prtd->state = Q6APM_STREAM_STOPPED; + } + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case APM_CLIENT_EVENT_DATA_WRITE_DONE: + spin_lock_irqsave(&prtd->lock, flags); + bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT; + prtd->copied_total += bytes_written; + snd_compr_fragment_elapsed(substream); + + if (prtd->state != Q6APM_STREAM_RUNNING) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail > prtd->pcm_count) { + bytes_to_write = prtd->pcm_count; + } else { + if (substream->partial_drain || prtd->notify_on_drain) + is_last_buffer = true; + bytes_to_write = avail; + } + + if (bytes_to_write) { + if (substream->partial_drain && is_last_buffer) + wflags |= APM_LAST_BUFFER_FLAG; + + q6apm_write_async(prtd->graph, + bytes_to_write, 0, 0, wflags); + + prtd->bytes_sent += bytes_to_write; + + if (prtd->notify_on_drain && is_last_buffer) + audioreach_shared_memory_send_eos(prtd->graph); + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + default: + break; + } +} + static int q6apm_dai_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -155,6 +242,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.sample_rate = runtime->rate; cfg.num_channels = runtime->channels; cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = SND_AUDIOCODEC_PCM; if (prtd->state) { /* clear the previous setup if any */ @@ -386,6 +474,362 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); } +static int q6apm_dai_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd; + struct q6apm_dai_data *pdata; + struct device *dev = component->dev; + int ret, size; + int graph_id; + + graph_id = cpu_dai->driver->id; + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->cstream = stream; + prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id); + if (IS_ERR(prtd->graph)) { + ret = PTR_ERR(prtd->graph); + kfree(prtd); + return ret; + } + + runtime->private_data = prtd; + runtime->dma_bytes = BUFFER_BYTES_MAX; + size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer); + if (ret) + return ret; + + if (pdata->sid < 0) + prtd->phys = prtd->dma_buffer.addr; + else + prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); + + snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); + spin_lock_init(&prtd->lock); + + q6apm_enable_compress_module(dev, prtd->graph, true); + return 0; +} + +static int q6apm_dai_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + + q6apm_graph_stop(prtd->graph); + q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_graph_close(prtd->graph); + snd_dma_free_pages(&prtd->dma_buffer); + prtd->graph = NULL; + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int q6apm_dai_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + caps->direction = SND_COMPRESS_PLAYBACK; + caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + caps->num_codecs = 3; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_AAC; + caps->codecs[2] = SND_AUDIOCODEC_FLAC; + + return 0; +} + +static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + *codec = q6apm_compr_caps; + break; + default: + break; + } + + return 0; +} + +static int q6apm_dai_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + tstamp->copied_total = prtd->copied_total; + tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +static int q6apm_dai_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *stream, int cmd) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP); + break; + case SNDRV_PCM_TRIGGER_STOP: + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + prtd->next_track = true; + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + prtd->notify_on_drain = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + +static int q6apm_dai_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + struct q6apm_dai_data *pdata; + struct audioreach_module_config cfg; + struct snd_codec *codec = ¶ms->codec; + int dir = stream->direction; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + + prtd->pos = 0; + + if (prtd->next_track != true) { + memcpy(&prtd->codec, codec, sizeof(*codec)); + + ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id); + if (ret) + return ret; + + cfg.direction = dir; + cfg.sample_rate = codec->sample_rate; + cfg.num_channels = 2; + cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = codec->id; + memcpy(&cfg.codec, codec, sizeof(*codec)); + + ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); + if (ret < 0) + return ret; + + ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); + if (ret) + return ret; + + ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, + prtd->phys, (prtd->pcm_size / prtd->periods), + prtd->periods); + if (ret < 0) + return -ENOMEM; + + ret = q6apm_graph_prepare(prtd->graph); + if (ret) + return ret; + + ret = q6apm_graph_start(prtd->graph); + if (ret) + return ret; + + } else { + cfg.direction = dir; + cfg.sample_rate = codec->sample_rate; + cfg.num_channels = 2; + cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = codec->id; + memcpy(&cfg.codec, codec, sizeof(*codec)); + + ret = audioreach_compr_set_param(prtd->graph, &cfg); + if (ret < 0) + return ret; + } + prtd->state = Q6APM_STREAM_RUNNING; + + return 0; +} + +static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_ENCODER_PADDING: + prtd->trailing_samples_drop = metadata->value[0]; + q6apm_remove_trailing_silence(component->dev, prtd->graph, + prtd->trailing_samples_drop); + break; + case SNDRV_COMPRESS_ENCODER_DELAY: + prtd->initial_samples_drop = metadata->value[0]; + q6apm_remove_initial_silence(component->dev, prtd->graph, + prtd->initial_samples_drop); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6apm_dai_compr_mmap(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct vm_area_struct *vma) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + struct device *dev = component->dev; + + return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr, + prtd->dma_buffer.bytes); +} + +static int q6apm_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + void *dstn; + unsigned long flags; + size_t copy; + u32 wflags = 0; + u32 app_pointer; + u32 bytes_received; + uint32_t bytes_to_write; + int avail, bytes_in_flight = 0; + + bytes_received = prtd->bytes_received; + + /** + * Make sure that next track data pointer is aligned at 32 bit boundary + * This is a Mandatory requirement from DSP data buffers alignment + */ + if (prtd->next_track) + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + + app_pointer = bytes_received/prtd->pcm_size; + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + dstn = prtd->dma_buffer.area + app_pointer; + + if (count < prtd->pcm_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = prtd->pcm_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy)) + return -EFAULT; + } + + spin_lock_irqsave(&prtd->lock, flags); + bytes_in_flight = prtd->bytes_received - prtd->copied_total; + + if (prtd->next_track) { + prtd->next_track = false; + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); + } + + prtd->bytes_received = bytes_received + count; + + /* Kick off the data to dsp if its starving!! */ + if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) { + bytes_to_write = prtd->pcm_count; + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail < prtd->pcm_count) + bytes_to_write = avail; + + q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags); + prtd->bytes_sent += bytes_to_write; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + +static const struct snd_compress_ops q6apm_dai_compress_ops = { + .open = q6apm_dai_compr_open, + .free = q6apm_dai_compr_free, + .get_caps = q6apm_dai_compr_get_caps, + .get_codec_caps = q6apm_dai_compr_get_codec_caps, + .pointer = q6apm_dai_compr_pointer, + .trigger = q6apm_dai_compr_trigger, + .ack = q6apm_dai_compr_ack, + .set_params = q6apm_dai_compr_set_params, + .set_metadata = q6apm_dai_compr_set_metadata, + .mmap = q6apm_dai_compr_mmap, + .copy = q6apm_compr_copy, +}; + static const struct snd_soc_component_driver q6apm_fe_dai_component = { .name = DRV_NAME, .open = q6apm_dai_open, @@ -395,6 +839,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .hw_params = q6apm_dai_hw_params, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, + .compress_ops = &q6apm_dai_compress_ops, }; static int q6apm_dai_probe(struct platform_device *pdev) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 420e8aa11f42..7ad604b80e25 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -11,6 +11,7 @@ #include <sound/soc.h> #include <sound/pcm_params.h> #include "q6dsp-lpass-ports.h" +#include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" @@ -91,6 +92,36 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, return 0; } +static int q6hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + int channels = params_channels(params); + int ret; + + cfg->bit_width = params_width(params); + cfg->sample_rate = params_rate(params); + cfg->num_channels = channels; + + switch (dai->id) { + case DISPLAY_PORT_RX_0: + cfg->dp_idx = 0; + break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + cfg->dp_idx = dai->id - DISPLAY_PORT_RX_1 + 1; + break; + } + + ret = q6dsp_get_channel_allocation(channels); + if (ret < 0) + return ret; + + cfg->channel_allocation = ret; + + return 0; +} + static int q6dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -215,6 +246,13 @@ static const struct snd_soc_dai_ops q6i2s_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, +}; + +static const struct snd_soc_dai_ops q6hdmi_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6apm_lpass_dai_shutdown, + .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, }; @@ -242,6 +280,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; + cfg.q6hdmi_ops = &q6hdmi_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index a7a3f973eb6d..7bfac9492ab5 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -298,6 +298,71 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions); +int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence); + +int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence); + +int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en); +} +EXPORT_SYMBOL_GPL(q6apm_enable_compress_module); + +int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, + uint32_t codec_id) +{ + struct audioreach_module *module; + uint32_t module_id; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + switch (codec_id) { + case SND_AUDIOCODEC_MP3: + module_id = MODULE_ID_MP3_DECODE; + break; + case SND_AUDIOCODEC_AAC: + module_id = MODULE_ID_AAC_DEC; + break; + case SND_AUDIOCODEC_FLAC: + module_id = MODULE_ID_FLAC_DEC; + break; + default: + return -EINVAL; + } + + return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID, + module_id); +} +EXPORT_SYMBOL_GPL(q6apm_set_real_module_id); + int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { struct audioreach_graph_info *info = graph->info; @@ -497,6 +562,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) } break; case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED: + client_event = APM_CLIENT_EVENT_CMD_EOS_DONE; + if (graph->cb) + graph->cb(client_event, hdr->token, data->payload, graph->priv); break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7005be9b63e3..8ee40732ce9e 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -45,6 +45,8 @@ #define APM_WRITE_TOKEN_LEN_SHIFT 16 #define APM_MAX_SESSIONS 8 +#define APM_LAST_BUFFER_FLAG BIT(30) +#define NO_TIMESTAMP 0xFF00 struct q6apm { struct device *dev; @@ -147,4 +149,8 @@ int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph); bool q6apm_is_adsp_ready(void); +int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en); +int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); +int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); +int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id); #endif /* __APM_GRAPH_ */ diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.c b/sound/soc/qcom/qdsp6/q6dsp-common.c index d393003492c7..95585dea2b36 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-common.c +++ b/sound/soc/qcom/qdsp6/q6dsp-common.c @@ -63,4 +63,39 @@ int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch) return 0; } EXPORT_SYMBOL_GPL(q6dsp_map_channels); + +int q6dsp_get_channel_allocation(int channels) +{ + int channel_allocation; + + /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */ + switch (channels) { + case 2: + channel_allocation = 0; + break; + case 3: + channel_allocation = 0x02; + break; + case 4: + channel_allocation = 0x06; + break; + case 5: + channel_allocation = 0x0A; + break; + case 6: + channel_allocation = 0x0B; + break; + case 7: + channel_allocation = 0x12; + break; + case 8: + channel_allocation = 0x13; + break; + default: + return -EINVAL; + } + + return channel_allocation; +} +EXPORT_SYMBOL_GPL(q6dsp_get_channel_allocation); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.h b/sound/soc/qcom/qdsp6/q6dsp-common.h index 01094d108b8a..9e704db5f604 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-common.h +++ b/sound/soc/qcom/qdsp6/q6dsp-common.h @@ -20,5 +20,6 @@ #define PCM_CHANNELS 10 /* Top surround channel. */ int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch); +int q6dsp_get_channel_allocation(int channels); #endif /* __Q6DSP_COMMON_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index f67c16fd90b9..ac937a6bf909 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -79,6 +79,22 @@ .id = did, \ } +#define Q6AFE_DP_RX_DAI(did) { \ + .playback = { \ + .stream_name = #did" Playback", \ + .rates = SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE, \ + .channels_min = 2, \ + .channels_max = 8, \ + .rate_min = 48000, \ + .rate_max = 192000, \ + }, \ + .name = #did, \ + .id = did, \ + } static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { { @@ -528,22 +544,14 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5), Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6), Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7), - { - .playback = { - .stream_name = "Display Port Playback", - .rates = SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 2, - .channels_max = 8, - .rate_max = 192000, - .rate_min = 48000, - }, - .id = DISPLAY_PORT_RX, - .name = "DISPLAY_PORT", - }, + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_0), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_1), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_2), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_3), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_4), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_5), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_6), + Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_7), Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0), Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0), Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1), @@ -603,6 +611,9 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, case DISPLAY_PORT_RX: q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops; break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops; + break; case SLIMBUS_0_RX ... SLIMBUS_6_TX: q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops; break; diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index da7469a6a267..787dd49e03f6 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -14,6 +14,7 @@ #include <sound/soc.h> #include <sound/rt5682s.h> #include <linux/soundwire/sdw.h> +#include <sound/pcm_params.h> #include "../codecs/rt5682.h" #include "../codecs/rt5682s.h" @@ -196,8 +197,10 @@ static int sc7280_snd_hw_params(struct snd_pcm_substream *substream, struct sdw_stream_runtime *sruntime; int i; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000); + if (!rtd->dai_link->no_pcm) { + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000); + } switch (cpu_dai->id) { case LPASS_CDC_DMA_TX3: @@ -358,6 +361,20 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), }; +static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + static int sc7280_snd_platform_probe(struct platform_device *pdev) { struct snd_soc_card *card; @@ -387,6 +404,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev) for_each_card_prelinks(card, i, link) { link->init = sc7280_init; link->ops = &sc7280_ops; + if (link->no_pcm == 1) + link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup; } return devm_snd_soc_register_card(dev, card); diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index fd95a79cc9fa..a5442592bde4 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -205,7 +205,6 @@ static int odroid_audio_probe(struct platform_device *pdev) struct snd_soc_card *card; struct snd_soc_dai_link *link, *codec_link; int num_pcms, ret, i; - struct of_phandle_args args = {}; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -260,20 +259,7 @@ static int odroid_audio_probe(struct platform_device *pdev) } for (i = 0; i < num_pcms; i++, link += 2) { - ret = of_parse_phandle_with_args(cpu, "sound-dai", - "#sound-dai-cells", i, &args); - if (ret < 0) - break; - - if (!args.np) { - dev_err(dev, "sound-dai property parse error: %d\n", ret); - ret = -EINVAL; - break; - } - - ret = snd_soc_get_dai_name(&args, &link->cpus->dai_name); - of_node_put(args.np); - + ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name, i); if (ret < 0) break; } diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index 84e1b14e68e4..d0b5c543fd2f 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -796,3 +796,5 @@ module_platform_driver(siu_driver); MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>"); MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("siu_spb.bin"); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index ff25718ff2e8..4356cc320fea 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -236,6 +236,28 @@ int snd_soc_component_force_enable_pin_unlocked( } EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked); +int snd_soc_component_notify_control(struct snd_soc_component *component, + const char * const ctl) +{ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct snd_kcontrol *kctl; + + if (component->name_prefix) + snprintf(name, ARRAY_SIZE(name), "%s %s", component->name_prefix, ctl); + else + snprintf(name, ARRAY_SIZE(name), "%s", ctl); + + kctl = snd_soc_card_get_kcontrol(component->card, name); + if (!kctl) + return soc_component_ret(component, -EINVAL); + + snd_ctl_notify(component->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_component_notify_control); + /** * snd_soc_component_set_jack - configure component jack. * @component: COMPONENTs diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index d8715db5e415..02fdb683f75f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -20,7 +20,6 @@ #include <sound/initval.h> #include <sound/soc-dpcm.h> #include <sound/soc-link.h> -#include <linux/pm_runtime.h> static int snd_soc_compr_components_open(struct snd_compr_stream *cstream) { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b48efc3a08d2..11bc5250ffd0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3196,6 +3196,40 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_clock_provider_raw); +int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream) +{ + /* + * [Normal] + * + * Playback + * CPU : SNDRV_PCM_STREAM_PLAYBACK + * Codec: SNDRV_PCM_STREAM_PLAYBACK + * + * Capture + * CPU : SNDRV_PCM_STREAM_CAPTURE + * Codec: SNDRV_PCM_STREAM_CAPTURE + */ + if (!dai_link->c2c_params) + return stream; + + /* + * [Codec2Codec] + * + * Playback + * CPU : SNDRV_PCM_STREAM_CAPTURE + * Codec: SNDRV_PCM_STREAM_PLAYBACK + * + * Capture + * CPU : SNDRV_PCM_STREAM_PLAYBACK + * Codec: SNDRV_PCM_STREAM_CAPTURE + */ + if (stream == SNDRV_PCM_STREAM_CAPTURE) + return SNDRV_PCM_STREAM_PLAYBACK; + + return SNDRV_PCM_STREAM_CAPTURE; +} +EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu); + int snd_soc_get_dai_id(struct device_node *ep) { struct snd_soc_component *component; @@ -3223,12 +3257,13 @@ int snd_soc_get_dai_id(struct device_node *ep) } EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); -int snd_soc_get_dai_name(const struct of_phandle_args *args, - const char **dai_name) +int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_component *dlc) { struct snd_soc_component *pos; int ret = -EPROBE_DEFER; + dlc->of_node = args->np; + mutex_lock(&client_mutex); for_each_component(pos) { struct device_node *component_of_node = soc_component_to_node(pos); @@ -3236,7 +3271,7 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args, if (component_of_node != args->np || !pos->num_dai) continue; - ret = snd_soc_component_of_xlate_dai_name(pos, args, dai_name); + ret = snd_soc_component_of_xlate_dai_name(pos, args, &dlc->dai_name); if (ret == -ENOTSUPP) { struct snd_soc_dai *dai; int id = -1; @@ -3267,9 +3302,9 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args, id--; } - *dai_name = dai->driver->name; - if (!*dai_name) - *dai_name = pos->name; + dlc->dai_name = dai->driver->name; + if (!dlc->dai_name) + dlc->dai_name = pos->name; } else if (ret) { /* * if another error than ENOTSUPP is returned go on and @@ -3285,22 +3320,49 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args, mutex_unlock(&client_mutex); return ret; } -EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); +EXPORT_SYMBOL_GPL(snd_soc_get_dlc); -int snd_soc_of_get_dai_name(struct device_node *of_node, - const char **dai_name) +int snd_soc_of_get_dlc(struct device_node *of_node, + struct of_phandle_args *args, + struct snd_soc_dai_link_component *dlc, + int index) { - struct of_phandle_args args; + struct of_phandle_args __args; int ret; + if (!args) + args = &__args; + ret = of_parse_phandle_with_args(of_node, "sound-dai", - "#sound-dai-cells", 0, &args); + "#sound-dai-cells", index, args); if (ret) return ret; - ret = snd_soc_get_dai_name(&args, dai_name); + return snd_soc_get_dlc(args, dlc); +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dlc); + +int snd_soc_get_dai_name(const struct of_phandle_args *args, + const char **dai_name) +{ + struct snd_soc_dai_link_component dlc; + int ret = snd_soc_get_dlc(args, &dlc); + + if (ret == 0) + *dai_name = dlc.dai_name; + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); + +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name, int index) +{ + struct snd_soc_dai_link_component dlc; + int ret = snd_soc_of_get_dlc(of_node, NULL, &dlc, index); - of_node_put(args.np); + if (ret == 0) + *dai_name = dlc.dai_name; return ret; } @@ -3341,26 +3403,6 @@ static int __snd_soc_of_get_dai_link_component_alloc( return 0; } -static int __snd_soc_of_get_dai_link_component_parse( - struct device_node *of_node, - struct snd_soc_dai_link_component *component, int index) -{ - struct of_phandle_args args; - int ret; - - ret = of_parse_phandle_with_args(of_node, "sound-dai", "#sound-dai-cells", - index, &args); - if (ret) - return ret; - - ret = snd_soc_get_dai_name(&args, &component->dai_name); - if (ret < 0) - return ret; - - component->of_node = args.np; - return 0; -} - /* * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array * @dai_link: DAI link @@ -3405,7 +3447,7 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, /* Parse the list */ for_each_link_codecs(dai_link, index, component) { - ret = __snd_soc_of_get_dai_link_component_parse(of_node, component, index); + ret = snd_soc_of_get_dlc(of_node, NULL, component, index); if (ret) goto err; } @@ -3460,7 +3502,7 @@ int snd_soc_of_get_dai_link_cpus(struct device *dev, /* Parse the list */ for_each_link_cpus(dai_link, index, component) { - ret = __snd_soc_of_get_dai_link_component_parse(of_node, component, index); + ret = snd_soc_of_get_dlc(of_node, NULL, component, index); if (ret) goto err; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f2f04ce693a1..3091e8160bad 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2216,6 +2216,16 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) &dapm_widget_power_fops); } +static void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + if (!dapm->debugfs_dapm || !w->name) + return; + + debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm); +} + static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) { debugfs_remove_recursive(dapm->debugfs_dapm); @@ -2232,6 +2242,10 @@ static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) { } +static inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) +{ +} + static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) { } @@ -2495,6 +2509,8 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) dapm_free_path(p); } + dapm_debugfs_free_widget(w); + kfree(w->kcontrols); kfree_const(w->name); kfree_const(w->sname); @@ -4322,39 +4338,6 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm, snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL); } -static int get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream) -{ - /* - * [Normal] - * - * Playback - * CPU : SNDRV_PCM_STREAM_PLAYBACK - * Codec: SNDRV_PCM_STREAM_PLAYBACK - * - * Playback - * CPU : SNDRV_PCM_STREAM_CAPTURE - * Codec: SNDRV_PCM_STREAM_CAPTURE - */ - if (!dai_link->c2c_params) - return stream; - - /* - * [Codec2Codec] - * - * Playback - * CPU : SNDRV_PCM_STREAM_CAPTURE - * Codec: SNDRV_PCM_STREAM_PLAYBACK - * - * Capture - * CPU : SNDRV_PCM_STREAM_PLAYBACK - * Codec: SNDRV_PCM_STREAM_CAPTURE - */ - if (stream == SNDRV_PCM_STREAM_CAPTURE) - return SNDRV_PCM_STREAM_PLAYBACK; - - return SNDRV_PCM_STREAM_CAPTURE; -} - static void dapm_connect_dai_pair(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *codec_dai, @@ -4372,7 +4355,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, for_each_pcm_streams(stream) { int stream_cpu, stream_codec; - stream_cpu = get_stream_cpu(dai_link, stream); + stream_cpu = snd_soc_get_stream_cpu(dai_link, stream); stream_codec = stream; /* connect BE DAI playback if widgets are valid */ @@ -4461,9 +4444,31 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) for_each_rtd_codec_dais(rtd, i, codec_dai) dapm_connect_dai_pair(card, rtd, codec_dai, asoc_rtd_to_cpu(rtd, i)); + } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) { + int cpu_id; + + if (!rtd->dai_link->codec_ch_maps) { + dev_err(card->dev, "%s: no codec channel mapping table provided\n", + __func__); + continue; + } + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id; + if (cpu_id >= rtd->dai_link->num_cpus) { + dev_err(card->dev, + "%s: dai_link %s cpu_id %d too large, num_cpus is %d\n", + __func__, rtd->dai_link->name, cpu_id, + rtd->dai_link->num_cpus); + continue; + } + dapm_connect_dai_pair(card, rtd, codec_dai, + asoc_rtd_to_cpu(rtd, cpu_id)); + } } else { dev_err(card->dev, - "N cpus to M codecs link is not supported yet\n"); + "%s: codec number %d < cpu number %d is not supported\n", + __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus); } } } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4fb1ac8e1c4a..8896227e4fb7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -14,7 +14,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pinctrl/consumer.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/export.h> @@ -1035,6 +1034,10 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + struct snd_pcm_hw_params cpu_params; + unsigned int ch_mask = 0; + int j; + /* * Skip CPUs which don't support the current stream * type. See soc_pcm_init_runtime_hw() for more details @@ -1042,13 +1045,32 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) continue; - ret = snd_soc_dai_hw_params(cpu_dai, substream, params); + /* copy params for each cpu */ + cpu_params = *params; + + if (!rtd->dai_link->codec_ch_maps) + goto hw_params; + /* + * construct cpu channel mask by combining ch_mask of each + * codec which maps to the cpu. + */ + for_each_rtd_codec_dais(rtd, j, codec_dai) { + if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == i) + ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask; + } + + /* fixup cpu channel number */ + if (ch_mask) + soc_pcm_codec_params_fixup(&cpu_params, ch_mask); + +hw_params: + ret = snd_soc_dai_hw_params(cpu_dai, substream, &cpu_params); if (ret < 0) goto out; /* store the parameters for each DAI */ - soc_pcm_set_dai_params(cpu_dai, params); - snd_soc_dapm_update_dai(substream, params, cpu_dai); + soc_pcm_set_dai_params(cpu_dai, &cpu_params); + snd_soc_dapm_update_dai(substream, &cpu_params, cpu_dai); } ret = snd_soc_pcm_component_hw_params(substream, params); @@ -1072,49 +1094,67 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } +#define TRIGGER_MAX 3 +static int (* const trigger[][TRIGGER_MAX])(struct snd_pcm_substream *substream, int cmd, int rollback) = { + [SND_SOC_TRIGGER_ORDER_DEFAULT] = { + snd_soc_link_trigger, + snd_soc_pcm_component_trigger, + snd_soc_pcm_dai_trigger, + }, + [SND_SOC_TRIGGER_ORDER_LDC] = { + snd_soc_link_trigger, + snd_soc_pcm_dai_trigger, + snd_soc_pcm_component_trigger, + }, +}; + static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; - int ret = -EINVAL, _ret = 0, start_dma_last = 0, i; + int ret = 0, r = 0, i; int rollback = 0; + int start = 0, stop = 0; + /* + * select START/STOP sequence + */ + for_each_rtd_components(rtd, i, component) { + if (component->driver->trigger_start) + start = component->driver->trigger_start; + if (component->driver->trigger_stop) + stop = component->driver->trigger_stop; + } + if (rtd->dai_link->trigger_start) + start = rtd->dai_link->trigger_start; + if (rtd->dai_link->trigger_stop) + stop = rtd->dai_link->trigger_stop; + + if (start < 0 || start >= SND_SOC_TRIGGER_ORDER_MAX || + stop < 0 || stop >= SND_SOC_TRIGGER_ORDER_MAX) + return -EINVAL; + + /* + * START + */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - /* Do we need to start dma last? */ - for_each_rtd_components(rtd, i, component) { - if (component->driver->start_dma_last) { - start_dma_last = 1; + for (i = 0; i < TRIGGER_MAX; i++) { + r = trigger[start][i](substream, cmd, 0); + if (r < 0) break; - } - } - - ret = snd_soc_link_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; - - if (start_dma_last) { - ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; - - ret = snd_soc_pcm_component_trigger(substream, cmd, 0); - } else { - ret = snd_soc_pcm_component_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; - - ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); } -start_err: - if (ret < 0) - rollback = 1; } - if (rollback) { - _ret = ret; + /* + * Rollback if START failed + * find correspond STOP command + */ + if (r < 0) { + rollback = 1; + ret = r; switch (cmd) { case SNDRV_PCM_TRIGGER_START: cmd = SNDRV_PCM_TRIGGER_STOP; @@ -1128,34 +1168,20 @@ start_err: } } + /* + * STOP + */ switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (rtd->dai_link->stop_dma_first) { - ret = snd_soc_pcm_component_trigger(substream, cmd, rollback); - if (ret < 0) - break; - - ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback); - if (ret < 0) - break; - } else { - ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback); - if (ret < 0) - break; - - ret = snd_soc_pcm_component_trigger(substream, cmd, rollback); - if (ret < 0) - break; + for (i = TRIGGER_MAX; i > 0; i--) { + r = trigger[stop][i - 1](substream, cmd, rollback); + if (r < 0) + ret = r; } - ret = snd_soc_link_trigger(substream, cmd, rollback); - break; } - if (_ret) - ret = _ret; - return ret; } @@ -2734,48 +2760,50 @@ open_end: static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, int *playback, int *capture) { + struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai; + int has_playback = 0; + int has_capture = 0; int i; - if (rtd->dai_link->dynamic && rtd->dai_link->num_cpus > 1) { - dev_err(rtd->dev, - "DPCM doesn't support Multi CPU for Front-Ends yet\n"); + if (dai_link->dynamic && dai_link->num_cpus > 1) { + dev_err(rtd->dev, "DPCM doesn't support Multi CPU for Front-Ends yet\n"); return -EINVAL; } - if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { + if (dai_link->dynamic || dai_link->no_pcm) { int stream; - if (rtd->dai_link->dpcm_playback) { + if (dai_link->dpcm_playback) { stream = SNDRV_PCM_STREAM_PLAYBACK; for_each_rtd_cpu_dais(rtd, i, cpu_dai) { if (snd_soc_dai_stream_valid(cpu_dai, stream)) { - *playback = 1; + has_playback = 1; break; } } - if (!*playback) { + if (!has_playback) { dev_err(rtd->card->dev, "No CPU DAIs support playback for stream %s\n", - rtd->dai_link->stream_name); + dai_link->stream_name); return -EINVAL; } } - if (rtd->dai_link->dpcm_capture) { + if (dai_link->dpcm_capture) { stream = SNDRV_PCM_STREAM_CAPTURE; for_each_rtd_cpu_dais(rtd, i, cpu_dai) { if (snd_soc_dai_stream_valid(cpu_dai, stream)) { - *capture = 1; + has_capture = 1; break; } } - if (!*capture) { + if (!has_capture) { dev_err(rtd->card->dev, "No CPU DAIs support capture for stream %s\n", - rtd->dai_link->stream_name); + dai_link->stream_name); return -EINVAL; } } @@ -2783,41 +2811,58 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *codec_dai; /* Adapt stream for codec2codec links */ - int cpu_capture = rtd->dai_link->c2c_params ? - SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int cpu_playback = rtd->dai_link->c2c_params ? - SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE); + int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK); for_each_rtd_codec_dais(rtd, i, codec_dai) { - if (rtd->dai_link->num_cpus == 1) { + if (dai_link->num_cpus == 1) { cpu_dai = asoc_rtd_to_cpu(rtd, 0); - } else if (rtd->dai_link->num_cpus == rtd->dai_link->num_codecs) { + } else if (dai_link->num_cpus == dai_link->num_codecs) { cpu_dai = asoc_rtd_to_cpu(rtd, i); + } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) { + int cpu_id; + + if (!rtd->dai_link->codec_ch_maps) { + dev_err(rtd->card->dev, "%s: no codec channel mapping table provided\n", + __func__); + return -EINVAL; + } + + cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id; + cpu_dai = asoc_rtd_to_cpu(rtd, cpu_id); } else { dev_err(rtd->card->dev, - "N cpus to M codecs link is not supported yet\n"); + "%s codec number %d < cpu number %d is not supported\n", + __func__, rtd->dai_link->num_codecs, + rtd->dai_link->num_cpus); return -EINVAL; } if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) - *playback = 1; + has_playback = 1; if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && snd_soc_dai_stream_valid(cpu_dai, cpu_capture)) - *capture = 1; + has_capture = 1; } } - if (rtd->dai_link->playback_only) { - *playback = 1; - *capture = 0; - } + if (dai_link->playback_only) + has_capture = 0; + + if (dai_link->capture_only) + has_playback = 0; + + if (!has_playback && !has_capture) { + dev_err(rtd->dev, "substream %s has no playback, no capture\n", + dai_link->stream_name); - if (rtd->dai_link->capture_only) { - *playback = 0; - *capture = 1; + return -EINVAL; } + *playback = has_playback; + *capture = has_capture; + return 0; } diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index d0aca6b9058b..8add361e87c6 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -585,11 +585,15 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); static int soc_tplg_control_load(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { + int ret = 0; + if (tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, tplg->index, k, - hdr); + ret = tplg->ops->control_load(tplg->comp, tplg->index, k, hdr); - return 0; + if (ret) + dev_err(tplg->dev, "ASoC: failed to init %s\n", hdr->name); + + return ret; } @@ -691,17 +695,13 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) /* pass control to driver for optional further init */ ret = soc_tplg_control_load(tplg, &kc, &be->hdr); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name); + if (ret < 0) goto err; - } /* register control here */ ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to add %s\n", be->hdr.name); + if (ret < 0) goto err; - } list_add(&sbe->dobj.list, &tplg->comp->dobj_list); @@ -776,17 +776,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) /* pass control to driver for optional further init */ ret = soc_tplg_control_load(tplg, &kc, &mc->hdr); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name); + if (ret < 0) goto err; - } /* register control here */ ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to add %s\n", mc->hdr.name); + if (ret < 0) goto err; - } list_add(&sm->dobj.list, &tplg->comp->dobj_list); @@ -945,17 +941,13 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) /* pass control to driver for optional further init */ ret = soc_tplg_control_load(tplg, &kc, &ec->hdr); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); + if (ret < 0) goto err; - } /* register control here */ ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", ec->hdr.name); + if (ret < 0) goto err; - } list_add(&se->dobj.list, &tplg->comp->dobj_list); @@ -1162,11 +1154,8 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ /* pass control to driver for optional further init */ err = soc_tplg_control_load(tplg, kc, &mc->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - mc->hdr.name); + if (err < 0) return err; - } return 0; } @@ -1246,11 +1235,8 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k /* pass control to driver for optional further init */ err = soc_tplg_control_load(tplg, kc, &ec->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - ec->hdr.name); + if (err < 0) return err; - } return 0; } @@ -1298,11 +1284,8 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ /* pass control to driver for optional further init */ err = soc_tplg_control_load(tplg, kc, &be->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - be->hdr.name); + if (err < 0) return err; - } return 0; } @@ -1530,15 +1513,13 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) * If so, just return success. */ if (!snd_soc_card_is_instantiated(card)) { - dev_warn(tplg->dev, "ASoC: Parent card not yet available," - " widget card binding deferred\n"); + dev_warn(tplg->dev, "ASoC: Parent card not yet available, widget card binding deferred\n"); return 0; } ret = snd_soc_dapm_new_widgets(card); if (ret < 0) - dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", - ret); + dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", ret); return ret; } @@ -1693,10 +1674,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, dlc = (struct snd_soc_dai_link_component *)(link + 1); link->cpus = &dlc[0]; - link->codecs = &dlc[1]; - link->num_cpus = 1; - link->num_codecs = 1; link->dobj.index = tplg->index; link->dobj.type = SND_SOC_DOBJ_DAI_LINK; @@ -1721,16 +1699,19 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, } } - link->codecs->name = "snd-soc-dummy"; - link->codecs->dai_name = "snd-soc-dummy-dai"; - /* - * Many topology is assuming link has Platform. - * This might be overwritten at soc_tplg_dai_link_load(). + * Many topology are assuming link has Codec / Platform, and + * these might be overwritten at soc_tplg_dai_link_load(). + * Don't use &asoc_dummy_dlc here. */ - link->platforms = &dlc[2]; - link->platforms->name = "snd-soc-dummy"; - link->num_platforms = 1; + link->codecs = &dlc[1]; /* Don't use &asoc_dummy_dlc here */ + link->codecs->name = "snd-soc-dummy"; + link->codecs->dai_name = "snd-soc-dummy-dai"; + link->num_codecs = 1; + + link->platforms = &dlc[2]; /* Don't use &asoc_dummy_dlc here */ + link->platforms->name = "snd-soc-dummy"; + link->num_platforms = 1; /* enable DPCM */ link->dynamic = 1; @@ -2049,11 +2030,11 @@ static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, if (link->id != id) continue; - if (name && (!link->name || strcmp(name, link->name))) + if (name && (!link->name || !strstr(link->name, name))) continue; - if (stream_name && (!link->stream_name - || strcmp(stream_name, link->stream_name))) + if (stream_name && (!link->stream_name || + !strstr(link->stream_name, stream_name))) continue; return link; @@ -2505,11 +2486,8 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg) /* make sure header is valid before loading */ ret = soc_tplg_valid_header(tplg, hdr); - if (ret < 0) { - dev_err(tplg->dev, - "ASoC: topology: invalid header: %d\n", ret); + if (ret < 0) return ret; - } /* load the header object */ ret = soc_tplg_load_header(tplg, hdr); @@ -2529,9 +2507,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg) /* signal DAPM we are complete */ ret = soc_tplg_dapm_complete(tplg); - if (ret < 0) - dev_err(tplg->dev, - "ASoC: failed to initialise DAPM from Firmware\n"); return ret; } diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index a4dba0b751e7..11607c5f5d5a 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -224,6 +224,13 @@ int snd_soc_component_is_dummy(struct snd_soc_component *component) (component->driver == &dummy_codec)); } +struct snd_soc_dai_link_component asoc_dummy_dlc = { + .of_node = NULL, + .dai_name = "snd-soc-dummy-dai", + .name = "snd-soc-dummy", +}; +EXPORT_SYMBOL_GPL(asoc_dummy_dlc); + static int snd_soc_dummy_probe(struct platform_device *pdev) { int ret; diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index a2725188f4ce..80361139a49a 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -236,6 +236,17 @@ config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR Say Y if you want to enable the IPC message injector. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR + tristate "SOF enable IPC kernel injector" + depends on SND_SOC_SOF + select SND_SOC_SOF_CLIENT + help + This option enables the IPC kernel injector which can be used to send + crafted IPC messages to the kernel to test its robustness against + DSP messages. + Say Y if you want to enable the IPC kernel injector. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 308d87639916..744d40bd8c8b 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -26,6 +26,7 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o +snd-sof-ipc-kernel-injector-objs := sof-client-ipc-kernel-injector.o snd-sof-probes-objs := sof-client-probes.o ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) snd-sof-probes-objs += sof-client-probes-ipc3.o @@ -49,6 +50,7 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) += snd-sof-ipc-kernel-injector.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 749e856dc601..8a0fc635a997 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -130,6 +130,13 @@ static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev) memcpy(msg->reply_data, &reply, sizeof(reply)); ret = reply.error; } else { + /* + * To support an IPC tx_message with a + * reply_size set to zero. + */ + if (!msg->reply_size) + goto out; + /* reply correct size ? */ if (reply.hdr.size != msg->reply_size && !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 1c535cc6c3a9..dc624f727aa3 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -55,6 +55,9 @@ #define ACP_DSP_TO_HOST_IRQ 0x04 +#define ACP_RN_PCI_ID 0x01 +#define ACP_RMB_PCI_ID 0x6F + #define HOST_BRIDGE_CZN 0x1630 #define HOST_BRIDGE_RMB 0x14B5 #define ACP_SHA_STAT 0x8000 diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c index eaf70ea6e556..58b3092425f1 100644 --- a/sound/soc/sof/amd/pci-rmb.c +++ b/sound/soc/sof/amd/pci-rmb.c @@ -65,6 +65,9 @@ static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pc { unsigned int flag; + if (pci->revision != ACP_RMB_PCI_ID) + return -ENODEV; + flag = snd_amd_acp_find_config(pci); if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) return -ENODEV; diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index 4809cb644152..7409e21ce5aa 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -65,6 +65,9 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci { unsigned int flag; + if (pci->revision != ACP_RN_PCI_ID) + return -ENODEV; + flag = snd_amd_acp_find_config(pci); if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) return -ENODEV; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9a9d82220fd0..30db685cc5f4 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -504,8 +504,10 @@ int snd_sof_device_shutdown(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { + sof_fw_trace_free(sdev); return snd_sof_shutdown(sdev); + } return 0; } diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index f4eeacf1f281..69c1a370d3b6 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -284,8 +284,6 @@ if SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK bool "SOF support for HDA Links(HDA/HDMI)" - depends on SND_SOC_SOF_NOCODEC_SUPPORT=n - select SND_SOC_SOF_PROBE_WORK_QUEUE help This adds support for HDA links(HDA/HDMI) with Sound Open Firmware for Intel(R) platforms. @@ -295,6 +293,7 @@ config SND_SOC_SOF_HDA_LINK config SND_SOC_SOF_HDA_AUDIO_CODEC bool "SOF support for HDAudio codecs" depends on SND_SOC_SOF_HDA_LINK + select SND_SOC_SOF_PROBE_WORK_QUEUE help This adds support for HDAudio codecs with Sound Open Firmware for Intel(R) platforms. diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c index acb4b85868d0..fc63085d2d74 100644 --- a/sound/soc/sof/intel/hda-bus.c +++ b/sound/soc/sof/intel/hda-bus.c @@ -70,9 +70,14 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev) { struct hdac_bus *bus = sof_to_bus(sdev); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops); -#else /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#else + snd_hdac_ext_bus_init(bus, dev, NULL, NULL); +#endif +#else + memset(bus, 0, sizeof(*bus)); bus->dev = dev; @@ -87,12 +92,12 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev) bus->idx = 0; spin_lock_init(&bus->reg_lock); -#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ } void sof_hda_bus_exit(struct snd_sof_dev *sdev) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct hdac_bus *bus = sof_to_bus(sdev); snd_hdac_ext_bus_exit(bus); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 4b39cecacd68..f3513796c189 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -16,7 +16,7 @@ #include "hda.h" /* These ops are only applicable for the HDA DAI's in their current form */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) /* * This function checks if the host dma channel corresponding * to the link DMA stream_tag argument is assigned to one @@ -120,6 +120,26 @@ static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev, return snd_soc_dai_get_dma_data(cpu_dai, substream); } +static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; + struct snd_sof_widget *swidget; + struct snd_soc_dapm_widget *w; + + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + swidget = w->dobj.private; + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + + /* mark pipeline so that it can be skipped during FE trigger */ + pipeline->skip_during_fe_trigger = true; + + return snd_soc_dai_get_dma_data(cpu_dai, substream); +} + static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) @@ -155,20 +175,67 @@ static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stre snd_hdac_ext_stream_reset(hext_stream); } +static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hstream, substream->stream); +} + +static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, + 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_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int link_bps; + unsigned int format_val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link_bps = codec_dai->driver->playback.sig_bits; + else + link_bps = codec_dai->driver->capture.sig_bits; + + format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + params_format(params), link_bps, 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), params_channels(params), params_format(params)); + + return format_val; +} + +static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct hdac_bus *bus = sof_to_bus(sdev); + + return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - int ret; + int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + mutex_lock(&ipc4_data->pipeline_state_mutex); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -179,16 +246,17 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_PAUSED; break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); - return -EINVAL; + ret = -EINVAL; } - - return 0; +out: + mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, @@ -217,64 +285,76 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - int ret; + int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + mutex_lock(&ipc4_data->pipeline_state_mutex); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_PAUSED; } ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_RUNNING; + swidget->spipe->started_count++; break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, - SOF_IPC4_PIPE_RESET); + SOF_IPC4_PIPE_RUNNING); if (ret < 0) - return ret; - - pipeline->state = SOF_IPC4_PIPE_RESET; + goto out; + pipeline->state = SOF_IPC4_PIPE_RUNNING; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + /* + * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have + * been stopped. So, clear the started_count so that the pipeline can be reset + */ + swidget->spipe->started_count = 0; break; - } case SNDRV_PCM_TRIGGER_PAUSE_PUSH: break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); - return -EINVAL; + ret = -EINVAL; + break; } - - return 0; +out: + mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { - .get_hext_stream = hda_get_hext_stream, + .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, .release_hext_stream = hda_release_hext_stream, .setup_hext_stream = hda_setup_hext_stream, .reset_hext_stream = hda_reset_hext_stream, .pre_trigger = hda_ipc4_pre_trigger, .trigger = hda_trigger, - .post_trigger = hda_ipc4_post_trigger + .post_trigger = hda_ipc4_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { @@ -284,6 +364,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .setup_hext_stream = hda_setup_hext_stream, .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, @@ -317,6 +400,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, .post_trigger = hda_ipc3_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static struct hdac_ext_stream * @@ -343,6 +429,9 @@ static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { .get_hext_stream = hda_dspless_get_hext_stream, .setup_hext_stream = hda_dspless_setup_hext_stream, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; #endif @@ -350,7 +439,7 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { const struct hda_dai_widget_dma_ops * hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct snd_sof_dai *sdai; if (sdev->dspless_mode_selected) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 44a5d94c5050..3297dea493aa 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -27,20 +27,26 @@ static bool hda_use_tplg_nhlt; module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444); MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override"); +static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + + return snd_soc_component_get_drvdata(component); +} + int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; const struct sof_ipc_tplg_ops *tplg_ops; - struct snd_soc_component *component; struct snd_sof_dev *sdev; int ret; if (!swidget) return 0; - component = swidget->scomp; - sdev = snd_soc_component_get_drvdata(component); + sdev = widget_to_sdev(w); tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->dai_config) { @@ -55,16 +61,26 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, return 0; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) + +static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + + return widget_to_sdev(w); +} static const struct hda_dai_widget_dma_ops * hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; + sdev = widget_to_sdev(w); + /* * The swidget parameter of hda_select_dai_widget_ops() is ignored in * case of DSPless mode @@ -93,18 +109,22 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai static int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream, - struct snd_soc_dai *cpu_dai, - struct snd_soc_dai *codec_dai) + struct snd_soc_dai *cpu_dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct hdac_stream *hstream = &hext_stream->hstream; - struct hdac_bus *bus = hstream->bus; struct sof_intel_hda_stream *hda_stream; struct hdac_ext_link *hlink; + struct snd_sof_dev *sdev; int stream_tag; - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + sdev = dai_to_sdev(substream, cpu_dai); + + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -129,21 +149,20 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; - struct hdac_bus *bus; - unsigned int format_val; - unsigned int link_bps; int stream_tag; - sdev = snd_soc_component_get_drvdata(cpu_dai->component); - bus = sof_to_bus(sdev); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + sdev = dai_to_sdev(substream, cpu_dai); + + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -164,48 +183,32 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); /* set the hdac_stream in the codec dai */ - snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - link_bps = codec_dai->driver->playback.sig_bits; - else - link_bps = codec_dai->driver->capture.sig_bits; + if (ops->codec_dai_set_stream) + ops->codec_dai_set_stream(sdev, substream, hstream); if (ops->reset_hext_stream) ops->reset_hext_stream(sdev, hext_stream); - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), link_bps, 0); + if (ops->calc_stream_format && ops->setup_hext_stream) { + unsigned int format_val = ops->calc_stream_format(sdev, substream, params); - dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, - params_rate(params), params_channels(params), params_format(params)); - - if (ops->setup_hext_stream) ops->setup_hext_stream(sdev, hext_stream, format_val); + } hext_stream->link_prepared = 1; return 0; } -static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - int stream = substream->stream; - - return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); -} - -static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; + struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); if (!ops) { - dev_err(sdev->dev, "DAI widget ops not set\n"); + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } @@ -213,19 +216,19 @@ static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_d if (!hext_stream) return 0; - return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai); + return hda_link_dma_cleanup(substream, hext_stream, cpu_dai); } -static int hda_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dai_config_data data = { 0 }; unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; + struct snd_sof_dev *sdev = widget_to_sdev(w); int ret; if (!ops) { @@ -249,57 +252,32 @@ static int hda_dai_hw_params(struct snd_pcm_substream *substream, return hda_dai_config(w, flags, &data); } -static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); - struct hdac_ext_stream *hext_stream; - struct snd_sof_dai_config_data data = { 0 }; - unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; - int ret; - - hext_stream = ops->get_hext_stream(sdev, dai, substream); - if (hext_stream && hext_stream->link_prepared) - return 0; - - dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream); - - ret = hda_link_dma_prepare(substream, dai); - if (ret < 0) - return ret; - - hext_stream = ops->get_hext_stream(sdev, dai, substream); - - flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; - data.dai_data = hdac_stream(hext_stream)->stream_tag - 1; - - return hda_dai_config(w, flags, &data); -} - /* * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes * (over IPC channel) and DMA state change (direct host register changes). */ -static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; + struct snd_sof_dev *sdev; int ret; + if (!ops) { + dev_err(dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd, dai->name, substream->stream); + sdev = dai_to_sdev(substream, dai); + hext_stream = ops->get_hext_stream(sdev, dai, substream); if (!hext_stream) return -EINVAL; - rtd = asoc_substream_to_rtd(substream); - codec_dai = asoc_rtd_to_codec(rtd, 0); - if (ops->pre_trigger) { ret = ops->pre_trigger(sdev, dai, substream, cmd); if (ret < 0) @@ -320,7 +298,7 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct switch (cmd) { case SNDRV_PCM_TRIGGER_SUSPEND: - ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai); + ret = hda_link_dma_cleanup(substream, hext_stream, dai); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; @@ -333,6 +311,16 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + +static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int stream = substream->stream; + + return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); +} + static const struct snd_soc_dai_ops hda_dai_ops = { .hw_params = hda_dai_hw_params, .hw_free = hda_dai_hw_free, @@ -340,6 +328,8 @@ static const struct snd_soc_dai_ops hda_dai_ops = { .prepare = hda_dai_prepare, }; +#endif + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; @@ -362,23 +352,21 @@ static int hda_dai_suspend(struct hdac_bus *bus) const struct hda_dai_widget_dma_ops *ops; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; rtd = asoc_substream_to_rtd(hext_stream->link_substream); cpu_dai = asoc_rtd_to_cpu(rtd, 0); - codec_dai = asoc_rtd_to_codec(rtd, 0); w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); swidget = w->dobj.private; - sdev = snd_soc_component_get_drvdata(swidget->scomp); + sdev = widget_to_sdev(w); sdai = swidget->private; ops = sdai->platform_private; ret = hda_link_dma_cleanup(hext_stream->link_substream, hext_stream, - cpu_dai, codec_dai); + cpu_dai); if (ret < 0) return ret; @@ -588,7 +576,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev) * Since the component suspend is called last, we can trap this corner case * and force the DAIs to release their resources. */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) int ret; ret = hda_dai_suspend(sof_to_bus(sdev)); diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index 69fdef8f89ae..1e77ca936f80 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -15,7 +15,6 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <sound/hdaudio_ext.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 981e7b699bdb..f23c72cdff48 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -33,7 +33,7 @@ static bool hda_always_enable_dmi_l1; module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444); MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1"); -static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS); +static bool hda_disable_rewinds; module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 8de422604ad5..b13acb959653 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -15,7 +15,6 @@ * Hardware interface for generic Intel audio DSP HDA IP */ -#include <linux/pm_runtime.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 3153e21f100a..ec31a5762ddf 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -222,6 +222,31 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_ctx *ctx; + struct hdac_bus *bus; + u32 slcount; + + bus = sof_to_bus(sdev); + + hdev = sdev->pdata->hw_pdata; + ctx = hdev->sdw; + + slcount = hdac_bus_eml_get_count(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + + /* Check HW supported vs property value */ + if (slcount < ctx->count) { + dev_err(sdev->dev, + "%s: BIOS master count %d is larger than hardware capabilities %d\n", + __func__, ctx->count, slcount); + return -EINVAL; + } + + return 0; +} + static int hda_sdw_check_lcount(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; @@ -1343,12 +1368,22 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, hda_mach->mach_params.dmic_num = dmic_num; pdata->tplg_filename = tplg_filename; - if (codec_num == 2) { + if (codec_num == 2 || + (codec_num == 1 && !HDA_IDISP_CODEC(bus->codec_mask))) { /* * Prevent SoundWire links from starting when an external * HDaudio codec is used */ hda_mach->mach_params.link_mask = 0; + } else { + /* + * Allow SoundWire links to start when no external HDaudio codec + * was detected. This will not create a SoundWire card but + * will help detect if any SoundWire codec reports as ATTACHED. + */ + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + hda_mach->mach_params.link_mask = hdev->info.link_mask; } *mach = hda_mach; @@ -1562,7 +1597,11 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach, mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); - mach_params->num_dai_drivers = desc->ops->num_drv; + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + mach_params->num_dai_drivers = SOF_SKL_NUM_DAIS_NOCODEC; + else + mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index c4befacde23e..3f7c6fb05e5d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -414,10 +414,12 @@ (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) /* Number of DAIs */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +#define SOF_SKL_NUM_DAIS_NOCODEC 8 + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define SOF_SKL_NUM_DAIS 15 #else -#define SOF_SKL_NUM_DAIS 8 +#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC #endif /* Intel HD Audio SRAM Window 0*/ @@ -779,6 +781,7 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev); +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); @@ -792,6 +795,11 @@ static inline int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +static inline int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + return 0; +} + static inline int hda_sdw_startup(struct snd_sof_dev *sdev) { return 0; @@ -917,6 +925,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, * @pre_trigger: Function pointer for DAI DMA pre-trigger actions * @trigger: Function pointer for DAI DMA trigger actions * @post_trigger: Function pointer for DAI DMA post-trigger actions + * @codec_dai_set_stream: Function pointer to set codec-side stream information + * @calc_stream_format: Function pointer to determine stream format from hw_params and + * for HDaudio codec DAI from the .sig bits + * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV + * for legacy HDaudio links or program HDaudio Extended Link registers. */ struct hda_dai_widget_dma_ops { struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev, @@ -936,6 +949,14 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream, int cmd); int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd); + void (*codec_dai_set_stream)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream); + unsigned int (*calc_stream_format)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); }; const struct hda_dai_widget_dma_ops * diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 46caf3ccde66..30fe77fd87bf 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -55,7 +55,7 @@ static void mtl_ipc_dsp_done(struct snd_sof_dev *sdev) } /* Check if an IPC IRQ occurred */ -static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) { u32 irq_status; u32 hfintipptr; @@ -118,7 +118,7 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms return 0; } -static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -132,7 +132,7 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE); } -static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -173,7 +173,7 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) enable ? "enable" : "disable"); } -static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) { u32 hfintipptr; u32 irqinten; @@ -361,11 +361,17 @@ static int mtl_dsp_core_power_up(struct snd_sof_dev *sdev, int core) ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE, dspcxctl, (dspcxctl & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "%s: timeout on MTL_DSP2CXCTL_PRIMARY_CORE read\n", __func__); + return ret; + } - return ret; + /* set primary core mask and refcount to 1 */ + sdev->enabled_cores_mask = BIT(SOF_DSP_PRIMARY_CORE); + sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 1; + + return 0; } static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core) @@ -388,13 +394,18 @@ static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core) !(dspcxctl & MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK), HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "failed to power down primary core\n"); + return ret; + } - return ret; + sdev->enabled_cores_mask = 0; + sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 0; + + return 0; } -static int mtl_power_down_dsp(struct snd_sof_dev *sdev) +int mtl_power_down_dsp(struct snd_sof_dev *sdev) { u32 dsphfdsscs, cpa; int ret; @@ -421,7 +432,7 @@ static int mtl_power_down_dsp(struct snd_sof_dev *sdev) HDA_DSP_RESET_TIMEOUT_US); } -static int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -613,6 +624,36 @@ static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, return ((u64)llp_u << 32) | llp_l; } +static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + + if (core == SOF_DSP_PRIMARY_CORE) + return mtl_dsp_core_power_up(sdev, SOF_DSP_PRIMARY_CORE); + + if (pm_ops->set_core_state) + return pm_ops->set_core_state(sdev, core, true); + + return 0; +} + +static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + int ret; + + if (pm_ops->set_core_state) { + ret = pm_ops->set_core_state(sdev, core, false); + if (ret < 0) + return ret; + } + + if (core == SOF_DSP_PRIMARY_CORE) + return mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE); + + return 0; +} + /* Meteorlake ops */ struct snd_sof_dsp_ops sof_mtl_ops; EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -649,7 +690,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) sof_mtl_ops.parse_platform_ext_manifest = NULL; /* dsp core get/put */ - /* TODO: add core_get and core_put */ + sof_mtl_ops.core_get = mtl_dsp_core_get; + sof_mtl_ops.core_put = mtl_dsp_core_put; sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 26418fb08807..2794fe6e8139 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -82,3 +82,10 @@ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); + +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); +int mtl_power_down_dsp(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 13efdb94d071..d24e64e71b58 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/pci.h> -#include <linux/pm_runtime.h> #include <sound/hdaudio_ext.h> #include <sound/pcm_params.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 2713b7dc7931..8e2b07e1612b 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -39,14 +39,18 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + int ret; + + if (pm_ops->set_core_state) { + ret = pm_ops->set_core_state(sdev, core, false); + if (ret < 0) + return ret; + } /* power down primary core and return */ if (core == SOF_DSP_PRIMARY_CORE) return hda_dsp_core_reset_power_down(sdev, BIT(core)); - if (pm_ops->set_core_state) - return pm_ops->set_core_state(sdev, core, false); - return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index ad040e7bb850..a8deec7dc021 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -96,6 +96,26 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, cdata->elems_remaining = 0; ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + if (!set) + goto unlock; + + /* It is a set-data operation, and we have a backup that we can restore */ + if (ret < 0) { + if (!scontrol->old_ipc_control_data) + goto unlock; + /* + * Current ipc_control_data is not valid, we use the last known good + * configuration + */ + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + /* Send the last known good configuration to firmware */ + ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + if (ret < 0) + goto unlock; + } unlock: if (lock) @@ -351,6 +371,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct snd_ctl_tlv header; + int ret = -EINVAL; /* * The beginning of bytes data contains a header from where @@ -381,31 +402,52 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, return -EINVAL; } - if (copy_from_user(cdata->data, tlvd->tlv, header.length)) - return -EFAULT; + if (!scontrol->old_ipc_control_data) { + /* Create a backup of the current, valid bytes control */ + scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, + scontrol->max_size, GFP_KERNEL); + if (!scontrol->old_ipc_control_data) + return -ENOMEM; + } + + if (copy_from_user(cdata->data, tlvd->tlv, header.length)) { + ret = -EFAULT; + goto err_restore; + } if (cdata->data->magic != SOF_ABI_MAGIC) { dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); - return -EINVAL; + goto err_restore; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", cdata->data->abi); - return -EINVAL; + goto err_restore; } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); - return -EINVAL; + goto err_restore; } /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) + if (pm_runtime_active(scomp->dev)) { + /* Actually send the data to the DSP; this is an opportunity to validate the data */ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); + } return 0; + +err_restore: + /* If we have an issue, we restore the old, valid bytes control data */ + if (scontrol->old_ipc_control_data) { + memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + } + return ret; } static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index f5044202f3c5..0bbca418e67e 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -28,6 +28,8 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev); /* dtrace position update */ int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); +/* RX handler backend */ +void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf); /* dtrace platform callback wrappers */ static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index c67767742093..2c5aac31e8b0 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -223,6 +223,14 @@ static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) } #endif +static void sof_ipc3_dump_payload(struct snd_sof_dev *sdev, + void *ipc_data, size_t size) +{ + printk(KERN_DEBUG "Size of payload following the header: %zu\n", size); + print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, + 16, 4, ipc_data, size, false); +} + static int sof_ipc3_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -374,6 +382,29 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + size_t payload_bytes, header_bytes; + char *payload = NULL; + + /* payload is indicated by non zero msg/reply_bytes */ + if (msg_bytes > sizeof(struct sof_ipc_cmd_hdr)) { + payload = msg_data; + + header_bytes = sizeof(struct sof_ipc_cmd_hdr); + payload_bytes = msg_bytes - header_bytes; + } else if (reply_bytes > sizeof(struct sof_ipc_reply)) { + payload = reply_data; + + header_bytes = sizeof(struct sof_ipc_reply); + payload_bytes = reply_bytes - header_bytes; + } + + if (payload) { + payload += header_bytes; + sof_ipc3_dump_payload(sdev, payload, payload_bytes); + } + } + mutex_unlock(&ipc->tx_mutex); return ret; @@ -472,6 +503,14 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da offset += payload_size; } + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + size_t header_bytes = sizeof(struct sof_ipc_reply); + char *payload = (char *)cdata; + + payload += header_bytes; + sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes); + } + mutex_unlock(&sdev->ipc->tx_mutex); kfree(cdata_chunk); @@ -954,31 +993,21 @@ static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) } } -/* DSP firmware has sent host a message */ -static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) +void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf) { ipc3_rx_callback rx_callback = NULL; - struct sof_ipc_cmd_hdr hdr; - void *msg_buf; u32 cmd; int err; - /* read back header */ - err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); - if (err < 0) { - dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); - return; - } + ipc3_log_header(sdev->dev, "ipc rx", hdr->cmd); - if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) { + if (hdr->size < sizeof(hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) { dev_err(sdev->dev, "The received message size is invalid: %u\n", - hdr.size); + hdr->size); return; } - ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd); - - cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + cmd = hdr->cmd & SOF_GLB_TYPE_MASK; /* check message type */ switch (cmd) { @@ -1016,6 +1045,36 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) break; } + /* Call local handler for the message */ + if (rx_callback) + rx_callback(sdev, msg_buf); + + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, msg_buf); + + ipc3_log_header(sdev->dev, "ipc rx done", hdr->cmd); +} +EXPORT_SYMBOL(sof_ipc3_do_rx_work); + +/* DSP firmware has sent host a message */ +static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) +{ + struct sof_ipc_cmd_hdr hdr; + void *msg_buf; + int err; + + /* read back header */ + err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + if (err < 0) { + dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); + return; + } + + if (hdr.size < sizeof(hdr)) { + dev_err(sdev->dev, "The received message size is invalid\n"); + return; + } + /* read the full message */ msg_buf = kmalloc(hdr.size, GFP_KERNEL); if (!msg_buf) @@ -1024,18 +1083,13 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); if (err < 0) { dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); - } else { - /* Call local handler for the message */ - if (rx_callback) - rx_callback(sdev, msg_buf); - - /* Notify registered clients */ - sof_client_ipc_rx_dispatcher(sdev, msg_buf); + kfree(msg_buf); + return; } - kfree(msg_buf); + sof_ipc3_do_rx_work(sdev, &hdr, msg_buf); - ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd); + kfree(msg_buf); } static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 6f0698be9451..c6d404d44097 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -54,6 +54,26 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); ret = iops->set_get_data(sdev, msg, msg->data_size, set); + if (!set) + goto unlock; + + /* It is a set-data operation, and we have a valid backup that we can restore */ + if (ret < 0) { + if (!scontrol->old_ipc_control_data) + goto unlock; + /* + * Current ipc_control_data is not valid, we use the last known good + * configuration + */ + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + /* Send the last known good configuration to firmware */ + ret = iops->set_get_data(sdev, msg, msg->data_size, set); + if (ret < 0) + goto unlock; + } unlock: if (lock) @@ -327,13 +347,24 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, return -EINVAL; } + if (!scontrol->old_ipc_control_data) { + /* Create a backup of the current, valid bytes control */ + scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, + scontrol->max_size, GFP_KERNEL); + if (!scontrol->old_ipc_control_data) + return -ENOMEM; + } + /* Copy the whole binary data which includes the ABI header and the payload */ - if (copy_from_user(data, tlvd->tlv, header.length)) + if (copy_from_user(data, tlvd->tlv, header.length)) { + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; return -EFAULT; + } - sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); - - return 0; + return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); } static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 1321acc402fd..eaa04762eb11 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -112,16 +112,13 @@ static ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, return -EINVAL; } - /* a module's config is always the same size */ - fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes; + fw_module->fw_mod_cfg = &fm_config[fm_entry->cfg_offset]; dev_dbg(sdev->dev, "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n", fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count, - fw_module->bss_size); + fm_config[fm_entry->cfg_offset].is_bytes); } else { - fw_module->bss_size = 0; - dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name, &fm_entry->uuid); } @@ -426,6 +423,71 @@ int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev) return ret; } +/** + * sof_ipc4_update_cpc_from_manifest - Update the cpc in base config from manifest + * @sdev: SOF device + * @fw_module: pointer struct sof_ipc4_fw_module to parse + * @basecfg: Pointer to the base_config to update + */ +void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_module *fw_module, + struct sof_ipc4_base_module_cfg *basecfg) +{ + const struct sof_man4_module_config *fw_mod_cfg; + u32 cpc_pick = 0; + u32 max_cpc = 0; + const char *msg; + int i; + + if (!fw_module->fw_mod_cfg) { + msg = "No mod_cfg available for CPC lookup in the firmware file's manifest"; + goto no_cpc; + } + + /* + * Find the best matching (highest) CPC value based on the module's + * IBS/OBS configuration inferred from the audio format selection. + * + * The CPC value in each module config entry has been measured and + * recorded as a IBS/OBS/CPC triplet and stored in the firmware file's + * manifest + */ + fw_mod_cfg = fw_module->fw_mod_cfg; + for (i = 0; i < fw_module->man4_module_entry.cfg_count; i++) { + if (basecfg->obs == fw_mod_cfg[i].obs && + basecfg->ibs == fw_mod_cfg[i].ibs && + cpc_pick < fw_mod_cfg[i].cpc) + cpc_pick = fw_mod_cfg[i].cpc; + + if (max_cpc < fw_mod_cfg[i].cpc) + max_cpc = fw_mod_cfg[i].cpc; + } + + basecfg->cpc = cpc_pick; + + /* We have a matching configuration for CPC */ + if (basecfg->cpc) + return; + + /* + * No matching IBS/OBS found, the firmware manifest is missing + * information in the module's module configuration table. + */ + if (!max_cpc) + msg = "No CPC value available in the firmware file's manifest"; + else if (!cpc_pick) + msg = "No CPC match in the firmware file's manifest"; + +no_cpc: + dev_warn(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n", + fw_module->man4_module_entry.name, + &fw_module->man4_module_entry.uuid, msg, basecfg->ibs, + basecfg->obs); + dev_warn_once(sdev->dev, "Please try to update the firmware.\n"); + dev_warn_once(sdev->dev, "If the issue persists, file a bug at\n"); + dev_warn_once(sdev->dev, "https://github.com/thesofproject/sof/issues/\n"); +} + const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { .validate = sof_ipc4_validate_firmware, .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 9e2b6c45080d..0c905bd0fab4 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -69,7 +69,7 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger) + if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) return; switch (state) { @@ -108,7 +108,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; int i; - if (pipeline->skip_during_fe_trigger) + if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) return; /* set state for pipeline if it was just triggered */ diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index f461b8c70df3..a5d0b2eae464 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -28,14 +28,14 @@ enum sof_ipc4_mtrace_type { /** * struct sof_ipc4_fw_module - IPC4 module info * @sof_man4_module: Module info + * @fw_mod_cfg: Pointer to the module config start of the module * @m_ida: Module instance identifier - * @bss_size: Module object size * @private: Module private data */ struct sof_ipc4_fw_module { struct sof_man4_module man4_module_entry; + const struct sof_man4_module_config *fw_mod_cfg; struct ida m_ida; - u32 bss_size; void *private; }; @@ -114,4 +114,10 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev); struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, const guid_t *uuid); + +struct sof_ipc4_base_module_cfg; +void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_module *fw_module, + struct sof_ipc4_base_module_cfg *basecfg); + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 5abe616a2054..a4e1a70b607d 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -39,8 +39,6 @@ static const struct sof_topology_token pipeline_tokens[] = { }; static const struct sof_topology_token ipc4_comp_tokens[] = { - {SOF_TKN_COMP_CPC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_base_module_cfg, cpc)}, {SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_base_module_cfg, is_pages)}, }; @@ -235,7 +233,7 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, "Number of input audio formats: %d. Number of output audio formats: %d\n", available_fmt->num_input_formats, available_fmt->num_output_formats); - /* set cpc and is_pages in the module's base_config */ + /* set is_pages in the module's base_config */ ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*module_base_cfg), 1); if (ret) { @@ -244,8 +242,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, return ret; } - dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n", - swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages); + dev_dbg(scomp->dev, "widget %s: is_pages: %d\n", swidget->widget->name, + module_base_cfg->is_pages); if (available_fmt->num_input_formats) { in_format = kcalloc(available_fmt->num_input_formats, @@ -561,7 +559,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) strcmp(w->widget->sname, swidget->widget->sname)) continue; - blob->alh_cfg.count++; + blob->alh_cfg.device_count++; } ipc4_copier->copier_config = (uint32_t *)blob; @@ -723,9 +721,9 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget) } dev_dbg(scomp->dev, - "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n", + "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x\n", swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l, - gain->data.init_val, gain->base_config.cpc); + gain->data.init_val); ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg); if (ret) @@ -936,8 +934,8 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget) } static void -sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct sof_ipc4_base_module_cfg *base_config) +sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct sof_ipc4_base_module_cfg *base_config) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_widget *pipe_widget; @@ -968,6 +966,13 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage += total; + + /* Update base_config->cpc from the module manifest */ + sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); + + dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); } static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, @@ -1028,47 +1033,125 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw return 0; } -static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct sof_ipc4_base_module_cfg *base_config, - struct snd_pcm_hw_params *params, - struct sof_ipc4_available_audio_format *available_fmt, - struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) +static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, + struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) { - u32 valid_bits; - u32 channels; - u32 rate; - int sample_valid_bits; + struct sof_ipc4_audio_format *fmt; + u32 rate, channels, valid_bits; + int i; + + fmt = &pin_fmts[0].audio_fmt; + rate = fmt->sampling_frequency; + channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + /* check if all output formats in topology are the same */ + for (i = 1; i < pin_fmts_size; i++) { + u32 _rate, _channels, _valid_bits; + + fmt = &pin_fmts[i].audio_fmt; + _rate = fmt->sampling_frequency; + _channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (_rate != rate || _channels != channels || _valid_bits != valid_bits) + return false; + } + + return true; +} + +static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, + struct sof_ipc4_base_module_cfg *base_config, + struct sof_ipc4_available_audio_format *available_fmt, + u32 out_ref_rate, u32 out_ref_channels, + u32 out_ref_valid_bits) +{ + struct sof_ipc4_audio_format *out_fmt; + bool single_format; int i; - if (!pin_fmts) { - dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name); + if (!available_fmt->num_output_formats) return -EINVAL; + + single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts, + available_fmt->num_output_formats); + + /* pick the first format if there's only one available or if all formats are the same */ + if (single_format) { + base_config->obs = available_fmt->output_pin_fmts[0].buffer_size; + return 0; } + /* + * if there are multiple output formats, then choose the output format that matches + * the reference params + */ + for (i = 0; i < available_fmt->num_output_formats; i++) { + u32 _out_rate, _out_channels, _out_valid_bits; + + out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; + _out_rate = out_fmt->sampling_frequency; + _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg); + _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg); + + if (_out_rate == out_ref_rate && _out_channels == out_ref_channels && + _out_valid_bits == out_ref_valid_bits) { + base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; + return i; + } + } + + return -EINVAL; +} + +static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +{ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - sample_valid_bits = 16; - break; + return 16; case SNDRV_PCM_FORMAT_S24_LE: - sample_valid_bits = 24; - break; + return 24; case SNDRV_PCM_FORMAT_S32_LE: - sample_valid_bits = 32; - break; + return 32; default: dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); return -EINVAL; } +} + +static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_base_module_cfg *base_config, + struct snd_pcm_hw_params *params, + struct sof_ipc4_available_audio_format *available_fmt) +{ + struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts; + u32 pin_fmts_size = available_fmt->num_input_formats; + u32 valid_bits; + u32 channels; + u32 rate; + bool single_format; + int sample_valid_bits; + int i = 0; - if (!pin_fmts_size) { - dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name); + if (!available_fmt->num_input_formats) { + dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name); return -EINVAL; } + single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts, + available_fmt->num_input_formats); + if (single_format) + goto in_fmt; + + sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params); + if (sample_valid_bits < 0) + return sample_valid_bits; + /* - * Search supported audio formats with pin index 0 to match rate, channels ,and - * sample_valid_bytes from runtime params + * Search supported input audio formats with pin index 0 to match rate, channels and + * sample_valid_bits from reference params */ for (i = 0; i < pin_fmts_size; i++) { struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt; @@ -1093,6 +1176,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, return -EINVAL; } +in_fmt: /* copy input format */ if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) { memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, @@ -1105,10 +1189,6 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); } - if (available_fmt->num_output_formats && i < available_fmt->num_output_formats) - base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; - - /* Return the index of the matched format */ return i; } @@ -1145,7 +1225,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) unsigned int group_id; blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) - ALH_MULTI_GTW_BASE; ida_free(&alh_group_ida, group_id); @@ -1288,50 +1368,6 @@ static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth) return 0; } -static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev, - struct snd_pcm_hw_params *pipeline_params, - struct snd_pcm_hw_params *fe_params, - struct sof_ipc4_available_audio_format *available_fmt) -{ - struct sof_ipc4_audio_format *audio_fmt; - unsigned int sample_valid_bits; - bool multiple_formats = false; - bool fe_format_match = false; - struct snd_mask *fmt; - int i; - - for (i = 0; i < available_fmt->num_output_formats; i++) { - unsigned int val; - - audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; - val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg); - - if (i == 0) - sample_valid_bits = val; - else if (sample_valid_bits != val) - multiple_formats = true; - - if (snd_pcm_format_width(params_format(fe_params)) == val) - fe_format_match = true; - } - - fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); - snd_mask_none(fmt); - - if (multiple_formats) { - if (fe_format_match) { - /* multiple formats defined and one matches FE */ - snd_mask_set_format(fmt, params_format(fe_params)); - return 0; - } - - dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n"); - return -EINVAL; - } - - return ipc4_set_fmt_mask(fmt, sample_valid_bits); -} - static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -1341,19 +1377,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc4_pin_format *format_list_to_search; struct sof_ipc4_copier_data *copier_data; struct snd_pcm_hw_params *ref_params; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_dai *dai; struct snd_mask *fmt; int out_sample_valid_bits; + u32 gtw_cfg_config_length; + u32 dma_config_tlv_size = 0; void **ipc_config_data; int *ipc_config_size; u32 **data; - int ipc_size, ret; + int ipc_size, ret, out_ref_valid_bits; + u32 out_ref_rate, out_ref_channels; u32 deep_buffer_dma_ms = 0; - u32 format_list_count; + int output_fmt_index; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1417,13 +1455,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; - } else { - format_list_to_search = available_fmt->output_pin_fmts; - format_list_count = available_fmt->num_output_formats; - } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ref_params = fe_params; + else + ref_params = pipeline_params; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id |= @@ -1431,7 +1466,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* set gateway attributes */ gtw_attr->lp_buffer_alloc = pipeline->lp_mode; - ref_params = fe_params; break; } case snd_soc_dapm_dai_in: @@ -1448,20 +1482,17 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc4_copier = (struct sof_ipc4_copier *)dai->private; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - if (dir == SNDRV_PCM_STREAM_CAPTURE) { - format_list_to_search = available_fmt->output_pin_fmts; - format_list_count = available_fmt->num_output_formats; - - ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params, - available_fmt); - if (ret < 0) - return ret; - } else { - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; - } - ref_params = pipeline_params; + /* + * When there is format conversion within a pipeline, the number of supported + * output formats is typically limited to just 1 for the DAI copiers. But when there + * is no format conversion, the DAI copiers input format must match that of the + * FE hw_params for capture and the pipeline params for playback. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ref_params = pipeline_params; + else + ref_params = fe_params; ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index, ipc4_copier->dai_type, dir, @@ -1477,10 +1508,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc4_copier = (struct sof_ipc4_copier *)swidget->private; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - - /* Use the input formats to match pcm params */ - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; ref_params = pipeline_params; break; @@ -1492,11 +1519,51 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* set input and output audio formats */ - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, - available_fmt, format_list_to_search, format_list_count); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, + available_fmt); if (ret < 0) return ret; + /* set the reference params for output format selection */ + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_buffer: + { + struct sof_ipc4_audio_format *in_fmt; + + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + break; + } + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_in: + out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (out_ref_valid_bits < 0) + return out_ref_valid_bits; + + out_ref_rate = params_rate(fe_params); + out_ref_channels = params_channels(fe_params); + break; + default: + /* + * Unsupported type should be caught by the former switch default + * case, this should never happen in reality. + */ + return -EINVAL; + } + + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_fmt_index < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return output_fmt_index; + } + /* * Set the output format. Current topology defines pin 0 input and output formats in pairs. * This assumes that the pin 0 formats are defined before all other pins. @@ -1504,10 +1571,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * input format. This logic will need to be updated when the format definitions * in topology change. */ - memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt, + memcpy(&copier_data->out_format, + &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt, sizeof(struct sof_ipc4_audio_format)); dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1); + sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1); switch (swidget->id) { case snd_soc_dapm_dai_in: @@ -1515,7 +1583,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, { /* * Only SOF_DAI_INTEL_ALH needs copier_data to set blob. - * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt + * That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt */ if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_alh_configuration_blob *blob; @@ -1543,7 +1611,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ch_map >>= 4; } - step = ch_count / blob->alh_cfg.count; + step = ch_count / blob->alh_cfg.device_count; mask = GENMASK(step - 1, 0); /* * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[] @@ -1558,7 +1626,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, dai = w->private; alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; - blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id; + blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id; /* * Set the same channel mask for playback as the audio data is * duplicated for all speakers. For capture, split the channels @@ -1577,7 +1645,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, i++; } - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { int group_id; group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1, @@ -1633,7 +1701,27 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc_config_data = &ipc4_copier->ipc_config_data; /* config_length is DWORD based */ - ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; + ipc_size = sizeof(*copier_data) + gtw_cfg_config_length; + + if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID && + ipc4_copier->dma_config_tlv.length) { + dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) + + ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size; + + /* paranoia check on TLV size/length */ + if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length + + sizeof(uint32_t) * 2) { + dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n", + dma_config_tlv_size, ipc4_copier->dma_config_tlv.length); + return -EINVAL; + } + + ipc_size += dma_config_tlv_size; + + /* we also need to increase the size at the gtw level */ + copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4; + } dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); @@ -1645,12 +1733,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); - if (copier_data->gtw_cfg.config_length) + if (gtw_cfg_config_length) memcpy(*ipc_config_data + sizeof(*copier_data), - *data, copier_data->gtw_cfg.config_length * 4); + *data, gtw_cfg_config_length); + + /* add DMA Config TLV, if configured */ + if (dma_config_tlv_size) + memcpy(*ipc_config_data + sizeof(*copier_data) + + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, dma_config_tlv_size); /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); return 0; } @@ -1664,17 +1758,30 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_gain *gain = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return ret; + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &gain->base_config); return 0; } @@ -1688,17 +1795,30 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_mixer *mixer = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return ret; + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config); return 0; } @@ -1712,18 +1832,30 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_src *src = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; struct snd_interval *rate; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &src->base_config); /* update pipeline_params for sink widgets */ rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE); @@ -1820,20 +1952,37 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_process *process = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; void *cfg = process->ipc_config_data; + int output_fmt_index; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_fmt_index < 0 && available_fmt->num_output_formats) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return output_fmt_index; + } + /* copy Pin 0 output format */ - if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats && - !available_fmt->output_pin_fmts[ret].pin_index) { - memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt, + if (available_fmt->num_output_formats && + output_fmt_index < available_fmt->num_output_formats && + !available_fmt->output_pin_fmts[output_fmt_index].pin_index) { + memcpy(&process->output_format, + &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt, sizeof(struct sof_ipc4_audio_format)); /* modify the pipeline params with the pin 0 output format */ @@ -1843,7 +1992,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, } /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &process->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config); /* ipc_config_data is composed of the base_config followed by an optional extension */ memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg)); @@ -2271,11 +2420,11 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, int sink_id) { struct sof_ipc4_copier_config_set_sink_format format; + const struct sof_ipc_ops *iops = sdev->ipc->ops; struct sof_ipc4_base_module_cfg *src_config; const struct sof_ipc4_audio_format *pin_fmt; struct sof_ipc4_fw_module *fw_module; struct sof_ipc4_msg msg = {{ 0 }}; - u32 header, extension; dev_dbg(sdev->dev, "%s set copier sink %d format\n", src_widget->widget->name, sink_id); @@ -2305,22 +2454,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, msg.data_size = sizeof(format); msg.data_ptr = &format; - header = fw_module->man4_module_entry.id; - header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); - header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); - header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); - header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.primary = fw_module->man4_module_entry.id; + msg.primary |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); - extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size); - extension |= + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT); - extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1); - extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); - - msg.primary = header; - msg.extension = extension; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, msg.data_size); + return iops->set_get_data(sdev, &msg, msg.data_size, true); } static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) @@ -2507,7 +2649,6 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; - pipeline->skip_during_fe_trigger = true; fallthrough; case SOF_DAI_INTEL_ALH: /* diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index cf007282867b..6dcf14886e85 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -55,7 +55,7 @@ #define SOF_IPC4_GAIN_ALL_CHANNELS_MASK 0xffffffff #define SOF_IPC4_VOL_ZERO_DB 0x7fffffff -#define ALH_MAX_NUMBER_OF_GTW 16 +#define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16 #define SOF_IPC4_INVALID_NODE_ID 0xffffffff @@ -220,18 +220,64 @@ struct sof_ipc4_gtw_attributes { uint32_t rsvd : 30; }; -/** struct sof_ipc4_alh_multi_gtw_cfg: ALH gateway cfg data - * @count: Number of streams (valid items in mapping array) - * @alh_id: ALH stream id of a single ALH stream aggregated - * @channel_mask: Channel mask - * @mapping: ALH streams +/** + * struct sof_ipc4_dma_device_stream_ch_map: abstract representation of + * channel mapping to DMAs + * @device: representation of hardware device address or FIFO + * @channel_mask: channels handled by @device. Channels are expected to be + * contiguous + */ +struct sof_ipc4_dma_device_stream_ch_map { + uint32_t device; + uint32_t channel_mask; +}; + +/** + * struct sof_ipc4_dma_stream_ch_map: DMA configuration data + * @device_count: Number valid items in mapping array + * @mapping: device address and channel mask + */ +struct sof_ipc4_dma_stream_ch_map { + uint32_t device_count; + struct sof_ipc4_dma_device_stream_ch_map mapping[SOF_IPC4_DMA_DEVICE_MAX_COUNT]; +} __packed; + +#define SOF_IPC4_DMA_METHOD_HDA 1 +#define SOF_IPC4_DMA_METHOD_GPDMA 2 /* defined for consistency but not used */ + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @dma_method: HDAudio or GPDMA + * @pre_allocated_by_host: 1 if host driver allocates DMA channels, 0 otherwise + * @dma_channel_id: for HDaudio defined as @stream_id - 1 + * @stream_id: HDaudio stream tag + * @dma_stream_channel_map: array of device/channel mappings + * @dma_priv_config_size: currently not used + * @dma_priv_config: currently not used + */ +struct sof_ipc4_dma_config { + uint8_t dma_method; + uint8_t pre_allocated_by_host; + uint16_t rsvd; + uint32_t dma_channel_id; + uint32_t stream_id; + struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; + uint32_t dma_priv_config_size; + uint8_t dma_priv_config[]; +} __packed; + +#define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID + * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size + * @dma_config: actual DMA configuration */ -struct sof_ipc4_alh_multi_gtw_cfg { - uint32_t count; - struct { - uint32_t alh_id; - uint32_t channel_mask; - } mapping[ALH_MAX_NUMBER_OF_GTW]; +struct sof_ipc4_dma_config_tlv { + uint32_t type; + uint32_t length; + struct sof_ipc4_dma_config dma_config; } __packed; /** struct sof_ipc4_alh_configuration_blob: ALH blob @@ -240,7 +286,7 @@ struct sof_ipc4_alh_multi_gtw_cfg { */ struct sof_ipc4_alh_configuration_blob { struct sof_ipc4_gtw_attributes gw_attr; - struct sof_ipc4_alh_multi_gtw_cfg alh_cfg; + struct sof_ipc4_dma_stream_ch_map alh_cfg; }; /** @@ -254,6 +300,7 @@ struct sof_ipc4_alh_configuration_blob { * @gtw_attr: Gateway attributes for copier blob * @dai_type: DAI type * @dai_index: DAI index + * @dma_config_tlv: DMA configuration */ struct sof_ipc4_copier { struct sof_ipc4_copier_data data; @@ -266,6 +313,7 @@ struct sof_ipc4_copier { struct sof_ipc4_gtw_attributes *gtw_attr; u32 dai_type; int dai_index; + struct sof_ipc4_dma_config_tlv dma_config_tlv; }; /** diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 246b56d24a6f..ab6eddd91bb7 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -17,15 +17,6 @@ #include "ipc4-priv.h" #include "ops.h" -#ifdef DEBUG_VERBOSE -#define sof_ipc4_dump_payload(sdev, ipc_data, size) \ - print_hex_dump_debug("Message payload: ", \ - DUMP_PREFIX_OFFSET, \ - 16, 4, ipc_data, size, false) -#else -#define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0) -#endif - static const struct sof_ipc4_fw_status { int status; char *msg; @@ -256,6 +247,13 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms } #endif +static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, + void *ipc_data, size_t size) +{ + print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, + 16, 4, ipc_data, size, false); +} + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -362,9 +360,6 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ void *reply_data, size_t reply_bytes, bool no_pm) { struct snd_sof_ipc *ipc = sdev->ipc; -#ifdef DEBUG_VERBOSE - struct sof_ipc4_msg *msg = NULL; -#endif int ret; if (!msg_data) @@ -386,18 +381,20 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); - mutex_unlock(&ipc->tx_mutex); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + struct sof_ipc4_msg *msg = NULL; -#ifdef DEBUG_VERBOSE - /* payload is indicated by non zero msg/reply_bytes */ - if (msg_bytes) - msg = msg_data; - else if (reply_bytes) - msg = reply_data; + /* payload is indicated by non zero msg/reply_bytes */ + if (msg_bytes) + msg = msg_data; + else if (reply_bytes) + msg = reply_data; - if (msg) - sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); -#endif + if (msg) + sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); + } + + mutex_unlock(&ipc->tx_mutex); return ret; } @@ -516,7 +513,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, if (!set && payload_bytes != offset) ipc4_msg->data_size = offset; - sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) + sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); out: mutex_unlock(&sdev->ipc->tx_mutex); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c index 2df3b7ae1c6f..cb2ab5884b8c 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c @@ -8,7 +8,6 @@ // Hardware interface for mt8186 DSP clock #include <linux/clk.h> -#include <linux/pm_runtime.h> #include <linux/io.h> #include "../../sof-audio.h" diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 597cb4476acb..3e0ea0e109e2 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -24,6 +24,7 @@ #include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" +#include "../mtk-adsp-common.h" #include "mt8186.h" #include "mt8186-clk.h" @@ -48,47 +49,13 @@ static int mt8186_send_msg(struct snd_sof_dev *sdev, return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); } -static void mt8186_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc) { struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - mt8186_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -507,6 +474,26 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev, return pos; } +static void mt8186_adsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + u32 dbg_pc, dbg_data, dbg_inst, dbg_ls0stat, dbg_status, faultinfo; + + /* dump debug registers */ + dbg_pc = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGPC); + dbg_data = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGDATA); + dbg_inst = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGINST); + dbg_ls0stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS0STAT); + dbg_status = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGSTATUS); + faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO); + + dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, dbg_inst %#x,", + dbg_pc, dbg_data, dbg_inst); + dev_info(sdev->dev, "ls0stat %#x, status %#x, faultinfo %#x", + dbg_ls0stat, dbg_status, faultinfo); + + mtk_adsp_dump(sdev, flags); +} + static struct snd_soc_dai_driver mt8186_dai[] = { { .name = "SOF_DL1", @@ -589,6 +576,7 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = { .num_drv = ARRAY_SIZE(mt8186_dai), /* Debug information */ + .dbg_dump = mt8186_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* PM */ @@ -628,7 +616,65 @@ static const struct sof_dev_desc sof_of_mt8186_desc = { .ops = &sof_mt8186_ops, }; +/* + * DL2, DL3, UL4, UL5 are registered as SOF FE, so creating the corresponding + * SOF BE to complete the pipeline. + */ +static struct snd_soc_dai_driver mt8188_dai[] = { +{ + .name = "SOF_DL2", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_DL3", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL4", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL5", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +}; + +/* mt8188 ops */ +static struct snd_sof_dsp_ops sof_mt8188_ops; + +static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_mt8188_ops, &sof_mt8186_ops, sizeof(sof_mt8188_ops)); + + sof_mt8188_ops.drv = mt8188_dai; + sof_mt8188_ops.num_drv = ARRAY_SIZE(mt8188_dai); + + return 0; +} + +static struct snd_sof_of_mach sof_mt8188_machs[] = { + { + .compatible = "mediatek,mt8188", + .sof_tplg_filename = "sof-mt8188.tplg", + }, + {} +}; + static const struct sof_dev_desc sof_of_mt8188_desc = { + .of_machines = sof_mt8188_machs, .ipc_supported_mask = BIT(SOF_IPC), .ipc_default = SOF_IPC, .default_fw_path = { @@ -641,7 +687,8 @@ static const struct sof_dev_desc sof_of_mt8188_desc = { [SOF_IPC] = "sof-mt8188.ri", }, .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg", - .ops = &sof_mt8186_ops, + .ops = &sof_mt8188_ops, + .ops_init = sof_mt8188_ops_init, }; static const struct of_device_id sof_of_mt8186_ids[] = { diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h index 5b521c60b4e3..91323f492a1e 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.h +++ b/sound/soc/sof/mediatek/mt8186/mt8186.h @@ -38,6 +38,11 @@ struct snd_sof_dev; #define DSP_MBOX3_IRQ_EN BIT(3) #define DSP_MBOX4_IRQ_EN BIT(4) #define DSP_PDEBUGPC 0x013C +#define DSP_PDEBUGDATA 0x0140 +#define DSP_PDEBUGINST 0x0144 +#define DSP_PDEBUGLS0STAT 0x0148 +#define DSP_PDEBUGSTATUS 0x014C +#define DSP_PFAULTINFO 0x0150 #define ADSP_CK_EN 0x1000 #define CORE_CLK_EN BIT(0) #define COREDBG_EN BIT(1) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c index 9ef08e43aa38..7cffcad00f9b 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c @@ -7,7 +7,6 @@ // Hardware interface for mt8195 DSP clock #include <linux/clk.h> -#include <linux/pm_runtime.h> #include <linux/io.h> #include "mt8195.h" #include "mt8195-clk.h" diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 42bae574c87a..7d6a568556ea 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -49,47 +49,13 @@ static int mt8195_send_msg(struct snd_sof_dev *sdev, return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); } -static void mt8195_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc) { struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - mt8195_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index b13bfdeb2b70..7c5bb9badb6c 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -32,7 +32,7 @@ static int sof_nocodec_bes_setup(struct device *dev, /* set up BE dai_links */ for (i = 0; i < link_num; i++) { - dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL); + dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; @@ -44,8 +44,8 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].stream_name = links[i].name; links[i].cpus = &dlc[0]; - links[i].codecs = &dlc[1]; - links[i].platforms = &dlc[2]; + links[i].codecs = &asoc_dummy_dlc; + links[i].platforms = &dlc[1]; links[i].num_cpus = 1; links[i].num_codecs = 1; @@ -55,8 +55,6 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].no_pcm = 1; links[i].cpus->dai_name = drv[i].name; links[i].platforms->name = dev_name(dev->parent); - links[i].codecs->dai_name = "snd-soc-dummy-dai"; - links[i].codecs->name = "snd-soc-dummy"; if (drv[i].playback.channels_min) links[i].dpcm_playback = 1; if (drv[i].capture.channels_min) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d0ab6f390734..d778717cab10 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -369,7 +369,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */ - if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free)) + if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free) snd_sof_pcm_platform_trigger(sdev, substream, cmd); break; default: diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 2b232442e84b..704b21413c71 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -234,20 +234,16 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) pm_state.event = target_state; - /* Skip to platform-specific suspend if DSP is entering D0 */ - if (target_state == SOF_DSP_PM_D0) { - sof_fw_trace_suspend(sdev, pm_state); - /* Notify clients not managed by pm framework about core suspend */ - sof_suspend_clients(sdev, pm_state); - goto suspend; - } - /* suspend DMA trace */ sof_fw_trace_suspend(sdev, pm_state); /* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); + /* Skip to platform-specific suspend if DSP is entering D0 */ + if (target_state == SOF_DSP_PM_D0) + goto suspend; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1cbda595c518..e7ef77012c35 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,6 +14,20 @@ #include "sof-of-dev.h" #include "ops.h" +static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + const char *func) +{ + switch (widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name); + return true; + default: + return false; + } +} + static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); @@ -231,23 +245,9 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc bool route_found = false; /* ignore routes involving virtual widgets in topology */ - switch (src_widget->id) { - case snd_soc_dapm_out_drv: - case snd_soc_dapm_output: - case snd_soc_dapm_input: + if (is_virtual_widget(sdev, src_widget->widget, __func__) || + is_virtual_widget(sdev, sink_widget->widget, __func__)) return 0; - default: - break; - } - - switch (sink_widget->id) { - case snd_soc_dapm_out_drv: - case snd_soc_dapm_output: - case snd_soc_dapm_input: - return 0; - default: - break; - } /* find route matching source and sink widgets */ list_for_each_entry(sroute, &sdev->route_list, list) @@ -396,6 +396,9 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; + if (is_virtual_widget(sdev, widget, __func__)) + return; + /* skip if the widget is in use or if it is already unprepared */ if (!swidget || !swidget->prepared || swidget->use_count > 0) goto sink_unprepare; @@ -433,6 +436,9 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget struct snd_soc_dapm_path *p; int ret; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; if (!widget_ops) return 0; @@ -488,6 +494,9 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int err; int ret = 0; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + if (widget->dobj.private) { err = sof_widget_free(sdev, widget->dobj.private); if (err < 0) @@ -527,6 +536,9 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d struct snd_soc_dapm_path *p; int ret; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + if (swidget) { int i; @@ -592,6 +604,9 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; for_each_dapm_widgets(list, i, widget) { + if (is_virtual_widget(sdev, widget, __func__)) + continue; + /* starting widget for playback is AIF type */ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) continue; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a090a9eb4828..5d5eeb1a1a6f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -362,6 +362,7 @@ struct snd_sof_control { size_t priv_size; /* size of private data */ size_t max_size; void *ipc_control_data; + void *old_ipc_control_data; int max; /* applicable to volume controls */ u32 size; /* cdata size */ u32 *volume_table; /* volume table computed from tlv data*/ diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c new file mode 100644 index 000000000000..ad0ed2d570a9 --- /dev/null +++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2023 Google Inc. All rights reserved. +// +// Author: Curtis Malainey <cujomalainey@chromium.org> +// + +#include <linux/auxiliary_bus.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <sound/sof/header.h> + +#include "sof-client.h" + +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 + +struct sof_msg_inject_priv { + struct dentry *kernel_dfs_file; + size_t max_msg_size; + + void *kernel_buffer; +}; + +static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file) +{ + int ret = debugfs_file_get(file->f_path.dentry); + + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + +static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + struct sof_ipc_cmd_hdr *hdr = priv->kernel_buffer; + struct device *dev = &cdev->auxdev.dev; + ssize_t size; + int ret; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(priv->kernel_buffer, priv->max_msg_size, + ppos, buffer, count); + if (size < 0) + return size; + if (size != count) + return -EFAULT; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + return ret; + } + + sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); + + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret); + + return count; +}; + +static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file) +{ + debugfs_file_put(file->f_path.dentry); + + return 0; +} + +static const struct file_operations sof_kernel_msg_inject_fops = { + .open = sof_msg_inject_dfs_open, + .write = sof_kernel_msg_inject_dfs_write, + .release = sof_msg_inject_dfs_release, + + .owner = THIS_MODULE, +}; + +static int sof_msg_inject_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct sof_msg_inject_priv *priv; + size_t alloc_size; + + /* allocate memory for client data */ + priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev); + alloc_size = priv->max_msg_size; + priv->kernel_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL); + + if (!priv->kernel_buffer) + return -ENOMEM; + + cdev->data = priv; + + priv->kernel_dfs_file = debugfs_create_file("kernel_ipc_msg_inject", 0644, + debugfs_root, cdev, + &sof_kernel_msg_inject_fops); + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_msg_inject_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_msg_inject_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + debugfs_remove(priv->kernel_dfs_file); +} + +static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = { + { .name = "snd_sof.kernel_injector" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_msg_inject_client_drv = { + .probe = sof_msg_inject_probe, + .remove = sof_msg_inject_remove, + + .id_table = sof_msg_inject_client_id_table, +}; + +module_auxiliary_driver(sof_msg_inject_client_drv); + +MODULE_DESCRIPTION("SOF IPC Kernel Injector Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index d6b7caa0cf03..284de96e779c 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -16,6 +16,7 @@ #include "ops.h" #include "sof-client.h" #include "sof-priv.h" +#include "ipc3-priv.h" #include "ipc4-priv.h" /** @@ -126,6 +127,29 @@ static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) +static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + /* Only IPC3 supported right now */ + if (sdev->pdata->ipc_type != SOF_IPC) + return 0; + + return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0); +} + +static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "kernel_injector", 0); +} +#else +static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ + int sof_register_clients(struct snd_sof_dev *sdev) { int ret; @@ -146,6 +170,12 @@ int sof_register_clients(struct snd_sof_dev *sdev) goto err_msg_injector; } + ret = sof_register_ipc_kernel_injector(sdev); + if (ret) { + dev_err(sdev->dev, "IPC kernel injector client registration failed\n"); + goto err_kernel_injector; + } + /* Platform depndent client device registration */ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) @@ -154,6 +184,9 @@ int sof_register_clients(struct snd_sof_dev *sdev) if (!ret) return 0; + sof_unregister_ipc_kernel_injector(sdev); + +err_kernel_injector: sof_unregister_ipc_msg_injector(sdev); err_msg_injector: @@ -167,6 +200,7 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) sof_ops(sdev)->unregister_ipc_clients(sdev); + sof_unregister_ipc_kernel_injector(sdev); sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); } @@ -269,6 +303,24 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, } EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); +int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf) +{ + if (cdev->sdev->pdata->ipc_type == SOF_IPC) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + if (hdr->size < sizeof(hdr)) { + dev_err(cdev->sdev->dev, "The received message size is invalid\n"); + return -EINVAL; + } + + sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf); + return 0; + } + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, SND_SOC_SOF_CLIENT); + int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, bool set) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 10571d1ea9a7..b6ccc2cd69e5 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -75,5 +75,6 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, sof_client_fw_state_callback callback); void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev); enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev); +int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf); #endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index cd4f6ac126ec..d4f6702e93dc 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -48,6 +48,9 @@ struct snd_sof_pcm_stream; #define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related * configurations */ +#define SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD BIT(11) /* On top of the IPC message header + * dump the message payload also + */ #define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */ /* Flag definitions used for controlling the DSP dump behavior */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f160dc454b44..698129dccc7d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1077,7 +1077,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || - strcmp(w->sname, rtd->dai_link->stream_name)) + !strstr(rtd->dai_link->stream_name, w->sname)) continue; for_each_rtd_cpu_dais(rtd, i, cpu_dai) { diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig new file mode 100644 index 000000000000..fafb681f8c0a --- /dev/null +++ b/sound/soc/starfive/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_SOC_STARFIVE + tristate "Audio support for StarFive SoC" + depends on COMPILE_TEST || ARCH_STARFIVE + help + Say Y or M if you want to add support for codecs attached to + the Starfive SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + +config SND_SOC_JH7110_TDM + tristate "JH7110 TDM device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for StarFive TDM driver. diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile new file mode 100644 index 000000000000..f7d960211d72 --- /dev/null +++ b/sound/soc/starfive/Makefile @@ -0,0 +1,2 @@ +# StarFive Platform Support +obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c new file mode 100644 index 000000000000..5f5a6ca7dbda --- /dev/null +++ b/sound/soc/starfive/jh7110_tdm.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * jh7110_tdm.c -- StarFive JH7110 TDM driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Walker Chen <walker.chen@starfivetech.com> + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/types.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#define TDM_PCMGBCR 0x00 + #define PCMGBCR_ENABLE BIT(0) + #define CLKPOL_BIT 5 + #define ELM_BIT 3 + #define SYNCM_BIT 2 + #define MS_BIT 1 +#define TDM_PCMTXCR 0x04 + #define PCMTXCR_TXEN BIT(0) + #define IFL_BIT 11 + #define WL_BIT 8 + #define SSCALE_BIT 4 + #define SL_BIT 2 + #define LRJ_BIT 1 +#define TDM_PCMRXCR 0x08 + #define PCMRXCR_RXEN BIT(0) +#define TDM_PCMDIV 0x0c + +#define JH7110_TDM_FIFO 0x170c0000 +#define JH7110_TDM_FIFO_DEPTH 32 + +enum TDM_MASTER_SLAVE_MODE { + TDM_AS_MASTER = 0, + TDM_AS_SLAVE, +}; + +enum TDM_CLKPOL { + /* tx raising and rx falling */ + TDM_TX_RASING_RX_FALLING = 0, + /* tx falling and rx raising */ + TDM_TX_FALLING_RX_RASING, +}; + +enum TDM_ELM { + /* only work while SYNCM=0 */ + TDM_ELM_LATE = 0, + TDM_ELM_EARLY, +}; + +enum TDM_SYNCM { + /* short frame sync */ + TDM_SYNCM_SHORT = 0, + /* long frame sync */ + TDM_SYNCM_LONG, +}; + +enum TDM_IFL { + /* FIFO to send or received : half-1/2, Quarter-1/4 */ + TDM_FIFO_HALF = 0, + TDM_FIFO_QUARTER, +}; + +enum TDM_WL { + /* send or received word length */ + TDM_8BIT_WORD_LEN = 0, + TDM_16BIT_WORD_LEN, + TDM_20BIT_WORD_LEN, + TDM_24BIT_WORD_LEN, + TDM_32BIT_WORD_LEN, +}; + +enum TDM_SL { + /* send or received slot length */ + TDM_8BIT_SLOT_LEN = 0, + TDM_16BIT_SLOT_LEN, + TDM_32BIT_SLOT_LEN, +}; + +enum TDM_LRJ { + /* left-justify or right-justify */ + TDM_RIGHT_JUSTIFY = 0, + TDM_LEFT_JUSTIFT, +}; + +struct tdm_chan_cfg { + enum TDM_IFL ifl; + enum TDM_WL wl; + unsigned char sscale; + enum TDM_SL sl; + enum TDM_LRJ lrj; + unsigned char enable; +}; + +struct jh7110_tdm_dev { + void __iomem *tdm_base; + struct device *dev; + struct clk_bulk_data clks[6]; + struct reset_control *resets; + + enum TDM_CLKPOL clkpolity; + enum TDM_ELM elm; + enum TDM_SYNCM syncm; + enum TDM_MASTER_SLAVE_MODE ms_mode; + + struct tdm_chan_cfg tx; + struct tdm_chan_cfg rx; + + u16 syncdiv; + u32 samplerate; + u32 pcmclk; + + /* data related to DMA transfers b/w tdm and DMAC */ + struct snd_dmaengine_dai_dma_data play_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; + u32 saved_pcmgbcr; + u32 saved_pcmtxcr; + u32 saved_pcmrxcr; + u32 saved_pcmdiv; +}; + +static inline u32 jh7110_tdm_readl(struct jh7110_tdm_dev *tdm, u16 reg) +{ + return readl_relaxed(tdm->tdm_base + reg); +} + +static inline void jh7110_tdm_writel(struct jh7110_tdm_dev *tdm, u16 reg, u32 val) +{ + writel_relaxed(val, tdm->tdm_base + reg); +} + +static void jh7110_tdm_save_context(struct jh7110_tdm_dev *tdm, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tdm->saved_pcmtxcr = jh7110_tdm_readl(tdm, TDM_PCMTXCR); + else + tdm->saved_pcmrxcr = jh7110_tdm_readl(tdm, TDM_PCMRXCR); +} + +static void jh7110_tdm_start(struct jh7110_tdm_dev *tdm, + struct snd_pcm_substream *substream) +{ + u32 data; + + data = jh7110_tdm_readl(tdm, TDM_PCMGBCR); + jh7110_tdm_writel(tdm, TDM_PCMGBCR, data | PCMGBCR_ENABLE); + + /* restore context */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + jh7110_tdm_writel(tdm, TDM_PCMTXCR, tdm->saved_pcmtxcr | PCMTXCR_TXEN); + else + jh7110_tdm_writel(tdm, TDM_PCMRXCR, tdm->saved_pcmrxcr | PCMRXCR_RXEN); +} + +static void jh7110_tdm_stop(struct jh7110_tdm_dev *tdm, + struct snd_pcm_substream *substream) +{ + unsigned int val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = jh7110_tdm_readl(tdm, TDM_PCMTXCR); + val &= ~PCMTXCR_TXEN; + jh7110_tdm_writel(tdm, TDM_PCMTXCR, val); + } else { + val = jh7110_tdm_readl(tdm, TDM_PCMRXCR); + val &= ~PCMRXCR_RXEN; + jh7110_tdm_writel(tdm, TDM_PCMRXCR, val); + } +} + +static int jh7110_tdm_syncdiv(struct jh7110_tdm_dev *tdm) +{ + u32 sl, sscale, syncdiv; + + if (tdm->rx.sl >= tdm->tx.sl) + sl = tdm->rx.sl; + else + sl = tdm->tx.sl; + + if (tdm->rx.sscale >= tdm->tx.sscale) + sscale = tdm->rx.sscale; + else + sscale = tdm->tx.sscale; + + syncdiv = tdm->pcmclk / tdm->samplerate - 1; + + if ((syncdiv + 1) < (sl * sscale)) { + dev_err(tdm->dev, "Failed to set syncdiv!\n"); + return -EINVAL; + } + + if (tdm->syncm == TDM_SYNCM_LONG && + (tdm->rx.sscale <= 1 || tdm->tx.sscale <= 1) && + ((syncdiv + 1) <= sl)) { + dev_err(tdm->dev, "Wrong syncdiv! It must be (syncdiv+1) > max[tx.sl, rx.sl]\n"); + return -EINVAL; + } + + jh7110_tdm_writel(tdm, TDM_PCMDIV, syncdiv); + return 0; +} + +static int jh7110_tdm_config(struct jh7110_tdm_dev *tdm, + struct snd_pcm_substream *substream) +{ + u32 datarx, datatx; + int ret; + + ret = jh7110_tdm_syncdiv(tdm); + if (ret) + return ret; + + datarx = (tdm->rx.ifl << IFL_BIT) | + (tdm->rx.wl << WL_BIT) | + (tdm->rx.sscale << SSCALE_BIT) | + (tdm->rx.sl << SL_BIT) | + (tdm->rx.lrj << LRJ_BIT); + + datatx = (tdm->tx.ifl << IFL_BIT) | + (tdm->tx.wl << WL_BIT) | + (tdm->tx.sscale << SSCALE_BIT) | + (tdm->tx.sl << SL_BIT) | + (tdm->tx.lrj << LRJ_BIT); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + jh7110_tdm_writel(tdm, TDM_PCMTXCR, datatx); + else + jh7110_tdm_writel(tdm, TDM_PCMRXCR, datarx); + + return 0; +} + +static void jh7110_tdm_clk_disable(struct jh7110_tdm_dev *tdm) +{ + clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks); +} + +static int jh7110_tdm_clk_enable(struct jh7110_tdm_dev *tdm) +{ + int ret; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(tdm->clks), tdm->clks); + if (ret) { + dev_err(tdm->dev, "Failed to enable tdm clocks\n"); + return ret; + } + + ret = reset_control_deassert(tdm->resets); + if (ret) { + dev_err(tdm->dev, "Failed to deassert tdm resets\n"); + goto dis_tdm_clk; + } + + /* select tdm_ext clock as the clock source for tdm */ + ret = clk_set_parent(tdm->clks[5].clk, tdm->clks[4].clk); + if (ret) { + dev_err(tdm->dev, "Can't set extern clock source for clk_tdm\n"); + goto dis_tdm_clk; + } + + return 0; + +dis_tdm_clk: + clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks); + + return ret; +} + +static int jh7110_tdm_runtime_suspend(struct device *dev) +{ + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); + + jh7110_tdm_clk_disable(tdm); + return 0; +} + +static int jh7110_tdm_runtime_resume(struct device *dev) +{ + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); + + return jh7110_tdm_clk_enable(tdm); +} + +static int jh7110_tdm_system_suspend(struct device *dev) +{ + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); + + /* save context */ + tdm->saved_pcmgbcr = jh7110_tdm_readl(tdm, TDM_PCMGBCR); + tdm->saved_pcmdiv = jh7110_tdm_readl(tdm, TDM_PCMDIV); + + return pm_runtime_force_suspend(dev); +} + +static int jh7110_tdm_system_resume(struct device *dev) +{ + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev); + + /* restore context */ + jh7110_tdm_writel(tdm, TDM_PCMGBCR, tdm->saved_pcmgbcr); + jh7110_tdm_writel(tdm, TDM_PCMDIV, tdm->saved_pcmdiv); + + return pm_runtime_force_resume(dev); +} + +static const struct snd_soc_component_driver jh7110_tdm_component = { + .name = "jh7110-tdm", +}; + +static int jh7110_tdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + + return 0; +} + +static int jh7110_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); + int chan_wl, chan_sl, chan_nr; + unsigned int data_width; + unsigned int dma_bus_width; + struct snd_dmaengine_dai_dma_data *dma_data = NULL; + int ret; + + data_width = params_width(params); + + tdm->samplerate = params_rate(params); + tdm->pcmclk = params_channels(params) * tdm->samplerate * data_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + chan_wl = TDM_16BIT_WORD_LEN; + chan_sl = TDM_16BIT_SLOT_LEN; + dma_bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + chan_wl = TDM_32BIT_WORD_LEN; + chan_sl = TDM_32BIT_SLOT_LEN; + dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + + default: + dev_err(tdm->dev, "tdm: unsupported PCM fmt"); + return -EINVAL; + } + + chan_nr = params_channels(params); + switch (chan_nr) { + case 1: + case 2: + case 4: + case 6: + case 8: + break; + default: + dev_err(tdm->dev, "channel not supported\n"); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tdm->tx.wl = chan_wl; + tdm->tx.sl = chan_sl; + tdm->tx.sscale = chan_nr; + tdm->play_dma_data.addr_width = dma_bus_width; + dma_data = &tdm->play_dma_data; + } else { + tdm->rx.wl = chan_wl; + tdm->rx.sl = chan_sl; + tdm->rx.sscale = chan_nr; + tdm->capture_dma_data.addr_width = dma_bus_width; + dma_data = &tdm->capture_dma_data; + } + + snd_soc_dai_set_dma_data(dai, substream, dma_data); + + ret = jh7110_tdm_config(tdm, substream); + if (ret) + return ret; + + jh7110_tdm_save_context(tdm, substream); + return 0; +} + +static int jh7110_tdm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + jh7110_tdm_start(tdm, substream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + jh7110_tdm_stop(tdm, substream); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int gbcr; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + /* cpu is master */ + tdm->ms_mode = TDM_AS_MASTER; + break; + case SND_SOC_DAIFMT_BC_FC: + /* codec is master */ + tdm->ms_mode = TDM_AS_SLAVE; + break; + case SND_SOC_DAIFMT_BC_FP: + case SND_SOC_DAIFMT_BP_FC: + return -EINVAL; + default: + dev_dbg(tdm->dev, "dwc : Invalid clock provider format\n"); + return -EINVAL; + } + + gbcr = (tdm->clkpolity << CLKPOL_BIT) | + (tdm->elm << ELM_BIT) | + (tdm->syncm << SYNCM_BIT) | + (tdm->ms_mode << MS_BIT); + jh7110_tdm_writel(tdm, TDM_PCMGBCR, gbcr); + + return 0; +} + +static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { + .startup = jh7110_tdm_startup, + .hw_params = jh7110_tdm_hw_params, + .trigger = jh7110_tdm_trigger, + .set_fmt = jh7110_tdm_set_dai_fmt, +}; + +static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) +{ + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &tdm->play_dma_data, &tdm->capture_dma_data); + snd_soc_dai_set_drvdata(dai, tdm); + return 0; +} + +#define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000 + +#define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver jh7110_tdm_dai = { + .name = "sf_tdm", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = JH7110_TDM_RATES, + .formats = JH7110_TDM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = JH7110_TDM_RATES, + .formats = JH7110_TDM_FORMATS, + }, + .ops = &jh7110_tdm_dai_ops, + .probe = jh7110_tdm_dai_probe, + .symmetric_rate = 1, +}; + +static const struct snd_pcm_hardware jh7110_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + .buffer_bytes_max = 192512, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 48, + .fifo_size = 16, +}; + +static const struct snd_dmaengine_pcm_config jh7110_dmaengine_pcm_config = { + .pcm_hardware = &jh7110_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .prealloc_buffer_size = 192512, +}; + +static void jh7110_tdm_init_params(struct jh7110_tdm_dev *tdm) +{ + tdm->clkpolity = TDM_TX_RASING_RX_FALLING; + tdm->elm = TDM_ELM_LATE; + tdm->syncm = TDM_SYNCM_SHORT; + + tdm->rx.ifl = TDM_FIFO_HALF; + tdm->tx.ifl = TDM_FIFO_HALF; + tdm->rx.wl = TDM_16BIT_WORD_LEN; + tdm->tx.wl = TDM_16BIT_WORD_LEN; + tdm->rx.sscale = 2; + tdm->tx.sscale = 2; + tdm->rx.lrj = TDM_LEFT_JUSTIFT; + tdm->tx.lrj = TDM_LEFT_JUSTIFT; + + tdm->play_dma_data.addr = JH7110_TDM_FIFO; + tdm->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + tdm->play_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2; + tdm->play_dma_data.maxburst = 16; + + tdm->capture_dma_data.addr = JH7110_TDM_FIFO; + tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + tdm->capture_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2; + tdm->capture_dma_data.maxburst = 8; +} + +static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, + struct jh7110_tdm_dev *tdm) +{ + int ret; + + tdm->clks[0].id = "mclk_inner"; + tdm->clks[1].id = "tdm_ahb"; + tdm->clks[2].id = "tdm_apb"; + tdm->clks[3].id = "tdm_internal"; + tdm->clks[4].id = "tdm_ext"; + tdm->clks[5].id = "tdm"; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); + if (ret) { + dev_err(&pdev->dev, "Failed to get tdm clocks\n"); + return ret; + } + + tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(tdm->resets)) { + dev_err(&pdev->dev, "Failed to get tdm resets\n"); + return PTR_ERR(tdm->resets); + } + + return 0; +} + +static int jh7110_tdm_probe(struct platform_device *pdev) +{ + struct jh7110_tdm_dev *tdm; + int ret; + + tdm = devm_kzalloc(&pdev->dev, sizeof(*tdm), GFP_KERNEL); + if (!tdm) + return -ENOMEM; + + tdm->tdm_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tdm->tdm_base)) + return PTR_ERR(tdm->tdm_base); + + tdm->dev = &pdev->dev; + + ret = jh7110_tdm_clk_reset_get(pdev, tdm); + if (ret) { + dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); + return ret; + } + + jh7110_tdm_init_params(tdm); + + dev_set_drvdata(&pdev->dev, tdm); + ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, + &jh7110_tdm_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register dai\n"); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &jh7110_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); + if (ret) { + dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); + return ret; + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_tdm_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int jh7110_tdm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id jh7110_tdm_of_match[] = { + { .compatible = "starfive,jh7110-tdm", }, + {} +}; + +MODULE_DEVICE_TABLE(of, jh7110_tdm_of_match); + +static const struct dev_pm_ops jh7110_tdm_pm_ops = { + RUNTIME_PM_OPS(jh7110_tdm_runtime_suspend, + jh7110_tdm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(jh7110_tdm_system_suspend, + jh7110_tdm_system_resume) +}; + +static struct platform_driver jh7110_tdm_driver = { + .driver = { + .name = "jh7110-tdm", + .of_match_table = jh7110_tdm_of_match, + .pm = pm_ptr(&jh7110_tdm_pm_ops), + }, + .probe = jh7110_tdm_probe, + .remove = jh7110_tdm_dev_remove, +}; +module_platform_driver(jh7110_tdm_driver); + +MODULE_DESCRIPTION("StarFive JH7110 TDM ASoC Driver"); +MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index f6695dee353b..271ec5b3378d 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1485,12 +1485,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, if (ret < 0) return ret; } else { - sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK"); - if (IS_ERR(sai->sai_mclk)) { - if (PTR_ERR(sai->sai_mclk) != -ENOENT) - return PTR_ERR(sai->sai_mclk); - sai->sai_mclk = NULL; - } + sai->sai_mclk = devm_clk_get_optional(&pdev->dev, "MCLK"); + if (IS_ERR(sai->sai_mclk)) + return PTR_ERR(sai->sai_mclk); } return 0; diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c index e016a6a7f7c4..208e2fcefcf2 100644 --- a/sound/soc/tegra/tegra186_asrc.c +++ b/sound/soc/tegra/tegra186_asrc.c @@ -1024,8 +1024,8 @@ static void tegra186_asrc_platform_remove(struct platform_device *pdev) static const struct dev_pm_ops tegra186_asrc_pm_ops = { SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend, tegra186_asrc_runtime_resume, NULL) - SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static struct platform_driver tegra186_asrc_driver = { diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index c498145e76e0..a4073a746ae3 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -19,7 +19,6 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/slab.h> diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index e1a0f50969c1..d38b58305c6b 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -273,13 +273,12 @@ static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, struct snd_soc_dai *dai = rule->private; struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev); struct clk *parent = clk_get_parent(i2s->clk_i2s); - long i, parent_rate, valid_rates = 0; + unsigned long i, parent_rate, valid_rates = 0; parent_rate = clk_get_rate(parent); - if (parent_rate <= 0) { - dev_err(dai->dev, "Can't get parent clock rate: %ld\n", - parent_rate); - return parent_rate ?: -EINVAL; + if (!parent_rate) { + dev_err(dai->dev, "Can't get parent clock rate\n"); + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 86bef54dfdf2..d034803695a0 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -187,13 +187,12 @@ static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); struct clk *parent = clk_get_parent(spdif->clk_spdif_out); static const unsigned int rates[] = { 32000, 44100, 48000 }; - long i, parent_rate, valid_rates = 0; + unsigned long i, parent_rate, valid_rates = 0; parent_rate = clk_get_rate(parent); - if (parent_rate <= 0) { - dev_err(dai->dev, "Can't get parent clock rate: %ld\n", - parent_rate); - return parent_rate ?: -EINVAL; + if (!parent_rate) { + dev_err(dai->dev, "Can't get parent clock rate\n"); + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(rates); i++) { diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 41117c1d61fb..bd0b10c70c4c 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -109,7 +109,7 @@ static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev) static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, unsigned int channels, - unsigned int format, + snd_pcm_format_t format, unsigned int reg) { struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 8c00c09eeefb..3f114a2adfce 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -712,11 +712,6 @@ MUX_ENUM_CTRL_DECL_234(t234_asrc15_tx, 0x68); MUX_ENUM_CTRL_DECL_234(t234_asrc16_tx, 0x69); MUX_ENUM_CTRL_DECL_234(t234_asrc17_tx, 0x6a); -/* - * The number of entries in, and order of, this array is closely tied to the - * calculation of tegra210_ahub_codec.num_dapm_widgets near the end of - * tegra210_ahub_probe() - */ static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = { WIDGETS("ADMAIF1", t210_admaif1_tx), WIDGETS("ADMAIF2", t210_admaif2_tx), @@ -1092,11 +1087,6 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { { name " XBAR-Capture", NULL, name " XBAR-TX" }, \ { name " Capture", NULL, name " XBAR-Capture" }, -/* - * The number of entries in, and order of, this array is closely tied to the - * calculation of tegra210_ahub_codec.num_dapm_routes near the end of - * tegra210_ahub_probe() - */ static const struct snd_soc_dapm_route tegra210_ahub_routes[] = { TEGRA_FE_ROUTES("ADMAIF1") TEGRA_FE_ROUTES("ADMAIF2") diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index c0892be2992b..172fea764a31 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1328,15 +1328,16 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params, struct davinci_mcasp_ruledata *rd = rule->private; struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; - int i, slot_width; + int slot_width; + snd_pcm_format_t i; snd_mask_none(&nfmt); slot_width = rd->mcasp->slot_width; - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(fmt, i)) { + pcm_for_each_format(i) { + if (snd_mask_test_format(fmt, i)) { if (snd_pcm_format_width(i) <= slot_width) { - snd_mask_set(&nfmt, i); + snd_mask_set_format(&nfmt, i); } } } @@ -1350,15 +1351,16 @@ static int davinci_mcasp_hw_rule_format_width(struct snd_pcm_hw_params *params, struct davinci_mcasp_ruledata *rd = rule->private; struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; - int i, format_width; + int format_width; + snd_pcm_format_t i; snd_mask_none(&nfmt); format_width = rd->mcasp->max_format_width; - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(fmt, i)) { + pcm_for_each_format(i) { + if (snd_mask_test_format(fmt, i)) { if (snd_pcm_format_width(i) == format_width) { - snd_mask_set(&nfmt, i); + snd_mask_set_format(&nfmt, i); } } } @@ -1431,12 +1433,13 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_mask nfmt; int rate = params_rate(params); int slots = rd->mcasp->tdm_slots; - int i, count = 0; + int count = 0; + snd_pcm_format_t i; snd_mask_none(&nfmt); - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(fmt, i)) { + pcm_for_each_format(i) { + if (snd_mask_test_format(fmt, i)) { uint sbits = snd_pcm_format_width(i); unsigned int sysclk_freq; int ppm; @@ -1454,7 +1457,7 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, sbits * slots * rate, false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { - snd_mask_set(&nfmt, i); + snd_mask_set_format(&nfmt, i); count++; } } diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index 96c3569d7643..a3663ab065ac 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -365,19 +365,17 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) if (!card->dai_link) return -ENOMEM; - compnent = devm_kzalloc(dev, 2 * sizeof(*compnent), GFP_KERNEL); + compnent = devm_kzalloc(dev, sizeof(*compnent), GFP_KERNEL); if (!compnent) return -ENOMEM; - card->dai_link->cpus = &compnent[0]; + card->dai_link->cpus = compnent; card->dai_link->num_cpus = 1; - card->dai_link->codecs = &compnent[1]; + card->dai_link->codecs = &asoc_dummy_dlc; card->dai_link->num_codecs = 1; card->dai_link->name = card->name; card->dai_link->stream_name = card->name; card->dai_link->cpus->dai_name = dev_name(ad->dssdev); - card->dai_link->codecs->name = "snd-soc-dummy"; - card->dai_link->codecs->dai_name = "snd-soc-dummy-dai"; card->num_links = 1; card->dev = dev; diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c index 8163f453bf36..b047add5d887 100644 --- a/sound/soc/ti/omap-mcbsp-st.c +++ b/sound/soc/ti/omap-mcbsp-st.c @@ -19,7 +19,6 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> -#include <linux/pm_runtime.h> #include "omap-mcbsp.h" #include "omap-mcbsp-priv.h" |